File indexing completed on 2024-05-12 15:58:35

0001 /*
0002  *  SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
0003  *  SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
0004  *  SPDX-FileCopyrightText: 2004 Clarence Dang <dang@kde.org>
0005  *  SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com>
0006  *  SPDX-FileCopyrightText: 2004 Cyrille Berger <cberger@cberger.net>
0007  *  SPDX-FileCopyrightText: 2008-2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0008  *  SPDX-FileCopyrightText: 2010 José Luis Vergara Toloza <pentalis@gmail.com>
0009  *  SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
0010  *
0011  *  SPDX-License-Identifier: GPL-2.0-or-later
0012  */
0013 
0014 #include "kis_painter.h"
0015 #include <stdlib.h>
0016 #include <string.h>
0017 #include <cfloat>
0018 #include <cmath>
0019 #include <climits>
0020 #ifndef Q_OS_WIN
0021 #include <strings.h>
0022 #endif
0023 
0024 #include <QImage>
0025 #include <QRect>
0026 #include <QString>
0027 #include <QStringList>
0028 #include <kundo2command.h>
0029 
0030 #include <kis_debug.h>
0031 #include <klocalizedstring.h>
0032 
0033 #include "kis_image.h"
0034 #include "filter/kis_filter.h"
0035 #include "kis_layer.h"
0036 #include "kis_paint_device.h"
0037 #include "kis_fixed_paint_device.h"
0038 #include "kis_transaction.h"
0039 #include "kis_vec.h"
0040 #include "kis_iterator_ng.h"
0041 #include "kis_random_accessor_ng.h"
0042 
0043 #include "filter/kis_filter_configuration.h"
0044 #include "kis_pixel_selection.h"
0045 #include <brushengine/kis_paint_information.h>
0046 #include "kis_paintop_registry.h"
0047 #include "kis_perspective_math.h"
0048 #include "tiles3/kis_random_accessor.h"
0049 #include <kis_distance_information.h>
0050 #include <KoColorSpaceMaths.h>
0051 #include "kis_lod_transform.h"
0052 #include "kis_algebra_2d.h"
0053 #include "krita_utils.h"
0054 
0055 
0056 // Maximum distance from a Bezier control point to the line through the start
0057 // and end points for the curve to be considered flat.
0058 #define BEZIER_FLATNESS_THRESHOLD 0.5
0059 
0060 #include "kis_painter_p.h"
0061 
0062 KisPainter::KisPainter()
0063     : d(new Private(this))
0064 {
0065     init();
0066 }
0067 
0068 KisPainter::KisPainter(KisPaintDeviceSP device)
0069     : d(new Private(this, device->colorSpace()))
0070 {
0071     init();
0072     Q_ASSERT(device);
0073     begin(device);
0074 }
0075 
0076 KisPainter::KisPainter(KisPaintDeviceSP device, KisSelectionSP selection)
0077     : d(new Private(this, device->colorSpace()))
0078 {
0079     init();
0080     Q_ASSERT(device);
0081     begin(device);
0082     d->selection = selection;
0083 }
0084 
0085 void KisPainter::init()
0086 {
0087     d->paramInfo = KoCompositeOp::ParameterInfo();
0088     d->renderingIntent = KoColorConversionTransformation::internalRenderingIntent();
0089     d->conversionFlags = KoColorConversionTransformation::internalConversionFlags();
0090     d->patternTransform = QTransform();
0091 }
0092 
0093 KisPainter::~KisPainter()
0094 {
0095     // TODO: Maybe, don't be that strict?
0096     // deleteTransaction();
0097     end();
0098 
0099     delete d->paintOp;
0100     delete d->maskPainter;
0101     delete d->fillPainter;
0102     delete d;
0103 }
0104 
0105 template <bool useOldData>
0106 void copyAreaOptimizedImpl(const QPoint &dstPt,
0107                            KisPaintDeviceSP src,
0108                            KisPaintDeviceSP dst,
0109                            const QRect &srcRect)
0110 {
0111     const QRect dstRect(dstPt, srcRect.size());
0112 
0113     const QRect srcExtent = src->extent();
0114     const QRect dstExtent = dst->extent();
0115 
0116     const QRect srcSampleRect = srcExtent & srcRect;
0117     const QRect dstSampleRect = dstExtent & dstRect;
0118 
0119     const bool srcEmpty = srcSampleRect.isEmpty();
0120     const bool dstEmpty = dstSampleRect.isEmpty();
0121 
0122     if (!srcEmpty || !dstEmpty) {
0123         if (srcEmpty) {
0124             dst->clear(dstRect);
0125         } else {
0126             QRect srcCopyRect = srcRect;
0127             QRect dstCopyRect = dstRect;
0128 
0129             if (!srcExtent.contains(srcRect)) {
0130                 if (src->defaultPixel() == dst->defaultPixel()) {
0131                     const QRect dstSampleInSrcCoords = dstSampleRect.translated(srcRect.topLeft() - dstPt);
0132 
0133                     if (dstSampleInSrcCoords.isEmpty() || srcSampleRect.contains(dstSampleInSrcCoords)) {
0134                         srcCopyRect = srcSampleRect;
0135                     } else {
0136                         srcCopyRect = srcSampleRect | dstSampleInSrcCoords;
0137                     }
0138                     dstCopyRect = QRect(dstPt + srcCopyRect.topLeft() - srcRect.topLeft(), srcCopyRect.size());
0139                 }
0140             }
0141 
0142             KisPainter gc(dst);
0143             gc.setCompositeOpId(COMPOSITE_COPY);
0144 
0145             if (useOldData) {
0146                 gc.bitBltOldData(dstCopyRect.topLeft(), src, srcCopyRect);
0147             } else {
0148                 gc.bitBlt(dstCopyRect.topLeft(), src, srcCopyRect);
0149             }
0150         }
0151     }
0152 }
0153 
0154 void KisPainter::copyAreaOptimized(const QPoint &dstPt,
0155                                    KisPaintDeviceSP src,
0156                                    KisPaintDeviceSP dst,
0157                                    const QRect &srcRect)
0158 {
0159     copyAreaOptimizedImpl<false>(dstPt, src, dst, srcRect);
0160 }
0161 
0162 void KisPainter::copyAreaOptimizedOldData(const QPoint &dstPt,
0163                                           KisPaintDeviceSP src,
0164                                           KisPaintDeviceSP dst,
0165                                           const QRect &srcRect)
0166 {
0167     copyAreaOptimizedImpl<true>(dstPt, src, dst, srcRect);
0168 }
0169 
0170 void KisPainter::copyAreaOptimized(const QPoint &dstPt,
0171                                    KisPaintDeviceSP src,
0172                                    KisPaintDeviceSP dst,
0173                                    const QRect &originalSrcRect,
0174                                    KisSelectionSP selection)
0175 {
0176     if (!selection) {
0177         copyAreaOptimized(dstPt, src, dst, originalSrcRect);
0178         return;
0179     }
0180 
0181     const QRect selectionRect = selection->selectedRect();
0182     const QRect srcRect = originalSrcRect & selectionRect;
0183     const QPoint dstOffset = srcRect.topLeft() - originalSrcRect.topLeft();
0184     const QRect dstRect = QRect(dstPt + dstOffset, srcRect.size());
0185 
0186     const bool srcEmpty = (src->extent() & srcRect).isEmpty();
0187     const bool dstEmpty = (dst->extent() & dstRect).isEmpty();
0188 
0189     if (!srcEmpty || !dstEmpty) {
0190         //if (srcEmpty) {
0191         // doesn't support dstRect
0192         // dst->clearSelection(selection);
0193         // } else */
0194         {
0195             KisPainter gc(dst);
0196             gc.setSelection(selection);
0197             gc.setCompositeOpId(COMPOSITE_COPY);
0198             gc.bitBlt(dstRect.topLeft(), src, srcRect);
0199         }
0200     }
0201 }
0202 
0203 KisPaintDeviceSP KisPainter::convertToAlphaAsAlpha(KisPaintDeviceSP src)
0204 {
0205     const KoColorSpace *srcCS = src->colorSpace();
0206     const QRect processRect = src->extent();
0207     KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
0208 
0209     if (processRect.isEmpty()) return dst;
0210 
0211     KisSequentialConstIterator srcIt(src, processRect);
0212     KisSequentialIterator dstIt(dst, processRect);
0213 
0214     while (srcIt.nextPixel() && dstIt.nextPixel()) {
0215         const quint8 *srcPtr = srcIt.rawDataConst();
0216         quint8 *alpha8Ptr = dstIt.rawData();
0217 
0218         const quint8 white = srcCS->intensity8(srcPtr);
0219         const quint8 alpha = srcCS->opacityU8(srcPtr);
0220 
0221         *alpha8Ptr = KoColorSpaceMaths<quint8>::multiply(alpha, KoColorSpaceMathsTraits<quint8>::unitValue - white);
0222     }
0223 
0224     return dst;
0225 }
0226 
0227 KisPaintDeviceSP KisPainter::convertToAlphaAsGray(KisPaintDeviceSP src)
0228 {
0229     const KoColorSpace *srcCS = src->colorSpace();
0230     const QRect processRect = src->extent();
0231     KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
0232 
0233     if (processRect.isEmpty()) return dst;
0234 
0235     KisSequentialConstIterator srcIt(src, processRect);
0236     KisSequentialIterator dstIt(dst, processRect);
0237 
0238     while (srcIt.nextPixel() && dstIt.nextPixel()) {
0239         const quint8 *srcPtr = srcIt.rawDataConst();
0240         quint8 *alpha8Ptr = dstIt.rawData();
0241 
0242         *alpha8Ptr = srcCS->intensity8(srcPtr);
0243     }
0244 
0245     return dst;
0246 }
0247 
0248 KisPaintDeviceSP KisPainter::convertToAlphaAsPureAlpha(KisPaintDeviceSP src)
0249 {
0250     const KoColorSpace *srcCS = src->colorSpace();
0251     const QRect processRect = src->extent();
0252     KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
0253 
0254     if (processRect.isEmpty()) return dst;
0255 
0256     KisSequentialConstIterator srcIt(src, processRect);
0257     KisSequentialIterator dstIt(dst, processRect);
0258 
0259     while (srcIt.nextPixel() && dstIt.nextPixel()) {
0260         const quint8 *srcPtr = srcIt.rawDataConst();
0261         quint8 *alpha8Ptr = dstIt.rawData();
0262 
0263         *alpha8Ptr = srcCS->opacityU8(srcPtr);
0264     }
0265 
0266     return dst;
0267 }
0268 
0269 bool KisPainter::checkDeviceHasTransparency(KisPaintDeviceSP dev)
0270 {
0271     const QRect deviceBounds = dev->exactBounds();
0272     const QRect imageBounds = dev->defaultBounds()->bounds();
0273 
0274     if (deviceBounds.isEmpty() ||
0275         (deviceBounds & imageBounds) != imageBounds) {
0276 
0277         return true;
0278     }
0279 
0280     const KoColorSpace *cs = dev->colorSpace();
0281     KisSequentialConstIterator it(dev, deviceBounds);
0282 
0283     while(it.nextPixel()) {
0284         if (cs->opacityU8(it.rawDataConst()) != OPACITY_OPAQUE_U8) {
0285             return true;
0286         }
0287     }
0288 
0289     return false;
0290 }
0291 
0292 void KisPainter::begin(KisPaintDeviceSP device)
0293 {
0294     begin(device, d->selection);
0295 }
0296 
0297 void KisPainter::begin(KisPaintDeviceSP device, KisSelectionSP selection)
0298 {
0299     if (!device) return;
0300     d->selection = selection;
0301     Q_ASSERT(device->colorSpace());
0302 
0303     end();
0304 
0305     d->device = device;
0306     d->colorSpace = device->colorSpace();
0307     d->compositeOpId = COMPOSITE_OVER;
0308     d->cachedCompositeOp = nullptr;
0309     d->pixelSize = device->pixelSize();
0310 }
0311 
0312 void KisPainter::end()
0313 {
0314     Q_ASSERT_X(!d->transaction, "KisPainter::end()",
0315                "end() was called for the painter having a transaction. "
0316                "Please use end/deleteTransaction() instead");
0317 }
0318 
0319 void KisPainter::beginTransaction(const KUndo2MagicString& transactionName,int timedID)
0320 {
0321     Q_ASSERT_X(!d->transaction, "KisPainter::beginTransaction()",
0322                "You asked for a new transaction while still having "
0323                "another one. Please finish the first one with "
0324                "end/deleteTransaction() first");
0325 
0326     d->transaction = new KisTransaction(transactionName, d->device);
0327     Q_CHECK_PTR(d->transaction);
0328     d->transaction->undoCommand()->setTimedID(timedID);
0329 }
0330 
0331 void KisPainter::revertTransaction()
0332 {
0333     Q_ASSERT_X(d->transaction, "KisPainter::revertTransaction()",
0334                "No transaction is in progress");
0335 
0336     d->transaction->revert();
0337     delete d->transaction;
0338     d->transaction = 0;
0339 }
0340 
0341 void KisPainter::endTransaction(KisUndoAdapter *undoAdapter)
0342 {
0343     Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
0344                "No transaction is in progress");
0345 
0346     d->transaction->commit(undoAdapter);
0347     delete d->transaction;
0348     d->transaction = 0;
0349 }
0350 
0351 void KisPainter::endTransaction(KisPostExecutionUndoAdapter *undoAdapter)
0352 {
0353     Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
0354                "No transaction is in progress");
0355 
0356     d->transaction->commit(undoAdapter);
0357     delete d->transaction;
0358     d->transaction = 0;
0359 }
0360 
0361 KUndo2Command* KisPainter::endAndTakeTransaction()
0362 {
0363     Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
0364                "No transaction is in progress");
0365 
0366     KUndo2Command *transactionData = d->transaction->endAndTake();
0367     delete d->transaction;
0368     d->transaction = 0;
0369 
0370     return transactionData;
0371 }
0372 
0373 void KisPainter::deleteTransaction()
0374 {
0375     if (!d->transaction) return;
0376 
0377     delete d->transaction;
0378     d->transaction = 0;
0379 }
0380 
0381 void KisPainter::putTransaction(KisTransaction* transaction)
0382 {
0383     Q_ASSERT_X(!d->transaction, "KisPainter::putTransaction()",
0384                "You asked for a new transaction while still having "
0385                "another one. Please finish the first one with "
0386                "end/deleteTransaction() first");
0387 
0388     d->transaction = transaction;
0389 }
0390 
0391 KisTransaction* KisPainter::takeTransaction()
0392 {
0393     Q_ASSERT_X(d->transaction, "KisPainter::takeTransaction()",
0394                "No transaction is in progress");
0395     KisTransaction *temp = d->transaction;
0396     d->transaction = 0;
0397     return temp;
0398 }
0399 
0400 
0401 
0402 QVector<QRect> KisPainter::takeDirtyRegion()
0403 {
0404     QVector<QRect> vrect = d->dirtyRects;
0405     d->dirtyRects.clear();
0406     return vrect;
0407 }
0408 
0409 
0410 void KisPainter::addDirtyRect(const QRect & rc)
0411 {
0412     QRect r = rc.normalized();
0413     if (r.isValid()) {
0414         d->dirtyRects.append(rc);
0415     }
0416 }
0417 
0418 void KisPainter::addDirtyRects(const QVector<QRect> &rects)
0419 {
0420     d->dirtyRects.reserve(d->dirtyRects.size() + rects.size());
0421 
0422     Q_FOREACH (const QRect &rc, rects) {
0423         const QRect r = rc.normalized();
0424         if (r.isValid()) {
0425             d->dirtyRects.append(rc);
0426         }
0427     }
0428 }
0429 
0430 const KoCompositeOp *KisPainter::Private::compositeOp(const KoColorSpace *srcCS)
0431 {
0432     if (!cachedCompositeOp || !cachedSourceColorSpace || !(*cachedSourceColorSpace == *srcCS)) {
0433         cachedCompositeOp = colorSpace->compositeOp(compositeOpId, srcCS);
0434         cachedSourceColorSpace = srcCS;
0435         KIS_ASSERT(cachedCompositeOp);
0436     }
0437     return cachedCompositeOp;
0438 }
0439 
0440 inline bool KisPainter::Private::tryReduceSourceRect(const KisPaintDevice *srcDev,
0441                                                      QRect *srcRect,
0442                                                      qint32 *srcX,
0443                                                      qint32 *srcY,
0444                                                      qint32 *srcWidth,
0445                                                      qint32 *srcHeight,
0446                                                      qint32 *dstX,
0447                                                      qint32 *dstY)
0448 {
0449     bool needsReadjustParams = false;
0450 
0451     /**
0452      * In case of COMPOSITE_COPY and Wrap Around Mode even the pixels
0453      * outside the device extent matter, because they will be either
0454      * directly copied (former case) or cloned from another area of
0455      * the image.
0456      */
0457     if (compositeOpId != COMPOSITE_COPY &&
0458         compositeOpId != COMPOSITE_DESTINATION_IN  &&
0459         compositeOpId != COMPOSITE_DESTINATION_ATOP &&
0460         !srcDev->defaultBounds()->wrapAroundMode()) {
0461 
0462         /**
0463          * If srcDev->extent() (the area of the tiles containing
0464          * srcDev) is smaller than srcRect, then shrink srcRect to
0465          * that size. This is done as a speed optimization, useful for
0466          * stack recomposition in KisImage. srcRect won't grow if
0467          * srcDev->extent() is larger.
0468          */
0469         *srcRect &= srcDev->extent();
0470 
0471         if (srcRect->isEmpty()) return true;
0472         needsReadjustParams = true;
0473     }
0474 
0475     if (selection) {
0476         /**
0477          * We should also crop the blitted area by the selected region,
0478          * because we cannot paint outside the selection.
0479          */
0480         *srcRect &= selection->selectedRect().translated(*srcX - *dstX,
0481                                                          *srcY - *dstY);
0482 
0483         if (srcRect->isEmpty()) return true;
0484         needsReadjustParams = true;
0485     }
0486 
0487     if (!paramInfo.channelFlags.isEmpty()) {
0488         const QBitArray onlyColor = colorSpace->channelFlags(true, false);
0489         KIS_SAFE_ASSERT_RECOVER_NOOP(onlyColor.size() == paramInfo.channelFlags.size());
0490 
0491         // check if we have alpha channel locked
0492         if ((paramInfo.channelFlags & onlyColor) == paramInfo.channelFlags) {
0493             *srcRect &= device->extent().translated(*srcX - *dstX,
0494                                                     *srcY - *dstY);
0495 
0496             if (srcRect->isEmpty()) return true;
0497             needsReadjustParams = true;
0498         }
0499     }
0500 
0501     if (needsReadjustParams) {
0502         // Readjust the function parameters to the new dimensions.
0503         *dstX += srcRect->x() - *srcX;    // This will only add, not subtract
0504         *dstY += srcRect->y() - *srcY;    // Idem
0505         srcRect->getRect(srcX, srcY, srcWidth, srcHeight);
0506     }
0507 
0508     return false;
0509 }
0510 
0511 void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
0512                                           const KisPaintDeviceSP srcDev,
0513                                           const KisFixedPaintDeviceSP selection,
0514                                           qint32 selX, qint32 selY,
0515                                           qint32 srcX, qint32 srcY,
0516                                           qint32 srcWidth, qint32 srcHeight)
0517 {
0518     // TODO: get selX and selY working as intended
0519 
0520     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
0521     initializing they perform some dummy passes with those parameters, and it must not crash */
0522     if (srcWidth == 0 || srcHeight == 0) return;
0523     if (srcDev.isNull()) return;
0524     if (d->device.isNull()) return;
0525 
0526     // Check that selection has an alpha colorspace, crash if false
0527     Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8());
0528 
0529     const KoCompositeOp *compositeOp = d->compositeOp(srcDev->colorSpace());
0530 
0531     QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
0532 
0533     // save selection offset in case tryReduceSourceRect() will change rects
0534     const int xSelectionOffset = selX - srcX;
0535     const int ySelectionOffset = selY - srcY;
0536 
0537     /**
0538      * An optimization, which crops the source rect by the bounds of
0539      * the source device when it is possible
0540      */
0541     if (d->tryReduceSourceRect(srcDev, &srcRect,
0542                                &srcX, &srcY,
0543                                &srcWidth, &srcHeight,
0544                                &dstX, &dstY)) return;
0545 
0546     const QRect selRect = QRect(srcX + xSelectionOffset,
0547                                 srcY + ySelectionOffset,
0548                                 srcWidth, srcHeight);
0549 
0550     /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
0551     so crash if someone attempts to do this. Don't resize YET as it would obfuscate the mistake. */
0552     KIS_SAFE_ASSERT_RECOVER_RETURN(selection->bounds().contains(selRect));
0553     Q_UNUSED(selRect); // only used by the above Q_ASSERT
0554 
0555 
0556     /* Create an intermediate byte array to hold information before it is written
0557     to the current paint device (d->device) */
0558     quint8* dstBytes = 0;
0559     try {
0560         dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
0561     } catch (const std::bad_alloc&) {
0562         warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "dst bytes";
0563         return;
0564     }
0565 
0566     d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
0567 
0568     // Copy the relevant bytes of raw data from srcDev
0569     quint8* srcBytes = 0;
0570     try {
0571         srcBytes = new quint8[srcWidth * srcHeight * srcDev->pixelSize()];
0572     } catch (const std::bad_alloc&) {
0573         warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "src bytes";
0574         return;
0575     }
0576 
0577     srcDev->readBytes(srcBytes, srcX, srcY, srcWidth, srcHeight);
0578 
0579     const QRect selBounds = selection->bounds();
0580     const quint8 *selRowStart = selection->data() +
0581         (selBounds.width() * (selRect.y() - selBounds.top()) + (selRect.x() - selBounds.left())) * selection->pixelSize();
0582 
0583     /*
0584      * This checks whether there is nothing selected.
0585      */
0586     if (!d->selection) {
0587         /* As there's nothing selected, blit to dstBytes (intermediary bit array),
0588           ignoring d->selection (the user selection)*/
0589         d->paramInfo.dstRowStart   = dstBytes;
0590         d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
0591         d->paramInfo.srcRowStart   = srcBytes;
0592         d->paramInfo.srcRowStride  = srcWidth * srcDev->pixelSize();
0593         d->paramInfo.maskRowStart  = selRowStart;
0594         d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize();
0595         d->paramInfo.rows          = srcHeight;
0596         d->paramInfo.cols          = srcWidth;
0597         d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
0598     }
0599     else {
0600         /* Read the user selection (d->selection) bytes into an array, ready
0601         to merge in the next block*/
0602         quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize();
0603         quint8* mergedSelectionBytes = 0;
0604         try {
0605             mergedSelectionBytes = new quint8[ totalBytes ];
0606         } catch (const std::bad_alloc&) {
0607             warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
0608             return;
0609         }
0610 
0611         d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight);
0612 
0613         KoCompositeOp::ParameterInfo multiplyParamInfo;
0614         multiplyParamInfo.opacity = 1.0f;
0615         multiplyParamInfo.flow = 1.0f;
0616 
0617         // Merge selections here by multiplying them - compositeOP(COMPOSITE_MULT)
0618         multiplyParamInfo.dstRowStart   = mergedSelectionBytes;
0619         multiplyParamInfo.dstRowStride  = srcWidth * selection->pixelSize();
0620         multiplyParamInfo.srcRowStart   = selRowStart;
0621         multiplyParamInfo.srcRowStride  = selBounds.width() * selection->pixelSize();
0622         multiplyParamInfo.maskRowStart  = 0;
0623         multiplyParamInfo.maskRowStride = 0;
0624         multiplyParamInfo.rows          = srcHeight;
0625         multiplyParamInfo.cols          = srcWidth;
0626         KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(multiplyParamInfo);
0627 
0628         // Blit to dstBytes (intermediary bit array)
0629         d->paramInfo.dstRowStart   = dstBytes;
0630         d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
0631         d->paramInfo.srcRowStart   = srcBytes;
0632         d->paramInfo.srcRowStride  = srcWidth * srcDev->pixelSize();
0633         d->paramInfo.maskRowStart  = mergedSelectionBytes;
0634         d->paramInfo.maskRowStride = srcWidth * selection->pixelSize();
0635         d->paramInfo.rows          = srcHeight;
0636         d->paramInfo.cols          = srcWidth;
0637         d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
0638         delete[] mergedSelectionBytes;
0639     }
0640 
0641     d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
0642 
0643     delete[] dstBytes;
0644     delete[] srcBytes;
0645 
0646     addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
0647 }
0648 
0649 
0650 void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
0651                                           const KisPaintDeviceSP srcDev,
0652                                           const KisFixedPaintDeviceSP selection,
0653                                           qint32 srcWidth, qint32 srcHeight)
0654 {
0655     bitBltWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight);
0656 }
0657 
0658 template <bool useOldSrcData>
0659 void KisPainter::bitBltImpl(qint32 dstX, qint32 dstY,
0660                             const KisPaintDeviceSP srcDev,
0661                             qint32 srcX, qint32 srcY,
0662                             qint32 srcWidth, qint32 srcHeight)
0663 {
0664     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
0665     initializing they perform some dummy passes with those parameters, and it must not crash */
0666     if (srcWidth == 0 || srcHeight == 0) return;
0667     if (srcDev.isNull()) return;
0668     if (d->device.isNull()) return;
0669 
0670     QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
0671 
0672     if (d->compositeOpId == COMPOSITE_COPY) {
0673         if(!d->selection && d->isOpacityUnit &&
0674            srcX == dstX && srcY == dstY &&
0675            d->device->fastBitBltPossible(srcDev) &&
0676            (!srcDev->defaultBounds()->wrapAroundMode() ||
0677             srcDev->defaultBounds()->imageBorderRect().contains(srcRect))) {
0678 
0679             if(useOldSrcData) {
0680                 d->device->fastBitBltOldData(srcDev, srcRect);
0681             } else {
0682                 d->device->fastBitBlt(srcDev, srcRect);
0683             }
0684 
0685             addDirtyRect(srcRect);
0686             return;
0687         }
0688     }
0689     else {
0690         /**
0691          * An optimization, which crops the source rect by the bounds of
0692          * the source device when it is possible
0693          */
0694         if (d->tryReduceSourceRect(srcDev, &srcRect,
0695                                    &srcX, &srcY,
0696                                    &srcWidth, &srcHeight,
0697                                    &dstX, &dstY)) return;
0698     }
0699 
0700     qint32 dstY_ = dstY;
0701     qint32 srcY_ = srcY;
0702     qint32 rowsRemaining = srcHeight;
0703 
0704     const KoCompositeOp *compositeOp = d->compositeOp(srcDev->colorSpace());
0705 
0706     // Read below
0707     KisRandomConstAccessorSP srcIt = srcDev->createRandomConstAccessorNG();
0708     KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG();
0709 
0710     /* Here be a huge block of verbose code that does roughly the same than
0711     the other bit blit operations. This one is longer than the rest in an effort to
0712     optimize speed and memory use */
0713     if (d->selection) {
0714         KisPaintDeviceSP selectionProjection(d->selection->projection());
0715         KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG();
0716 
0717         while (rowsRemaining > 0) {
0718 
0719             qint32 dstX_ = dstX;
0720             qint32 srcX_ = srcX;
0721             qint32 columnsRemaining = srcWidth;
0722             qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_);
0723             qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_);
0724             qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY_);
0725 
0726             qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows);
0727             rows = qMin(rows, numContiguousSelRows);
0728             rows = qMin(rows, rowsRemaining);
0729 
0730             while (columnsRemaining > 0) {
0731 
0732                 qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_);
0733                 qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_);
0734                 qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX_);
0735 
0736                 qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
0737                 columns = qMin(columns, numContiguousSelColumns);
0738                 columns = qMin(columns, columnsRemaining);
0739 
0740                 qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_);
0741                 srcIt->moveTo(srcX_, srcY_);
0742 
0743                 qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_);
0744                 dstIt->moveTo(dstX_, dstY_);
0745 
0746                 qint32 maskRowStride = maskIt->rowStride(dstX_, dstY_);
0747                 maskIt->moveTo(dstX_, dstY_);
0748 
0749                 d->paramInfo.dstRowStart   = dstIt->rawData();
0750                 d->paramInfo.dstRowStride  = dstRowStride;
0751                 // if we don't use the oldRawData, we need to access the rawData of the source device.
0752                 d->paramInfo.srcRowStart   = useOldSrcData ? srcIt->oldRawData() : static_cast<KisRandomAccessor2*>(srcIt.data())->rawData();
0753                 d->paramInfo.srcRowStride  = srcRowStride;
0754                 d->paramInfo.maskRowStart  = static_cast<KisRandomAccessor2*>(maskIt.data())->rawData();
0755                 d->paramInfo.maskRowStride = maskRowStride;
0756                 d->paramInfo.rows          = rows;
0757                 d->paramInfo.cols          = columns;
0758                 d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
0759 
0760                 srcX_ += columns;
0761                 dstX_ += columns;
0762                 columnsRemaining -= columns;
0763             }
0764 
0765             srcY_ += rows;
0766             dstY_ += rows;
0767             rowsRemaining -= rows;
0768         }
0769     }
0770     else {
0771 
0772         while (rowsRemaining > 0) {
0773 
0774             qint32 dstX_ = dstX;
0775             qint32 srcX_ = srcX;
0776             qint32 columnsRemaining = srcWidth;
0777             qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_);
0778             qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_);
0779 
0780             qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows);
0781             rows = qMin(rows, rowsRemaining);
0782 
0783             while (columnsRemaining > 0) {
0784 
0785                 qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_);
0786                 qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_);
0787 
0788                 qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
0789                 columns = qMin(columns, columnsRemaining);
0790 
0791                 qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_);
0792                 srcIt->moveTo(srcX_, srcY_);
0793 
0794                 qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_);
0795                 dstIt->moveTo(dstX_, dstY_);
0796 
0797                 d->paramInfo.dstRowStart   = dstIt->rawData();
0798                 d->paramInfo.dstRowStride  = dstRowStride;
0799                 // if we don't use the oldRawData, we need to access the rawData of the source device.
0800                 d->paramInfo.srcRowStart   = useOldSrcData ? srcIt->oldRawData() : static_cast<KisRandomAccessor2*>(srcIt.data())->rawData();
0801                 d->paramInfo.srcRowStride  = srcRowStride;
0802                 d->paramInfo.maskRowStart  = 0;
0803                 d->paramInfo.maskRowStride = 0;
0804                 d->paramInfo.rows          = rows;
0805                 d->paramInfo.cols          = columns;
0806                 d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
0807 
0808                 srcX_ += columns;
0809                 dstX_ += columns;
0810                 columnsRemaining -= columns;
0811             }
0812 
0813             srcY_ += rows;
0814             dstY_ += rows;
0815             rowsRemaining -= rows;
0816         }
0817     }
0818 
0819     addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
0820 
0821 }
0822 
0823 void KisPainter::bitBlt(qint32 dstX, qint32 dstY,
0824                         const KisPaintDeviceSP srcDev,
0825                         qint32 srcX, qint32 srcY,
0826                         qint32 srcWidth, qint32 srcHeight)
0827 {
0828     bitBltImpl<false>(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight);
0829 }
0830 
0831 
0832 void KisPainter::bitBlt(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect)
0833 {
0834     bitBlt(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
0835 }
0836 
0837 void KisPainter::bitBltOldData(qint32 dstX, qint32 dstY,
0838                                const KisPaintDeviceSP srcDev,
0839                                qint32 srcX, qint32 srcY,
0840                                qint32 srcWidth, qint32 srcHeight)
0841 {
0842     bitBltImpl<true>(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight);
0843 }
0844 
0845 
0846 void KisPainter::bitBltOldData(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect)
0847 {
0848     bitBltOldData(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
0849 }
0850 
0851 
0852 void KisPainter::fill(qint32 x, qint32 y, qint32 width, qint32 height, const KoColor& color)
0853 {
0854     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
0855      * initializing they perform some dummy passes with those parameters, and it must not crash */
0856     if(width == 0 || height == 0 || d->device.isNull())
0857         return;
0858 
0859     KoColor srcColor(color, d->device->compositionSourceColorSpace());
0860     const KoCompositeOp *compositeOp = d->compositeOp(srcColor.colorSpace());
0861 
0862     qint32  dstY          = y;
0863     qint32  rowsRemaining = height;
0864 
0865     KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG();
0866 
0867     if(d->selection) {
0868         KisPaintDeviceSP selectionProjection(d->selection->projection());
0869         KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG();
0870 
0871         while(rowsRemaining > 0) {
0872 
0873             qint32 dstX                 = x;
0874             qint32 columnsRemaining     = width;
0875             qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY);
0876             qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY);
0877             qint32 rows = qMin(numContiguousDstRows, numContiguousSelRows);
0878             rows = qMin(rows, rowsRemaining);
0879 
0880             while (columnsRemaining > 0) {
0881 
0882                 qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX);
0883                 qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX);
0884 
0885                 qint32 columns = qMin(numContiguousDstColumns, numContiguousSelColumns);
0886                 columns = qMin(columns, columnsRemaining);
0887 
0888                 qint32 dstRowStride = dstIt->rowStride(dstX, dstY);
0889                 dstIt->moveTo(dstX, dstY);
0890                 qint32 maskRowStride = maskIt->rowStride(dstX, dstY);
0891 
0892                 maskIt->moveTo(dstX, dstY);
0893 
0894                 d->paramInfo.dstRowStart   = dstIt->rawData();
0895                 d->paramInfo.dstRowStride  = dstRowStride;
0896                 d->paramInfo.srcRowStart   = srcColor.data();
0897                 d->paramInfo.srcRowStride  = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel
0898                 d->paramInfo.maskRowStart  = maskIt->oldRawData();
0899                 d->paramInfo.maskRowStride = maskRowStride;
0900                 d->paramInfo.rows          = rows;
0901                 d->paramInfo.cols          = columns;
0902                 d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
0903 
0904                 dstX             += columns;
0905                 columnsRemaining -= columns;
0906             }
0907 
0908             dstY          += rows;
0909             rowsRemaining -= rows;
0910         }
0911     }
0912     else {
0913 
0914         while(rowsRemaining > 0) {
0915 
0916             qint32 dstX                 = x;
0917             qint32 columnsRemaining     = width;
0918             qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY);
0919             qint32 rows                 = qMin(numContiguousDstRows, rowsRemaining);
0920 
0921             while(columnsRemaining > 0) {
0922 
0923                 qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX);
0924                 qint32 columns                 = qMin(numContiguousDstColumns, columnsRemaining);
0925                 qint32 dstRowStride            = dstIt->rowStride(dstX, dstY);
0926                 dstIt->moveTo(dstX, dstY);
0927 
0928                 d->paramInfo.dstRowStart   = dstIt->rawData();
0929                 d->paramInfo.dstRowStride  = dstRowStride;
0930                 d->paramInfo.srcRowStart   = srcColor.data();
0931                 d->paramInfo.srcRowStride  = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel
0932                 d->paramInfo.maskRowStart  = 0;
0933                 d->paramInfo.maskRowStride = 0;
0934                 d->paramInfo.rows          = rows;
0935                 d->paramInfo.cols          = columns;
0936                 d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
0937 
0938                 dstX             += columns;
0939                 columnsRemaining -= columns;
0940             }
0941 
0942             dstY          += rows;
0943             rowsRemaining -= rows;
0944         }
0945     }
0946 
0947     addDirtyRect(QRect(x, y, width, height));
0948 }
0949 
0950 
0951 void KisPainter::bltFixed(qint32 dstX, qint32 dstY,
0952                           const KisFixedPaintDeviceSP srcDev,
0953                           qint32 srcX, qint32 srcY,
0954                           qint32 srcWidth, qint32 srcHeight)
0955 {
0956     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
0957     initializing they perform some dummy passes with those parameters, and it must not crash */
0958     if (srcWidth == 0 || srcHeight == 0) return;
0959     if (srcDev.isNull()) return;
0960     if (d->device.isNull()) return;
0961 
0962     QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
0963     QRect srcBounds = srcDev->bounds();
0964 
0965     /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
0966     so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */
0967     KIS_SAFE_ASSERT_RECOVER_RETURN(srcBounds.contains(srcRect));
0968     Q_UNUSED(srcRect); // only used in above assertion
0969 
0970     const KoCompositeOp *compositeOp = d->compositeOp(srcDev->colorSpace());
0971 
0972     /* Create an intermediate byte array to hold information before it is written
0973     to the current paint device (aka: d->device) */
0974     quint8* dstBytes = 0;
0975     try {
0976          dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
0977     } catch (const std::bad_alloc&) {
0978         warnKrita << "KisPainter::bltFixed std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
0979         return;
0980     }
0981     d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
0982 
0983     const quint8 *srcRowStart = srcDev->data() +
0984         (srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize();
0985 
0986     d->paramInfo.dstRowStart   = dstBytes;
0987     d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
0988     d->paramInfo.srcRowStart   = srcRowStart;
0989     d->paramInfo.srcRowStride  = srcBounds.width() * srcDev->pixelSize();
0990     d->paramInfo.maskRowStart  = 0;
0991     d->paramInfo.maskRowStride = 0;
0992     d->paramInfo.rows          = srcHeight;
0993     d->paramInfo.cols          = srcWidth;
0994 
0995     if (d->selection) {
0996         /* d->selection is a KisPaintDevice, so first a readBytes is performed to
0997         get the area of interest... */
0998         KisPaintDeviceSP selectionProjection(d->selection->projection());
0999         quint8* selBytes = 0;
1000         try {
1001             selBytes = new quint8[srcWidth * srcHeight * selectionProjection->pixelSize()];
1002         }
1003         catch (const std::bad_alloc&) {
1004             delete[] dstBytes;
1005             return;
1006         }
1007 
1008         selectionProjection->readBytes(selBytes, dstX, dstY, srcWidth, srcHeight);
1009         d->paramInfo.maskRowStart = selBytes;
1010         d->paramInfo.maskRowStride = srcWidth * selectionProjection->pixelSize();
1011     }
1012 
1013     // ...and then blit.
1014     d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
1015     d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
1016 
1017     delete[] d->paramInfo.maskRowStart;
1018     delete[] dstBytes;
1019 
1020     addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
1021 }
1022 
1023 void KisPainter::bltFixed(const QPoint & pos, const KisFixedPaintDeviceSP srcDev, const QRect & srcRect)
1024 {
1025     bltFixed(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
1026 }
1027 
1028 void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
1029                                             const KisFixedPaintDeviceSP srcDev,
1030                                             const KisFixedPaintDeviceSP selection,
1031                                             qint32 selX, qint32 selY,
1032                                             qint32 srcX, qint32 srcY,
1033                                             quint32 srcWidth, quint32 srcHeight)
1034 {
1035     // TODO: get selX and selY working as intended
1036 
1037     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
1038     initializing they perform some dummy passes with those parameters, and it must not crash */
1039     if (srcWidth == 0 || srcHeight == 0) return;
1040     if (srcDev.isNull()) return;
1041     if (d->device.isNull()) return;
1042 
1043      // Check that selection has an alpha colorspace, crash if false
1044     Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8());
1045 
1046     const KoCompositeOp *compositeOp = d->compositeOp(srcDev->colorSpace());
1047 
1048     QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
1049     QRect selRect = QRect(selX, selY, srcWidth, srcHeight);
1050 
1051     QRect srcBounds = srcDev->bounds();
1052     QRect selBounds = selection->bounds();
1053 
1054     /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
1055     so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */
1056     KIS_ASSERT(srcBounds.contains(srcRect));
1057     Q_UNUSED(srcRect); // only used in above assertion
1058     KIS_ASSERT(selBounds.contains(selRect));
1059     Q_UNUSED(selRect); // only used in above assertion
1060 
1061     /* Create an intermediate byte array to hold information before it is written
1062     to the current paint device (aka: d->device) */
1063     quint8* dstBytes = 0;
1064     try {
1065         dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
1066     } catch (const std::bad_alloc&) {
1067         warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
1068         return;
1069     }
1070     d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
1071 
1072     const quint8 *srcRowStart = srcDev->data() +
1073         (srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize();
1074     const quint8 *selRowStart = selection->data() +
1075         (selBounds.width() * (selY - selBounds.top()) + (selX - selBounds.left())) * selection->pixelSize();
1076 
1077     if (!d->selection) {
1078         /* As there's nothing selected, blit to dstBytes (intermediary bit array),
1079           ignoring d->selection (the user selection)*/
1080         d->paramInfo.dstRowStart   = dstBytes;
1081         d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
1082         d->paramInfo.srcRowStart   = srcRowStart;
1083         d->paramInfo.srcRowStride  = srcBounds.width() * srcDev->pixelSize();
1084         d->paramInfo.maskRowStart  = selRowStart;
1085         d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize();
1086         d->paramInfo.rows          = srcHeight;
1087         d->paramInfo.cols          = srcWidth;
1088         d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
1089     }
1090     else {
1091         /* Read the user selection (d->selection) bytes into an array, ready
1092         to merge in the next block*/
1093         quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize();
1094         quint8 * mergedSelectionBytes = 0;
1095         try {
1096             mergedSelectionBytes = new quint8[ totalBytes ];
1097         } catch (const std::bad_alloc&) {
1098             warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << totalBytes << "total bytes";
1099             delete[] dstBytes;
1100             return;
1101         }
1102         d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight);
1103 
1104         KoCompositeOp::ParameterInfo multiplyParamInfo;
1105         multiplyParamInfo.opacity = 1.0f;
1106         multiplyParamInfo.flow = 1.0f;
1107 
1108         // Merge selections here by multiplying them - compositeOp(COMPOSITE_MULT)
1109         multiplyParamInfo.dstRowStart   = mergedSelectionBytes;
1110         multiplyParamInfo.dstRowStride  = srcWidth * selection->pixelSize();
1111         multiplyParamInfo.srcRowStart   = selRowStart;
1112         multiplyParamInfo.srcRowStride  = selBounds.width() * selection->pixelSize();
1113         multiplyParamInfo.maskRowStart  = 0;
1114         multiplyParamInfo.maskRowStride = 0;
1115         multiplyParamInfo.rows          = srcHeight;
1116         multiplyParamInfo.cols          = srcWidth;
1117         KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(multiplyParamInfo);
1118 
1119         // Blit to dstBytes (intermediary bit array)
1120         d->paramInfo.dstRowStart   = dstBytes;
1121         d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
1122         d->paramInfo.srcRowStart   = srcRowStart;
1123         d->paramInfo.srcRowStride  = srcBounds.width() * srcDev->pixelSize();
1124         d->paramInfo.maskRowStart  = mergedSelectionBytes;
1125         d->paramInfo.maskRowStride = srcWidth * selection->pixelSize();
1126         d->paramInfo.rows          = srcHeight;
1127         d->paramInfo.cols          = srcWidth;
1128         d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, compositeOp, d->renderingIntent, d->conversionFlags);
1129 
1130         delete[] mergedSelectionBytes;
1131     }
1132 
1133     d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
1134 
1135     delete[] dstBytes;
1136 
1137     addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
1138 }
1139 
1140 void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
1141                                             const KisFixedPaintDeviceSP srcDev,
1142                                             const KisFixedPaintDeviceSP selection,
1143                                             quint32 srcWidth, quint32 srcHeight)
1144 {
1145     bltFixedWithFixedSelection(dstX, dstY, srcDev, selection, selection->bounds().x(), selection->bounds().y(), srcDev->bounds().x(), srcDev->bounds().y(), srcWidth, srcHeight);
1146 }
1147 
1148 
1149 
1150 
1151 
1152 void KisPainter::paintLine(const KisPaintInformation &pi1,
1153                            const KisPaintInformation &pi2,
1154                            KisDistanceInformation *currentDistance)
1155 {
1156     if (d->device && d->paintOp && d->paintOp->canPaint()) {
1157         d->paintOp->paintLine(pi1, pi2, currentDistance);
1158     }
1159 }
1160 
1161 
1162 void KisPainter::paintPolyline(const vQPointF &points,
1163                                int index, int numPoints)
1164 {
1165     if (d->fillStyle != FillStyleNone) {
1166         fillPolygon(points, d->fillStyle);
1167     }
1168 
1169     if (d->strokeStyle == StrokeStyleNone) return;
1170 
1171     if (index >= points.count())
1172         return;
1173 
1174     if (numPoints < 0)
1175         numPoints = points.count();
1176 
1177     if (index + numPoints > points.count())
1178         numPoints = points.count() - index;
1179 
1180     if (numPoints > 1) {
1181         KisRandomSourceSP rnd = new KisRandomSource();
1182         KisPerStrokeRandomSourceSP strokeRnd = new KisPerStrokeRandomSource();
1183 
1184         auto point = [rnd, strokeRnd] (const QPointF &pt) {
1185             KisPaintInformation pi(pt);
1186             pi.setRandomSource(rnd);
1187             pi.setPerStrokeRandomSource(strokeRnd);
1188             return pi;
1189         };
1190 
1191         KisDistanceInformation saveDist(points[0],
1192                 KisAlgebra2D::directionBetweenPoints(points[0], points[1], 0.0));
1193         for (int i = index; i < index + numPoints - 1; i++) {
1194             paintLine(point(points[i]), point(points[i + 1]), &saveDist);
1195         }
1196     }
1197 
1198 }
1199 
1200 static void getBezierCurvePoints(const KisVector2D &pos1,
1201                                  const KisVector2D &control1,
1202                                  const KisVector2D &control2,
1203                                  const KisVector2D &pos2,
1204                                  vQPointF& points)
1205 {
1206     LineEquation line = LineEquation::Through(pos1, pos2);
1207     qreal d1 = line.absDistance(control1);
1208     qreal d2 = line.absDistance(control2);
1209 
1210     if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
1211         points.push_back(toQPointF(pos1));
1212     } else {
1213         // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
1214 
1215         KisVector2D l2 = (pos1 + control1) / 2;
1216         KisVector2D h = (control1 + control2) / 2;
1217         KisVector2D l3 = (l2 + h) / 2;
1218         KisVector2D r3 = (control2 + pos2) / 2;
1219         KisVector2D r2 = (h + r3) / 2;
1220         KisVector2D l4 = (l3 + r2) / 2;
1221 
1222         getBezierCurvePoints(pos1, l2, l3, l4, points);
1223         getBezierCurvePoints(l4, r2, r3, pos2, points);
1224     }
1225 }
1226 
1227 void KisPainter::getBezierCurvePoints(const QPointF &pos1,
1228                                       const QPointF &control1,
1229                                       const QPointF &control2,
1230                                       const QPointF &pos2,
1231                                       vQPointF& points) const
1232 {
1233     ::getBezierCurvePoints(toKisVector2D(pos1), toKisVector2D(control1), toKisVector2D(control2), toKisVector2D(pos2), points);
1234 }
1235 
1236 void KisPainter::paintBezierCurve(const KisPaintInformation &pi1,
1237                                   const QPointF &control1,
1238                                   const QPointF &control2,
1239                                   const KisPaintInformation &pi2,
1240                                   KisDistanceInformation *currentDistance)
1241 {
1242     if (d->paintOp && d->paintOp->canPaint()) {
1243         d->paintOp->paintBezierCurve(pi1, control1, control2, pi2, currentDistance);
1244     }
1245 }
1246 
1247 void KisPainter::paintRect(const QRectF &rect)
1248 {
1249     QRectF normalizedRect = rect.normalized();
1250 
1251     vQPointF points;
1252 
1253     points.push_back(normalizedRect.topLeft());
1254     points.push_back(normalizedRect.bottomLeft());
1255     points.push_back(normalizedRect.bottomRight());
1256     points.push_back(normalizedRect.topRight());
1257 
1258     paintPolygon(points);
1259 }
1260 
1261 void KisPainter::paintRect(const qreal x,
1262                            const qreal y,
1263                            const qreal w,
1264                            const qreal h)
1265 {
1266     paintRect(QRectF(x, y, w, h));
1267 }
1268 
1269 void KisPainter::paintEllipse(const QRectF &rect)
1270 {
1271     QRectF r = rect.normalized(); // normalize before checking as negative width and height are empty too
1272     if (r.isEmpty()) return;
1273 
1274     // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation.
1275     // kappa = (4/3*(sqrt(2)-1))
1276     const qreal kappa = 0.5522847498;
1277     const qreal lx = (r.width() / 2) * kappa;
1278     const qreal ly = (r.height() / 2) * kappa;
1279 
1280     QPointF center = r.center();
1281 
1282     QPointF p0(r.left(), center.y());
1283     QPointF p1(r.left(), center.y() - ly);
1284     QPointF p2(center.x() - lx, r.top());
1285     QPointF p3(center.x(), r.top());
1286 
1287     vQPointF points;
1288 
1289     getBezierCurvePoints(p0, p1, p2, p3, points);
1290 
1291     QPointF p4(center.x() + lx, r.top());
1292     QPointF p5(r.right(), center.y() - ly);
1293     QPointF p6(r.right(), center.y());
1294 
1295     getBezierCurvePoints(p3, p4, p5, p6, points);
1296 
1297     QPointF p7(r.right(), center.y() + ly);
1298     QPointF p8(center.x() + lx, r.bottom());
1299     QPointF p9(center.x(), r.bottom());
1300 
1301     getBezierCurvePoints(p6, p7, p8, p9, points);
1302 
1303     QPointF p10(center.x() - lx, r.bottom());
1304     QPointF p11(r.left(), center.y() + ly);
1305 
1306     getBezierCurvePoints(p9, p10, p11, p0, points);
1307 
1308     paintPolygon(points);
1309 }
1310 
1311 void KisPainter::paintEllipse(const qreal x,
1312                               const qreal y,
1313                               const qreal w,
1314                               const qreal h)
1315 {
1316     paintEllipse(QRectF(x, y, w, h));
1317 }
1318 
1319 void KisPainter::paintAt(const KisPaintInformation& pi,
1320                          KisDistanceInformation *savedDist)
1321 {
1322     if (d->paintOp && d->paintOp->canPaint()) {
1323         d->paintOp->paintAt(pi, savedDist);
1324     }
1325 }
1326 
1327 void KisPainter::fillPolygon(const vQPointF& points, FillStyle fillStyle)
1328 {
1329     if (points.count() < 3) {
1330         return;
1331     }
1332 
1333     if (fillStyle == FillStyleNone) {
1334         return;
1335     }
1336 
1337     QPainterPath polygonPath;
1338 
1339     polygonPath.moveTo(points.at(0));
1340 
1341     for (int pointIndex = 1; pointIndex < points.count(); pointIndex++) {
1342         polygonPath.lineTo(points.at(pointIndex));
1343     }
1344 
1345     polygonPath.closeSubpath();
1346 
1347     d->fillStyle = fillStyle;
1348     fillPainterPath(polygonPath);
1349 }
1350 
1351 void KisPainter::paintPolygon(const vQPointF& points)
1352 {
1353     if (d->fillStyle != FillStyleNone) {
1354         fillPolygon(points, d->fillStyle);
1355     }
1356 
1357     if (d->strokeStyle != StrokeStyleNone) {
1358         if (points.count() > 1) {
1359             KisDistanceInformation distance(points[0],
1360                                             KisAlgebra2D::directionBetweenPoints(points[0], points[1], 0.0));
1361 
1362             KisRandomSourceSP rnd = new KisRandomSource();
1363             KisPerStrokeRandomSourceSP strokeRnd = new KisPerStrokeRandomSource();
1364 
1365             auto point = [rnd, strokeRnd] (const QPointF &pt) {
1366                 KisPaintInformation pi(pt);
1367                 pi.setRandomSource(rnd);
1368                 pi.setPerStrokeRandomSource(strokeRnd);
1369                 return pi;
1370             };
1371 
1372             for (int i = 0; i < points.count() - 1; i++) {
1373                 paintLine(point(points[i]), point(points[i + 1]), &distance);
1374             }
1375             paintLine(point(points[points.count() - 1]), point(points[0]), &distance);
1376         }
1377     }
1378 }
1379 
1380 void KisPainter::paintPainterPath(const QPainterPath& path)
1381 {
1382     if (d->fillStyle != FillStyleNone) {
1383         fillPainterPath(path);
1384     }
1385 
1386     if (d->strokeStyle == StrokeStyleNone) return;
1387 
1388     QPointF lastPoint, nextPoint;
1389     int elementCount = path.elementCount();
1390     KisDistanceInformation saveDist;
1391 
1392     KisRandomSourceSP rnd = new KisRandomSource();
1393     KisPerStrokeRandomSourceSP strokeRnd = new KisPerStrokeRandomSource();
1394 
1395     auto point = [rnd, strokeRnd] (const QPointF &pt) {
1396         KisPaintInformation pi(pt);
1397         pi.setRandomSource(rnd);
1398         pi.setPerStrokeRandomSource(strokeRnd);
1399         return pi;
1400     };
1401 
1402     for (int i = 0; i < elementCount; i++) {
1403         QPainterPath::Element element = path.elementAt(i);
1404         switch (element.type) {
1405         case QPainterPath::MoveToElement:
1406             lastPoint = QPointF(element.x, element.y);
1407             break;
1408         case QPainterPath::LineToElement:
1409             nextPoint = QPointF(element.x, element.y);
1410             paintLine(point(lastPoint), point(nextPoint), &saveDist);
1411             lastPoint = nextPoint;
1412             break;
1413         case QPainterPath::CurveToElement:
1414             nextPoint = QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y);
1415             paintBezierCurve(point(lastPoint),
1416                              QPointF(path.elementAt(i).x, path.elementAt(i).y),
1417                              QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
1418                              point(nextPoint), &saveDist);
1419             lastPoint = nextPoint;
1420             break;
1421         default:
1422             continue;
1423         }
1424     }
1425 }
1426 
1427 void KisPainter::fillPainterPath(const QPainterPath& path)
1428 {
1429     fillPainterPath(path, QRect());
1430 }
1431 
1432 void KisPainter::fillPainterPath(const QPainterPath& path, const QRect &requestedRect)
1433 {
1434     if (d->mirrorHorizontally || d->mirrorVertically) {
1435         KisLodTransform lod(d->device);
1436         QPointF effectiveAxesCenter = lod.map(d->axesCenter);
1437 
1438         QTransform C1 = QTransform::fromTranslate(-effectiveAxesCenter.x(), -effectiveAxesCenter.y());
1439         QTransform C2 = QTransform::fromTranslate(effectiveAxesCenter.x(), effectiveAxesCenter.y());
1440 
1441         QTransform t;
1442         QPainterPath newPath;
1443         QRect newRect;
1444 
1445         if (d->mirrorHorizontally) {
1446             t = C1 * QTransform::fromScale(-1,1) * C2;
1447             newPath = t.map(path);
1448             newRect = t.mapRect(requestedRect);
1449             d->fillPainterPathImpl(newPath, newRect);
1450         }
1451 
1452         if (d->mirrorVertically) {
1453             t = C1 * QTransform::fromScale(1,-1) * C2;
1454             newPath = t.map(path);
1455             newRect = t.mapRect(requestedRect);
1456             d->fillPainterPathImpl(newPath, newRect);
1457         }
1458 
1459         if (d->mirrorHorizontally && d->mirrorVertically) {
1460             t = C1 * QTransform::fromScale(-1,-1) * C2;
1461             newPath = t.map(path);
1462             newRect = t.mapRect(requestedRect);
1463             d->fillPainterPathImpl(newPath, newRect);
1464         }
1465     }
1466 
1467     d->fillPainterPathImpl(path, requestedRect);
1468 }
1469 
1470 void KisPainter::Private::fillPainterPathImpl(const QPainterPath& path, const QRect &requestedRect)
1471 {
1472     if (fillStyle == FillStyleNone) {
1473         return;
1474     }
1475 
1476     // Fill the polygon bounding rectangle with the required contents then we'll
1477     // create a mask for the actual polygon coverage.
1478 
1479     if (!fillPainter) {
1480         polygon = device->createCompositionSourceDevice();
1481         fillPainter = new KisFillPainter(polygon);
1482     } else {
1483         polygon->clear();
1484     }
1485 
1486     Q_CHECK_PTR(polygon);
1487 
1488     QRectF boundingRect = path.boundingRect();
1489     QRect fillRect = boundingRect.toAlignedRect();
1490 
1491     // Expand the rectangle to allow for anti-aliasing.
1492     fillRect.adjust(-1, -1, 1, 1);
1493 
1494     if (requestedRect.isValid()) {
1495         fillRect &= requestedRect;
1496     }
1497 
1498     switch (fillStyle) {
1499     default:
1500         Q_FALLTHROUGH();
1501     case FillStyleForegroundColor:
1502         fillPainter->fillRect(fillRect, q->paintColor(), OPACITY_OPAQUE_U8);
1503         break;
1504     case FillStyleBackgroundColor:
1505         fillPainter->fillRect(fillRect, q->backgroundColor(), OPACITY_OPAQUE_U8);
1506         break;
1507     case FillStylePattern:
1508         if (pattern) { // if the user hasn't got any patterns installed, we shouldn't crash...
1509             fillPainter->fillRectNoCompose(fillRect, pattern, patternTransform);
1510         }
1511         break;
1512     case FillStyleGenerator:
1513         if (generator) { // if the user hasn't got any generators, we shouldn't crash...
1514             fillPainter->fillRect(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), q->generator());
1515         }
1516         break;
1517     }
1518 
1519     if (polygonMaskImage.isNull() || (maskPainter == 0)) {
1520         polygonMaskImage = QImage(maskImageWidth, maskImageHeight, QImage::Format_ARGB32_Premultiplied);
1521         maskPainter = new QPainter(&polygonMaskImage);
1522         maskPainter->setRenderHint(QPainter::Antialiasing, q->antiAliasPolygonFill());
1523     }
1524 
1525     // Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
1526     const QColor black(Qt::black);
1527     const QBrush brush(Qt::white);
1528     for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += maskImageWidth) {
1529         for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += maskImageHeight) {
1530 
1531             polygonMaskImage.fill(black.rgb());
1532             maskPainter->translate(-x, -y);
1533             maskPainter->fillPath(path, brush);
1534             maskPainter->translate(x, y);
1535 
1536             qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, maskImageWidth);
1537             qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, maskImageHeight);
1538 
1539             KisHLineIteratorSP lineIt = polygon->createHLineIteratorNG(x, y, rectWidth);
1540 
1541             quint8 tmp;
1542             for (int row = y; row < y + rectHeight; row++) {
1543                 QRgb* line = reinterpret_cast<QRgb*>(polygonMaskImage.scanLine(row - y));
1544                 do {
1545                     tmp = qRed(line[lineIt->x() - x]);
1546                     polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1);
1547                 } while (lineIt->nextPixel());
1548                 lineIt->nextRow();
1549             }
1550 
1551         }
1552     }
1553 
1554     QRect bltRect = !requestedRect.isEmpty() ? requestedRect : fillRect;
1555     q->bitBlt(bltRect.x(), bltRect.y(), polygon, bltRect.x(), bltRect.y(), bltRect.width(), bltRect.height());
1556 }
1557 
1558 void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& pen)
1559 {
1560     drawPainterPath(path, pen, QRect());
1561 }
1562 
1563 void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& _pen, const QRect &requestedRect)
1564 {
1565     QPen pen(_pen);
1566     pen.setColor(Qt::white);
1567 
1568     if (!d->fillPainter) {
1569         d->polygon = d->device->createCompositionSourceDevice();
1570         d->fillPainter = new KisFillPainter(d->polygon);
1571     } else {
1572         d->polygon->clear();
1573     }
1574 
1575     Q_CHECK_PTR(d->polygon);
1576 
1577     QRectF boundingRect = path.boundingRect();
1578     QRect fillRect = boundingRect.toAlignedRect();
1579 
1580     // take width of the pen into account
1581     int penWidth = qRound(pen.widthF());
1582     fillRect.adjust(-penWidth, -penWidth, penWidth, penWidth);
1583 
1584     // Expand the rectangle to allow for anti-aliasing.
1585     fillRect.adjust(-1, -1, 1, 1);
1586 
1587     if (!requestedRect.isNull()) {
1588         fillRect &= requestedRect;
1589     }
1590 
1591     d->fillPainter->fillRect(fillRect, paintColor(), OPACITY_OPAQUE_U8);
1592 
1593     if (d->polygonMaskImage.isNull() || (d->maskPainter == 0)) {
1594         d->polygonMaskImage = QImage(d->maskImageWidth, d->maskImageHeight, QImage::Format_ARGB32_Premultiplied);
1595         d->maskPainter = new QPainter(&d->polygonMaskImage);
1596         d->maskPainter->setRenderHint(QPainter::Antialiasing, antiAliasPolygonFill());
1597     }
1598 
1599     // Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
1600     const QColor black(Qt::black);
1601     QPen oldPen = d->maskPainter->pen();
1602     d->maskPainter->setPen(pen);
1603 
1604     for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += d->maskImageWidth) {
1605         for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += d->maskImageHeight) {
1606 
1607             d->polygonMaskImage.fill(black.rgb());
1608             d->maskPainter->translate(-x, -y);
1609             d->maskPainter->drawPath(path);
1610             d->maskPainter->translate(x, y);
1611 
1612             qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, d->maskImageWidth);
1613             qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, d->maskImageHeight);
1614 
1615             KisHLineIteratorSP lineIt = d->polygon->createHLineIteratorNG(x, y, rectWidth);
1616 
1617             quint8 tmp;
1618             for (int row = y; row < y + rectHeight; row++) {
1619                 QRgb* line = reinterpret_cast<QRgb*>(d->polygonMaskImage.scanLine(row - y));
1620                 do {
1621                     tmp = qRed(line[lineIt->x() - x]);
1622                     d->polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1);
1623                 } while (lineIt->nextPixel());
1624                 lineIt->nextRow();
1625             }
1626 
1627         }
1628     }
1629 
1630     d->maskPainter->setPen(oldPen);
1631     QRect r = d->polygon->extent();
1632 
1633     bitBlt(r.x(), r.y(), d->polygon, r.x(), r.y(), r.width(), r.height());
1634 }
1635 
1636 inline void KisPainter::compositeOnePixel(quint8 *dst, const KoColor &color)
1637 {
1638     d->paramInfo.dstRowStart = dst;
1639     d->paramInfo.dstRowStride = 0;
1640     d->paramInfo.srcRowStart = color.data();
1641     d->paramInfo.srcRowStride = 0;
1642     d->paramInfo.maskRowStart = 0;
1643     d->paramInfo.maskRowStride = 0;
1644     d->paramInfo.rows = 1;
1645     d->paramInfo.cols = 1;
1646 
1647     d->colorSpace->bitBlt(color.colorSpace(), d->paramInfo, d->compositeOp(color.colorSpace()),
1648                           d->renderingIntent,
1649                           d->conversionFlags);
1650 }
1651 
1652 /**/
1653 void KisPainter::drawLine(const QPointF& start, const QPointF& end, qreal width, bool antialias){
1654     int x1 = qFloor(start.x());
1655     int y1 = qFloor(start.y());
1656     int x2 = qFloor(end.x());
1657     int y2 = qFloor(end.y());
1658 
1659     if ((x2 == x1 ) && (y2 == y1)) return;
1660 
1661     int dstX = x2-x1;
1662     int dstY = y2-y1;
1663 
1664     qreal uniC = dstX*y1 - dstY*x1;
1665     qreal projectionDenominator = 1.0 / (pow((double)dstX, 2) + pow((double)dstY, 2));
1666 
1667     qreal subPixel;
1668     if (qAbs(dstX) > qAbs(dstY)){
1669         subPixel = start.x() - x1;
1670     }else{
1671         subPixel = start.y() - y1;
1672     }
1673 
1674     qreal halfWidth = width * 0.5 + subPixel;
1675     int W_ = qRound(halfWidth) + 1;
1676 
1677     // save the state
1678     int X1_ = x1;
1679     int Y1_ = y1;
1680     int X2_ = x2;
1681     int Y2_ = y2;
1682 
1683     if (x2<x1) std::swap(x1,x2);
1684     if (y2<y1) std::swap(y1,y2);
1685 
1686     qreal denominator = sqrt(pow((double)dstY,2) + pow((double)dstX,2));
1687     if (denominator == 0.0) {
1688         denominator = 1.0;
1689     }
1690     denominator = 1.0/denominator;
1691 
1692     qreal projection,scanX,scanY,AA_;
1693     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
1694     KisRandomConstAccessorSP selectionAccessor;
1695     if (d->selection) {
1696         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
1697     }
1698 
1699     for (int y = y1-W_; y < y2+W_ ; y++){
1700         for (int x = x1-W_; x < x2+W_; x++){
1701 
1702             projection = ( (x-X1_)* dstX + (y-Y1_)*dstY ) * projectionDenominator;
1703             scanX = X1_ + projection * dstX;
1704             scanY = Y1_ + projection * dstY;
1705 
1706             if (((scanX < x1) || (scanX > x2)) || ((scanY < y1) || (scanY > y2))) {
1707                 AA_ = qMin( sqrt( pow((double)x - X1_, 2) + pow((double)y - Y1_, 2) ),
1708                             sqrt( pow((double)x - X2_, 2) + pow((double)y - Y2_, 2) ));
1709             }else{
1710                 AA_ = qAbs(dstY*x - dstX*y + uniC) * denominator;
1711             }
1712 
1713             if (AA_>halfWidth) {
1714                 continue;
1715             }
1716 
1717             accessor->moveTo(x, y);
1718             if (selectionAccessor) selectionAccessor->moveTo(x,y);
1719 
1720             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1721                 KoColor mycolor = d->paintColor;
1722 
1723                 if (antialias && AA_ > halfWidth - 1.0) {
1724                     mycolor.colorSpace()->multiplyAlpha(mycolor.data(), (1.0 - (AA_ - (halfWidth - 1.0))) * 256, 1);
1725                 }
1726 
1727                 compositeOnePixel(accessor->rawData(), mycolor);
1728             }
1729         }
1730     }
1731 }
1732 
1733 /**/
1734 
1735 void KisPainter::drawLine(const QPointF & start, const QPointF & end)
1736 {
1737     drawThickLine(start, end, 1, 1);
1738 }
1739 
1740 
1741 void KisPainter::drawDDALine(const QPointF & start, const QPointF & end)
1742 {
1743     int x = qFloor(start.x());
1744     int y = qFloor(start.y());
1745 
1746     int x2 = qFloor(end.x());
1747     int y2 = qFloor(end.y());
1748 
1749     // Width and height of the line
1750     int xd = x2 - x;
1751     int yd = y2 - y;
1752 
1753     float m = 0;
1754     bool lockAxis = true;
1755 
1756     if (xd == 0) {
1757         m = 2.0;
1758     } else if ( yd != 0) {
1759         lockAxis = false;
1760         m = (float)yd / (float)xd;
1761     }
1762 
1763     float fx = x;
1764     float fy = y;
1765     int inc;
1766 
1767     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
1768     KisRandomConstAccessorSP selectionAccessor;
1769     if (d->selection) {
1770         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
1771     }
1772 
1773 
1774     accessor->moveTo(x, y);
1775     if (selectionAccessor) selectionAccessor->moveTo(x,y);
1776 
1777     if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1778         compositeOnePixel(accessor->rawData(), d->paintColor);
1779     }
1780 
1781     if (fabs(m) > 1.0f) {
1782         inc = (yd > 0) ? 1 : -1;
1783         m = (lockAxis)? 0 : 1.0f / m;
1784         m *= inc;
1785         while (y != y2) {
1786             y = y + inc;
1787             fx = fx + m;
1788             x = qRound(fx);
1789 
1790             accessor->moveTo(x, y);
1791             if (selectionAccessor) selectionAccessor->moveTo(x, y);
1792 
1793             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1794                 compositeOnePixel(accessor->rawData(), d->paintColor);
1795             }
1796         }
1797     } else {
1798         inc = (xd > 0) ? 1 : -1;
1799         m *= inc;
1800         while (x != x2) {
1801             x = x + inc;
1802             fy = fy + m;
1803             y = qRound(fy);
1804 
1805             accessor->moveTo(x, y);
1806             if (selectionAccessor) selectionAccessor->moveTo(x, y);
1807 
1808             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1809                 compositeOnePixel(accessor->rawData(), d->paintColor);
1810             }
1811         }
1812     }
1813 }
1814 
1815 void KisPainter::drawWobblyLine(const QPointF & start, const QPointF & end)
1816 {
1817     KoColor mycolor(d->paintColor);
1818 
1819     int x1 = qFloor(start.x());
1820     int y1 = qFloor(start.y());
1821     int x2 = qFloor(end.x());
1822     int y2 = qFloor(end.y());
1823 
1824     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
1825     KisRandomConstAccessorSP selectionAccessor;
1826     if (d->selection) {
1827         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
1828     }
1829 
1830     // Width and height of the line
1831     int xd = (x2 - x1);
1832     int yd = (y2 - y1);
1833 
1834     int x;
1835     int y;
1836     float fx = (x = x1);
1837     float fy = (y = y1);
1838     float m = (float)yd / (float)xd;
1839     int inc;
1840 
1841     if (fabs(m) > 1) {
1842         inc = (yd > 0) ? 1 : -1;
1843         m = 1.0f / m;
1844         m *= inc;
1845         while (y != y2) {
1846             fx = fx + m;
1847             y = y + inc;
1848             x = qRound(fx);
1849 
1850             float br1 = qFloor(fx + 1) - fx;
1851             float br2 = fx - qFloor(fx);
1852 
1853             accessor->moveTo(x, y);
1854             if (selectionAccessor) selectionAccessor->moveTo(x, y);
1855 
1856             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1857                 mycolor.setOpacity((quint8)(255*br1));
1858                 compositeOnePixel(accessor->rawData(), mycolor);
1859             }
1860 
1861             accessor->moveTo(x + 1, y);
1862             if (selectionAccessor) selectionAccessor->moveTo(x + 1, y);
1863 
1864             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1865                 mycolor.setOpacity((quint8)(255*br2));
1866                 compositeOnePixel(accessor->rawData(), mycolor);
1867             }
1868         }
1869     } else {
1870         inc = (xd > 0) ? 1 : -1;
1871         m *= inc;
1872         while (x != x2) {
1873             fy = fy + m;
1874             x = x + inc;
1875             y = qRound(fy);
1876 
1877             float br1 = qFloor(fy + 1) - fy;
1878             float br2 = fy - qFloor(fy);
1879 
1880             accessor->moveTo(x, y);
1881             if (selectionAccessor) selectionAccessor->moveTo(x, y);
1882 
1883             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1884                 mycolor.setOpacity((quint8)(255*br1));
1885                 compositeOnePixel(accessor->rawData(), mycolor);
1886             }
1887 
1888             accessor->moveTo(x, y + 1);
1889             if (selectionAccessor) selectionAccessor->moveTo(x, y + 1);
1890 
1891             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1892                 mycolor.setOpacity((quint8)(255*br2));
1893                 compositeOnePixel(accessor->rawData(), mycolor);
1894             }
1895         }
1896     }
1897 
1898 }
1899 
1900 void KisPainter::drawWuLine(const QPointF & start, const QPointF & end)
1901 {
1902     KoColor lineColor(d->paintColor);
1903 
1904     int x1 = qFloor(start.x());
1905     int y1 = qFloor(start.y());
1906     int x2 = qFloor(end.x());
1907     int y2 = qFloor(end.y());
1908 
1909     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
1910     KisRandomConstAccessorSP selectionAccessor;
1911     if (d->selection) {
1912         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
1913     }
1914 
1915     float grad, xd, yd;
1916     float xgap, ygap, xend, yend, yf, xf;
1917     float brightness1, brightness2;
1918 
1919     int ix1, ix2, iy1, iy2;
1920     quint8 c1, c2;
1921 
1922     // gradient of line
1923     xd = (x2 - x1);
1924     yd = (y2 - y1);
1925 
1926     if (yd == 0) {
1927         /* Horizontal line */
1928         int incr = (x1 < x2) ? 1 : -1;
1929         ix1 = x1;
1930         ix2 = x2;
1931         iy1 = y1;
1932         while (ix1 != ix2) {
1933             ix1 = ix1 + incr;
1934 
1935             accessor->moveTo(ix1, iy1);
1936             if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
1937 
1938             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1939                 compositeOnePixel(accessor->rawData(), lineColor);
1940             }
1941         }
1942         return;
1943     }
1944 
1945     if (xd == 0) {
1946         /* Vertical line */
1947         int incr = (y1 < y2) ? 1 : -1;
1948         iy1 = y1;
1949         iy2 = y2;
1950         ix1 = x1;
1951         while (iy1 != iy2) {
1952             iy1 = iy1 + incr;
1953 
1954             accessor->moveTo(ix1, iy1);
1955             if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
1956 
1957             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1958                 compositeOnePixel(accessor->rawData(), lineColor);
1959             }
1960         }
1961         return;
1962     }
1963 
1964     if (fabs(xd) > fabs(yd)) {
1965         // horizontal line
1966         // line have to be paint from left to right
1967         if (x1 > x2) {
1968             std::swap(x1, x2);
1969             std::swap(y1, y2);
1970             xd = (x2 - x1);
1971             yd = (y2 - y1);
1972         }
1973         grad = yd / xd;
1974         // nearest X,Y integer coordinates
1975         xend = x1;
1976         yend = y1 + grad * (xend - x1);
1977 
1978         xgap = invertFrac(x1 + 0.5f);
1979 
1980         ix1 = x1;
1981         iy1 = qFloor(yend);
1982 
1983         // calc the intensity of the other end point pixel pair.
1984         brightness1 = invertFrac(yend) * xgap;
1985         brightness2 =       frac(yend) * xgap;
1986 
1987         c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
1988         c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
1989 
1990         accessor->moveTo(ix1, iy1);
1991         if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
1992 
1993         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1994             lineColor.setOpacity(c1);
1995             compositeOnePixel(accessor->rawData(), lineColor);
1996         }
1997 
1998         accessor->moveTo(ix1, iy1 + 1);
1999         if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1 + 1);
2000 
2001         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2002             lineColor.setOpacity(c2);
2003             compositeOnePixel(accessor->rawData(), lineColor);
2004         }
2005 
2006         // calc first Y-intersection for main loop
2007         yf = yend + grad;
2008 
2009         xend = x2;
2010         yend = y2 + grad * (xend - x2);
2011 
2012         xgap = invertFrac(x2 - 0.5f);
2013 
2014         ix2 = x2;
2015         iy2 = qFloor(yend);
2016 
2017         brightness1 = invertFrac(yend) * xgap;
2018         brightness2 =    frac(yend) * xgap;
2019 
2020         c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2021         c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2022 
2023         accessor->moveTo(ix2, iy2);
2024         if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2);
2025 
2026         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2027             lineColor.setOpacity(c1);
2028             compositeOnePixel(accessor->rawData(), lineColor);
2029         }
2030 
2031         accessor->moveTo(ix2, iy2 + 1);
2032         if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2 + 1);
2033 
2034         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2035             lineColor.setOpacity(c2);
2036             compositeOnePixel(accessor->rawData(), lineColor);
2037         }
2038 
2039         // main loop
2040         for (int x = ix1 + 1; x <= ix2 - 1; x++) {
2041             brightness1 = invertFrac(yf);
2042             brightness2 =    frac(yf);
2043             c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2044             c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2045 
2046             accessor->moveTo(x, qFloor(yf));
2047             if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yf));
2048 
2049             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2050                 lineColor.setOpacity(c1);
2051                 compositeOnePixel(accessor->rawData(), lineColor);
2052             }
2053 
2054             accessor->moveTo(x, qFloor(yf) + 1);
2055             if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yf) + 1);
2056 
2057             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2058                 lineColor.setOpacity(c2);
2059                 compositeOnePixel(accessor->rawData(), lineColor);
2060             }
2061 
2062             yf = yf + grad;
2063         }
2064     } else {
2065         //vertical
2066         // line have to be painted from left to right
2067         if (y1 > y2) {
2068             std::swap(x1, x2);
2069             std::swap(y1, y2);
2070             xd = (x2 - x1);
2071             yd = (y2 - y1);
2072         }
2073 
2074         grad = xd / yd;
2075 
2076         // nearest X,Y integer coordinates
2077         yend = y1;
2078         xend = x1 + grad * (yend - y1);
2079 
2080         ygap = y1;
2081 
2082         ix1 = qFloor(xend);
2083         iy1 = y1;
2084 
2085         // calc the intensity of the other end point pixel pair.
2086         brightness1 = invertFrac(xend) * ygap;
2087         brightness2 =       frac(xend) * ygap;
2088 
2089         c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2090         c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2091 
2092         accessor->moveTo(ix1, iy1);
2093         if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
2094 
2095         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2096             lineColor.setOpacity(c1);
2097             compositeOnePixel(accessor->rawData(), lineColor);
2098         }
2099 
2100         accessor->moveTo(x1 + 1, y1);
2101         if (selectionAccessor) selectionAccessor->moveTo(x1 + 1, y1);
2102 
2103         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2104             lineColor.setOpacity(c2);
2105             compositeOnePixel(accessor->rawData(), lineColor);
2106         }
2107 
2108         // calc first Y-intersection for main loop
2109         xf = xend + grad;
2110 
2111         yend = y2;
2112         xend = x2 + grad * (yend - y2);
2113 
2114         ygap = invertFrac(y2 - 0.5f);
2115 
2116         ix2 = qFloor(xend);
2117         iy2 = y2;
2118 
2119         brightness1 = invertFrac(xend) * ygap;
2120         brightness2 =    frac(xend) * ygap;
2121 
2122         c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2123         c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2124 
2125         accessor->moveTo(ix2, iy2);
2126         if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2);
2127 
2128         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2129             lineColor.setOpacity(c1);
2130             compositeOnePixel(accessor->rawData(), lineColor);
2131         }
2132 
2133         accessor->moveTo(ix2 + 1, iy2);
2134         if (selectionAccessor) selectionAccessor->moveTo(ix2 + 1, iy2);
2135 
2136         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2137             lineColor.setOpacity(c2);
2138             compositeOnePixel(accessor->rawData(), lineColor);
2139         }
2140 
2141         // main loop
2142         for (int y = iy1 + 1; y <= iy2 - 1; y++) {
2143             brightness1 = invertFrac(xf);
2144             brightness2 =    frac(xf);
2145             c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2146             c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2147 
2148             accessor->moveTo(qFloor(xf), y);
2149             if (selectionAccessor) selectionAccessor->moveTo(qFloor(xf), y);
2150 
2151             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2152                 lineColor.setOpacity(c1);
2153                 compositeOnePixel(accessor->rawData(), lineColor);
2154             }
2155 
2156             accessor->moveTo(qFloor(xf) + 1, y);
2157             if (selectionAccessor) selectionAccessor->moveTo(qFloor(xf) + 1, y);
2158 
2159             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2160                 lineColor.setOpacity(c2);
2161                 compositeOnePixel(accessor->rawData(), lineColor);
2162             }
2163 
2164             xf = xf + grad;
2165         }
2166     }//end-of-else
2167 
2168 }
2169 
2170 void KisPainter::drawThickLine(const QPointF & start, const QPointF & end, int startWidth, int endWidth)
2171 {
2172 
2173     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
2174     KisRandomConstAccessorSP selectionAccessor;
2175     if (d->selection) {
2176         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
2177     }
2178 
2179     const KoColorSpace *cs = d->device->colorSpace();
2180 
2181     KoColor c1(d->paintColor);
2182     KoColor c2(d->paintColor);
2183     KoColor c3(d->paintColor);
2184     KoColor col1(c1);
2185     KoColor col2(c1);
2186 
2187     float grada, gradb, dxa, dxb, dya, dyb, fraca, fracb,
2188     xfa, yfa, xfb, yfb, b1a, b2a, b1b, b2b, dstX, dstY;
2189     int x, y, ix1, ix2, iy1, iy2;
2190 
2191     int x0a, y0a, x1a, y1a, x0b, y0b, x1b, y1b;
2192     int tp0, tn0, tp1, tn1;
2193 
2194     int horizontal = 0;
2195     float opacity = 1.0;
2196 
2197     tp0 = startWidth / 2;
2198     tn0 = startWidth / 2;
2199     if (startWidth % 2 == 0) // even width startWidth
2200         tn0--;
2201 
2202     tp1 = endWidth / 2;
2203     tn1 = endWidth / 2;
2204     if (endWidth % 2 == 0) // even width endWidth
2205         tn1--;
2206 
2207     int x0 = qRound(start.x());
2208     int y0 = qRound(start.y());
2209     int x1 = qRound(end.x());
2210     int y1 = qRound(end.y());
2211 
2212     dstX = x1 - x0; // run of general line
2213     dstY = y1 - y0; // rise of general line
2214 
2215     if (dstY < 0) dstY = -dstY;
2216     if (dstX < 0) dstX = -dstX;
2217 
2218     if (dstX > dstY) { // horizontalish
2219         horizontal = 1;
2220         x0a = x0;   y0a = y0 - tn0;
2221         x0b = x0;   y0b = y0 + tp0;
2222         x1a = x1;   y1a = y1 - tn1;
2223         x1b = x1;   y1b = y1 + tp1;
2224     } else {
2225         x0a = x0 - tn0;   y0a = y0;
2226         x0b = x0 + tp0;   y0b = y0;
2227         x1a = x1 - tn1;   y1a = y1;
2228         x1b = x1 + tp1;   y1b = y1;
2229     }
2230 
2231     if (horizontal) { // draw endpoints
2232         for (int i = y0a; i <= y0b; i++) {
2233 
2234             accessor->moveTo(x0, i);
2235             if (selectionAccessor) selectionAccessor->moveTo(x0, i);
2236 
2237             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2238                 compositeOnePixel(accessor->rawData(), c1);
2239             }
2240         }
2241         for (int i = y1a; i <= y1b; i++) {
2242 
2243             accessor->moveTo(x1, i);
2244             if (selectionAccessor) selectionAccessor->moveTo(x1, i);
2245 
2246             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2247                 compositeOnePixel(accessor->rawData(), c1);
2248             }
2249         }
2250 
2251     } else {
2252         for (int i = x0a; i <= x0b; i++) {
2253 
2254             accessor->moveTo(i, y0);
2255             if (selectionAccessor) selectionAccessor->moveTo(i, y0);
2256 
2257             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2258                 compositeOnePixel(accessor->rawData(), c1);
2259             }
2260         }
2261         for (int i = x1a; i <= x1b; i++) {
2262             accessor->moveTo(i, y1);
2263             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2264                 compositeOnePixel(accessor->rawData(), c1);
2265             }
2266         }
2267     }
2268 
2269     //antialias endpoints
2270     if (x1 != x0 && y1 != y0) {
2271         if (horizontal) {
2272 
2273             accessor->moveTo(x0a, y0a - 1);
2274             if (selectionAccessor) selectionAccessor->moveTo(x0a, y0a - 1);
2275 
2276             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2277                 qreal alpha = cs->opacityF(accessor->rawData());
2278                 opacity = .25 * c1.opacityF() + (1 - .25) * alpha;
2279                 col1.setOpacity(opacity);
2280                 compositeOnePixel(accessor->rawData(), col1);
2281             }
2282 
2283             accessor->moveTo(x1b, y1b + 1);
2284             if (selectionAccessor) selectionAccessor->moveTo(x1b, y1b + 1);
2285 
2286             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2287                 qreal alpha = cs->opacityF(accessor->rawData());
2288                 opacity = .25 * c2.opacityF() + (1 - .25) * alpha;
2289                 col1.setOpacity(opacity);
2290                 compositeOnePixel(accessor->rawData(), col1);
2291             }
2292 
2293         } else {
2294 
2295             accessor->moveTo(x0a - 1, y0a);
2296             if (selectionAccessor) selectionAccessor->moveTo(x0a - 1, y0a);
2297 
2298             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2299                 qreal alpha = cs->opacityF(accessor->rawData());
2300                 opacity = .25 * c1.opacityF() + (1 - .25) * alpha;
2301                 col1.setOpacity(opacity);
2302                 compositeOnePixel(accessor->rawData(), col1);
2303             }
2304 
2305             accessor->moveTo(x1b + 1, y1b);
2306             if (selectionAccessor) selectionAccessor->moveTo(x1b + 1, y1b);
2307 
2308             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2309                 qreal alpha = cs->opacityF(accessor->rawData());
2310                 opacity = .25 * c2.opacityF() + (1 - .25) * alpha;
2311                 col1.setOpacity(opacity);
2312                 compositeOnePixel(accessor->rawData(), col1);
2313             }
2314         }
2315     }
2316 
2317     dxa = x1a - x0a; // run of a
2318     dya = y1a - y0a; // rise of a
2319     dxb = x1b - x0b; // run of b
2320     dyb = y1b - y0b; // rise of b
2321 
2322     if (horizontal) { // horizontal-ish lines
2323         if (x1 < x0) {
2324             int xt, yt, wt;
2325             KoColor tmp;
2326             xt = x1a;     x1a = x0a;    x0a = xt;
2327             yt = y1a;     y1a = y0a;    y0a = yt;
2328             xt = x1b;     x1b = x0b;    x0b = xt;
2329             yt = y1b;     y1b = y0b;    y0b = yt;
2330             xt = x1;      x1 = x0;      x0 = xt;
2331             yt = y1;      y1 = y0;      y0 = yt;
2332 
2333             tmp = c1; c1 = c2; c2 = tmp;
2334             wt = startWidth;      startWidth = endWidth;      endWidth = wt;
2335         }
2336 
2337         grada = dya / dxa;
2338         gradb = dyb / dxb;
2339 
2340         ix1 = x0;   iy1 = y0;
2341         ix2 = x1;   iy2 = y1;
2342 
2343         yfa = y0a + grada;
2344         yfb = y0b + gradb;
2345 
2346         for (x = ix1 + 1; x <= ix2 - 1; x++) {
2347             fraca = yfa - qFloor(yfa);
2348             b1a = 1 - fraca;
2349             b2a = fraca;
2350 
2351             fracb = yfb - qFloor(yfb);
2352             b1b = 1 - fracb;
2353             b2b = fracb;
2354 
2355             // color first pixel of bottom line
2356             opacity = ((x - ix1) / dstX) * c2.opacityF() + (1 - (x - ix1) / dstX) * c1.opacityF();
2357             c3.setOpacity(opacity);
2358 
2359             accessor->moveTo(x, qFloor(yfa));
2360             if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfa));
2361 
2362             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2363                 qreal alpha = cs->opacityF(accessor->rawData());
2364                 opacity = b1a * c3.opacityF() + (1 - b1a) * alpha;
2365                 col1.setOpacity(opacity);
2366                 compositeOnePixel(accessor->rawData(), col1);
2367             }
2368 
2369             // color first pixel of top line
2370             if (!(startWidth == 1 && endWidth == 1)) {
2371                 accessor->moveTo(x, qFloor(yfb));
2372                 if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfb));
2373 
2374                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2375                     qreal alpha = cs->opacityF(accessor->rawData());
2376                     opacity = b1b * c3.opacityF() + (1 - b1b) * alpha;
2377                     col1.setOpacity(opacity);
2378                     compositeOnePixel(accessor->rawData(), col1);
2379                 }
2380             }
2381 
2382             // color second pixel of bottom line
2383             if (grada != 0 && grada != 1) { // if not flat or exact diagonal
2384 
2385                 accessor->moveTo(x, qFloor(yfa) + 1);
2386                 if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfa) + 1);
2387 
2388                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2389                     qreal alpha = cs->opacityF(accessor->rawData());
2390                     opacity = b2a * c3.opacityF() + (1 - b2a)  * alpha;
2391                     col2.setOpacity(opacity);
2392                     compositeOnePixel(accessor->rawData(), col2);
2393                 }
2394 
2395             }
2396 
2397             // color second pixel of top line
2398             if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) {
2399 
2400                 accessor->moveTo(x, qFloor(yfb) + 1);
2401                 if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfb) + 1);
2402 
2403                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2404                     qreal alpha = cs->opacityF(accessor->rawData());
2405                     opacity = b2b * c3.opacityF() + (1 - b2b) * alpha;
2406                     col2.setOpacity(opacity);
2407                     compositeOnePixel(accessor->rawData(), col2);
2408                 }
2409 
2410             }
2411 
2412             // fill remaining pixels
2413             if (!(startWidth == 1 && endWidth == 1)) {
2414                 if (yfa < yfb)
2415                     for (int i = qFloor(yfa) + 1; i <= qFloor(yfb); i++) {
2416 
2417                         accessor->moveTo(x, i);
2418                         if (selectionAccessor) selectionAccessor->moveTo(x, i);
2419 
2420                         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2421                             compositeOnePixel(accessor->rawData(), c3);
2422                         }
2423                     }
2424                 else
2425                     for (int i = qFloor(yfa) + 1; i >= qFloor(yfb); i--) {
2426 
2427                         accessor->moveTo(x, i);
2428                         if (selectionAccessor) selectionAccessor->moveTo(x, i);
2429 
2430                         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2431                             compositeOnePixel(accessor->rawData(), c3);
2432                         }
2433                     }
2434 
2435             }
2436 
2437             yfa += grada;
2438             yfb += gradb;
2439         }
2440     } else { // vertical-ish lines
2441         if (y1 < y0) {
2442             int xt, yt, wt;
2443             xt = x1a;     x1a = x0a;    x0a = xt;
2444             yt = y1a;     y1a = y0a;    y0a = yt;
2445             xt = x1b;     x1b = x0b;    x0b = xt;
2446             yt = y1b;     y1b = y0b;    y0b = yt;
2447             xt = x1;      x1 = x0;      x0 = xt;
2448             yt = y1;      y1 = y0;      y0 = yt;
2449 
2450             KoColor tmp;
2451             tmp = c1; c1 = c2; c2 = tmp;
2452             wt = startWidth;      startWidth = endWidth;      endWidth = wt;
2453         }
2454 
2455         grada = dxa / dya;
2456         gradb = dxb / dyb;
2457 
2458         ix1 = x0;   iy1 = y0;
2459         ix2 = x1;   iy2 = y1;
2460 
2461         xfa = x0a + grada;
2462         xfb = x0b + gradb;
2463 
2464         for (y = iy1 + 1; y <= iy2 - 1; y++) {
2465             fraca = xfa - qFloor(xfa);
2466             b1a = 1 - fraca;
2467             b2a = fraca;
2468 
2469             fracb = xfb - qFloor(xfb);
2470             b1b = 1 - fracb;
2471             b2b = fracb;
2472 
2473             // color first pixel of left line
2474             opacity = ((y - iy1) / dstY) * c2.opacityF() + (1 - (y - iy1) / dstY) * c1.opacityF();
2475             c3.setOpacity(opacity);
2476 
2477             accessor->moveTo(qFloor(xfa), y);
2478             if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfa), y);
2479 
2480             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2481                 qreal alpha = cs->opacityF(accessor->rawData());
2482                 opacity = b1a * c3.opacityF() + (1 - b1a) * alpha;
2483                 col1.setOpacity(opacity);
2484                 compositeOnePixel(accessor->rawData(), col1);
2485             }
2486 
2487             // color first pixel of right line
2488             if (!(startWidth == 1 && endWidth == 1)) {
2489 
2490                 accessor->moveTo(qFloor(xfb), y);
2491                 if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfb), y);
2492 
2493                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2494                     qreal alpha = cs->opacityF(accessor->rawData());
2495                     opacity = b1b * c3.opacityF() + (1 - b1b)  * alpha;
2496                     col1.setOpacity(opacity);
2497                     compositeOnePixel(accessor->rawData(), col1);
2498                 }
2499             }
2500 
2501             // color second pixel of left line
2502             if (grada != 0 && grada != 1) { // if not flat or exact diagonal
2503 
2504                 accessor->moveTo(qFloor(xfa) + 1, y);
2505                 if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfa) + 1, y);
2506 
2507                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2508                     qreal alpha = cs->opacityF(accessor->rawData());
2509                     opacity = b2a * c3.opacityF() + (1 - b2a) * alpha;
2510                     col2.setOpacity(opacity);
2511                     compositeOnePixel(accessor->rawData(), col2);
2512                 }
2513 
2514             }
2515 
2516             // color second pixel of right line
2517             if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) {
2518 
2519                 accessor->moveTo(qFloor(xfb) + 1, y);
2520                 if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfb) + 1, y);
2521 
2522                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2523                     qreal alpha = cs->opacityF(accessor->rawData());
2524                     opacity = b2b * c3.opacityF() + (1 - b2b) * alpha;
2525                     col2.setOpacity(opacity);
2526                     compositeOnePixel(accessor->rawData(), col2);
2527                 }
2528             }
2529 
2530             // fill remaining pixels between current xfa,xfb
2531             if (!(startWidth == 1 && endWidth == 1)) {
2532                 if (xfa < xfb)
2533                     for (int i = qFloor(xfa) + 1; i <= qFloor(xfb); i++) {
2534 
2535                         accessor->moveTo(i, y);
2536                         if (selectionAccessor) selectionAccessor->moveTo(i, y);
2537 
2538                         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2539                             compositeOnePixel(accessor->rawData(), c3);
2540                         }
2541                     }
2542                 else
2543                     for (int i = qFloor(xfb); i <= qFloor(xfa) + 1; i++) {
2544 
2545                         accessor->moveTo(i, y);
2546                         if (selectionAccessor) selectionAccessor->moveTo(i, y);
2547 
2548                         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2549                             compositeOnePixel(accessor->rawData(), c3);
2550                         }
2551                     }
2552             }
2553 
2554             xfa += grada;
2555             xfb += gradb;
2556         }
2557     }
2558 
2559 }
2560 
2561 
2562 
2563 void KisPainter::setProgress(KoUpdater * progressUpdater)
2564 {
2565     d->progressUpdater = progressUpdater;
2566 }
2567 
2568 const KisPaintDeviceSP KisPainter::device() const
2569 {
2570     return d->device;
2571 }
2572 KisPaintDeviceSP KisPainter::device()
2573 {
2574     return d->device;
2575 }
2576 
2577 void KisPainter::setChannelFlags(QBitArray channelFlags)
2578 {
2579     // Q_ASSERT(channelFlags.isEmpty() || quint32(channelFlags.size()) == d->colorSpace->channelCount());
2580     // Now, if all bits in the channelflags are true, pass an empty channel flags bitarray
2581     // because otherwise the compositeops cannot optimize.
2582     d->paramInfo.channelFlags = channelFlags;
2583 
2584     if (!channelFlags.isEmpty() && channelFlags == QBitArray(channelFlags.size(), true)) {
2585         d->paramInfo.channelFlags = QBitArray();
2586     }
2587 }
2588 
2589 QBitArray KisPainter::channelFlags()
2590 {
2591     return d->paramInfo.channelFlags;
2592 }
2593 
2594 void KisPainter::setPattern(const KoPatternSP pattern)
2595 {
2596     d->pattern = pattern;
2597 }
2598 
2599 const KoPatternSP KisPainter::pattern() const
2600 {
2601     return d->pattern;
2602 }
2603 
2604 void KisPainter::setPaintColor(const KoColor& color)
2605 {
2606     d->paintColor = color;
2607     if (d->device) {
2608         d->paintColor.convertTo(d->device->compositionSourceColorSpace());
2609     }
2610 }
2611 
2612 const KoColor &KisPainter::paintColor() const
2613 {
2614     return d->paintColor;
2615 }
2616 
2617 void KisPainter::setBackgroundColor(const KoColor& color)
2618 {
2619     d->backgroundColor = color;
2620     if (d->device) {
2621         d->backgroundColor.convertTo(d->device->compositionSourceColorSpace());
2622     }
2623 }
2624 
2625 const KoColor &KisPainter::backgroundColor() const
2626 {
2627     return d->backgroundColor;
2628 }
2629 
2630 void KisPainter::setGenerator(KisFilterConfigurationSP  generator)
2631 {
2632     d->generator = generator;
2633 }
2634 
2635 const KisFilterConfigurationSP  KisPainter::generator() const
2636 {
2637     return d->generator;
2638 }
2639 
2640 void KisPainter::setFillStyle(FillStyle fillStyle)
2641 {
2642     d->fillStyle = fillStyle;
2643 }
2644 
2645 KisPainter::FillStyle KisPainter::fillStyle() const
2646 {
2647     return d->fillStyle;
2648 }
2649 
2650 void KisPainter::setPatternTransform(QTransform transform)
2651 {
2652     d->patternTransform = transform;
2653 }
2654 
2655 QTransform KisPainter::patternTransform()
2656 {
2657     return d->patternTransform;
2658 }
2659 
2660 void KisPainter::setAntiAliasPolygonFill(bool antiAliasPolygonFill)
2661 {
2662     d->antiAliasPolygonFill = antiAliasPolygonFill;
2663 }
2664 
2665 bool KisPainter::antiAliasPolygonFill()
2666 {
2667     return d->antiAliasPolygonFill;
2668 }
2669 
2670 void KisPainter::setStrokeStyle(KisPainter::StrokeStyle strokeStyle)
2671 {
2672     d->strokeStyle = strokeStyle;
2673 }
2674 KisPainter::StrokeStyle KisPainter::strokeStyle() const
2675 {
2676     return d->strokeStyle;
2677 }
2678 
2679 void KisPainter::setFlow(quint8 flow)
2680 {
2681     d->paramInfo.flow = float(flow) / 255.0f;
2682 }
2683 
2684 quint8 KisPainter::flow() const
2685 {
2686     return quint8(d->paramInfo.flow * 255.0f);
2687 }
2688 
2689 void KisPainter::setOpacityUpdateAverage(quint8 opacity)
2690 {
2691     d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8;
2692     d->paramInfo.updateOpacityAndAverage(float(opacity) / 255.0f);
2693 }
2694 
2695 void KisPainter::setAverageOpacity(qreal averageOpacity)
2696 {
2697     d->paramInfo.setOpacityAndAverage(d->paramInfo.opacity, averageOpacity);
2698 }
2699 
2700 qreal KisPainter::blendAverageOpacity(qreal opacity, qreal averageOpacity)
2701 {
2702     const float exponent = 0.1;
2703 
2704     return averageOpacity < opacity ?
2705         opacity :
2706         exponent * opacity + (1.0 - exponent) * (averageOpacity);
2707 }
2708 
2709 void KisPainter::setOpacity(quint8 opacity)
2710 {
2711     d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8;
2712     d->paramInfo.opacity = float(opacity) / 255.0f;
2713 }
2714 
2715 quint8 KisPainter::opacity() const
2716 {
2717     return quint8(d->paramInfo.opacity * 255.0f);
2718 }
2719 
2720 void KisPainter::setCompositeOpId(const KoCompositeOp * op)
2721 {
2722     setCompositeOpId(op->id());
2723 }
2724 
2725 QString KisPainter::compositeOpId()
2726 {
2727     return d->compositeOpId;
2728 }
2729 
2730 void KisPainter::setCompositeOpId(const QString& op)
2731 {
2732     if (op != d->compositeOpId) {
2733         d->compositeOpId = op;
2734         d->cachedCompositeOp = nullptr;
2735     }
2736 }
2737 
2738 void KisPainter::setSelection(KisSelectionSP selection)
2739 {
2740     d->selection = selection;
2741 }
2742 
2743 KisSelectionSP KisPainter::selection()
2744 {
2745     return d->selection;
2746 }
2747 
2748 KoUpdater * KisPainter::progressUpdater()
2749 {
2750     return d->progressUpdater;
2751 }
2752 
2753 void KisPainter::setGradient(const KoAbstractGradientSP gradient)
2754 {
2755     d->gradient = gradient;
2756 }
2757 
2758 const KoAbstractGradientSP KisPainter::gradient() const
2759 {
2760     return d->gradient;
2761 }
2762 
2763 void KisPainter::setPaintOpPreset(KisPaintOpPresetSP preset, KisNodeSP node, KisImageSP image)
2764 {
2765     d->paintOpPreset = preset;
2766     KisPaintOp *paintop = KisPaintOpRegistry::instance()->paintOp(preset, this, node, image);
2767     Q_ASSERT(paintop);
2768     if (paintop) {
2769         delete d->paintOp;
2770         d->paintOp = paintop;
2771     }
2772     else {
2773         warnKrita << "Could not create paintop for preset " << preset->name();
2774     }
2775 }
2776 
2777 KisPaintOpPresetSP KisPainter::preset() const
2778 {
2779     return d->paintOpPreset;
2780 }
2781 
2782 KisPaintOp* KisPainter::paintOp() const
2783 {
2784     return d->paintOp;
2785 }
2786 
2787 void KisPainter::setMirrorInformation(const QPointF& axesCenter, bool mirrorHorizontally, bool mirrorVertically)
2788 {
2789     d->axesCenter = axesCenter;
2790     d->mirrorHorizontally = mirrorHorizontally;
2791     d->mirrorVertically = mirrorVertically;
2792 }
2793 
2794 void KisPainter::copyMirrorInformationFrom(const KisPainter *other)
2795 {
2796     d->axesCenter = other->d->axesCenter;
2797     d->mirrorHorizontally = other->d->mirrorHorizontally;
2798     d->mirrorVertically = other->d->mirrorVertically;
2799 }
2800 
2801 bool KisPainter::hasMirroring() const
2802 {
2803     return d->mirrorHorizontally || d->mirrorVertically;
2804 }
2805 
2806 bool KisPainter::hasHorizontalMirroring() const
2807 {
2808     return d->mirrorHorizontally;
2809 }
2810 
2811 bool KisPainter::hasVerticalMirroring() const
2812 {
2813     return d->mirrorVertically;
2814 }
2815 
2816 void KisPainter::setMaskImageSize(qint32 width, qint32 height)
2817 {
2818 
2819     d->maskImageWidth = qBound(1, width, 256);
2820     d->maskImageHeight = qBound(1, height, 256);
2821     d->fillPainter = 0;
2822     d->polygonMaskImage = QImage();
2823 }
2824 
2825 //void KisPainter::setLockAlpha(bool protect)
2826 //{
2827 //    if(d->paramInfo.channelFlags.isEmpty()) {
2828 //        d->paramInfo.channelFlags = d->colorSpace->channelFlags(true, true);
2829 //    }
2830 
2831 //    QBitArray switcher =
2832 //        d->colorSpace->channelFlags(protect, !protect);
2833 
2834 //    if(protect) {
2835 //        d->paramInfo.channelFlags &= switcher;
2836 //    }
2837 //    else {
2838 //        d->paramInfo.channelFlags |= switcher;
2839 //    }
2840 
2841 //    Q_ASSERT(quint32(d->paramInfo.channelFlags.size()) == d->colorSpace->channelCount());
2842 //}
2843 
2844 //bool KisPainter::alphaLocked() const
2845 //{
2846 //    QBitArray switcher = d->colorSpace->channelFlags(false, true);
2847 //    return !(d->paramInfo.channelFlags & switcher).count(true);
2848 //}
2849 
2850 void KisPainter::setRenderingIntent(KoColorConversionTransformation::Intent intent)
2851 {
2852     d->renderingIntent = intent;
2853 }
2854 
2855 void KisPainter::setColorConversionFlags(KoColorConversionTransformation::ConversionFlags conversionFlags)
2856 {
2857     d->conversionFlags = conversionFlags;
2858 }
2859 
2860 void KisPainter::setRunnableStrokeJobsInterface(KisRunnableStrokeJobsInterface *interface)
2861 {
2862     d->runnableStrokeJobsInterface = interface;
2863 }
2864 
2865 KisRunnableStrokeJobsInterface *KisPainter::runnableStrokeJobsInterface() const
2866 {
2867     if (!d->runnableStrokeJobsInterface) {
2868         if (!d->fakeRunnableStrokeJobsInterface) {
2869             d->fakeRunnableStrokeJobsInterface.reset(new KisFakeRunnableStrokeJobsExecutor());
2870         }
2871         return d->fakeRunnableStrokeJobsInterface.data();
2872     }
2873 
2874     return d->runnableStrokeJobsInterface;
2875 }
2876 
2877 void KisPainter::renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, bool preserveDab)
2878 {
2879     if (!d->mirrorHorizontally && !d->mirrorVertically) return;
2880 
2881     KisFixedPaintDeviceSP dabToProcess = dab;
2882     if (preserveDab) {
2883         dabToProcess = new KisFixedPaintDevice(*dab);
2884     }
2885     renderMirrorMask(rc, dabToProcess);
2886 }
2887 
2888 void KisPainter::renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, KisFixedPaintDeviceSP mask, bool preserveDab)
2889 {
2890     if (!d->mirrorHorizontally && !d->mirrorVertically) return;
2891 
2892     KisFixedPaintDeviceSP dabToProcess = dab;
2893     if (preserveDab) {
2894         dabToProcess = new KisFixedPaintDevice(*dab);
2895     }
2896     renderMirrorMask(rc, dabToProcess, mask);
2897 }
2898 
2899 void KisPainter::renderMirrorMaskSafe(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask, bool preserveMask)
2900 {
2901     if (!d->mirrorHorizontally && !d->mirrorVertically) return;
2902 
2903     KisFixedPaintDeviceSP maskToProcess = mask;
2904     if (preserveMask) {
2905         maskToProcess = new KisFixedPaintDevice(*mask);
2906     }
2907     renderMirrorMask(rc, dab, sx, sy, maskToProcess);
2908 }
2909 
2910 void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab)
2911 {
2912     int x = rc.topLeft().x();
2913     int y = rc.topLeft().y();
2914 
2915     KisLodTransform t(d->device);
2916     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
2917 
2918     int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
2919     int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
2920 
2921     if (d->mirrorHorizontally && d->mirrorVertically){
2922         dab->mirror(true, false);
2923         bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
2924         dab->mirror(false,true);
2925         bltFixed(mirrorX, mirrorY, dab, 0,0,rc.width(),rc.height());
2926         dab->mirror(true, false);
2927         bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height());
2928 
2929     }
2930     else if (d->mirrorHorizontally){
2931         dab->mirror(true, false);
2932         bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
2933     }
2934     else if (d->mirrorVertically){
2935         dab->mirror(false, true);
2936         bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height());
2937     }
2938 
2939 }
2940 
2941 void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab, KisFixedPaintDeviceSP mask)
2942 {
2943     int x = rc.topLeft().x();
2944     int y = rc.topLeft().y();
2945 
2946     KisLodTransform t(d->device);
2947     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
2948 
2949     int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
2950     int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
2951 
2952     if (d->mirrorHorizontally && d->mirrorVertically){
2953         dab->mirror(true, false);
2954         mask->mirror(true, false);
2955         bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
2956 
2957         dab->mirror(false,true);
2958         mask->mirror(false, true);
2959         bltFixedWithFixedSelection(mirrorX,mirrorY, dab, mask, rc.width() ,rc.height() );
2960 
2961         dab->mirror(true, false);
2962         mask->mirror(true, false);
2963         bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() );
2964 
2965     }else if (d->mirrorHorizontally){
2966         dab->mirror(true, false);
2967         mask->mirror(true, false);
2968         bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
2969 
2970     }else if (d->mirrorVertically){
2971         dab->mirror(false, true);
2972         mask->mirror(false, true);
2973         bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() );
2974     }
2975 
2976 }
2977 
2978 
2979 void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab){
2980     if (d->mirrorHorizontally || d->mirrorVertically){
2981         KisFixedPaintDeviceSP mirrorDab(new KisFixedPaintDevice(dab->colorSpace()));
2982         QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) );
2983         mirrorDab->setRect(dabRc);
2984         mirrorDab->lazyGrowBufferWithoutInitialization();
2985 
2986         dab->readBytes(mirrorDab->data(),rc);
2987 
2988         renderMirrorMask( QRect(rc.topLeft(),dabRc.size()), mirrorDab);
2989     }
2990 }
2991 
2992 void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask)
2993 {
2994     if (d->mirrorHorizontally || d->mirrorVertically){
2995         KisFixedPaintDeviceSP mirrorDab(new KisFixedPaintDevice(dab->colorSpace()));
2996         QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) );
2997         mirrorDab->setRect(dabRc);
2998         mirrorDab->lazyGrowBufferWithoutInitialization();
2999         dab->readBytes(mirrorDab->data(),QRect(QPoint(sx,sy),rc.size()));
3000         renderMirrorMask(rc, mirrorDab, mask);
3001     }
3002 }
3003 
3004 void KisPainter::renderDabWithMirroringNonIncremental(QRect rc, KisPaintDeviceSP dab)
3005 {
3006     QVector<QRect> rects;
3007 
3008     int x = rc.topLeft().x();
3009     int y = rc.topLeft().y();
3010 
3011     KisLodTransform t(d->device);
3012     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
3013 
3014     int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
3015     int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
3016 
3017     rects << rc;
3018 
3019     if (d->mirrorHorizontally && d->mirrorVertically){
3020         rects << QRect(mirrorX, y, rc.width(), rc.height());
3021         rects << QRect(mirrorX, mirrorY, rc.width(), rc.height());
3022         rects << QRect(x, mirrorY, rc.width(), rc.height());
3023     } else if (d->mirrorHorizontally) {
3024         rects << QRect(mirrorX, y, rc.width(), rc.height());
3025     } else if (d->mirrorVertically) {
3026         rects << QRect(x, mirrorY, rc.width(), rc.height());
3027     }
3028 
3029     Q_FOREACH (const QRect &rc, rects) {
3030         d->device->clear(rc);
3031     }
3032 
3033     QRect resultRect = dab->extent() | rc;
3034     bool intersects = false;
3035 
3036     for (int i = 1; i < rects.size(); i++) {
3037         if (rects[i].intersects(resultRect)) {
3038             intersects = true;
3039             break;
3040         }
3041     }
3042 
3043     /**
3044      * If there are no cross-intersections, we can use a fast path
3045      * and do no cycling recompositioning
3046      */
3047     if (!intersects) {
3048         rects.resize(1);
3049     }
3050 
3051     Q_FOREACH (const QRect &rc, rects) {
3052         bitBlt(rc.topLeft(), dab, rc);
3053     }
3054 
3055     Q_FOREACH (const QRect &rc, rects) {
3056         renderMirrorMask(rc, dab);
3057     }
3058 }
3059 
3060 bool KisPainter::hasDirtyRegion() const
3061 {
3062     return !d->dirtyRects.isEmpty();
3063 }
3064 
3065 void KisPainter::mirrorRect(Qt::Orientation direction, QRect *rc) const
3066 {
3067     KisLodTransform t(d->device);
3068     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
3069 
3070     KritaUtils::mirrorRect(direction, effectiveAxesCenter, rc);
3071 }
3072 
3073 void KisPainter::mirrorDab(Qt::Orientation direction, KisRenderedDab *dab, bool skipMirrorPixels) const
3074 {
3075     KisLodTransform t(d->device);
3076     QPointF effectiveAxesCenter = t.map(d->axesCenter);
3077 
3078     KritaUtils::mirrorDab(direction, effectiveAxesCenter, dab, skipMirrorPixels);
3079 }
3080 
3081 namespace {
3082 
3083 inline void mirrorOneObject(Qt::Orientation dir, const QPointF &center, QRect *rc) {
3084     KritaUtils::mirrorRect(dir, center, rc);
3085 }
3086 
3087 inline void mirrorOneObject(Qt::Orientation dir, const QPointF &center, QPointF *pt) {
3088     KritaUtils::mirrorPoint(dir, center, pt);
3089 }
3090 
3091 inline void mirrorOneObject(Qt::Orientation dir, const QPointF &center, QPair<QPointF, QPointF> *pair) {
3092     KritaUtils::mirrorPoint(dir, center, &pair->first);
3093     KritaUtils::mirrorPoint(dir, center, &pair->second);
3094 }
3095 }
3096 
3097 template<class T> QVector<T> KisPainter::Private::calculateMirroredObjects(const T &object)
3098 {
3099     QVector<T> result;
3100 
3101     KisLodTransform t(this->device);
3102     const QPointF effectiveAxesCenter = t.map(this->axesCenter);
3103 
3104     T baseObject = object;
3105     result << baseObject;
3106 
3107     if (this->mirrorHorizontally && this->mirrorVertically){
3108         mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
3109         result << baseObject;
3110         mirrorOneObject(Qt::Vertical, effectiveAxesCenter, &baseObject);
3111         result << baseObject;
3112         mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
3113         result << baseObject;
3114     } else if (this->mirrorHorizontally) {
3115         mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
3116         result << baseObject;
3117     } else if (this->mirrorVertically) {
3118         mirrorOneObject(Qt::Vertical, effectiveAxesCenter, &baseObject);
3119         result << baseObject;
3120     }
3121 
3122     return result;
3123 }
3124 
3125 const QVector<QRect> KisPainter::calculateAllMirroredRects(const QRect &rc)
3126 {
3127     return d->calculateMirroredObjects(rc);
3128 }
3129 
3130 const QVector<QPointF> KisPainter::calculateAllMirroredPoints(const QPointF &pos)
3131 {
3132     return d->calculateMirroredObjects(pos);
3133 }
3134 
3135 const QVector<QPair<QPointF, QPointF>> KisPainter::calculateAllMirroredPoints(const QPair<QPointF, QPointF> &pair)
3136 {
3137     return d->calculateMirroredObjects(pair);
3138 }