示例#1
0
void CDrawShapeView::OnFileSave()
{
    if (m_filename.IsEmpty())
    {
        CFileDialog dlg(FALSE, L".vg", NULL,
            OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST,
            L"Shape files (*.vg)|*.vg||", this);
        if (dlg.DoModal() != IDOK)
            return;
        m_filename = dlg.GetPathName();
    }

    MgJsonStorage s;

    shapes()->setZoomRectW(m_graph->xf.getWndRectW(), m_graph->xf.getViewScale());
    if (shapes()->save(s.storageForWrite()))
    {
        const char* content = s.stringify(true);
        CFile file;
        if (file.Open(m_filename, CFile::modeWrite | CFile::modeCreate)) {
            file.Write(content, strlen(content));
        }
    }
    else
    {
        AfxMessageBox(L"得到文件内容出错。");
    }
}
示例#2
0
void CDrawShapeView::OnInitialUpdate()
{
    if (!m_filename.IsEmpty()) {
        m_graph->xf.setModelTransform(shapes()->modelTransform());
        m_graph->xf.zoomTo(shapes()->getZoomRectW());
        m_graph->xf.zoomScale(shapes()->getViewScale());
        OnZoomed();
    }
}
示例#3
0
static cairo_test_status_t
draw (cairo_t *cr, int width, int height)
{
    cairo_set_source_rgb (cr, 1, 1, 1);
    cairo_paint (cr);

    cairo_translate (cr, 10, 10);

    /* simple clip */
    cairo_save (cr);
    cairo_rectangle (cr, 0, 0, 20, 20);
    cairo_clip (cr);
    shapes (cr);
    cairo_restore (cr);

    cairo_translate (cr, WIDTH, 0);

    /* unaligned clip */
    cairo_save (cr);
    cairo_rectangle (cr, 0.5, 0.5, 20, 20);
    cairo_clip (cr);
    shapes (cr);
    cairo_restore (cr);

    cairo_translate (cr, -WIDTH, HEIGHT);

    /* aligned-clip */
    cairo_save (cr);
    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
    cairo_rectangle (cr, 0, 0, 20, 20);
    cairo_rectangle (cr, 3, 3, 10, 10);
    cairo_rectangle (cr, 7, 7, 10, 10);
    cairo_clip (cr);
    shapes (cr);
    cairo_restore (cr);

    cairo_translate (cr, WIDTH, 0);

    /* force a clip-mask */
    cairo_save (cr);
    cairo_arc (cr, 10, 10, 10, 0, 2 * M_PI);
    cairo_new_sub_path (cr);
    cairo_arc_negative (cr, 10, 10, 5, 2 * M_PI, 0);
    cairo_new_sub_path (cr);
    cairo_arc (cr, 10, 10, 2, 0, 2 * M_PI);
    cairo_clip (cr);
    shapes (cr);
    cairo_restore (cr);

    return CAIRO_TEST_SUCCESS;
}
示例#4
0
    // Finds the index of the tab, which contains the given point.
    int IndexFromPoint(int x, int y, bool *inXbutton=nullptr) {
        Point point(x, y);
        Graphics graphics(hwnd);
        GraphicsPath shapes(data->Points, data->Types, data->Count);
        GraphicsPath shape;
        GraphicsPathIterator iterator(&shapes);
        iterator.NextMarker(&shape);

        ClientRect rClient(hwnd);
        REAL yPosTab = inTitlebar ? 0.0f : REAL(rClient.dy - height - 1);
        graphics.TranslateTransform(1.0f, yPosTab);
        for (int i = 0; i < Count(); i++) {
            Point pt(point);
            graphics.TransformPoints( CoordinateSpaceWorld, CoordinateSpaceDevice, &pt, 1);
            if (shape.IsVisible(pt, &graphics)) {
                iterator.NextMarker(&shape);
                if (inXbutton)
                    *inXbutton = shape.IsVisible(pt, &graphics) ? true : false;
                return i;
            }
            graphics.TranslateTransform(REAL(width + 1), 0.0f);
        }
        if (inXbutton)
            *inXbutton = false;
        return -1;
    }
