File indexing completed on 2024-12-22 04:09:11

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
0003  *  SPDX-FileCopyrightText: 2022 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "KoSvgTextShape.h"
0009 #include "KoSvgTextShape_p.h"
0010 
0011 #include "KoSvgTextProperties.h"
0012 
0013 #include <KoClipMaskPainter.h>
0014 #include <KoColorBackground.h>
0015 #include <KoPathShape.h>
0016 #include <KoShapeStroke.h>
0017 
0018 #include <QPainter>
0019 #include <QtMath>
0020 
0021 #include <variant>
0022 
0023 void inheritPaintProperties(KisForest<KoSvgTextContentElement>::composition_iterator it,
0024                        KoShapeStrokeModelSP &stroke,
0025                        QSharedPointer<KoShapeBackground> &background,
0026                        QVector<KoShape::PaintOrder> &paintOrder) {
0027     for (auto parentIt = KisForestDetail::hierarchyBegin(siblingCurrent(it)); parentIt != KisForestDetail::hierarchyEnd(siblingCurrent(it)); parentIt++) {
0028         if (parentIt->properties.hasProperty(KoSvgTextProperties::StrokeId)) {
0029             stroke = parentIt->properties.stroke();
0030             break;
0031         }
0032     }
0033     for (auto parentIt = KisForestDetail::hierarchyBegin(siblingCurrent(it)); parentIt != KisForestDetail::hierarchyEnd(siblingCurrent(it)); parentIt++) {
0034         if (parentIt->properties.hasProperty(KoSvgTextProperties::FillId)) {
0035             background = parentIt->properties.background();
0036             break;
0037         }
0038     }
0039     for (auto parentIt = KisForestDetail::hierarchyBegin(siblingCurrent(it)); parentIt != KisForestDetail::hierarchyEnd(siblingCurrent(it)); parentIt++) {
0040         if (parentIt->properties.hasProperty(KoSvgTextProperties::PaintOrder)) {
0041             paintOrder = parentIt->properties.propertyOrDefault(KoSvgTextProperties::PaintOrder).value<QVector<KoShape::PaintOrder>>();
0042             break;
0043         }
0044     }
0045 }
0046 
0047 
0048 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
0049 void KoSvgTextShape::Private::paintPaths(QPainter &painter,
0050                                          const QPainterPath &rootOutline,
0051                                          const KoShape *rootShape,
0052                                          const QVector<CharacterResult> &result,
0053                                          QPainterPath &chunk,
0054                                          int &currentIndex)
0055 {
0056 
0057     KoShapeStrokeModelSP stroke = rootShape->stroke();
0058     QSharedPointer<KoShapeBackground> background = rootShape->background();
0059     QVector<KoShape::PaintOrder> paintOrder = rootShape->paintOrder();
0060 
0061     for (auto it = compositionBegin(textData); it != compositionEnd(textData); it++) {
0062         inheritPaintProperties(it, stroke, background, paintOrder);
0063 
0064         if (it.state() == KisForestDetail::Enter) {
0065             QMap<KoSvgText::TextDecoration, QPainterPath> textDecorations = it->textDecorations;
0066             QColor textDecorationColor = it->properties.propertyOrDefault(KoSvgTextProperties::TextDecorationColorId).value<QColor>();
0067             if (textDecorations.contains(KoSvgText::DecorationUnderline)) {
0068                 Q_FOREACH(const KoShape::PaintOrder p, paintOrder) {
0069                     if (p == KoShape::Fill) {
0070                         if (background && !textDecorationColor.isValid() && textDecorationColor != Qt::transparent) {
0071                             background->paint(painter, textDecorations.value(KoSvgText::DecorationUnderline));
0072                         } else if (textDecorationColor.isValid()) {
0073                             painter.fillPath(textDecorations.value(KoSvgText::DecorationUnderline), textDecorationColor);
0074                         }
0075                     } else if (p == KoShape::Stroke) {
0076                         if (stroke) {
0077                             QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(textDecorations.value(KoSvgText::DecorationUnderline)));
0078                             stroke->paint(shape.data(), painter);
0079                         }
0080                     }
0081                 }
0082 
0083             }
0084             if (textDecorations.contains(KoSvgText::DecorationOverline)) {
0085                 Q_FOREACH(const KoShape::PaintOrder p, paintOrder) {
0086                     if (p == KoShape::Fill) {
0087                         if (background && !textDecorationColor.isValid() && textDecorationColor != Qt::transparent) {
0088                             background->paint(painter, textDecorations.value(KoSvgText::DecorationOverline));
0089                         } else if (textDecorationColor.isValid()) {
0090                             painter.fillPath(textDecorations.value(KoSvgText::DecorationOverline), textDecorationColor);
0091                         }
0092                     } else if (p == KoShape::Stroke) {
0093                         if (stroke) {
0094                             QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(textDecorations.value(KoSvgText::DecorationOverline)));
0095                             stroke->paint(shape.data(), painter);
0096                         }
0097                     }
0098                 }
0099             }
0100 
0101             if (childCount(siblingCurrent(it)) == 0) {
0102                 const int j = currentIndex + it->numChars(true);
0103 
0104                 const QRect shapeGlobalClipRect = painter.transform().mapRect(it->associatedOutline.boundingRect()).toAlignedRect();
0105 
0106                 if (shapeGlobalClipRect.isValid()) {
0107                     KoClipMaskPainter fillPainter(&painter, shapeGlobalClipRect);
0108                     if (background) {
0109                         background->paint(*fillPainter.shapePainter(), rootOutline);
0110                         fillPainter.maskPainter()->fillPath(rootOutline, Qt::black);
0111                         if (textRendering != OptimizeSpeed) {
0112                             fillPainter.maskPainter()->setRenderHint(QPainter::Antialiasing, true);
0113                             fillPainter.maskPainter()->setRenderHint(QPainter::SmoothPixmapTransform, true);
0114                         } else {
0115                             fillPainter.maskPainter()->setRenderHint(QPainter::Antialiasing, false);
0116                             fillPainter.maskPainter()->setRenderHint(QPainter::SmoothPixmapTransform, false);
0117                         }
0118                     }
0119                     QPainterPath textDecorationsRest;
0120                     textDecorationsRest.setFillRule(Qt::WindingFill);
0121 
0122                     for (int i = currentIndex; i < j; i++) {
0123                         if (result.at(i).addressable && !result.at(i).hidden) {
0124                             const QTransform tf = result.at(i).finalTransform();
0125 
0126                             /**
0127                      * Make sure the character touches the painter's clip rect,
0128                      * otherwise we can just skip it
0129                      */
0130                             const QRectF boundingRect = tf.mapRect(result.at(i).boundingBox);
0131                             const QRectF clipRect = painter.clipBoundingRect();
0132                             if (boundingRect.isEmpty() ||
0133                                     (!clipRect.contains(boundingRect) &&
0134                                      !clipRect.intersects(boundingRect))) continue;
0135 
0136                             /**
0137                      * There's an annoying problem here that officially speaking
0138                      * the chunks need to be unified into one single path before
0139                      * drawing, so there's no weirdness with the stroke, but
0140                      * QPainterPath's union function will frequently lead to
0141                      * reduced quality of the paths because of 'numerical
0142                      * instability'.
0143                      */
0144 
0145                             if (const auto *colorGlyph = std::get_if<Glyph::ColorLayers>(&result.at(i).glyph)) {
0146                                 for (int c = 0; c < colorGlyph->paths.size(); c++) {
0147                                     QBrush color = colorGlyph->colors.at(c);
0148                                     bool replace = colorGlyph->replaceWithForeGroundColor.at(c);
0149                                     // In theory we can use the pattern or gradient as well
0150                                     // for ColorV0 fonts, but ColorV1 fonts can have
0151                                     // gradients, so I am hesitant.
0152                                     KoColorBackground *b = dynamic_cast<KoColorBackground *>(background.data());
0153                                     if (b && replace) {
0154                                         color = b->brush();
0155                                     }
0156                                     painter.fillPath(tf.map(colorGlyph->paths.at(c)), color);
0157                                 }
0158                             } else if (const auto *outlineGlyph = std::get_if<Glyph::Outline>(&result.at(i).glyph)) {
0159                                 chunk.addPath(tf.map(outlineGlyph->path));
0160                             } else if (const auto *bitmapGlyph = std::get_if<Glyph::Bitmap>(&result.at(i).glyph)) {
0161                                 if (bitmapGlyph->image.isGrayscale() || bitmapGlyph->image.format() == QImage::Format_Mono) {
0162                                     fillPainter.maskPainter()->save();
0163                                     fillPainter.maskPainter()->translate(result.at(i).finalPosition.x(), result.at(i).finalPosition.y());
0164                                     fillPainter.maskPainter()->rotate(qRadiansToDegrees(result.at(i).rotate));
0165                                     fillPainter.maskPainter()->setCompositionMode(QPainter::CompositionMode_Plus);
0166                                     fillPainter.maskPainter()->drawImage(bitmapGlyph->drawRect, bitmapGlyph->image);
0167                                     fillPainter.maskPainter()->restore();
0168                                 } else {
0169                                     painter.save();
0170                                     painter.translate(result.at(i).finalPosition.x(), result.at(i).finalPosition.y());
0171                                     painter.rotate(qRadiansToDegrees(result.at(i).rotate));
0172                                     painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
0173                                     painter.drawImage(bitmapGlyph->drawRect, bitmapGlyph->image);
0174                                     painter.restore();
0175                                 }
0176                             }
0177                         }
0178                     }
0179                     Q_FOREACH(const KoShape::PaintOrder p, paintOrder) {
0180                         if (p == KoShape::Fill) {
0181                             if (background) {
0182                                 chunk.setFillRule(Qt::WindingFill);
0183                                 fillPainter.maskPainter()->fillPath(chunk, Qt::white);
0184                             }
0185                             if (!textDecorationsRest.isEmpty()) {
0186                                 fillPainter.maskPainter()->fillPath(textDecorationsRest.simplified(), Qt::white);
0187                             }
0188                             fillPainter.renderOnGlobalPainter();
0189                         } else if (p == KoShape::Stroke) {
0190                             KoShapeStrokeSP maskStroke;
0191                             if (stroke) {
0192                                 KoShapeStrokeSP strokeSP = qSharedPointerDynamicCast<KoShapeStroke>(stroke);
0193 
0194                                 if (strokeSP) {
0195                                     if (strokeSP->lineBrush().gradient()) {
0196                                         KoClipMaskPainter strokePainter(&painter, shapeGlobalClipRect);
0197                                         strokePainter.shapePainter()->fillRect(rootOutline.boundingRect(), strokeSP->lineBrush());
0198                                         maskStroke = KoShapeStrokeSP(new KoShapeStroke(*strokeSP.data()));
0199                                         maskStroke->setColor(Qt::white);
0200                                         maskStroke->setLineBrush(Qt::white);
0201                                         strokePainter.maskPainter()->fillPath(rootOutline, Qt::black);
0202                                         if (textRendering != OptimizeSpeed) {
0203                                             strokePainter.maskPainter()->setRenderHint(QPainter::Antialiasing, true);
0204                                         } else {
0205                                             strokePainter.maskPainter()->setRenderHint(QPainter::Antialiasing, false);
0206                                         }
0207                                         {
0208                                             QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(chunk));
0209                                             maskStroke->paint(shape.data(), *strokePainter.maskPainter());
0210                                         }
0211                                         if (!textDecorationsRest.isEmpty()) {
0212                                             QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(textDecorationsRest));
0213                                             maskStroke->paint(shape.data(), *strokePainter.maskPainter());
0214                                         }
0215                                         strokePainter.renderOnGlobalPainter();
0216                                     } else {
0217                                         {
0218                                             QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(chunk));
0219                                             stroke->paint(shape.data(), painter);
0220                                         }
0221                                         if (!textDecorationsRest.isEmpty()) {
0222                                             QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(textDecorationsRest));
0223                                             stroke->paint(shape.data(), painter);
0224                                         }
0225                                     }
0226                                 }
0227                             }
0228                         }
0229                     }
0230                 }
0231                 chunk = QPainterPath();
0232                 currentIndex = j;
0233             }
0234         }
0235 
0236         if (it.state() == KisForestDetail::Leave) {
0237             inheritPaintProperties(it, stroke, background, paintOrder);
0238             QMap<KoSvgText::TextDecoration, QPainterPath> textDecorations = it->textDecorations;
0239             QColor textDecorationColor = it->properties.propertyOrDefault(KoSvgTextProperties::TextDecorationColorId).value<QColor>();
0240             if (textDecorations.contains(KoSvgText::DecorationLineThrough)) {
0241                 Q_FOREACH(const KoShape::PaintOrder p, paintOrder) {
0242                     if (p == KoShape::Fill) {
0243                         if (background && !textDecorationColor.isValid() && textDecorationColor != Qt::transparent) {
0244                             background->paint(painter, textDecorations.value(KoSvgText::DecorationLineThrough));
0245                         } else if (textDecorationColor.isValid()) {
0246                             painter.fillPath(textDecorations.value(KoSvgText::DecorationLineThrough), textDecorationColor);
0247                         }
0248                     } else if (p == KoShape::Stroke) {
0249                         if (stroke) {
0250                             QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(textDecorations.value(KoSvgText::DecorationLineThrough)));
0251                             stroke->paint(shape.data(), painter);
0252                         }
0253                     }
0254                 }
0255             }
0256         }
0257     }
0258 }
0259 
0260 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
0261 QList<KoShape *>
0262 KoSvgTextShape::Private::collectPaths(const KoShape *rootShape, QVector<CharacterResult> &result, int &currentIndex)
0263 {
0264 
0265     QList<KoShape *> shapes;
0266     KoShapeStrokeModelSP stroke = rootShape->stroke();
0267     QSharedPointer<KoShapeBackground> background = rootShape->background();
0268     QVector<KoShape::PaintOrder> paintOrder = rootShape->paintOrder();
0269 
0270     for (auto it = compositionBegin(textData); it != compositionEnd(textData); it++) {
0271         bool hasPaintOrder = it->properties.hasProperty(KoSvgTextProperties::PaintOrder);
0272         inheritPaintProperties(it, stroke, background, paintOrder);
0273         QMap<KoSvgText::TextDecoration, QPainterPath> textDecorations = it->textDecorations;
0274         QColor textDecorationColor = it->properties.propertyOrDefault(KoSvgTextProperties::TextDecorationColorId).value<QColor>();
0275         QSharedPointer<KoShapeBackground> decorationColor = background;
0276         if (textDecorationColor.isValid()) {
0277             decorationColor = QSharedPointer<KoColorBackground>(new KoColorBackground(textDecorationColor));
0278         }
0279 
0280         KoShape::PaintOrder first = paintOrder.at(0);
0281         KoShape::PaintOrder second = paintOrder.at(1);
0282 
0283         if (it.state() == KisForestDetail::Enter) {
0284 
0285 
0286             if (textDecorations.contains(KoSvgText::DecorationUnderline)) {
0287                 KoPathShape *shape = KoPathShape::createShapeFromPainterPath(textDecorations.value(KoSvgText::DecorationUnderline));
0288                 shape->setBackground(decorationColor);
0289                 shape->setStroke(stroke);
0290                 shape->setZIndex(shapes.size());
0291                 shape->setFillRule(Qt::WindingFill);
0292                 if (hasPaintOrder)
0293                     shape->setPaintOrder(first, second);
0294                 shapes.append(shape);
0295             }
0296             if (textDecorations.contains(KoSvgText::DecorationOverline)) {
0297                 KoPathShape *shape = KoPathShape::createShapeFromPainterPath(textDecorations.value(KoSvgText::DecorationOverline));
0298                 shape->setBackground(decorationColor);
0299                 shape->setStroke(stroke);
0300                 shape->setZIndex(shapes.size());
0301                 shape->setFillRule(Qt::WindingFill);
0302                 if (hasPaintOrder)
0303                     shape->setPaintOrder(first, second);
0304                 shapes.append(shape);
0305             }
0306 
0307             if (childCount(siblingCurrent(it)) == 0) {
0308                 QPainterPath chunk;
0309 
0310                 const int j = currentIndex + it->numChars(true);
0311                 for (int i = currentIndex; i < j; i++) {
0312                     if (result.at(i).addressable && !result.at(i).hidden) {
0313                         const QTransform tf = result.at(i).finalTransform();
0314                         if (const auto *colorGlyph = std::get_if<Glyph::ColorLayers>(&result.at(i).glyph)) {
0315                             for (int c = 0; c < colorGlyph->paths.size(); c++) {
0316                                 QBrush color = colorGlyph->colors.at(c);
0317                                 bool replace = colorGlyph->replaceWithForeGroundColor.at(c);
0318                                 // In theory we can use the pattern or gradient as well
0319                                 // for ColorV0 fonts, but ColorV1 fonts can have
0320                                 // gradients, so I am hesitant.
0321                                 KoColorBackground *b = dynamic_cast<KoColorBackground *>(background.data());
0322                                 if (b && replace) {
0323                                     color = b->brush();
0324                                 }
0325                                 KoPathShape *shape = KoPathShape::createShapeFromPainterPath(tf.map(colorGlyph->paths.at(c)));
0326                                 shape->setBackground(QSharedPointer<KoColorBackground>(new KoColorBackground(color.color())));
0327                                 shape->setZIndex(shapes.size());
0328                                 shape->setFillRule(Qt::WindingFill);
0329                                 shape->setPaintOrder(first, second);
0330                                 shapes.append(shape);
0331                             }
0332                         } else if (const auto *outlineGlyph = std::get_if<Glyph::Outline>(&result.at(i).glyph)) {
0333                             chunk.addPath(tf.map(outlineGlyph->path));
0334                         }
0335                     }
0336                 }
0337                 KoPathShape *shape = KoPathShape::createShapeFromPainterPath(chunk);
0338                 shape->setBackground(background);
0339                 shape->setStroke(stroke);
0340                 shape->setZIndex(shapes.size());
0341                 shape->setFillRule(Qt::WindingFill);
0342                 if (hasPaintOrder)
0343                     shape->setPaintOrder(first, second);
0344                 shapes.append(shape);
0345                 currentIndex = j;
0346 
0347             }
0348         }
0349         if (it.state() ==KisForestDetail::Leave) {
0350             inheritPaintProperties(it, stroke, background, paintOrder);
0351             if (textDecorations.contains(KoSvgText::DecorationLineThrough)) {
0352                 KoPathShape *shape = KoPathShape::createShapeFromPainterPath(textDecorations.value(KoSvgText::DecorationLineThrough));
0353                 shape->setBackground(decorationColor);
0354                 shape->setStroke(stroke);
0355                 shape->setZIndex(shapes.size());
0356                 shape->setFillRule(Qt::WindingFill);
0357                 shape->setPaintOrder(first, second);
0358                 shapes.append(shape);
0359             }
0360         }
0361     }
0362     return shapes;
0363 }
0364 
0365 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
0366 void KoSvgTextShape::Private::paintDebug(QPainter &painter,
0367                                          const QVector<CharacterResult> &result,
0368                                          int &currentIndex)
0369 {
0370 
0371     for (auto it = textData.depthFirstTailBegin(); it != textData.depthFirstTailEnd(); it++) {
0372         const int j = currentIndex + it->numChars(true);
0373 
0374         const QRect shapeGlobalClipRect = painter.transform().mapRect(it->associatedOutline.boundingRect()).toAlignedRect();
0375 
0376         painter.save();
0377 
0378         QFont font(QFont(), painter.device());
0379         font.setPointSizeF(16.0);
0380 
0381         if (shapeGlobalClipRect.isValid() && childCount(siblingCurrent(it)) == 0) {
0382             for (int i = currentIndex; i < j; i++) {
0383                 if (result.at(i).addressable && !result.at(i).hidden) {
0384                     const QTransform tf = result.at(i).finalTransform();
0385 
0386 #if 1 // Debug: draw character bounding boxes
0387                     painter.setBrush(Qt::transparent);
0388                     QPen pen(QColor(0, 0, 0, 50));
0389                     pen.setCosmetic(true);
0390                     pen.setWidth(2);
0391                     painter.setPen(pen);
0392                     if (const auto *bitmapGlyph = std::get_if<Glyph::Bitmap>(&result.at(i).glyph)) {
0393                         painter.drawPolygon(tf.map(bitmapGlyph->drawRect));
0394                     } else if (const auto *colorGlyph = std::get_if<Glyph::ColorLayers>(&result.at(i).glyph)) {
0395                         QRectF boundingRect;
0396                         Q_FOREACH (const QPainterPath &p, colorGlyph->paths) {
0397                             boundingRect |= p.boundingRect();
0398                         }
0399                         painter.drawPolygon(tf.map(boundingRect));
0400                     } else if (const auto *outlineGlyph = std::get_if<Glyph::Outline>(&result.at(i).glyph)) {
0401                         painter.drawPolygon(tf.map(outlineGlyph->path.boundingRect()));
0402                     }
0403                     QColor penColor = result.at(i).anchored_chunk ? result.at(i).isHanging ? Qt::red : Qt::magenta
0404                         : result.at(i).lineEnd == LineEdgeBehaviour::NoChange ? Qt::cyan
0405                                                                               : Qt::yellow;
0406                     penColor.setAlpha(192);
0407                     pen.setColor(penColor);
0408                     painter.setPen(pen);
0409                     painter.drawPolygon(tf.map(result.at(i).boundingBox));
0410 
0411                     penColor.setAlpha(96);
0412                     pen.setColor(penColor);
0413                     pen.setWidth(1);
0414                     pen.setStyle(Qt::DotLine);
0415                     painter.setPen(pen);
0416                     painter.drawPolygon(tf.map(result.at(i).lineHeightBox));
0417 
0418                     pen.setWidth(2);
0419                     pen.setStyle(Qt::SolidLine);
0420 
0421                     const QPointF center = tf.mapRect(result.at(i).boundingBox).center();
0422                     QString text = "#";
0423                     text += QString::number(i);
0424                     {
0425                         // Find the range of this typographic character
0426                         int end = i + 1;
0427                         while (end < result.size() && result[end].middle) {
0428                             end++;
0429                         }
0430                         end--;
0431                         if (end != i) {
0432                             text += "~";
0433                             text += QString::number(end);
0434                         }
0435                     }
0436                     text += QString("\n(%1)").arg(result.at(i).plaintTextIndex);
0437                     painter.setWorldMatrixEnabled(false);
0438                     painter.setPen(Qt::red);
0439                     painter.drawText(QRectF(painter.transform().map(center), QSizeF(0, 64)).adjusted(-128, 0, 128, 0),
0440                                      Qt::AlignHCenter | Qt::AlignTop,
0441                                      text);
0442                     painter.setWorldMatrixEnabled(true);
0443 
0444                     pen.setWidth(6);
0445                     const BreakType breakType = result.at(i).breakType;
0446                     if (breakType == BreakType::SoftBreak || breakType == BreakType::HardBreak) {
0447                         if (breakType == BreakType::SoftBreak) {
0448                             penColor = Qt::blue;
0449                         } else if (breakType == BreakType::HardBreak) {
0450                             penColor = Qt::red;
0451                         }
0452                         penColor.setAlpha(128);
0453                         pen.setColor(penColor);
0454                         painter.setPen(pen);
0455                         painter.drawPoint(center);
0456                     }
0457                     //ligature carets
0458                     penColor = Qt::darkGreen;
0459                     penColor.setAlpha(192);
0460                     pen.setColor(penColor);
0461                     painter.setPen(pen);
0462                     QVector<QPointF> offset = result.at(i).cursorInfo.offsets;
0463                     for (int k=0; k<offset.size(); k++) {
0464                         painter.drawPoint(tf.map(offset.at(k)));
0465                     }
0466                     // Finalpos
0467                     penColor = Qt::red;
0468                     penColor.setAlpha(192);
0469                     pen.setColor(penColor);
0470                     painter.setPen(pen);
0471                     painter.drawPoint(result.at(i).finalPosition);
0472 #endif
0473                 }
0474             }
0475         }
0476         painter.restore();
0477         currentIndex = j;
0478     }
0479 }