QPainterPath CPcbCircle::path()
{
	QPainterPath ppath;
	QPoint topLeft(-radius(),-radius());
	QPoint bottomRight(radius(),radius());
	ppath.addEllipse(QRectF(topLeft,bottomRight));
	return ppath.simplified();
}
void BalloonTip::paintEvent(QPaintEvent * /*ev*/)
{
    QPainter painter(this);

    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
    painter.setBrush(Qt::white);
    painter.setPen(QColor(0, 0, 0, 130));
    painter.setFont(this->font());

    QRect popupRect = relativePopupRect();
    QRect textRect  = relativeTextRect();

    QPainterPath path;
    QPolygon arrowTriangle;

    switch (my_arrowPos) {
    case BottomLeft:
        arrowTriangle << QPoint(30, popupRect.height() + 60) << QPoint(60, popupRect.height() + 30)
                      << QPoint(90, popupRect.height() + 30);
        break;
    case TopLeft:
        arrowTriangle << QPoint(30, 0) << QPoint(60, 15) << QPoint(90, 15);
        break;
    case BottomRight:
        arrowTriangle << QPoint(popupRect.width() - 30, popupRect.height() + 60)
                      << QPoint(popupRect.width() - 60, popupRect.height() + 30)
                      << QPoint(popupRect.width() - 90, popupRect.height() + 30);
        break;
    case TopRight:
        arrowTriangle << QPoint(popupRect.width() - 30, 0) << QPoint(popupRect.width() - 60, 30)
                      << QPoint(popupRect.width() - 90, 30);
        break;
    case LeftTop:
        arrowTriangle << QPoint(popupRect.left() - 10, popupRect.height() * 0.6)
                      << QPoint(popupRect.left(), popupRect.height() * 0.6 + 5)
                      << QPoint(popupRect.left(), popupRect.height() * 0.6 - 5);
        break;
    }

    path.addPolygon(arrowTriangle);
    path.addRoundedRect(popupRect, 1, 1);
    path = path.simplified();
    painter.drawPath(path);
    painter.setPen(QColor(20, 20, 20));
    painter.drawText(textRect, my_text);

    QFont font = this->font();
    font.setBold(true);
    font.setPixelSize(12);
    painter.setFont(font);
    painter.setPen(QColor(48, 159, 220));
    if (!my_icon.isNull()) {
        painter.drawText(textRect.topLeft() + QPoint(20, -10), my_title);
        painter.drawPixmap(textRect.topLeft() + QPoint(0, -22), my_icon);
    } else {
        painter.drawText(textRect.topLeft() + QPoint(5, -10), my_title);
    }
}
void KisGradientPainterTest::testSplitDisjointPaths()
{
    QPainterPath path;

    // small bug: the smaller rect is also merged
    path.addRect(QRectF(323, 123, 4, 4));
    path.addRect(QRectF(300, 100, 50, 50));
    path.addRect(QRectF(320, 120, 10, 10));

    path.addRect(QRectF(200, 100, 50, 50));
    path.addRect(QRectF(240, 120, 70, 10));

    path.addRect(QRectF(100, 100, 50, 50));
    path.addRect(QRectF(120, 120, 10, 10));

    path = path.simplified();

    {
        QImage srcImage(450, 250, QImage::Format_ARGB32);
        srcImage.fill(0);
        QPainter gc(&srcImage);
        gc.fillPath(path, Qt::red);
        //srcImage.save("src_disjoint_paths.png");
    }

    QList<QPainterPath> result = KritaUtils::splitDisjointPaths(path);

    {
        QImage dstImage(450, 250, QImage::Format_ARGB32);
        dstImage.fill(0);
        QPainter gc(&dstImage);

        QVector<QBrush> brushes;
        brushes << Qt::red;
        brushes << Qt::green;
        brushes << Qt::blue;
        brushes << Qt::cyan;
        brushes << Qt::magenta;
        brushes << Qt::yellow;
        brushes << Qt::black;
        brushes << Qt::white;

        int index = 0;
        Q_FOREACH (const QPainterPath &p, result) {
            gc.fillPath(p, brushes[index]);
            index = (index + 1) % brushes.size();
        }


        TestUtil::checkQImageExternal(dstImage,
                                      "shaped_gradient",
                                      "test",
                                      "disjoint_paths");
    }
Exemple #4
0
void Callout::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* w)
{
  Q_UNUSED(option);
  Q_UNUSED(w);

  QPainterPath path;
  path.addRoundedRect(rect, 5, 5);

  auto anchor = mapFromParent(chart->mapToPosition(this->anchor));
  if (!rect.contains(anchor)) 
  {
    QPointF point1, point2;

    // establish the position of the anchor point in relation to rect
    auto above = anchor.y() <= rect.top();
    auto aboveCenter = anchor.y() > rect.top() && anchor.y() <= rect.center().y();
    auto belowCenter = anchor.y() > rect.center().y() && anchor.y() <= rect.bottom();
    auto below = anchor.y() > rect.bottom();

    auto onLeft = anchor.x() <= rect.left();
    auto leftOfCenter = anchor.x() > rect.left() && anchor.x() <= rect.center().x();
    auto rightOfCenter = anchor.x() > rect.center().x() && anchor.x() <= rect.right();
    auto onRight = anchor.x() > rect.right();

    // get the nearest rect corner.
    auto x = (onRight + rightOfCenter) * rect.width();
    auto y = (below + belowCenter) * rect.height();
    auto cornerCase = (above && onLeft) || (above && onRight) || (below && onLeft) || (below && onRight);
    auto vertical   = qAbs(anchor.x() - x) > qAbs(anchor.y() - y);

    auto x1 = x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * !vertical * (onLeft * 10 - onRight * 20);
    auto y1 = y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20);;
    point1.setX(x1);
    point1.setY(y1);

    auto x2 = x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * !vertical * (onLeft * 20 - onRight * 10);;
    auto y2 = y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 - below * 10);;
    point2.setX(x2);
    point2.setY(y2);

    path.moveTo(point1);
    path.lineTo(anchor);
    path.lineTo(point2);
    path = path.simplified();
  }

  painter->setBrush(QColor(255, 255, 255));
  painter->drawPath(path);
  painter->drawText(textRect, text);
}
Exemple #5
0
void TitleBar::paintEvent(QPaintEvent *) {
    QPainter painter(this);
    painter.setBrush(_background);
    painter.setPen(Qt::NoPen);
    painter.setRenderHint(QPainter::Antialiasing);

    QPainterPath path;
    path.setFillRule(Qt::WindingFill);
    path.addRoundedRect(QRect(0, 0, width(), height()), _radius, _radius);
    //bottomRight
    path.addRect(QRect(width() - _radius, height() - _radius, _radius, _radius));
    //bottomLeft
    path.addRect(QRect(0, height() - _radius, _radius, _radius));
    painter.drawPath(path.simplified());
}
Exemple #6
0
void Window::paintEvent(QPaintEvent *) {
	QPainter painter(this);
	if (_hasShadow) {
		drawShadow(painter, _borderWidth, _radius, QColor(120, 120, 120, 32), QColor(255, 255, 255, 0), 0.0, 1.0, 0.6, width(), height());
	} else {
		painter.setBrush(QColor("#FFFFFF"));
		painter.setPen(Qt::NoPen);
		painter.setRenderHint(QPainter::Antialiasing);

		QPainterPath path;
		path.setFillRule(Qt::WindingFill);
		path.addRoundedRect(QRect(0, 0, width(), height()), _radius, _radius);
		painter.drawPath(path.simplified());
	}
}
QPainterPath XYTempWindows::getPainterPath(const QRect &rect, XYTempWindows::DIRECTION direction)
{
    QPainterPath path;
    int arrow_h = 5;
    switch (direction)
    {
    case TOP:
        arrow_h = rect.height() * 0.1;
        textRect = QRect(rect.x() + 1, rect.y() + arrow_h + 1,
                         rect.width() - 2, rect.height() - arrow_h - 2);
        path.addRoundedRect(textRect, 4, 4);
        path.moveTo(rect.x() + rect.width() / 2 - arrow_h / 2, rect.y() + arrow_h + 1);
        path.lineTo(rect.x() + rect.width() / 2, rect.y());
        path.lineTo(rect.x() + rect.width() / 2 + arrow_h / 2, rect.y() + arrow_h + 1);
        break;
    case BOTTOM:
        arrow_h = rect.height() * 0.1;
        textRect = QRect(rect.x() + 1, rect.y() + 1,
                         rect.width() - 2, rect.height() - arrow_h - 1);
        path.addRoundedRect(textRect, 4, 4);
        path.moveTo(rect.x() + rect.width() / 2 - arrow_h / 2, rect.y() + rect.height() - arrow_h);
        path.lineTo(rect.x() + rect.width() / 2, rect.y() + rect.height());
        path.lineTo(rect.x() + rect.width() / 2 + arrow_h / 2, rect.y() + rect.height() - arrow_h);
        break;
    case LEFT:
        arrow_h = rect.height() * 0.1;
        textRect = QRect(rect.x() + arrow_h, rect.y() + 1,
                         rect.width() - arrow_h - 2, rect.height() - 2);
        path.addRoundedRect(textRect, 4, 4);
        path.moveTo(rect.x() + arrow_h, rect.y() + rect.height() / 2 -  arrow_h / 2);
        path.lineTo(rect.x() - 2, rect.y() + rect.height() / 2);
        path.lineTo(rect.x() + arrow_h, rect.y() + rect.height() / 2 +  arrow_h / 2);
        break;
    case RIGHT:
        arrow_h = rect.height() * 0.1;
        textRect = QRect(rect.x() + 1, rect.y() + 1,
                         rect.width() - arrow_h - 2, rect.height() - 2);
        path.addRoundedRect(textRect, 4, 4);
        path.moveTo(rect.x() - 1 + rect.width() - arrow_h, rect.y() + rect.height() / 2 -  arrow_h / 2);
        path.lineTo(rect.x() - 1 + rect.width(), rect.y() + rect.height() / 2);
        path.lineTo(rect.x() - 1 + rect.width() - arrow_h, rect.y() + rect.height() / 2 +  arrow_h / 2);
        break;
    default:
        break;
    }
    return path.simplified();
}
Exemple #8
0
QPainterPath EvButton::textPath() const
{
    QPainterPath path;
    path.setFillRule( Qt::WindingFill);

    QRect rect = textRect();
    if(m_rounded){
        float roundness = this->roundness();
        path.addRoundRect( rect, roundness );
        path.addRect( QRect(rect.x(),0, roundness,roundness ) );
        path.addRect( QRect( rect.x(),rect.y() , roundness,roundness ) );
    }
    else{
        path.addRect(rect);
    }
    return path.simplified();
}
Exemple #9
0
QPolygonF Carton::outline() const
{
#if QT_VERSION >= 0x040400
    QPainterPath painterPath;

    if (isFaceVisibleFromFront(Top))
        painterPath.addPolygon(face2d(Top));
    if (isFaceVisibleFromFront(Left))
        painterPath.addPolygon(face2d(Left));
    else if (isFaceVisibleFromFront(Right))
        painterPath.addPolygon(face2d(Right));
    if (isFaceVisibleFromFront(Front))
        painterPath.addPolygon(face2d(Front));
    else if (isFaceVisibleFromFront(Back))
        painterPath.addPolygon(face2d(Back));

    return painterPath.simplified().toFillPolygon();
#else
    return QPolygonF();
#endif
}
/*!
 \fn QPainterPath NmHsWidget::shape()

 Called by home screen fw to check widget boundaries, needed to draw
 outside widget boundingRect.
 /return QPainterPath path describing actual boundaries of widget 
  including child items
 */