示例#5
0
文件: main.cpp 项目: joonhwan/study
void test1()
{
	QAxObject excel( "Excel.Application", 0);
	excel.setProperty("Visible", true);

	QAxObjectPtr workbooks(excel.querySubObject("Workbooks"));
	QAxObjectPtr workbook(workbooks->querySubObject("Add()"));
	QAxObjectPtr sheets(workbook->querySubObject("Worksheets"));
	sheets->dynamicCall("Add()");
	QAxObjectPtr sheet(sheets->querySubObject( "Item( int )", 1 ));
	// sheet->setProperty("Name","Nova Planilha");

	QAxObjectPtr range(sheet->querySubObject("Cells(int,int)",1,1));
	range->setProperty("Value", QVariant(1234));

	QImage image("c:/Users/jhlee/Dropbox/orgwiki/img/class01.png");
	QClipboard* clip = QApplication::clipboard();
	clip->setImage(image);
	QAxObjectPtr shapes(sheet->querySubObject("Shapes"));
	sheet->dynamicCall("Paste()");
	int shapeCount = shapes->property("Count").toInt();
	QAxObjectPtr shape(shapes->querySubObject(QString::fromLatin1("Item(%1)").arg(shapeCount).toAscii()));

	// shapes->dynamicCall("AddPicture( QString&, bool, bool, double, double, double, double","c:\\Users\\jhlee\\Dropbox\\orgwiki\\img\\class01.png",true,true,100,100,70,70);

	excel.setProperty("DisplayAlerts", false);
	workbook->dynamicCall("SaveAs(QString&)", "c:\\temp\\testexcel.xlsx");
	// //workbook->dynamicCall("Close()");
	// //excel.dynamicCall("Quit()");
	// workbook->dynamicCall("Close (Boolean)", true);
	excel.dynamicCall("Quit()");
}
示例#6
0
void GiCoreView::setContext(const GiContext& ctx, int mask, int apply)
{
    DrawLocker locker(impl);

    if (mask != 0) {
        int n = impl->_cmds->getSelection(impl, 0, NULL, true);
        std::vector<MgShape*> shapes(n, (MgShape*)0);

        if (n > 0 && impl->_cmds->getSelection(impl, n, 
            (MgShape**)&shapes.front(), true) > 0)
        {
            for (int i = 0; i < n; i++) {
                if (shapes[i]) {
                    shapes[i]->context()->copy(ctx, mask);
                }
            }
            impl->redraw();
        }
        else {
            impl->context()->copy(ctx, mask);
        }
    }

    if (apply != 0) {
        impl->_cmds->dynamicChangeEnded(impl, apply > 0);
    }
}
示例#7
0
文件: SCPage.cpp 项目: KDE/koffice
SCPage::SCPage(SCDocument *document)
    : KoPAPage(document),
    d(new Private(this, document))
{
    setApplicationData(new SCPageApplicationData());
    placeholders().init(0, shapes());
}
示例#8
0
int MyShape::addShapeToList( shape_pointer insertShape )
{
    int curSize = shapes.size();
    shapes.resize(curSize + 1);
    shapes(curSize) = insertShape;
    return curSize;
}
示例#9
0
void KTextDocumentLayout::documentChanged(int position, int charsRemoved, int charsAdded)
{
    Q_UNUSED(charsRemoved);
    if (shapes().count() == 0) // nothing to do.
        return;

/*
    switch (document()->documentLayout()->property("KoTextRelayoutForPage").toInt()) {
    case KTextShapeData::NormalState:
        kDebug() << "KoTextRelayoutForPage in NormalState"; break;
    case KTextShapeData::LayoutCopyShape:
        kDebug() << "KoTextRelayoutForPage in LayoutCopyShape"; break;
    case KTextShapeData::LayoutOrig:
        kDebug() << "KoTextRelayoutForPage in LayoutOrig, skipping relayout"; break;
    }
*/
    if (document()->documentLayout()->property("KoTextRelayoutForPage").toInt() == KTextShapeData::LayoutOrig) {
        // don't refresh if we relayout after a relayout-for-page
        return;
    }

    int from = position;
    const int to = from + charsAdded;
    while (from < to) { // find blocks that have been added
        QTextBlock block = document()->findBlock(from);
        if (! block.isValid())
            break;
        if (from == block.position() && block.textList()) {
            KTextBlockData *data = dynamic_cast<KTextBlockData*>(block.userData());
            if (data)
                data->setCounterWidth(-1); // invalidate whole list.
        }

        from = block.position() + block.length();
    }

    foreach (KShape *shape, shapes()) {
        KTextShapeData *data = qobject_cast<KTextShapeData*>(shape->userData());
        Q_ASSERT(data);
        if (data && data->position() <= position && data->endPosition() >= position) {
            // found our (first) shape to re-layout
            data->foul();
            m_state->interrupted = true;
            scheduleLayout();
            return;
        }
    }
示例#10
0
void KoPAMasterPage::paintPage( QPainter & painter, KoZoomHandler & zoomHandler )
{
    paintBackground( painter, zoomHandler );

    KoShapePainter shapePainter;
    shapePainter.setShapes( shapes() );
    shapePainter.paint(painter, zoomHandler);
}
示例#11
0
文件: SCPage.cpp 项目: KDE/koffice
void SCPage::setLayout(SCPageLayout * layout, KoPADocument * document)
{
    QSizeF pageSize(pageLayout().width, pageLayout().height);
    SCMasterPage * master = dynamic_cast<SCMasterPage *>(masterPage());
    Q_ASSERT(master);
    placeholders().setLayout(layout, document, shapes(), pageSize, master ? master->placeholders().styles() : QMap<QString, KTextShapeData*>());
    kDebug(33001) << "master placeholders";
    master->placeholders().debug();
}
示例#12
0
bool createSimulationFilter( PxRigidActor* actor, const PxFilterData& filter )
{
    if ( !actor ) return false;
    std::vector<PxShape*> shapes( actor->getNbShapes() );

    PxU32 num = actor->getShapes( &(shapes[0]), actor->getNbShapes() );
    for ( PxU32 i=0; i<num; ++i )
        shapes[i]->setSimulationFilterData( filter );
    return true;
}
示例#13
0
void KoPAPage::paintPage( QPainter & painter, KoZoomHandler & zoomHandler )
{
    paintBackground( painter, zoomHandler );

    KoShapePainter shapePainter( getPaintingStrategy() );
    if ( displayMasterShapes() ) {
        shapePainter.setShapes( masterPage()->shapes() );
        shapePainter.paint(painter, zoomHandler);
    }
    shapePainter.setShapes( shapes() );
    shapePainter.paint(painter, zoomHandler);
}
示例#14
0
QRectF KoShapeLayer::boundingRect() const
{
    QRectF bb;

    Q_FOREACH (KoShape* shape, shapes()) {
        if (bb.isEmpty())
            bb = shape->boundingRect();
        else
            bb = bb.united(shape->boundingRect());
    }

    return bb;
}
示例#15
0
bool KoShapePaste::process(const KoXmlElement & body, KoOdfReadStore & odfStore)
{
    d->pastedShapes.clear();
    KoOdfLoadingContext loadingContext(odfStore.styles(), odfStore.store());
    KoShapeLoadingContext context(loadingContext, d->canvas->shapeController()->resourceManager());

    QList<KoShape*> shapes(d->layer ? d->layer->shapes(): d->canvas->shapeManager()->topLevelShapes());

    int zIndex = 0;
    if (!shapes.isEmpty()) {
        zIndex = shapes.first()->zIndex();
        foreach (KoShape * shape, shapes) {
            zIndex = qMax(zIndex, shape->zIndex());
        }
示例#16
0
void KoPAPageBase::saveOdfLayers(KoPASavingContext &paContext) const
{
    QList<KoShape*> shapes(this->shapes());
    qSort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
    foreach(KoShape* shape, shapes) {
        KoShapeLayer *layer = dynamic_cast<KoShapeLayer*>(shape);
        if (layer) {
            paContext.addLayerForSaving(layer);
        }
        else {
            Q_ASSERT(layer);
            kWarning(30010) << "Page contains non layer where a layer is expected";
        }
    }
示例#17
0
QSizeF KoShapeGroup::size() const
{
    Q_D(const KoShapeGroup);
    //debugFlake << "size" << d->size;
    if (!d->sizeCached) {
        QRectF bound;
        Q_FOREACH (KoShape *shape, shapes()) {
            if (bound.isEmpty())
                bound = shape->transformation().mapRect(shape->outlineRect());
            else
                bound |= shape->transformation().mapRect(shape->outlineRect());
        }
        d->size = bound.size();
        d->sizeCached = true;
        debugFlake << "recalculated size" << d->size;
    }
示例#18
0
    // Invalidates the tab's region in the client area.
    void Invalidate(int index) {
        if (index < 0) return;

        Graphics graphics(hwnd);
        GraphicsPath shapes(data->Points, data->Types, data->Count);
        GraphicsPath shape;
        GraphicsPathIterator iterator(&shapes);
        iterator.NextMarker(&shape);
        Region region(&shape);

        ClientRect rClient(hwnd);
        REAL yPosTab = inTitlebar ? 0.0f : REAL(rClient.dy - height - 1);
        graphics.TranslateTransform(REAL((width + 1) * index) + 1.0f, yPosTab);
        HRGN hRgn = region.GetHRGN(&graphics);
        InvalidateRgn(hwnd, hRgn, FALSE);
        DeleteObject(hRgn);
    }
示例#19
0
static void prepare(KoShape *s, QMap<KoShape*, QList<KoShape*> > &newOrder, KoShapeManager *manager, KoShapeReorderCommand::MoveShapeType move)
{
    KoShapeContainer *parent = s->parent();
    QMap<KoShape*, QList<KoShape*> >::iterator it(newOrder.find(parent));
    if (it == newOrder.end()) {
        QList<KoShape*> children;
        if (parent != 0) {
            children = parent->shapes();
        }
        else {
            // get all toplevel shapes
            children = manager->topLevelShapes();
        }
        qSort(children.begin(), children.end(), KoShape::compareShapeZIndex);
        // the append and prepend are needed so that the raise/lower of all shapes works as expected.
        children.append(0);
        children.prepend(0);
        it = newOrder.insert(parent, children);
    }
    QList<KoShape *> & shapes(newOrder[parent]);
    int index = shapes.indexOf(s);
    if (index != -1) {
        shapes.removeAt(index);
        switch (move) {
        case KoShapeReorderCommand::BringToFront:
            index = shapes.size();
            break;
        case KoShapeReorderCommand::RaiseShape:
            if (index < shapes.size()) {
                ++index;
            }
            break;
        case KoShapeReorderCommand::LowerShape:
            if (index > 0) {
                --index;
            }
            break;
        case KoShapeReorderCommand::SendToBack:
            index = 0;
            break;
        }
        shapes.insert(index,s);
    }
}
示例#20
0
QDomDocument FolderShape::save() const
{
    QDomDocument doc;
    QDomElement element = doc.createElement("book");
    doc.appendChild(element);
    foreach (KShape *child, shapes()) {
        IconShape *ic = dynamic_cast<IconShape*>(child);
        if (ic) {
            ic->save(element);
            continue;
        }
        ClipboardProxyShape *proxy = dynamic_cast<ClipboardProxyShape*>(child);
        if (proxy) {
            QDomElement clipboard = doc.createElement("clipboard");
            element.appendChild(clipboard);
            QDomText text = doc.createCDATASection( QString::fromAscii( proxy->clipboardData(), proxy->clipboardData().size() ) );
            clipboard.appendChild(text);
            continue;
        }
    }
示例#21
0
verdict test_randomized()
{
#if !defined(NO_RANDOMIZATION)
    srand (unsigned(get_time_stamp()/get_frequency())); // Randomize pseudo random number generator
#endif
    std::cout << "=================== Randomized Test ===================" << std::endl;

    size_t a1 = 0, a2 = 0;
    std::vector<long long> mediansV(K); // Final verdict of medians for each of the K experiments with visitors
    std::vector<long long> mediansM(K); // Final verdict of medians for each of the K experiments with matching
    std::vector<long long> timingsV(M);
    std::vector<long long> timingsM(M);
    std::vector<Shape*>    shapes(N);

    for (size_t k = 0; k < K; ++k)
    {
        for (size_t i = 0; i < N; ++i)
            shapes[i] = make_shape(rand());

        run_timings(shapes, timingsV, timingsM, a1, a2);
        mediansV[k] = display("AreaVisRnd", timingsV);
        mediansM[k] = display("AreaMatRnd", timingsM);
        std::cout << "\t\t" << verdict(mediansV[k], mediansM[k]) << std::endl;

        for (size_t i = 0; i < N; ++i)
        {
            delete shapes[i];
            shapes[i] = 0;
        }
    }

    if (a1 != a2)
    {
        std::cout << "ERROR: Invariant " << a1 << "==" << a2 << " doesn't hold." << std::endl;
        exit(42);
    }

    std::sort(mediansV.begin(), mediansV.end());
    std::sort(mediansM.begin(), mediansM.end());
    return verdict(mediansV[K/2],mediansM[K/2]);
}
示例#22
0
verdict test_repetitive()
{
    std::cout << "=================== Repetitive Test ===================" << std::endl;

    size_t a1 = 0, a2 = 0;
    std::vector<long long> mediansV(K); // Final verdict of medians for each of the K experiments with visitors
    std::vector<long long> mediansM(K); // Final verdict of medians for each of the K experiments with matching
    std::vector<long long> timingsV(M);
    std::vector<long long> timingsM(M);
    std::vector<Shape*>    shapes(N);

    for (size_t k = 0; k < K; ++k)
    {
        for (size_t i = 0; i < N; ++i)
            shapes[i] = make_shape((k+i)*2-k-2*i);

        run_timings(shapes, timingsV, timingsM, a1, a2);
        mediansV[k] = display("AreaVisRep", timingsV);
        mediansM[k] = display("AreaMatRep", timingsM);
        std::cout << "\t\t" << verdict(mediansV[k], mediansM[k]) << std::endl;

        for (size_t i = 0; i < N; ++i)
        {
            delete shapes[i];
            shapes[i] = 0;
        }
    }

    if (a1 != a2)
    {
        std::cout << "ERROR: Invariant " << a1 << "==" << a2 << " doesn't hold." << std::endl;
        exit(42);
    }

    std::sort(mediansV.begin(), mediansV.end());
    std::sort(mediansM.begin(), mediansM.end());
    return verdict(mediansV[K/2],mediansM[K/2]);
}
示例#23
0
int main(){
	int f[5][MAX/MIN_STEP] = {{0}};
	for(int s = 0; s < 5; ++s){
		for(int g = 1, n = 1; n <= MAX; g += s + 1, n += g){
			if(n < MIN){
				continue;
			}
			f[s][n/MIN_STEP] = n;
		}
	}
	char f3, f4, f5, f6, f1, boxcars, threes;
	int i1, i2;
	for(int g = 1, n = 1; n <= MAX; g += 6, n += g){
		if(n < MIN){
			continue;
		}
		i2 = n%100;
		for(int i3 = 10; i3 < 100; ++i3){
			f3 = shapes(i2*100 + i3, f);
			if(!f3){
				continue;
			}
			for(int i4 = 10; i4 < 100; ++i4){
				f4 = shapes(i3*100 + i4, f);
				if(!f4){
					continue;
				}
				for(int i5 = 10; i5 < 100; ++i5){
					f5 = shapes(i4*100 + i5, f);
					if(!f5){
						continue;
					}
					for(int i6 = 10; i6 < 100; ++i6){
						f6 = shapes(i5*100 + i6, f);
						if(!f6){
							continue;
						}
						i1 = n/100;
						f1 = shapes(i6*100 + i1, f);
						if(!f1){
							continue;
						}
						if((f3 | f4 | f5 | f6 | f1) != 037){
							continue;
						}
						boxcars = 0;
						if(f3 & 011){
							f3 ^= 011;
							++boxcars;
						}
						if(f4 & 011){
							f4 ^= 011;
							++boxcars;
						}
						if(f5 & 011){
							f5 ^= 011;
							++boxcars;
						}
						if(f6 & 011){
							f6 ^= 011;
							++boxcars;
						}
						if(f1 & 011){
							f1 ^= 011;
							++boxcars;
						}
						if(boxcars == 1){
							threes = 0;
							if(f3 & 01){
								++threes;
							}
							if(f4 & 01){
								++threes;
							}
							if(f5 & 01){
								++threes;
							}
							if(f6 & 01){
								++threes;
							}
							if(f1 & 01){
								++threes;
							}
							if(!threes){
								continue;
							}
						}
						printf("%i\n", 101*(i1 + i2 + i3 + i4 + i5 + i6));
						return 0;
					}
				}
			}
		}
	}
}
示例#24
0
Foam::pointIndexHit Foam::octree<Type>::findLine
(
    const point& treeStart,
    const point& treeEnd
) const
{
    // Initialize to a miss
    pointIndexHit hitInfo(false, treeStart, -1);

    const vector dir(treeEnd - treeStart);

    // Current line segment to search
    point start(treeStart);
    point end(treeEnd);

    while (true)
    {
        // Find nearest treeLeaf intersected by line
        point leafIntPoint;

        const treeLeaf<Type>* leafPtr = findLeafLine
        (
            start,
            end,
            leafIntPoint
        );

        if (!leafPtr)
        {
            // Reached end of string of treeLeaves to be searched. Return
            // whatever we have found so far.
            break;
        }

        // Inside treeLeaf find nearest intersection
        scalar minS = GREAT;

        const labelList& indices = leafPtr->indices();

        forAll(indices, elemI)
        {
            label index = indices[elemI];

            point pt;
            bool hit = shapes().intersects(index, start, end, pt);

            if (hit)
            {
                // Check whether intersection nearer p
                scalar s = (pt - treeStart) & dir;

                if (s < minS)
                {
                    minS = s;

                    // Update hit info
                    hitInfo.setHit();
                    hitInfo.setPoint(pt);
                    hitInfo.setIndex(index);

                    // Update segment to search
                    end = pt;
                }
            }
        }

        if (hitInfo.hit())
        {
            // Found intersected shape.
            break;
        }

        // Start from end of current leaf
        start = leafIntPoint;
    }
示例#25
0
bool simple_undo::modify (sxsdk::scene_interface *scene, void *) { //  \en This plugin multiplies geometry attributes with 0.5. This is not a good way to scale shape objects, but we do it this way to show how undo works. \enden  \ja このプラグインは、それぞれの形状の幾何属性に0.5を掛ける。形状を縮小する方法としては適切ではないが、アンドゥの機能をわかりやすくするために、このようにして幾何属性を変更する。 \endja 
	scene->reset_undo_obsolete(); //  \en Since we may register more than one undo action, call reset_undo() before registering any undo actions. \enden  \ja ここでは一個以上のアンドゥアクションを登録するので、登録するまえにreset_undo()を呼ぶ。 \endja 
	const int n = scene->get_active_shapes(0);
	if (0 < n) {
		std::vector<sxsdk::shape_class *> shapes(n);
		scene->get_active_shapes(&(shapes[0]));
		for (int i = 0; i < n; ++i) {
			sxsdk::shape_class &shape = *(shapes[i]);
			compointer<sxsdk::shape_saver_interface> shape_saver(shape.create_shape_saver_interface()); //  \en Create a sxsdk::shape_saver_interface object. This object holds original geometry attributes. \enden  \ja sxsdk::shape_saver_interfaceオブジェクトを生成する。このオブジェクトが元の形状の幾何属性を保持する。 \endja 

			//  \en Multilie geometry attributes with 0.5. \enden  \ja 幾何属性に0.5を掛ける \endja 
			switch (shape.get_type()) {
			case sxsdk::enums::polygon_mesh:
				{   sxsdk::polygon_mesh_class &p = sxsdk::polygon_mesh_class::cast(shape);
					const int n = p.get_number_of_points();
					for (int i = 0; i < n; ++i) {
						sxsdk::vertex_class &vertex = p.vertex(i);
						vertex.set_position(vertex.get_position() * 0.5f);
					}
				}
				break;
			case sxsdk::enums::line:
				{   sxsdk::line_class &p = sxsdk::line_class::cast(shape);
					const int n = p.get_number_of_points();
					for (int i = 0; i < n; ++i) {
						sxsdk::control_point_class &control_point = p.control_point(i);
						control_point.set_position(control_point.get_position() * 0.5f);
					}
				}
				break;
			case sxsdk::enums::part:
				{   sxsdk::part_class &p = sxsdk::part_class::cast(shape);
					for (sxsdk::shape_class::iterator s = p.begin(); s != p.end(); ++s) {
						sxsdk::line_class &line = sxsdk::line_class::cast(*s);
						const int n = line.get_number_of_control_points();
						for (int i = 0; i < n; ++i) {
							sxsdk::control_point_class &control_point = line.control_point(i);
							control_point.set_position(control_point.get_position() * 0.5f);
						}
					}
				}
				break;
			case sxsdk::enums::light:
				{   sxsdk::light_class &p = sxsdk::light_class::cast(shape);
					p.set_intensity(p.get_intensity() * 0.5f);
				}
				break;
			case sxsdk::enums::sphere:
				{   sxsdk::sphere_class &p = sxsdk::sphere_class::cast(shape);
					p.set_radius(p.get_radius() * 0.5f);
				}
				break;
			case sxsdk::enums::disk:
				{   sxsdk::disk_class &p = sxsdk::disk_class::cast(shape);
					p.set_radius(p.get_radius() * 0.5f);
				}
				break;
			}

			shape_saver->set_undo_action(); //  \en Register an undo action. \enden  \ja アンドゥアクションを登録する。 \endja 
		}
	}
	return true;
}
示例#26
0
    // Paints the tabs that intersect the window's update rectangle.
    void Paint(HDC hdc, RECT &rc) {
        IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);

        // paint the background
        bool isTranslucentMode = inTitlebar && dwm::IsCompositionEnabled();
        if (isTranslucentMode)
            PaintParentBackground(hwnd, hdc);
        else {
            HBRUSH brush = CreateSolidBrush(color.bar);
            FillRect(hdc, &rc, brush);
            DeleteObject(brush);
        }

        // TODO: GDI+ doesn't seem to cope well with SetWorldTransform
        XFORM ctm = { 1.0, 0, 0, 1.0, 0, 0 };
        SetWorldTransform(hdc, &ctm);

        Graphics graphics(hdc);
        graphics.SetCompositingMode(CompositingModeSourceCopy);
        graphics.SetCompositingQuality(CompositingQualityHighQuality);
        graphics.SetSmoothingMode(SmoothingModeHighQuality);
        graphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
        graphics.SetPageUnit(UnitPixel);
        GraphicsPath shapes(data->Points, data->Types, data->Count);
        GraphicsPath shape;
        GraphicsPathIterator iterator(&shapes);

        SolidBrush br(Color(0, 0, 0));
        Pen pen(&br, 2.0f);

        Font f(hdc, GetDefaultGuiFont());
        // TODO: adjust these constant values for DPI?
        RectF layout((REAL)DpiScaleX(hwnd,3), 1.0f, REAL(width - DpiScaleX(hwnd,20)), (REAL)height);
        StringFormat sf(StringFormat::GenericDefault());
        sf.SetFormatFlags(StringFormatFlagsNoWrap);
        sf.SetLineAlignment(StringAlignmentCenter);
        sf.SetTrimming(StringTrimmingEllipsisCharacter);

        REAL yPosTab = inTitlebar ? 0.0f : REAL(ClientRect(hwnd).dy - height - 1);
        for (int i = 0; i < Count(); i++) {
            graphics.ResetTransform();
            graphics.TranslateTransform(1.f + (REAL)(width + 1) * i - (REAL)rc.left, yPosTab - (REAL)rc.top);

            if (!graphics.IsVisible(0, 0, width + 1, height + 1))
                continue;

            // in firefox style we only paint current and highlighed tabs
            // all other tabs only show
            bool onlyText = g_FirefoxStyle && !((current == i) || (highlighted == i));
            if (onlyText) {
#if 0
                // we need to first paint the background with the same color as caption,
                // otherwise the text looks funny (because is transparent?)
                // TODO: what is the damn bg color of caption? bar is too light, outline is too dark
                Color bgColTmp;
                bgColTmp.SetFromCOLORREF(color.bar);
                {
                    SolidBrush bgBr(bgColTmp);
                    graphics.FillRectangle(&bgBr, layout);
                }
                bgColTmp.SetFromCOLORREF(color.outline);
                {
                    SolidBrush bgBr(bgColTmp);
                    graphics.FillRectangle(&bgBr, layout);
                }
#endif
                // TODO: this is a hack. If I use no background and cleartype, the
                // text looks funny (is bold).
                // CompositingModeSourceCopy doesn't work with clear type
                // another option is to draw background before drawing text, but
                // I can't figure out what is the actual color of caption
                graphics.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
                graphics.SetCompositingMode(CompositingModeSourceCopy);
                //graphics.SetCompositingMode(CompositingModeSourceOver);
                br.SetColor(ToColor(color.text));
                graphics.DrawString(text.At(i), -1, &f, layout, &sf, &br);
                graphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
                continue;
            }

            COLORREF bgCol = color.background;;
            if (current == i) {
                bgCol = color.current;
            } else if (highlighted == i) {
                bgCol = color.highlight;
            }

            // ensure contrast between text and background color
            // TODO: adjust threshold (and try adjusting both current/background tabs)
            COLORREF textCol = color.text;
            float bgLight = GetLightness(bgCol), textLight = GetLightness(textCol);
            if (textLight < bgLight ? bgLight < 0x70 : bgLight > 0x90)
                textCol = textLight ? AdjustLightness(textCol, 255.0f / textLight - 1.0f) : RGB(255, 255, 255);
            if (fabs(textLight - bgLight) < 0x40)
                textCol = bgLight < 0x80 ? RGB(255, 255, 255) : RGB(0, 0, 0);

            // paint tab's body
            graphics.SetCompositingMode(CompositingModeSourceCopy);
            iterator.NextMarker(&shape);
            br.SetColor(ToColor(bgCol));
            graphics.FillPath(&br, &shape);

            // draw tab's text
            graphics.SetCompositingMode(CompositingModeSourceOver);
            br.SetColor(ToColor(textCol));
            graphics.DrawString(text.At(i), -1, &f, layout, &sf, &br);

            // paint "x"'s circle
            iterator.NextMarker(&shape);
            if (xClicked == i || xHighlighted == i) {
                br.SetColor(ToColor(i == xClicked ? color.x_click : color.x_highlight));
                graphics.FillPath(&br, &shape);
            }

            // paint "x"
            iterator.NextMarker(&shape);
            if (xClicked == i || xHighlighted == i)
                pen.SetColor(ToColor(color.x_line));
            else
                pen.SetColor(ToColor(color.outline));
            graphics.DrawPath(&pen, &shape);
            iterator.Rewind();
        }
    }
示例#27
0
hkpRigidBody* MeshWeldingDemo::loadAndInitSimpleMesh(const hkVector4& position, hkpWorld* world, hkPackfileReader::AllocatedData*& allocatedData)
{
	hkpRigidBody* mesh = loadRigidBodyFromXmlFile("Resources/Physics/Landscapes/weldtestmesh.xml" , allocatedData);

	mesh->setPosition(position);

	//HK_SET_OBJECT_COLOR((hkUlong)mesh->getCollidable(), hkColor::rgbFromChars(255, 255, 255, 128)); // RGBA

	HK_ASSERT(0xaf639541, mesh->getCollidable()->getShape()->getType() == HK_SHAPE_MOPP);
	const hkpMoppBvTreeShape* moppShape = static_cast<const hkpMoppBvTreeShape*>(mesh->getCollidable()->getShape());

	hkpSimpleMeshShape* meshShape = (hkpSimpleMeshShape*)moppShape->getShapeCollection();

	HK_ASSERT(0xaf639541, moppShape->getShapeCollection()->getType() == HK_SHAPE_TRIANGLE_COLLECTION);


	// Replace the mesh shape with an extendedMeshShape
	//
	hkpExtendedMeshShape* extShape = new hkpExtendedMeshShape();

	hkpExtendedMeshShape::TrianglesSubpart part;
	part.m_numTriangleShapes = meshShape->m_triangles.getSize();
	part.m_vertexBase = &meshShape->m_vertices[0](0);
	part.m_vertexStriding = sizeof(hkVector4);
	part.m_numVertices = meshShape->m_vertices.getSize();
	part.m_extrusion.setZero4(); 
	part.m_indexBase = meshShape->m_triangles.begin();
	part.m_indexStriding = sizeof(meshShape->m_triangles[0]);
	part.m_stridingType = hkpExtendedMeshShape::INDICES_INT32;

	extShape->addTrianglesSubpart(part);
	
	hkpMoppCompilerInput mci;
	mci.m_enableChunkSubdivision = true;
	hkpMoppCode* newCode = hkpMoppUtility::buildCode( extShape,mci);
	hkpMoppBvTreeShape* newMopp = new hkpMoppBvTreeShape( extShape, newCode );

	mesh->setShape(newMopp);

	extShape->removeReference();
	newCode->removeReference();
	newMopp->removeReference();

	//
	// Setup welding for this object.
	//


	{
		extShape->setRadius(0);

		const WeldingVariant& variant =  g_variants[m_variantId];
		switch (variant.m_weldingType )
		{
			case ONE_SIDED:
			{
				hkpMeshWeldingUtility::ShapeInfo info;
				info.m_transform = hkTransform::getIdentity();
				info.m_shape = newMopp;

				hkLocalArray< hkpMeshWeldingUtility::ShapeInfo > shapes( 1 );
				shapes.pushBack( info );

				extShape->m_weldingType = hkpWeldingUtility::WELDING_TYPE_CLOCKWISE;
				hkpMeshWeldingUtility::computeWeldingInfo( hkTransform::getIdentity(), extShape, shapes, true, hkpMeshWeldingUtility::WINDING_IGNORE_CONSISTENCY );

				break;
			}
			case TWO_SIDED:
			case TWO_SIDED_NO_TOIS:
			{
				extShape->computeWeldingInfo( newMopp, hkpWeldingUtility::WELDING_TYPE_TWO_SIDED );
				break;
			}
			case NONE:
			{
				extShape->m_disableWelding = true;
				break;
			}
			case OLD_STYLE:
			{
				break;
			}
		}
	}

	world->addEntity(mesh);

	return mesh;
}
    void IntersectorShortStack::Process(World const& world)
    {

        // If something has been changed we need to rebuild BVH
        if (!m_bvh || world.has_changed() || world.GetStateChange() != ShapeImpl::kStateChangeNone)
        {
            if (m_bvh)
            {
                m_device->DeleteBuffer(m_gpudata->bvh);
                m_device->DeleteBuffer(m_gpudata->vertices);
            }

            // Check if we can allocate enough stack memory
            Calc::DeviceSpec spec;
            m_device->GetSpec(spec);
            if (spec.max_alloc_size <= kMaxBatchSize * kMaxStackSize * sizeof(int))
            {
                throw ExceptionImpl("fatbvh accelerator can't allocate enough stack memory, try using bvh instead");
            }

            int numshapes = (int)world.shapes_.size();
            int numvertices = 0;
            int numfaces = 0;

            // This buffer tracks mesh start index for next stage as mesh face indices are relative to 0
            std::vector<int> mesh_vertices_start_idx(numshapes);
            std::vector<int> mesh_faces_start_idx(numshapes);

            auto builder = world.options_.GetOption("bvh.builder");
            auto splits = world.options_.GetOption("bvh.sah.use_splits");
            auto maxdepth = world.options_.GetOption("bvh.sah.max_split_depth");
            auto overlap = world.options_.GetOption("bvh.sah.min_overlap");
            auto tcost = world.options_.GetOption("bvh.sah.traversal_cost");
            auto node_budget = world.options_.GetOption("bvh.sah.extra_node_budget");
            auto nbins = world.options_.GetOption("bvh.sah.num_bins");

            bool use_sah = false;
            bool use_splits = false;
            int max_split_depth = maxdepth ? (int)maxdepth->AsFloat() : 10;
            int num_bins = nbins ? (int)nbins->AsFloat() : 64;
            float min_overlap = overlap ? overlap->AsFloat() : 0.05f;
            float traversal_cost = tcost ? tcost->AsFloat() : 10.f;
            float extra_node_budget = node_budget ? node_budget->AsFloat() : 0.5f;

            if (builder && builder->AsString() == "sah")
            {
                use_sah = true;
            }

            if (splits && splits->AsFloat() > 0.f)
            {
                use_splits = true;
            }

            m_bvh.reset(use_splits ?
                new SplitBvh(traversal_cost, num_bins, max_split_depth, min_overlap, extra_node_budget) :
                new Bvh(traversal_cost, num_bins, use_sah)
            );

            // Partition the array into meshes and instances
            std::vector<Shape const*> shapes(world.shapes_);

            auto firstinst = std::partition(shapes.begin(), shapes.end(),
                [&](Shape const* shape)
            {
                return !static_cast<ShapeImpl const*>(shape)->is_instance();
            });

            // Count the number of meshes
            int nummeshes = (int)std::distance(shapes.begin(), firstinst);
            // Count the number of instances
            int numinstances = (int)std::distance(firstinst, shapes.end());

            for (int i = 0; i < nummeshes; ++i)
            {
                Mesh const* mesh = static_cast<Mesh const*>(shapes[i]);

                mesh_faces_start_idx[i] = numfaces;
                mesh_vertices_start_idx[i] = numvertices;

                numfaces += mesh->num_faces();
                numvertices += mesh->num_vertices();
            }

            for (int i = nummeshes; i < nummeshes + numinstances; ++i)
            {
                Instance const* instance = static_cast<Instance const*>(shapes[i]);
                Mesh const* mesh = static_cast<Mesh const*>(instance->GetBaseShape());

                mesh_faces_start_idx[i] = numfaces;
                mesh_vertices_start_idx[i] = numvertices;

                numfaces += mesh->num_faces();
                numvertices += mesh->num_vertices();
            }


            // We can't avoild allocating it here, since bounds aren't stored anywhere
            std::vector<bbox> bounds(numfaces);

            // We handle meshes first collecting their world space bounds
#pragma omp parallel for
            for (int i = 0; i < nummeshes; ++i)
            {
                Mesh const* mesh = static_cast<Mesh const*>(shapes[i]);

                for (int j = 0; j < mesh->num_faces(); ++j)
                {
                    // Here we directly get world space bounds
                    mesh->GetFaceBounds(j, false, bounds[mesh_faces_start_idx[i] + j]);
                }
            }

            // Then we handle instances. Need to flatten them into actual geometry.
#pragma omp parallel for
            for (int i = nummeshes; i < nummeshes + numinstances; ++i)
            {
                Instance const* instance = static_cast<Instance const*>(shapes[i]);
                Mesh const* mesh = static_cast<Mesh const*>(instance->GetBaseShape());

                // Instance is using its own transform for base shape geometry
                // so we need to get object space bounds and transform them manually
                matrix m, minv;
                instance->GetTransform(m, minv);

                for (int j = 0; j < mesh->num_faces(); ++j)
                {
                    bbox tmp;
                    mesh->GetFaceBounds(j, true, tmp);
                    bounds[mesh_faces_start_idx[i] + j] = transform_bbox(tmp, m);
                }
            }

            m_bvh->Build(&bounds[0], numfaces);

#ifdef RR_PROFILE
            m_bvh->PrintStatistics(std::cout);
#endif

            // Check if the tree height is reasonable
            if (m_bvh->GetHeight() >= kMaxStackSize)
            {
                m_bvh.reset(nullptr);
                throw ExceptionImpl("fatbvh accelerator can cause stack overflow for this scene, try using bvh instead");
            }

            FatNodeBvhTranslator translator;
            translator.Process(*m_bvh);

            // Update GPU data

            // Create vertex buffer
            {
                // Vertices
                m_gpudata->vertices = m_device->CreateBuffer(numvertices * sizeof(float3), Calc::BufferType::kRead);

                // Get the pointer to mapped data
                float3* vertexdata = nullptr;
                Calc::Event* e = nullptr;

                m_device->MapBuffer(m_gpudata->vertices, 0, 0, numvertices * sizeof(float3), Calc::MapType::kMapWrite, (void**)&vertexdata, &e);

                e->Wait();
                m_device->DeleteEvent(e);

                // Here we need to put data in world space rather than object space
                // So we need to get the transform from the mesh and multiply each vertex
                matrix m, minv;

#pragma omp parallel for
                for (int i = 0; i < nummeshes; ++i)
                {
                    // Get the mesh
                    Mesh const* mesh = static_cast<Mesh const*>(shapes[i]);
                    // Get vertex buffer of the current mesh
                    float3 const* myvertexdata = mesh->GetVertexData();
                    // Get mesh transform
                    mesh->GetTransform(m, minv);

                    //#pragma omp parallel for
                    // Iterate thru vertices multiply and append them to GPU buffer
                    for (int j = 0; j < mesh->num_vertices(); ++j)
                    {
                        vertexdata[mesh_vertices_start_idx[i] + j] = transform_point(myvertexdata[j], m);
                    }
                }

#pragma omp parallel for
                for (int i = nummeshes; i < nummeshes + numinstances; ++i)
                {
                    Instance const* instance = static_cast<Instance const*>(shapes[i]);
                    // Get the mesh
                    Mesh const* mesh = static_cast<Mesh const*>(instance->GetBaseShape());
                    // Get vertex buffer of the current mesh
                    float3 const* myvertexdata = mesh->GetVertexData();
                    // Get mesh transform
                    instance->GetTransform(m, minv);

                    //#pragma omp parallel for
                    // Iterate thru vertices multiply and append them to GPU buffer
                    for (int j = 0; j < mesh->num_vertices(); ++j)
                    {
                        vertexdata[mesh_vertices_start_idx[i] + j] = transform_point(myvertexdata[j], m);
                    }
                }

                m_device->UnmapBuffer(m_gpudata->vertices, 0, vertexdata, &e);
                e->Wait();
                m_device->DeleteEvent(e);
            }

            // Create face buffer
            {
                
                // This number is different from the number of faces for some BVHs 
                auto numindices = m_bvh->GetNumIndices();
                std::vector<FatNodeBvhTranslator::Face> facedata(numindices);

                // Here the point is to add mesh starting index to actual index contained within the mesh,
                // getting absolute index in the buffer.
                // Besides that we need to permute the faces accorningly to BVH reordering, whihc
                // is contained within bvh.primids_
                int const* reordering = m_bvh->GetIndices();
                for (int i = 0; i < numindices; ++i)
                {
                    int indextolook4 = reordering[i];

                    // We need to find a shape corresponding to current face
                    auto iter = std::upper_bound(mesh_faces_start_idx.cbegin(), mesh_faces_start_idx.cend(), indextolook4);

                    // Find the index of the shape
                    int shapeidx = static_cast<int>(std::distance(mesh_faces_start_idx.cbegin(), iter) - 1);

                    // Get the mesh directly or out of instance
                    Mesh const* mesh = nullptr;
                    if (shapeidx < nummeshes)
                    {
                        mesh = static_cast<Mesh const*>(shapes[shapeidx]);
                    }
                    else
                    {
                        mesh = static_cast<Mesh const*>(static_cast<Instance const*>(shapes[shapeidx])->GetBaseShape());
                    }

                    // Get vertex buffer of the current mesh
                    Mesh::Face const* myfacedata = mesh->GetFaceData();
                    // Find face idx
                    int faceidx = indextolook4 - mesh_faces_start_idx[shapeidx];
                    // Find mesh start idx
                    int mystartidx = mesh_vertices_start_idx[shapeidx];

                    // Copy face data to GPU buffer
                    facedata[i].idx[0] = myfacedata[faceidx].idx[0] + mystartidx;
                    facedata[i].idx[1] = myfacedata[faceidx].idx[1] + mystartidx;
                    facedata[i].idx[2] = myfacedata[faceidx].idx[2] + mystartidx;

                    facedata[i].shapeidx = shapes[shapeidx]->GetId();
                    facedata[i].shape_mask = shapes[shapeidx]->GetMask();
                    facedata[i].id = faceidx;
                }

                translator.InjectIndices(&facedata[0]);
            }

            // Copy translated nodes first
            m_gpudata->bvh = m_device->CreateBuffer(translator.nodes_.size() * sizeof(FatNodeBvhTranslator::Node), Calc::BufferType::kRead, &translator.nodes_[0]);

            // Stack
            m_gpudata->stack = m_device->CreateBuffer(kMaxBatchSize*kMaxStackSize, Calc::BufferType::kWrite);

            // Make sure everything is commited
            m_device->Finish(0);
        }
    }
示例#29
0
bool FontContext::layoutText(TextStyle::Parameters& _params, const icu::UnicodeString& _text,
                             std::vector<GlyphQuad>& _quads, std::bitset<max_textures>& _refs,
                             glm::vec2& _size, TextRange& _textRanges) {

    std::lock_guard<std::mutex> lock(m_fontMutex);

    alfons::LineLayout line = m_shaper.shapeICU(_params.font, _text, MIN_LINE_WIDTH,
                                                _params.wordWrap ? _params.maxLineWidth : 0);

    if (line.missingGlyphs() || line.shapes().size() == 0) {
        // Nothing to do!
        return false;
    }

    line.setScale(_params.fontScale);

    // m_batch.drawShapeRange() calls FontContext's TextureCallback for new glyphs
    // and MeshCallback (drawGlyph) for vertex quads of each glyph in LineLayout.

    m_scratch.quads = &_quads;

    size_t quadsStart = _quads.size();
    alfons::LineMetrics metrics;

    std::array<bool, 3> alignments = {};
    if (_params.align != TextLabelProperty::Align::none) {
        alignments[int(_params.align)] = true;
    }

    // Collect possible alignment from anchor fallbacks
    for (int i = 0; i < _params.labelOptions.anchors.count; i++) {
        auto anchor = _params.labelOptions.anchors[i];
        TextLabelProperty::Align alignment = TextLabelProperty::alignFromAnchor(anchor);
        if (alignment != TextLabelProperty::Align::none) {
            alignments[int(alignment)] = true;
        }
    }

    if (_params.wordWrap) {
        m_textWrapper.clearWraps();

        if (_params.maxLines != 0) {
            uint32_t numLines = 0;
            int pos = 0;
            int max = line.shapes().size();

            for (auto& shape : line.shapes()) {
                pos++;
                if (shape.mustBreak) {
                    numLines++;
                    if (numLines >= _params.maxLines && pos < max) {
                        shape.mustBreak = false;
                        line.removeShapes(shape.isSpace ? pos-1 : pos, max);

                        auto ellipsis = m_shaper.shape(_params.font, "…");
                        line.addShapes(ellipsis.shapes());
                        break;
                    }
                }
            }
        }

        float width = m_textWrapper.getShapeRangeWidth(line);

        for (size_t i = 0; i < 3; i++) {

            int rangeStart = m_scratch.quads->size();
            if (!alignments[i]) {
                _textRanges[i] = Range(rangeStart, 0);
                continue;
            }
            int numLines = m_textWrapper.draw(m_batch, width, line, TextLabelProperty::Align(i),
                                              _params.lineSpacing, metrics);
            int rangeEnd = m_scratch.quads->size();

            _textRanges[i] = Range(rangeStart, rangeEnd - rangeStart);

            // For single line text alignments are the same
            if (i == 0 && numLines == 1) {
                _textRanges[1] = Range(rangeEnd, 0);
                _textRanges[2] = Range(rangeEnd, 0);
                break;
            }
        }
    } else {
        glm::vec2 position(0);
        int rangeStart = m_scratch.quads->size();
        m_batch.drawShapeRange(line, 0, line.shapes().size(), position, metrics);
        int rangeEnd = m_scratch.quads->size();

        _textRanges[0] = Range(rangeStart, rangeEnd - rangeStart);

        _textRanges[1] = Range(rangeEnd, 0);
        _textRanges[2] = Range(rangeEnd, 0);
    }

    auto it = _quads.begin() + quadsStart;
    if (it == _quads.end()) {
        // No glyphs added
        return false;
    }

    // TextLabel parameter: Dimension
    float width = metrics.aabb.z - metrics.aabb.x;
    float height = metrics.aabb.w - metrics.aabb.y;
    _size = glm::vec2(width, height);

    // Offset to center all glyphs around 0/0
    glm::vec2 offset((metrics.aabb.x + width * 0.5) * TextVertex::position_scale,
                     (metrics.aabb.y + height * 0.5) * TextVertex::position_scale);

    {
        std::lock_guard<std::mutex> lock(m_textureMutex);
        for (; it != _quads.end(); ++it) {

            if (!_refs[it->atlas]) {
                _refs[it->atlas] = true;
                m_atlasRefCount[it->atlas]++;
            }

            it->quad[0].pos -= offset;
            it->quad[1].pos -= offset;
            it->quad[2].pos -= offset;
            it->quad[3].pos -= offset;
        }

        // Clear unused textures
        for (size_t i = 0; i < m_textures.size(); i++) {
            if (m_atlasRefCount[i] == 0) {
                m_atlas.clear(i);
                m_textures[i].texData.assign(GlyphTexture::size *
                                             GlyphTexture::size, 0);
            }
        }
    }

    return true;
}
示例#30
0
    // Paints the tabs that intersect the window's update rectangle.
    void Paint(HDC hdc, RECT& rc) {
        IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);

// paint the background
#if 0
        bool isTranslucentMode = inTitlebar && dwm::IsCompositionEnabled();
        if (isTranslucentMode) {
            PaintParentBackground(hwnd, hdc);
        } else {
            // note: not sure what color should be used here and painting
            // background works fine
            /*HBRUSH brush = CreateSolidBrush(colors.bar);
            FillRect(hdc, &rc, brush);
            DeleteObject(brush);*/
        }
#else
        PaintParentBackground(hwnd, hdc);
#endif
        // TODO: GDI+ doesn't seem to cope well with SetWorldTransform
        XFORM ctm = {1.0, 0, 0, 1.0, 0, 0};
        SetWorldTransform(hdc, &ctm);

        Graphics gfx(hdc);
        gfx.SetCompositingMode(CompositingModeSourceCopy);
        gfx.SetCompositingQuality(CompositingQualityHighQuality);
        gfx.SetSmoothingMode(SmoothingModeHighQuality);
        gfx.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
        gfx.SetPageUnit(UnitPixel);
        GraphicsPath shapes(data->Points, data->Types, data->Count);
        GraphicsPath shape;
        GraphicsPathIterator iterator(&shapes);

        SolidBrush br(Color(0, 0, 0));
        Pen pen(&br, 2.0f);

        Font f(hdc, GetDefaultGuiFont());
        // TODO: adjust these constant values for DPI?
        RectF layout((REAL)DpiScaleX(hwnd, 3), 1.0f, REAL(width - DpiScaleX(hwnd, 20)), (REAL)height);
        StringFormat sf(StringFormat::GenericDefault());
        sf.SetFormatFlags(StringFormatFlagsNoWrap);
        sf.SetLineAlignment(StringAlignmentCenter);
        sf.SetTrimming(StringTrimmingEllipsisCharacter);

        REAL yPosTab = inTitlebar ? 0.0f : REAL(ClientRect(hwnd).dy - height - 1);
        for (int i = 0; i < Count(); i++) {
            gfx.ResetTransform();
            gfx.TranslateTransform(1.f + (REAL)(width + 1) * i - (REAL)rc.left, yPosTab - (REAL)rc.top);

            if (!gfx.IsVisible(0, 0, width + 1, height + 1))
                continue;

            // Get the correct colors based on the state and the current theme
            COLORREF bgCol = GetAppColor(AppColor::TabBackgroundBg);
            COLORREF textCol = GetAppColor(AppColor::TabBackgroundText);
            COLORREF xColor = GetAppColor(AppColor::TabBackgroundCloseX);
            COLORREF circleColor = GetAppColor(AppColor::TabBackgroundCloseCircle);

            if (selectedTabIdx == i) {
                bgCol = GetAppColor(AppColor::TabSelectedBg);
                textCol = GetAppColor(AppColor::TabSelectedText);
                xColor = GetAppColor(AppColor::TabSelectedCloseX);
                circleColor = GetAppColor(AppColor::TabSelectedCloseCircle);
            } else if (highlighted == i) {
                bgCol = GetAppColor(AppColor::TabHighlightedBg);
                textCol = GetAppColor(AppColor::TabHighlightedText);
                xColor = GetAppColor(AppColor::TabHighlightedCloseX);
                circleColor = GetAppColor(AppColor::TabHighlightedCloseCircle);
            }
            if (xHighlighted == i) {
                xColor = GetAppColor(AppColor::TabHoveredCloseX);
                circleColor = GetAppColor(AppColor::TabHoveredCloseCircle);
            }
            if (xClicked == i) {
                xColor = GetAppColor(AppColor::TabClickedCloseX);
                circleColor = GetAppColor(AppColor::TabClickedCloseCircle);
            }

            // paint tab's body
            gfx.SetCompositingMode(CompositingModeSourceCopy);
            iterator.NextMarker(&shape);
            br.SetColor(ToColor(bgCol));
            Point points[4];
            shape.GetPathPoints(points, 4);
            Rect body(points[0].X, points[0].Y, points[2].X - points[0].X, points[2].Y - points[0].Y);
            body.Inflate(0, 0);
            gfx.SetClip(body);
            body.Inflate(5, 5);
            gfx.FillRectangle(&br, body);
            gfx.ResetClip();

            // draw tab's text
            gfx.SetCompositingMode(CompositingModeSourceOver);
            br.SetColor(ToColor(textCol));
            gfx.DrawString(text.at(i), -1, &f, layout, &sf, &br);

            // paint "x"'s circle
            iterator.NextMarker(&shape);
            bool closeCircleEnabled = true;
            if ((xClicked == i || xHighlighted == i) && closeCircleEnabled) {
                br.SetColor(ToColor(circleColor));
                gfx.FillPath(&br, &shape);
            }

            // paint "x"
            iterator.NextMarker(&shape);
            pen.SetColor(ToColor(xColor));
            gfx.DrawPath(&pen, &shape);
            iterator.Rewind();
        }
    }