QPainterPath NmHsWidget::shape() const
{
    NM_FUNCTION;
    
    QPainterPath path;
    path.setFillRule(Qt::WindingFill);
    if (mWidgetContainer){
        //add mWidgetContainer using geometry to get
        //correct point for top-left-corner
        QRectF widgetRect = mWidgetContainer->geometry();
        path.addRect(widgetRect); 
        
        //then fetch shape from title row 
        QPainterPath titlepath;
        titlepath.addPath(mTitleRow->shape());
        //translate it's location to be inside mWidgetContainer
        titlepath.translate(widgetRect.topLeft());
        //and finally add it to path
        path.addPath(titlepath);    
    }
    //simplified path, i.e. only outlines
    return path.simplified();
}
static QPainterPath qwtCombinePathList( const QRectF &rect, 
    const QList<QPainterPath> &pathList )
{
    if ( pathList.isEmpty() )
        return QPainterPath();

    QPainterPath ordered[8]; // starting top left

    for ( int i = 0; i < pathList.size(); i++ )
    {
        int index = -1;
        QPainterPath subPath = pathList[i];

        const QRectF br = pathList[i].controlPointRect();
        if ( br.center().x() < rect.center().x() )
        {
            if ( br.center().y() < rect.center().y() )
            {
                if ( qAbs( br.top() - rect.top() ) < 
                    qAbs( br.left() - rect.left() ) )
                {
                    index = 1;
                }
                else
                {
                    index = 0;
                }
            }
            else
            {
                if ( qAbs( br.bottom() - rect.bottom() ) < 
                    qAbs( br.left() - rect.left() ) )
                {
                    index = 6;
                }
                else
                {
                    index = 7;
                }
            }

            if ( subPath.currentPosition().y() > br.center().y() )
                qwtRevertPath( subPath );
        }
        else
        {
            if ( br.center().y() < rect.center().y() )
            {
                if ( qAbs( br.top() - rect.top() ) < 
                    qAbs( br.right() - rect.right() ) )
                {
                    index = 2;
                }
                else
                {
                    index = 3;
                }
            }
            else
            {
                if ( qAbs( br.bottom() - rect.bottom() ) < 
                    qAbs( br.right() - rect.right() ) )
                {
                    index = 5;
                }
                else
                {
                    index = 4;
                }
            }
            if ( subPath.currentPosition().y() < br.center().y() )
                qwtRevertPath( subPath );
        }   
        ordered[index] = subPath;
    }

    for ( int i = 0; i < 4; i++ )
    {
        if ( ordered[ 2 * i].isEmpty() != ordered[2 * i + 1].isEmpty() )
        {
            // we don't accept incomplete rounded borders
            return QPainterPath();
        }
    }


    const QPolygonF corners( rect );

    QPainterPath path;
    //path.moveTo( rect.topLeft() );

    for ( int i = 0; i < 4; i++ )
    {
        if ( ordered[2 * i].isEmpty() )
        {
            path.lineTo( corners[i] );
        }
        else
        {
            path.connectPath( ordered[2 * i] );
            path.connectPath( ordered[2 * i + 1] );
        }
    }

    path.closeSubpath();

#if 0
    return path.simplified();
#else
    return path;
#endif
}
void QWaylandMaterialDecoration::paint(QPaintDevice *device)
{
    QRect surfaceRect(QPoint(), window()->frameGeometry().size());
    QRect top(QPoint(), QSize(window()->frameGeometry().width(), margins().top()));

    QPainter p(device);
    p.setRenderHint(QPainter::Antialiasing);

    // Title bar
    int radius = waylandWindow()->isMaximized() ? 0 : dp(3);
    QPainterPath roundedRect;
    roundedRect.addRoundedRect(0, 0, window()->frameGeometry().width(), margins().top() * 1.5,
                               radius, radius);

    p.fillPath(roundedRect.simplified(), m_backgroundColor);

    // Window icon
    QIcon icon = waylandWindow()->windowIcon();
    if (!icon.isNull()) {
        QPixmap pixmap = icon.pixmap(QSize(128, 128));
        QPixmap scaled = pixmap.scaled(22, 22, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

        QRectF iconRect(0, 0, 22, 22);
        p.drawPixmap(iconRect.adjusted(margins().left() + BUTTON_SPACING, 4,
                                       margins().left() + BUTTON_SPACING, 4),
                     scaled, iconRect);
    }

    // Window title
    QString windowTitleText = window()->title();
    if (!windowTitleText.isEmpty()) {
        if (m_windowTitle.text() != windowTitleText) {
            m_windowTitle.setText(windowTitleText);
            m_windowTitle.prepare();
        }

        QRect titleBar = top;
        titleBar.setLeft(margins().left() + BUTTON_SPACING +
                         (icon.isNull() ? 0 : 22 + BUTTON_SPACING));
        titleBar.setRight(minimizeButtonRect().left() - BUTTON_SPACING);

        p.save();
        p.setClipRect(titleBar);
        p.setPen(m_textColor);
        QSizeF size = m_windowTitle.size();
        int dx = (top.width() - size.width()) / 2;
        int dy = (top.height() - size.height()) / 2;
        QFont font = p.font();
        font.setBold(true);
        font.setFamily("Roboto");
        p.setFont(font);
        QPoint windowTitlePoint(top.topLeft().x() + dx, top.topLeft().y() + dy);
        p.drawStaticText(windowTitlePoint, m_windowTitle);
        p.restore();
    }

    p.save();
    p.setPen(m_iconColor);

    // Close button
    QBitmap closeIcon = buttonIcon("window-close");
    p.drawPixmap(closeButtonRect(), closeIcon, closeIcon.rect());

    // Maximize button
    QBitmap maximizeIcon =
            buttonIcon(waylandWindow()->isMaximized() ? "window-restore" : "window-maximize");
    p.drawPixmap(maximizeButtonRect(), maximizeIcon, maximizeIcon.rect());

    // Minimize button
    QBitmap minimizeIcon = buttonIcon("window-minimize");
    p.drawPixmap(minimizeButtonRect(), minimizeIcon, minimizeIcon.rect());

    p.restore();
}
void QRibbonComboBoxStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const
{
	QProxyStyle::drawComplexControl(control, option, painter, widget);

	int rightButtonWidth = 18;

	int x = option->rect.x();
	int y = option->rect.y();

	int cbWidth = option->rect.width();
	int cbHeight = option->rect.height();

	float arrowX = cbWidth - arrowWidth - (rightButtonWidth - arrowWidth) / 2.0;
	float arrowY = (cbHeight - arrowHeight) / 2.0;

	QPainterPath* painterPath = new QPainterPath();

	// Erase the old border
	QRect* borderErased = new QRect(1, 1, cbWidth - 2, cbHeight - 2);
	painterPath->addRoundedRect(*borderErased, 0, 0);
	painter->strokePath(painterPath->simplified(), QPen(Qt::white, 2));

	QDELETE(borderErased);
	QDELETE(painterPath);

	// Erase the right button part
	QRect* rightPartErased = new QRect(0, 0, rightButtonWidth, cbHeight);
	rightPartErased->moveLeft(cbWidth - rightButtonWidth);
	painter->fillRect(*rightPartErased, Qt::white);

	QDELETE(rightPartErased);

	// Paint the border of button
	painterPath = new QPainterPath();
	QRect* border = new QRect(0, 0, cbWidth - 1, cbHeight - 1);
	painterPath->addRoundedRect(*border, 0, 0);

	bool isHovered = option->state & State_MouseOver;
	bool isOpened = option->state & (State_On | State_Sunken);
	if (isOpened == true)
	{
		painter->strokePath(painterPath->simplified(), QPen(QColor("#0078d7"), 1));
	}
	else
	{
		painter->strokePath(painterPath->simplified(), QPen(QColor("#5c5c5c"), 1));
	}

	QDELETE(border);
	QDELETE(painterPath);

	QPoint globalCursorPos = QCursor::pos();
	QPoint widgetPos = widget->mapFromGlobal(globalCursorPos);

	// Paint the right part of combobox
	if ((isHovered == true || isOpened == true) && widgetPos.x() >= (cbWidth - rightButtonWidth) && widgetPos.x() <= cbWidth)
	{
		QColor hoveredColor(Qt::green);

		QRect* hoverRightPart = new QRect(0, 0, rightButtonWidth, cbHeight);
		hoverRightPart->moveLeft(cbWidth - rightButtonWidth);
		painter->fillRect(*hoverRightPart, hoveredColor);

		QDELETE(hoverRightPart);

		// Draw right button border
		painterPath = new QPainterPath();
		QRect* rightButtonBorder = new QRect(cbWidth - rightButtonWidth, 0, rightButtonWidth - 1, cbHeight - 1);
		painterPath->addRoundedRect(*rightButtonBorder, 0, 0);
		painter->strokePath(painterPath->simplified(), QPen(QColor("#0078d7"), 1));

		QDELETE(rightButtonBorder);
		QDELETE(painterPath);
	}

	// Draw the down arrow
	painter->setPen(QPen(QColor("#5c5c5c"), 2));

	QLineF arrowDown(QPointF(arrowX, arrowY), QPointF(arrowX + arrowHeight, arrowY + arrowHeight));
	painter->drawLine(arrowDown);
	arrowDown.setLine(arrowX + arrowHeight, arrowY + arrowHeight, arrowX + arrowWidth, arrowY);
	painter->drawLine(arrowDown);
}
Exemple #14
0
void TextLayer::recreateTexture(VidgfxContext *gfx)
{
	if(!m_isTexDirty)
		return; // Don't waste any time if it hasn't changed
	m_isTexDirty = false;

	// Delete existing texture if one exists
	if(m_texture != NULL)
		vidgfx_context_destroy_tex(gfx, m_texture);
	m_texture = NULL;

	// Determine texture size. We need to keep in mind that the text in the
	// document might extend outside of the layer's bounds.
	m_document.setTextWidth(m_rect.width());
	QSize size(
		(int)ceilf(m_document.size().width()),
		(int)ceilf(m_document.size().height()));

	if(m_document.isEmpty() || size.isEmpty()) {
		// Nothing to display
		return;
	}

	// Create temporary canvas. We need to be careful here as text is rendered
	// differently on premultiplied vs non-premultiplied pixel formats. On a
	// premultiplied format text is rendered with subpixel rendering enabled
	// while on a non-premultiplied format it is not. As we don't want subpixel
	// rendering we use the standard ARGB32 format.
	QSize imgSize(
		size.width() + m_strokeSize * 2, size.height() + m_strokeSize * 2);
	QImage img(imgSize, QImage::Format_ARGB32);
	img.fill(Qt::transparent);
	QPainter p(&img);
	p.setRenderHint(QPainter::Antialiasing, true);

	// Render text
	//m_document.drawContents(&p);

	// Render stroke
	if(m_strokeSize > 0) {
#define STROKE_TECHNIQUE 0
#if STROKE_TECHNIQUE == 0
		// Technique 0: Use QTextDocument's built-in text outliner
		//quint64 timeStart = App->getUsecSinceExec();

		QTextDocument *outlineDoc = m_document.clone(this);

		QTextCharFormat format;
		QPen pen(m_strokeColor, (double)(m_strokeSize * 2));
		pen.setJoinStyle(Qt::RoundJoin);
		format.setTextOutline(pen);
		QTextCursor cursor(outlineDoc);
		cursor.select(QTextCursor::Document);
		cursor.mergeCharFormat(format);

		// Take into account the stroke offset
		p.translate(m_strokeSize, m_strokeSize);

		//quint64 timePath = App->getUsecSinceExec();
		outlineDoc->drawContents(&p);
		delete outlineDoc;

		//quint64 timeEnd = App->getUsecSinceExec();
		//appLog() << "Path time = " << (timePath - timeStart) << " usec";
		//appLog() << "Render time = " << (timeEnd - timePath) << " usec";
		//appLog() << "Full time = " << (timeEnd - timeStart) << " usec";
#elif STROKE_TECHNIQUE == 1
		// Technique 1: Create a text QPainterPath and stroke it
		quint64 timeStart = App->getUsecSinceExec();

		// Create the path for the text's stroke
		QPainterPath path;
		QTextBlock &block = m_document.firstBlock();
		int numBlocks = m_document.blockCount();
		for(int i = 0; i < numBlocks; i++) {
			QTextLayout *layout = block.layout();
			for(int j = 0; j < layout->lineCount(); j++) {
				QTextLine &line = layout->lineAt(j);
				const QString text = block.text().mid(
					line.textStart(), line.textLength());
				QPointF pos = layout->position() + line.position();
				pos.ry() += line.ascent();
				//appLog() << pos << ": " << text;
				path.addText(pos, block.charFormat().font(), text);
			}
			block = block.next();
		}

		quint64 timePath = App->getUsecSinceExec();
		path = path.simplified(); // Fixes gaps with large stroke sizes
		quint64 timeSimplify = App->getUsecSinceExec();

		// Render the path
		//p.strokePath(path, QPen(m_strokeColor, m_strokeSize));

		// Convert it to a stroke
		QPainterPathStroker stroker;
		stroker.setWidth(m_strokeSize);
		//stroker.setCurveThreshold(2.0);
		stroker.setJoinStyle(Qt::RoundJoin);
		path = stroker.createStroke(path);

		// Render the path
		p.fillPath(path, m_strokeColor);

		quint64 timeEnd = App->getUsecSinceExec();
		appLog() << "Path time = " << (timePath - timeStart) << " usec";
		appLog() << "Simplify time = " << (timeSimplify - timePath) << " usec";
		appLog() << "Render time = " << (timeEnd - timeSimplify) << " usec";
		appLog() << "Full time = " << (timeEnd - timeStart) << " usec";
#elif STROKE_TECHNIQUE == 2
		// Technique 2: Similar to technique 1 but do each block separately
		quint64 timeStart = App->getUsecSinceExec();
		quint64 timeTotalSimplify = 0;
		quint64 timeTotalRender = 0;

		// Create the path for the text's stroke
		QTextBlock &block = m_document.firstBlock();
		int numBlocks = m_document.blockCount();
		for(int i = 0; i < numBlocks; i++) {
			// Convert this block to a painter path
			QPainterPath path;
			QTextLayout *layout = block.layout();
			for(int j = 0; j < layout->lineCount(); j++) {
				QTextLine &line = layout->lineAt(j);
				const QString text = block.text().mid(
					line.textStart(), line.textLength());
				QPointF pos = layout->position() + line.position() +
					QPointF(m_strokeSize, m_strokeSize);
				pos.ry() += line.ascent();
				//appLog() << pos << ": " << text;
				path.addText(pos, block.charFormat().font(), text);
			}

			// Prevent gaps appearing at larger stroke sizes
			quint64 timeA = App->getUsecSinceExec();
			path = path.simplified();
			quint64 timeB = App->getUsecSinceExec();
			timeTotalSimplify += timeB - timeA;

			// Render the path
			QPen pen(m_strokeColor, m_strokeSize * 2);
			pen.setJoinStyle(Qt::RoundJoin);
			p.strokePath(path, pen);
			timeA = App->getUsecSinceExec();
			timeTotalRender += timeA - timeB;

			// Iterate
			block = block.next();
		}

		// Make the final draw take into account the stroke offset
		p.translate(m_strokeSize, m_strokeSize);

		quint64 timeEnd = App->getUsecSinceExec();
		appLog() << "Simplify time = " << timeTotalSimplify << " usec";
		appLog() << "Render time = " << timeTotalRender << " usec";
		appLog() << "Full time = " << (timeEnd - timeStart) << " usec";
#elif STROKE_TECHNIQUE == 3
		// Technique 3: Raster brute-force where for each destination pixel
		// we measure the distance to the closest opaque source pixel
		quint64 timeStart = App->getUsecSinceExec();

		// Get bounding region based on text line bounding rects
		QRegion region;
		QTextBlock &block = m_document.firstBlock();
		int numBlocks = m_document.blockCount();
		for(int i = 0; i < numBlocks; i++) {
			QTextLayout *layout = block.layout();
			for(int j = 0; j < layout->lineCount(); j++) {
				QTextLine &line = layout->lineAt(j);
				const QString text = block.text().mid(
					line.textStart(), line.textLength());
				QRect rect = line.naturalTextRect()
					.translated(layout->position()).toAlignedRect();
				if(rect.isEmpty())
					continue; // Don't add empty rectangles
				rect.adjust(0, 0, 1, 0); // QTextLine is incorrect?
				rect.adjust(
					-m_strokeSize, -m_strokeSize,
					m_strokeSize, m_strokeSize);
				//appLog() << rect;
				region += rect;
			}

			// Iterate
			block = block.next();
		}
		quint64 timeRegion = App->getUsecSinceExec();

#if 0
		// Debug bounding region
		QPainterPath regionPath;
		regionPath.addRegion(region);
		regionPath.setFillRule(Qt::WindingFill);
		p.fillPath(regionPath, QColor(255, 0, 0, 128));
#endif // 0

		// We cannot read and write to the same image at the same time so
		// create a second one. Note that this is not premultiplied.
		QImage imgOut(size, QImage::Format_ARGB32);
		imgOut.fill(Qt::transparent);

		// Do distance calculation. We assume that non-fully transparent
		// pixels are always next to a fully opaque one so if the closest
		// "covered" pixel is not fully opaque then we can use that pixel's
		// opacity to determine the distance to the shape's edge.
		for(int y = 0; y < img.height(); y++) {
			for(int x = 0; x < img.width(); x++) {
				if(!region.contains(QPoint(x, y)))
					continue;
				float dist = getDistance(img, x, y, m_strokeSize);

				// We fake antialiasing by blurring the edge by 1px
				float outEdge = (float)m_strokeSize;
				if(dist >= outEdge)
					continue; // Outside stroke completely
				float opacity = qMin(1.0f, outEdge - dist);
				QColor col = m_strokeColor;
				col.setAlphaF(col.alphaF() * opacity);

				// Blend the stroke so that it appears under the existing
				// pixel data
				QRgb origRgb = img.pixel(x, y);
				QColor origCol(origRgb);
				origCol.setAlpha(qAlpha(origRgb));
				col = blendColors(col, origCol, 1.0f);
				imgOut.setPixel(x, y, col.rgba());
			}
		}
		quint64 timeRender = App->getUsecSinceExec();

		// Swap image data
		p.end();
		img = imgOut;
		p.begin(&img);

		quint64 timeEnd = App->getUsecSinceExec();
		appLog() << "Region time = " << (timeRegion - timeStart) << " usec";
		appLog() << "Render time = " << (timeRender - timeRegion) << " usec";
		appLog() << "Swap time = " << (timeEnd - timeRender) << " usec";
		appLog() << "Full time = " << (timeEnd - timeStart) << " usec";
#endif // STROKE_TECHNIQUE
	}

	// Render text
	m_document.drawContents(&p);

	// Convert the image to a GPU texture
	m_texture = vidgfx_context_new_tex(gfx, img);

	// Preview texture for debugging
	//img.save(App->getDataDirectory().filePath("Preview.png"));
}
// This function generates puzzle piece shapes.
// ----------
// unit - the size of the rectangular base of the puzzle piece
// status - flags of TabStatus values that describe the shape of this puzzle piece
// tabFull - the full size of tabs including stroke, offset, tolerance
// tabSize - size of tabs
// tabOffset - offset of tabs (meaning: how far away they are from the edge of the piece)
// tabTolerance - extra size to the tabs (so that merging works flawlessly, without visual artifacts)
// blankSize - size of blanks
// blankOffset - offset of blanks (meaning: how far away they are from the edge of the piece)
// ----------
static QPainterPath createPuzzleShape(QSize unit, int status, qreal tabFull, qreal tabSize, qreal tabOffset, qreal tabTolerance, qreal blankSize, qreal blankOffset)
{
    QPainterPath rectClip;
    rectClip.addRect(tabFull - 1, tabFull - 1, unit.width() + 1, unit.height() + 1);
    QPainterPath clip = rectClip;

    // Left
    if (status & Puzzle::Creation::LeftBlank)
    {
        QPainterPath leftBlank;
        leftBlank.addEllipse(QPointF(tabFull + blankOffset, tabFull + unit.height() / 2.0), blankSize, blankSize);
        clip = clip.subtracted(leftBlank);
    }
    else if (status & Puzzle::Creation::LeftTab)
    {
        QPainterPath leftTab;
        leftTab.addEllipse(QPointF(tabSize + tabTolerance, tabFull + unit.height() / 2.0), tabSize + tabTolerance, tabSize + tabTolerance);
        clip = clip.united(leftTab);
    }

    // Top
    if (status & Puzzle::Creation::TopBlank)
    {
        QPainterPath topBlank;
        topBlank.addEllipse(QPointF(tabFull + unit.width() / 2.0, tabFull + blankOffset), blankSize, blankSize);
        clip = clip.subtracted(topBlank);
    }
    else if (status & Puzzle::Creation::TopTab)
    {
        QPainterPath topTab;
        topTab.addEllipse(QPointF(tabFull + unit.width() / 2.0, tabSize + tabTolerance), tabSize + tabTolerance, tabSize + tabTolerance);
        clip = clip.united(topTab);
    }

    // Right
    if (status & Puzzle::Creation::RightTab)
    {
        QPainterPath rightTab;
        rightTab.addEllipse(QPointF(tabFull + unit.width() + tabOffset, tabFull + unit.height() / 2.0), tabSize + tabTolerance, tabSize + tabTolerance);
        clip = clip.united(rightTab);
    }
    else if (status & Puzzle::Creation::RightBlank)
    {
        QPainterPath rightBlank;
        rightBlank.addEllipse(QPointF(tabFull + unit.width() - blankOffset, tabFull + unit.height() / 2.0), blankSize, blankSize);
        clip = clip.subtracted(rightBlank);
    }

    // Bottom
    if (status & Puzzle::Creation::BottomTab)
    {
        QPainterPath bottomTab;
        bottomTab.addEllipse(QPointF(tabFull + unit.width() / 2.0, tabFull + unit.height() + tabOffset), tabSize + tabTolerance, tabSize + tabTolerance);
        clip = clip.united(bottomTab);
    }
    else if (status & Puzzle::Creation::BottomBlank)
    {
        QPainterPath bottomBlank;
        bottomBlank.addEllipse(QPointF(tabFull + unit.width() / 2.0, tabFull + unit.height() - blankOffset), blankSize, blankSize);
        clip = clip.subtracted(bottomBlank);
    }

    clip = clip.simplified();
    return clip;
}
void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    QPainterPath outlinePath = shape();

    //Fill the node's colour
    QBrush brush(m_colour);
    painter->fillPath(outlinePath, brush);

    //If the node contains a BLAST hit, draw that on top.
    if (g_settings->nodeColourScheme == BLAST_HITS_COLOUR)
    {
        std::vector<BlastHitPart> parts;

        if (g_settings->doubleMode)
        {
            if (m_deBruijnNode->thisNodeHasBlastHits())
                parts = m_deBruijnNode->getBlastHitPartsForThisNode();
        }
        else
        {
            if (m_deBruijnNode->thisNodeOrReverseComplementHasBlastHits())
                parts = m_deBruijnNode->getBlastHitPartsForThisNodeOrReverseComplement();
        }

        if (parts.size() > 0)
        {
            QPen partPen;
            partPen.setWidthF(m_width);
            partPen.setCapStyle(Qt::FlatCap);
            partPen.setJoinStyle(Qt::BevelJoin);

            for (size_t i = 0; i < parts.size(); ++i)
            {
                partPen.setColor(parts[i].m_colour);
                painter->setPen(partPen);

                painter->drawPath(makePartialPath(parts[i].m_nodeFractionStart, parts[i].m_nodeFractionEnd));
            }
        }
    }

    //Draw the node outline
    QColor outlineColour = g_settings->outlineColour;
    double outlineThickness = g_settings->outlineThickness;
    if (isSelected())
    {
        outlineColour = g_settings->selectionColour;
        outlineThickness = g_settings->selectionThickness;
    }
    if (outlineThickness > 0.0)
    {
        outlinePath = outlinePath.simplified();
        QPen outlinePen(QBrush(outlineColour), outlineThickness, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);
        painter->setPen(outlinePen);
        painter->drawPath(outlinePath);
    }


    //Draw text if there is any to display.
    if (g_settings->anyNodeDisplayText())
    {
        //The text should always be displayed upright, so
        //counter the view's rotation here.

        painter->setRenderHint(QPainter::TextAntialiasing, true);
        painter->setFont(g_settings->labelFont);
        QString displayText = getNodeText();
        QSize textSize = getNodeTextSize(displayText);

        double textWidth = textSize.width();
        double textHeight = textSize.height();

        //The text outline is made by drawing the text first in white at a slight offset
        //at many angles.  The larger the text outline, the more angles are needed to
        //make the outline look nice.
        if (g_settings->textOutline)
        {
            int offsetSteps = 8;
            if (g_settings->textOutlineThickness > 0.5)
                offsetSteps = 16;
            if (g_settings->textOutlineThickness > 1.0)
                offsetSteps = 32;

            double offsetDistance = g_settings->textOutlineThickness;

            painter->translate(getCentre());
            painter->rotate(-g_graphicsView->m_rotation);

            for (int i = 0; i < offsetSteps; ++i)
            {
                double offsetAngle = 6.2832 * (double(i) / offsetSteps);
                double xOffset = offsetDistance * cos(offsetAngle);
                double yOffset = offsetDistance * sin(offsetAngle);
                QRectF shadowTextRectangle(-textWidth / 2.0 + xOffset,
                                           -textHeight / 2.0 + yOffset,
                                           textWidth, textHeight);
                painter->setPen(Qt::white);
                painter->drawText(shadowTextRectangle, Qt::AlignCenter, displayText);
            }

            painter->rotate(g_graphicsView->m_rotation);
            painter->translate(-1.0 * getCentre());
        }

        QRectF textRectangle(-textWidth / 2.0, -textHeight / 2.0,
                             textWidth, textHeight);
        painter->setPen(g_settings->textColour);

        painter->translate(getCentre());
        painter->rotate(-g_graphicsView->m_rotation);
        painter->drawText(textRectangle, Qt::AlignCenter, displayText);
        painter->rotate(g_graphicsView->m_rotation);
        painter->translate(-1.0 * getCentre());
    }
}
void QWaylandMaterialDecoration::paint(QPaintDevice *device)
{
    const QRect frameGeometry = window()->frameGeometry();
    QRect top(QPoint(WINDOW_BORDER, WINDOW_BORDER), QSize(frameGeometry.width(), margins().top()));

    QPainter p(device);
    p.setRenderHint(QPainter::Antialiasing);

    // Title bar
    int radius = waylandWindow()->isMaximized() ? 0 : dp(3);
    QPainterPath roundedRect;
    roundedRect.addRoundedRect(0, 0, frameGeometry.width(), margins().top() * 1.5,
                               radius, radius);
    p.fillPath(roundedRect.simplified(), m_backgroundColor);

    // Borders
    QPainterPath borderPath;
    borderPath.addRect(0, margins().top(), margins().left(), frameGeometry.height() - margins().top());
    borderPath.addRect(0, frameGeometry.height() - margins().bottom(), frameGeometry.width(), margins().bottom());
    borderPath.addRect(frameGeometry.width() - margins().right(), margins().top(), margins().right(), frameGeometry.height() - margins().bottom());
    p.fillPath(borderPath, m_backgroundColor);

    // Window title
    QString windowTitleText = window()->title();
    if (!windowTitleText.isEmpty()) {
        if (m_windowTitle.text() != windowTitleText) {
            m_windowTitle.setText(windowTitleText);
            m_windowTitle.prepare();
        }

        QRect titleBar = top;
        titleBar.setLeft(margins().left() + BUTTON_SPACING);
        titleBar.setRight(minimizeButtonRect().left() - BUTTON_SPACING);

        p.save();
        p.setClipRect(titleBar);
        p.setPen(m_textColor);
        QSizeF size = m_windowTitle.size();
        int dx = (top.width() - size.width()) / 2;
        int dy = (top.height() - size.height()) / 2;
        QFont font = p.font();
        font.setBold(true);
        font.setFamily("Roboto");
        p.setFont(font);
        QPoint windowTitlePoint(top.topLeft().x() + dx, top.topLeft().y() + dy);
        p.drawStaticText(windowTitlePoint, m_windowTitle);
        p.restore();
    }

    p.save();
    p.setPen(m_iconColor);

    // Close button
    QBitmap closeIcon = buttonIcon("window-close");
    p.drawPixmap(closeButtonRect(), closeIcon, closeIcon.rect());

    // Maximize button
    if (window()->flags() & Qt::WindowMaximizeButtonHint) {
        QBitmap maximizeIcon =
                buttonIcon(waylandWindow()->isMaximized() ? "window-restore" : "window-maximize");
        p.drawPixmap(maximizeButtonRect(), maximizeIcon, maximizeIcon.rect());
    }

    // Minimize button
    if (window()->flags() & Qt::WindowMinimizeButtonHint) {
        QBitmap minimizeIcon = buttonIcon("window-minimize");
        p.drawPixmap(minimizeButtonRect(), minimizeIcon, minimizeIcon.rect());
    }

    p.restore();
}