1 /*
2  *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
4  *  Copyright (c) 2004 Clarence Dang <dang@kde.org>
5  *  Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
6  *  Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
7  *  Copyright (c) 2008-2010 Lukáš Tvrdý <lukast.dev@gmail.com>
8  *  Copyright (c) 2010 José Luis Vergara Toloza <pentalis@gmail.com>
9  *  Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  */
25 
26 #include "kis_painter.h"
27 #include <stdlib.h>
28 #include <string.h>
29 #include <cfloat>
30 #include <cmath>
31 #include <climits>
32 #ifndef Q_OS_WIN
33 #include <strings.h>
34 #endif
35 
36 #include <QImage>
37 #include <QRect>
38 #include <QString>
39 #include <QStringList>
40 #include <kundo2command.h>
41 
42 #include <kis_debug.h>
43 #include <klocalizedstring.h>
44 
45 #include "kis_image.h"
46 #include "filter/kis_filter.h"
47 #include "kis_layer.h"
48 #include "kis_paint_device.h"
49 #include "kis_fixed_paint_device.h"
50 #include "kis_transaction.h"
51 #include "kis_vec.h"
52 #include "kis_iterator_ng.h"
53 #include "kis_random_accessor_ng.h"
54 
55 #include "filter/kis_filter_configuration.h"
56 #include "kis_pixel_selection.h"
57 #include <brushengine/kis_paint_information.h>
58 #include "kis_paintop_registry.h"
59 #include "kis_perspective_math.h"
60 #include "tiles3/kis_random_accessor.h"
61 #include <kis_distance_information.h>
62 #include <KoColorSpaceMaths.h>
63 #include "kis_lod_transform.h"
64 #include "kis_algebra_2d.h"
65 #include "krita_utils.h"
66 
67 
68 // Maximum distance from a Bezier control point to the line through the start
69 // and end points for the curve to be considered flat.
70 #define BEZIER_FLATNESS_THRESHOLD 0.5
71 
72 #include "kis_painter_p.h"
73 
KisPainter()74 KisPainter::KisPainter()
75     : d(new Private(this))
76 {
77     init();
78 }
79 
KisPainter(KisPaintDeviceSP device)80 KisPainter::KisPainter(KisPaintDeviceSP device)
81     : d(new Private(this, device->colorSpace()))
82 {
83     init();
84     Q_ASSERT(device);
85     begin(device);
86 }
87 
KisPainter(KisPaintDeviceSP device,KisSelectionSP selection)88 KisPainter::KisPainter(KisPaintDeviceSP device, KisSelectionSP selection)
89     : d(new Private(this, device->colorSpace()))
90 {
91     init();
92     Q_ASSERT(device);
93     begin(device);
94     d->selection = selection;
95 }
96 
init()97 void KisPainter::init()
98 {
99     d->selection = 0 ;
100     d->transaction = 0;
101     d->paintOp = 0;
102     d->pattern = 0;
103     d->sourceLayer = 0;
104     d->fillStyle = FillStyleNone;
105     d->strokeStyle = StrokeStyleBrush;
106     d->antiAliasPolygonFill = true;
107     d->progressUpdater = 0;
108     d->gradient = 0;
109     d->maskPainter = 0;
110     d->fillPainter = 0;
111     d->maskImageWidth = 255;
112     d->maskImageHeight = 255;
113     d->mirrorHorizontally = false;
114     d->mirrorVertically = false;
115     d->isOpacityUnit = true;
116     d->paramInfo = KoCompositeOp::ParameterInfo();
117     d->renderingIntent = KoColorConversionTransformation::internalRenderingIntent();
118     d->conversionFlags = KoColorConversionTransformation::internalConversionFlags();
119     d->patternTransform = QTransform();
120 }
121 
~KisPainter()122 KisPainter::~KisPainter()
123 {
124     // TODO: Maybe, don't be that strict?
125     // deleteTransaction();
126     end();
127 
128     delete d->paintOp;
129     delete d->maskPainter;
130     delete d->fillPainter;
131     delete d;
132 }
133 
134 template <bool useOldData>
copyAreaOptimizedImpl(const QPoint & dstPt,KisPaintDeviceSP src,KisPaintDeviceSP dst,const QRect & srcRect)135 void copyAreaOptimizedImpl(const QPoint &dstPt,
136                            KisPaintDeviceSP src,
137                            KisPaintDeviceSP dst,
138                            const QRect &srcRect)
139 {
140     const QRect dstRect(dstPt, srcRect.size());
141 
142     const QRect srcExtent = src->extent();
143     const QRect dstExtent = dst->extent();
144 
145     const QRect srcSampleRect = srcExtent & srcRect;
146     const QRect dstSampleRect = dstExtent & dstRect;
147 
148     const bool srcEmpty = srcSampleRect.isEmpty();
149     const bool dstEmpty = dstSampleRect.isEmpty();
150 
151     if (!srcEmpty || !dstEmpty) {
152         if (srcEmpty) {
153             dst->clear(dstRect);
154         } else {
155             QRect srcCopyRect = srcRect;
156             QRect dstCopyRect = dstRect;
157 
158             if (!srcExtent.contains(srcRect)) {
159                 if (src->defaultPixel() == dst->defaultPixel()) {
160                     const QRect dstSampleInSrcCoords = dstSampleRect.translated(srcRect.topLeft() - dstPt);
161 
162                     if (dstSampleInSrcCoords.isEmpty() || srcSampleRect.contains(dstSampleInSrcCoords)) {
163                         srcCopyRect = srcSampleRect;
164                     } else {
165                         srcCopyRect = srcSampleRect | dstSampleInSrcCoords;
166                     }
167                     dstCopyRect = QRect(dstPt + srcCopyRect.topLeft() - srcRect.topLeft(), srcCopyRect.size());
168                 }
169             }
170 
171             KisPainter gc(dst);
172             gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY));
173 
174             if (useOldData) {
175                 gc.bitBltOldData(dstCopyRect.topLeft(), src, srcCopyRect);
176             } else {
177                 gc.bitBlt(dstCopyRect.topLeft(), src, srcCopyRect);
178             }
179         }
180     }
181 }
182 
copyAreaOptimized(const QPoint & dstPt,KisPaintDeviceSP src,KisPaintDeviceSP dst,const QRect & srcRect)183 void KisPainter::copyAreaOptimized(const QPoint &dstPt,
184                                    KisPaintDeviceSP src,
185                                    KisPaintDeviceSP dst,
186                                    const QRect &srcRect)
187 {
188     copyAreaOptimizedImpl<false>(dstPt, src, dst, srcRect);
189 }
190 
copyAreaOptimizedOldData(const QPoint & dstPt,KisPaintDeviceSP src,KisPaintDeviceSP dst,const QRect & srcRect)191 void KisPainter::copyAreaOptimizedOldData(const QPoint &dstPt,
192                                           KisPaintDeviceSP src,
193                                           KisPaintDeviceSP dst,
194                                           const QRect &srcRect)
195 {
196     copyAreaOptimizedImpl<true>(dstPt, src, dst, srcRect);
197 }
198 
copyAreaOptimized(const QPoint & dstPt,KisPaintDeviceSP src,KisPaintDeviceSP dst,const QRect & originalSrcRect,KisSelectionSP selection)199 void KisPainter::copyAreaOptimized(const QPoint &dstPt,
200                                    KisPaintDeviceSP src,
201                                    KisPaintDeviceSP dst,
202                                    const QRect &originalSrcRect,
203                                    KisSelectionSP selection)
204 {
205     if (!selection) {
206         copyAreaOptimized(dstPt, src, dst, originalSrcRect);
207         return;
208     }
209 
210     const QRect selectionRect = selection->selectedRect();
211     const QRect srcRect = originalSrcRect & selectionRect;
212     const QPoint dstOffset = srcRect.topLeft() - originalSrcRect.topLeft();
213     const QRect dstRect = QRect(dstPt + dstOffset, srcRect.size());
214 
215     const bool srcEmpty = (src->extent() & srcRect).isEmpty();
216     const bool dstEmpty = (dst->extent() & dstRect).isEmpty();
217 
218     if (!srcEmpty || !dstEmpty) {
219         //if (srcEmpty) {
220         // doesn't support dstRect
221         // dst->clearSelection(selection);
222         // } else */
223         {
224             KisPainter gc(dst);
225             gc.setSelection(selection);
226             gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY));
227             gc.bitBlt(dstRect.topLeft(), src, srcRect);
228         }
229     }
230 }
231 
convertToAlphaAsAlpha(KisPaintDeviceSP src)232 KisPaintDeviceSP KisPainter::convertToAlphaAsAlpha(KisPaintDeviceSP src)
233 {
234     const KoColorSpace *srcCS = src->colorSpace();
235     const QRect processRect = src->extent();
236     KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
237 
238     if (processRect.isEmpty()) return dst;
239 
240     KisSequentialConstIterator srcIt(src, processRect);
241     KisSequentialIterator dstIt(dst, processRect);
242 
243     while (srcIt.nextPixel() && dstIt.nextPixel()) {
244         const quint8 *srcPtr = srcIt.rawDataConst();
245         quint8 *alpha8Ptr = dstIt.rawData();
246 
247         const quint8 white = srcCS->intensity8(srcPtr);
248         const quint8 alpha = srcCS->opacityU8(srcPtr);
249 
250         *alpha8Ptr = KoColorSpaceMaths<quint8>::multiply(alpha, KoColorSpaceMathsTraits<quint8>::unitValue - white);
251     }
252 
253     return dst;
254 }
255 
convertToAlphaAsGray(KisPaintDeviceSP src)256 KisPaintDeviceSP KisPainter::convertToAlphaAsGray(KisPaintDeviceSP src)
257 {
258     const KoColorSpace *srcCS = src->colorSpace();
259     const QRect processRect = src->extent();
260     KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
261 
262     if (processRect.isEmpty()) return dst;
263 
264     KisSequentialConstIterator srcIt(src, processRect);
265     KisSequentialIterator dstIt(dst, processRect);
266 
267     while (srcIt.nextPixel() && dstIt.nextPixel()) {
268         const quint8 *srcPtr = srcIt.rawDataConst();
269         quint8 *alpha8Ptr = dstIt.rawData();
270 
271         *alpha8Ptr = srcCS->intensity8(srcPtr);
272     }
273 
274     return dst;
275 }
276 
convertToAlphaAsPureAlpha(KisPaintDeviceSP src)277 KisPaintDeviceSP KisPainter::convertToAlphaAsPureAlpha(KisPaintDeviceSP src)
278 {
279     const KoColorSpace *srcCS = src->colorSpace();
280     const QRect processRect = src->extent();
281     KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
282 
283     if (processRect.isEmpty()) return dst;
284 
285     KisSequentialConstIterator srcIt(src, processRect);
286     KisSequentialIterator dstIt(dst, processRect);
287 
288     while (srcIt.nextPixel() && dstIt.nextPixel()) {
289         const quint8 *srcPtr = srcIt.rawDataConst();
290         quint8 *alpha8Ptr = dstIt.rawData();
291 
292         *alpha8Ptr = srcCS->opacityU8(srcPtr);
293     }
294 
295     return dst;
296 }
297 
checkDeviceHasTransparency(KisPaintDeviceSP dev)298 bool KisPainter::checkDeviceHasTransparency(KisPaintDeviceSP dev)
299 {
300     const QRect deviceBounds = dev->exactBounds();
301     const QRect imageBounds = dev->defaultBounds()->bounds();
302 
303     if (deviceBounds.isEmpty() ||
304         (deviceBounds & imageBounds) != imageBounds) {
305 
306         return true;
307     }
308 
309     const KoColorSpace *cs = dev->colorSpace();
310     KisSequentialConstIterator it(dev, deviceBounds);
311 
312     while(it.nextPixel()) {
313         if (cs->opacityU8(it.rawDataConst()) != OPACITY_OPAQUE_U8) {
314             return true;
315         }
316     }
317 
318     return false;
319 }
320 
begin(KisPaintDeviceSP device)321 void KisPainter::begin(KisPaintDeviceSP device)
322 {
323     begin(device, d->selection);
324 }
325 
begin(KisPaintDeviceSP device,KisSelectionSP selection)326 void KisPainter::begin(KisPaintDeviceSP device, KisSelectionSP selection)
327 {
328     if (!device) return;
329     d->selection = selection;
330     Q_ASSERT(device->colorSpace());
331 
332     end();
333 
334     d->device = device;
335     d->colorSpace = device->colorSpace();
336     d->compositeOp = d->colorSpace->compositeOp(COMPOSITE_OVER);
337     d->pixelSize = device->pixelSize();
338 }
339 
end()340 void KisPainter::end()
341 {
342     Q_ASSERT_X(!d->transaction, "KisPainter::end()",
343                "end() was called for the painter having a transaction. "
344                "Please use end/deleteTransaction() instead");
345 }
346 
beginTransaction(const KUndo2MagicString & transactionName,int timedID)347 void KisPainter::beginTransaction(const KUndo2MagicString& transactionName,int timedID)
348 {
349     Q_ASSERT_X(!d->transaction, "KisPainter::beginTransaction()",
350                "You asked for a new transaction while still having "
351                "another one. Please finish the first one with "
352                "end/deleteTransaction() first");
353 
354     d->transaction = new KisTransaction(transactionName, d->device);
355     Q_CHECK_PTR(d->transaction);
356     d->transaction->undoCommand()->setTimedID(timedID);
357 }
358 
revertTransaction()359 void KisPainter::revertTransaction()
360 {
361     Q_ASSERT_X(d->transaction, "KisPainter::revertTransaction()",
362                "No transaction is in progress");
363 
364     d->transaction->revert();
365     delete d->transaction;
366     d->transaction = 0;
367 }
368 
endTransaction(KisUndoAdapter * undoAdapter)369 void KisPainter::endTransaction(KisUndoAdapter *undoAdapter)
370 {
371     Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
372                "No transaction is in progress");
373 
374     d->transaction->commit(undoAdapter);
375     delete d->transaction;
376     d->transaction = 0;
377 }
378 
endTransaction(KisPostExecutionUndoAdapter * undoAdapter)379 void KisPainter::endTransaction(KisPostExecutionUndoAdapter *undoAdapter)
380 {
381     Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
382                "No transaction is in progress");
383 
384     d->transaction->commit(undoAdapter);
385     delete d->transaction;
386     d->transaction = 0;
387 }
388 
endAndTakeTransaction()389 KUndo2Command* KisPainter::endAndTakeTransaction()
390 {
391     Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
392                "No transaction is in progress");
393 
394     KUndo2Command *transactionData = d->transaction->endAndTake();
395     delete d->transaction;
396     d->transaction = 0;
397 
398     return transactionData;
399 }
400 
deleteTransaction()401 void KisPainter::deleteTransaction()
402 {
403     if (!d->transaction) return;
404 
405     delete d->transaction;
406     d->transaction = 0;
407 }
408 
putTransaction(KisTransaction * transaction)409 void KisPainter::putTransaction(KisTransaction* transaction)
410 {
411     Q_ASSERT_X(!d->transaction, "KisPainter::putTransaction()",
412                "You asked for a new transaction while still having "
413                "another one. Please finish the first one with "
414                "end/deleteTransaction() first");
415 
416     d->transaction = transaction;
417 }
418 
takeTransaction()419 KisTransaction* KisPainter::takeTransaction()
420 {
421     Q_ASSERT_X(d->transaction, "KisPainter::takeTransaction()",
422                "No transaction is in progress");
423     KisTransaction *temp = d->transaction;
424     d->transaction = 0;
425     return temp;
426 }
427 
428 
429 
takeDirtyRegion()430 QVector<QRect> KisPainter::takeDirtyRegion()
431 {
432     QVector<QRect> vrect = d->dirtyRects;
433     d->dirtyRects.clear();
434     return vrect;
435 }
436 
437 
addDirtyRect(const QRect & rc)438 void KisPainter::addDirtyRect(const QRect & rc)
439 {
440     QRect r = rc.normalized();
441     if (r.isValid()) {
442         d->dirtyRects.append(rc);
443     }
444 }
445 
addDirtyRects(const QVector<QRect> & rects)446 void KisPainter::addDirtyRects(const QVector<QRect> &rects)
447 {
448     d->dirtyRects.reserve(d->dirtyRects.size() + rects.size());
449 
450     Q_FOREACH (const QRect &rc, rects) {
451         const QRect r = rc.normalized();
452         if (r.isValid()) {
453             d->dirtyRects.append(rc);
454         }
455     }
456 }
457 
tryReduceSourceRect(const KisPaintDevice * srcDev,QRect * srcRect,qint32 * srcX,qint32 * srcY,qint32 * srcWidth,qint32 * srcHeight,qint32 * dstX,qint32 * dstY)458 inline bool KisPainter::Private::tryReduceSourceRect(const KisPaintDevice *srcDev,
459                                                      QRect *srcRect,
460                                                      qint32 *srcX,
461                                                      qint32 *srcY,
462                                                      qint32 *srcWidth,
463                                                      qint32 *srcHeight,
464                                                      qint32 *dstX,
465                                                      qint32 *dstY)
466 {
467     bool needsReadjustParams = false;
468 
469     /**
470      * In case of COMPOSITE_COPY and Wrap Around Mode even the pixels
471      * outside the device extent matter, because they will be either
472      * directly copied (former case) or cloned from another area of
473      * the image.
474      */
475     if (compositeOp->id() != COMPOSITE_COPY &&
476         compositeOp->id() != COMPOSITE_DESTINATION_IN  &&
477         compositeOp->id() != COMPOSITE_DESTINATION_ATOP &&
478         !srcDev->defaultBounds()->wrapAroundMode()) {
479 
480         /**
481          * If srcDev->extent() (the area of the tiles containing
482          * srcDev) is smaller than srcRect, then shrink srcRect to
483          * that size. This is done as a speed optimization, useful for
484          * stack recomposition in KisImage. srcRect won't grow if
485          * srcDev->extent() is larger.
486          */
487         *srcRect &= srcDev->extent();
488 
489         if (srcRect->isEmpty()) return true;
490         needsReadjustParams = true;
491     }
492 
493     if (selection) {
494         /**
495          * We should also crop the blitted area by the selected region,
496          * because we cannot paint outside the selection.
497          */
498         *srcRect &= selection->selectedRect().translated(*srcX - *dstX,
499                                                          *srcY - *dstY);
500 
501         if (srcRect->isEmpty()) return true;
502         needsReadjustParams = true;
503     }
504 
505     if (!paramInfo.channelFlags.isEmpty()) {
506         const QBitArray onlyColor = colorSpace->channelFlags(true, false);
507         KIS_SAFE_ASSERT_RECOVER_NOOP(onlyColor.size() == paramInfo.channelFlags.size());
508 
509         // check if we have alpha channel locked
510         if ((paramInfo.channelFlags & onlyColor) == paramInfo.channelFlags) {
511             *srcRect &= device->extent().translated(*srcX - *dstX,
512                                                     *srcY - *dstY);
513 
514             if (srcRect->isEmpty()) return true;
515             needsReadjustParams = true;
516         }
517     }
518 
519     if (needsReadjustParams) {
520         // Readjust the function paramenters to the new dimensions.
521         *dstX += srcRect->x() - *srcX;    // This will only add, not subtract
522         *dstY += srcRect->y() - *srcY;    // Idem
523         srcRect->getRect(srcX, srcY, srcWidth, srcHeight);
524     }
525 
526     return false;
527 }
528 
bitBltWithFixedSelection(qint32 dstX,qint32 dstY,const KisPaintDeviceSP srcDev,const KisFixedPaintDeviceSP selection,qint32 selX,qint32 selY,qint32 srcX,qint32 srcY,qint32 srcWidth,qint32 srcHeight)529 void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
530                                           const KisPaintDeviceSP srcDev,
531                                           const KisFixedPaintDeviceSP selection,
532                                           qint32 selX, qint32 selY,
533                                           qint32 srcX, qint32 srcY,
534                                           qint32 srcWidth, qint32 srcHeight)
535 {
536     // TODO: get selX and selY working as intended
537 
538     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
539     initializing they perform some dummy passes with those parameters, and it must not crash */
540     if (srcWidth == 0 || srcHeight == 0) return;
541     if (srcDev.isNull()) return;
542     if (d->device.isNull()) return;
543 
544     // Check that selection has an alpha colorspace, crash if false
545     Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8());
546 
547     QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
548 
549     // save selection offset in case tryReduceSourceRect() will change rects
550     const int xSelectionOffset = selX - srcX;
551     const int ySelectionOffset = selY - srcY;
552 
553     /**
554      * An optimization, which crops the source rect by the bounds of
555      * the source device when it is possible
556      */
557     if (d->tryReduceSourceRect(srcDev, &srcRect,
558                                &srcX, &srcY,
559                                &srcWidth, &srcHeight,
560                                &dstX, &dstY)) return;
561 
562     const QRect selRect = QRect(srcX + xSelectionOffset,
563                                 srcY + ySelectionOffset,
564                                 srcWidth, srcHeight);
565 
566     /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
567     so crash if someone attempts to do this. Don't resize YET as it would obfuscate the mistake. */
568     KIS_SAFE_ASSERT_RECOVER_RETURN(selection->bounds().contains(selRect));
569     Q_UNUSED(selRect); // only used by the above Q_ASSERT
570 
571 
572     /* Create an intermediate byte array to hold information before it is written
573     to the current paint device (d->device) */
574     quint8* dstBytes = 0;
575     try {
576         dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
577     } catch (const std::bad_alloc&) {
578         warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "dst bytes";
579         return;
580     }
581 
582     d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
583 
584     // Copy the relevant bytes of raw data from srcDev
585     quint8* srcBytes = 0;
586     try {
587         srcBytes = new quint8[srcWidth * srcHeight * srcDev->pixelSize()];
588     } catch (const std::bad_alloc&) {
589         warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "src bytes";
590         return;
591     }
592 
593     srcDev->readBytes(srcBytes, srcX, srcY, srcWidth, srcHeight);
594 
595     const QRect selBounds = selection->bounds();
596     const quint8 *selRowStart = selection->data() +
597         (selBounds.width() * (selRect.y() - selBounds.top()) + (selRect.x() - selBounds.left())) * selection->pixelSize();
598 
599     /*
600      * This checks whether there is nothing selected.
601      */
602     if (!d->selection) {
603         /* As there's nothing selected, blit to dstBytes (intermediary bit array),
604           ignoring d->selection (the user selection)*/
605         d->paramInfo.dstRowStart   = dstBytes;
606         d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
607         d->paramInfo.srcRowStart   = srcBytes;
608         d->paramInfo.srcRowStride  = srcWidth * srcDev->pixelSize();
609         d->paramInfo.maskRowStart  = selRowStart;
610         d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize();
611         d->paramInfo.rows          = srcHeight;
612         d->paramInfo.cols          = srcWidth;
613         d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
614     }
615     else {
616         /* Read the user selection (d->selection) bytes into an array, ready
617         to merge in the next block*/
618         quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize();
619         quint8* mergedSelectionBytes = 0;
620         try {
621             mergedSelectionBytes = new quint8[ totalBytes ];
622         } catch (const std::bad_alloc&) {
623             warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
624             return;
625         }
626 
627         d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight);
628 
629         KoCompositeOp::ParameterInfo multiplyParamInfo;
630         multiplyParamInfo.opacity = 1.0f;
631         multiplyParamInfo.flow = 1.0f;
632 
633         // Merge selections here by multiplying them - compositeOP(COMPOSITE_MULT)
634         multiplyParamInfo.dstRowStart   = mergedSelectionBytes;
635         multiplyParamInfo.dstRowStride  = srcWidth * selection->pixelSize();
636         multiplyParamInfo.srcRowStart   = selRowStart;
637         multiplyParamInfo.srcRowStride  = selBounds.width() * selection->pixelSize();
638         multiplyParamInfo.maskRowStart  = 0;
639         multiplyParamInfo.maskRowStride = 0;
640         multiplyParamInfo.rows          = srcHeight;
641         multiplyParamInfo.cols          = srcWidth;
642         KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(multiplyParamInfo);
643 
644         // Blit to dstBytes (intermediary bit array)
645         d->paramInfo.dstRowStart   = dstBytes;
646         d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
647         d->paramInfo.srcRowStart   = srcBytes;
648         d->paramInfo.srcRowStride  = srcWidth * srcDev->pixelSize();
649         d->paramInfo.maskRowStart  = mergedSelectionBytes;
650         d->paramInfo.maskRowStride = srcWidth * selection->pixelSize();
651         d->paramInfo.rows          = srcHeight;
652         d->paramInfo.cols          = srcWidth;
653         d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
654         delete[] mergedSelectionBytes;
655     }
656 
657     d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
658 
659     delete[] dstBytes;
660     delete[] srcBytes;
661 
662     addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
663 }
664 
665 
bitBltWithFixedSelection(qint32 dstX,qint32 dstY,const KisPaintDeviceSP srcDev,const KisFixedPaintDeviceSP selection,qint32 srcWidth,qint32 srcHeight)666 void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
667                                           const KisPaintDeviceSP srcDev,
668                                           const KisFixedPaintDeviceSP selection,
669                                           qint32 srcWidth, qint32 srcHeight)
670 {
671     bitBltWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight);
672 }
673 
674 template <bool useOldSrcData>
bitBltImpl(qint32 dstX,qint32 dstY,const KisPaintDeviceSP srcDev,qint32 srcX,qint32 srcY,qint32 srcWidth,qint32 srcHeight)675 void KisPainter::bitBltImpl(qint32 dstX, qint32 dstY,
676                             const KisPaintDeviceSP srcDev,
677                             qint32 srcX, qint32 srcY,
678                             qint32 srcWidth, qint32 srcHeight)
679 {
680     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
681     initializing they perform some dummy passes with those parameters, and it must not crash */
682     if (srcWidth == 0 || srcHeight == 0) return;
683     if (srcDev.isNull()) return;
684     if (d->device.isNull()) return;
685 
686     QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
687 
688     if (d->compositeOp->id() == COMPOSITE_COPY) {
689         if(!d->selection && d->isOpacityUnit &&
690            srcX == dstX && srcY == dstY &&
691            d->device->fastBitBltPossible(srcDev) &&
692            (!srcDev->defaultBounds()->wrapAroundMode() ||
693             srcDev->defaultBounds()->imageBorderRect().contains(srcRect))) {
694 
695             if(useOldSrcData) {
696                 d->device->fastBitBltOldData(srcDev, srcRect);
697             } else {
698                 d->device->fastBitBlt(srcDev, srcRect);
699             }
700 
701             addDirtyRect(srcRect);
702             return;
703         }
704     }
705     else {
706         /**
707          * An optimization, which crops the source rect by the bounds of
708          * the source device when it is possible
709          */
710         if (d->tryReduceSourceRect(srcDev, &srcRect,
711                                    &srcX, &srcY,
712                                    &srcWidth, &srcHeight,
713                                    &dstX, &dstY)) return;
714     }
715 
716     qint32 dstY_ = dstY;
717     qint32 srcY_ = srcY;
718     qint32 rowsRemaining = srcHeight;
719 
720     // Read below
721     KisRandomConstAccessorSP srcIt = srcDev->createRandomConstAccessorNG();
722     KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG();
723 
724     /* Here be a huge block of verbose code that does roughly the same than
725     the other bit blit operations. This one is longer than the rest in an effort to
726     optimize speed and memory use */
727     if (d->selection) {
728         KisPaintDeviceSP selectionProjection(d->selection->projection());
729         KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG();
730 
731         while (rowsRemaining > 0) {
732 
733             qint32 dstX_ = dstX;
734             qint32 srcX_ = srcX;
735             qint32 columnsRemaining = srcWidth;
736             qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_);
737             qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_);
738             qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY_);
739 
740             qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows);
741             rows = qMin(rows, numContiguousSelRows);
742             rows = qMin(rows, rowsRemaining);
743 
744             while (columnsRemaining > 0) {
745 
746                 qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_);
747                 qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_);
748                 qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX_);
749 
750                 qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
751                 columns = qMin(columns, numContiguousSelColumns);
752                 columns = qMin(columns, columnsRemaining);
753 
754                 qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_);
755                 srcIt->moveTo(srcX_, srcY_);
756 
757                 qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_);
758                 dstIt->moveTo(dstX_, dstY_);
759 
760                 qint32 maskRowStride = maskIt->rowStride(dstX_, dstY_);
761                 maskIt->moveTo(dstX_, dstY_);
762 
763                 d->paramInfo.dstRowStart   = dstIt->rawData();
764                 d->paramInfo.dstRowStride  = dstRowStride;
765                 // if we don't use the oldRawData, we need to access the rawData of the source device.
766                 d->paramInfo.srcRowStart   = useOldSrcData ? srcIt->oldRawData() : static_cast<KisRandomAccessor2*>(srcIt.data())->rawData();
767                 d->paramInfo.srcRowStride  = srcRowStride;
768                 d->paramInfo.maskRowStart  = static_cast<KisRandomAccessor2*>(maskIt.data())->rawData();
769                 d->paramInfo.maskRowStride = maskRowStride;
770                 d->paramInfo.rows          = rows;
771                 d->paramInfo.cols          = columns;
772                 d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
773 
774                 srcX_ += columns;
775                 dstX_ += columns;
776                 columnsRemaining -= columns;
777             }
778 
779             srcY_ += rows;
780             dstY_ += rows;
781             rowsRemaining -= rows;
782         }
783     }
784     else {
785 
786         while (rowsRemaining > 0) {
787 
788             qint32 dstX_ = dstX;
789             qint32 srcX_ = srcX;
790             qint32 columnsRemaining = srcWidth;
791             qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_);
792             qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_);
793 
794             qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows);
795             rows = qMin(rows, rowsRemaining);
796 
797             while (columnsRemaining > 0) {
798 
799                 qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_);
800                 qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_);
801 
802                 qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
803                 columns = qMin(columns, columnsRemaining);
804 
805                 qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_);
806                 srcIt->moveTo(srcX_, srcY_);
807 
808                 qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_);
809                 dstIt->moveTo(dstX_, dstY_);
810 
811                 d->paramInfo.dstRowStart   = dstIt->rawData();
812                 d->paramInfo.dstRowStride  = dstRowStride;
813                 // if we don't use the oldRawData, we need to access the rawData of the source device.
814                 d->paramInfo.srcRowStart   = useOldSrcData ? srcIt->oldRawData() : static_cast<KisRandomAccessor2*>(srcIt.data())->rawData();
815                 d->paramInfo.srcRowStride  = srcRowStride;
816                 d->paramInfo.maskRowStart  = 0;
817                 d->paramInfo.maskRowStride = 0;
818                 d->paramInfo.rows          = rows;
819                 d->paramInfo.cols          = columns;
820                 d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
821 
822                 srcX_ += columns;
823                 dstX_ += columns;
824                 columnsRemaining -= columns;
825             }
826 
827             srcY_ += rows;
828             dstY_ += rows;
829             rowsRemaining -= rows;
830         }
831     }
832 
833     addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
834 
835 }
836 
bitBlt(qint32 dstX,qint32 dstY,const KisPaintDeviceSP srcDev,qint32 srcX,qint32 srcY,qint32 srcWidth,qint32 srcHeight)837 void KisPainter::bitBlt(qint32 dstX, qint32 dstY,
838                         const KisPaintDeviceSP srcDev,
839                         qint32 srcX, qint32 srcY,
840                         qint32 srcWidth, qint32 srcHeight)
841 {
842     bitBltImpl<false>(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight);
843 }
844 
845 
bitBlt(const QPoint & pos,const KisPaintDeviceSP srcDev,const QRect & srcRect)846 void KisPainter::bitBlt(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect)
847 {
848     bitBlt(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
849 }
850 
bitBltOldData(qint32 dstX,qint32 dstY,const KisPaintDeviceSP srcDev,qint32 srcX,qint32 srcY,qint32 srcWidth,qint32 srcHeight)851 void KisPainter::bitBltOldData(qint32 dstX, qint32 dstY,
852                                const KisPaintDeviceSP srcDev,
853                                qint32 srcX, qint32 srcY,
854                                qint32 srcWidth, qint32 srcHeight)
855 {
856     bitBltImpl<true>(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight);
857 }
858 
859 
bitBltOldData(const QPoint & pos,const KisPaintDeviceSP srcDev,const QRect & srcRect)860 void KisPainter::bitBltOldData(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect)
861 {
862     bitBltOldData(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
863 }
864 
865 
fill(qint32 x,qint32 y,qint32 width,qint32 height,const KoColor & color)866 void KisPainter::fill(qint32 x, qint32 y, qint32 width, qint32 height, const KoColor& color)
867 {
868     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
869      * initializing they perform some dummy passes with those parameters, and it must not crash */
870     if(width == 0 || height == 0 || d->device.isNull())
871         return;
872 
873     KoColor srcColor(color, d->device->compositionSourceColorSpace());
874     qint32  dstY          = y;
875     qint32  rowsRemaining = height;
876 
877     KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG();
878 
879     if(d->selection) {
880         KisPaintDeviceSP selectionProjection(d->selection->projection());
881         KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG();
882 
883         while(rowsRemaining > 0) {
884 
885             qint32 dstX                 = x;
886             qint32 columnsRemaining     = width;
887             qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY);
888             qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY);
889             qint32 rows = qMin(numContiguousDstRows, numContiguousSelRows);
890             rows = qMin(rows, rowsRemaining);
891 
892             while (columnsRemaining > 0) {
893 
894                 qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX);
895                 qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX);
896 
897                 qint32 columns = qMin(numContiguousDstColumns, numContiguousSelColumns);
898                 columns = qMin(columns, columnsRemaining);
899 
900                 qint32 dstRowStride = dstIt->rowStride(dstX, dstY);
901                 dstIt->moveTo(dstX, dstY);
902                 qint32 maskRowStride = maskIt->rowStride(dstX, dstY);
903 
904                 maskIt->moveTo(dstX, dstY);
905 
906                 d->paramInfo.dstRowStart   = dstIt->rawData();
907                 d->paramInfo.dstRowStride  = dstRowStride;
908                 d->paramInfo.srcRowStart   = srcColor.data();
909                 d->paramInfo.srcRowStride  = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel
910                 d->paramInfo.maskRowStart  = maskIt->oldRawData();
911                 d->paramInfo.maskRowStride = maskRowStride;
912                 d->paramInfo.rows          = rows;
913                 d->paramInfo.cols          = columns;
914                 d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
915 
916                 dstX             += columns;
917                 columnsRemaining -= columns;
918             }
919 
920             dstY          += rows;
921             rowsRemaining -= rows;
922         }
923     }
924     else {
925 
926         while(rowsRemaining > 0) {
927 
928             qint32 dstX                 = x;
929             qint32 columnsRemaining     = width;
930             qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY);
931             qint32 rows                 = qMin(numContiguousDstRows, rowsRemaining);
932 
933             while(columnsRemaining > 0) {
934 
935                 qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX);
936                 qint32 columns                 = qMin(numContiguousDstColumns, columnsRemaining);
937                 qint32 dstRowStride            = dstIt->rowStride(dstX, dstY);
938                 dstIt->moveTo(dstX, dstY);
939 
940                 d->paramInfo.dstRowStart   = dstIt->rawData();
941                 d->paramInfo.dstRowStride  = dstRowStride;
942                 d->paramInfo.srcRowStart   = srcColor.data();
943                 d->paramInfo.srcRowStride  = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel
944                 d->paramInfo.maskRowStart  = 0;
945                 d->paramInfo.maskRowStride = 0;
946                 d->paramInfo.rows          = rows;
947                 d->paramInfo.cols          = columns;
948                 d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
949 
950                 dstX             += columns;
951                 columnsRemaining -= columns;
952             }
953 
954             dstY          += rows;
955             rowsRemaining -= rows;
956         }
957     }
958 
959     addDirtyRect(QRect(x, y, width, height));
960 }
961 
962 
bltFixed(qint32 dstX,qint32 dstY,const KisFixedPaintDeviceSP srcDev,qint32 srcX,qint32 srcY,qint32 srcWidth,qint32 srcHeight)963 void KisPainter::bltFixed(qint32 dstX, qint32 dstY,
964                           const KisFixedPaintDeviceSP srcDev,
965                           qint32 srcX, qint32 srcY,
966                           qint32 srcWidth, qint32 srcHeight)
967 {
968     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
969     initializing they perform some dummy passes with those parameters, and it must not crash */
970     if (srcWidth == 0 || srcHeight == 0) return;
971     if (srcDev.isNull()) return;
972     if (d->device.isNull()) return;
973 
974     QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
975     QRect srcBounds = srcDev->bounds();
976 
977     /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
978     so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */
979     KIS_SAFE_ASSERT_RECOVER_RETURN(srcBounds.contains(srcRect));
980     Q_UNUSED(srcRect); // only used in above assertion
981 
982     /* Create an intermediate byte array to hold information before it is written
983     to the current paint device (aka: d->device) */
984     quint8* dstBytes = 0;
985     try {
986          dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
987     } catch (const std::bad_alloc&) {
988         warnKrita << "KisPainter::bltFixed std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
989         return;
990     }
991     d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
992 
993     const quint8 *srcRowStart = srcDev->data() +
994         (srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize();
995 
996     d->paramInfo.dstRowStart   = dstBytes;
997     d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
998     d->paramInfo.srcRowStart   = srcRowStart;
999     d->paramInfo.srcRowStride  = srcBounds.width() * srcDev->pixelSize();
1000     d->paramInfo.maskRowStart  = 0;
1001     d->paramInfo.maskRowStride = 0;
1002     d->paramInfo.rows          = srcHeight;
1003     d->paramInfo.cols          = srcWidth;
1004 
1005     if (d->selection) {
1006         /* d->selection is a KisPaintDevice, so first a readBytes is performed to
1007         get the area of interest... */
1008         KisPaintDeviceSP selectionProjection(d->selection->projection());
1009         quint8* selBytes = 0;
1010         try {
1011             selBytes = new quint8[srcWidth * srcHeight * selectionProjection->pixelSize()];
1012         }
1013         catch (const std::bad_alloc&) {
1014             delete[] dstBytes;
1015             return;
1016         }
1017 
1018         selectionProjection->readBytes(selBytes, dstX, dstY, srcWidth, srcHeight);
1019         d->paramInfo.maskRowStart = selBytes;
1020         d->paramInfo.maskRowStride = srcWidth * selectionProjection->pixelSize();
1021     }
1022 
1023     // ...and then blit.
1024     d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
1025     d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
1026 
1027     delete[] d->paramInfo.maskRowStart;
1028     delete[] dstBytes;
1029 
1030     addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
1031 }
1032 
bltFixed(const QPoint & pos,const KisFixedPaintDeviceSP srcDev,const QRect & srcRect)1033 void KisPainter::bltFixed(const QPoint & pos, const KisFixedPaintDeviceSP srcDev, const QRect & srcRect)
1034 {
1035     bltFixed(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
1036 }
1037 
bltFixedWithFixedSelection(qint32 dstX,qint32 dstY,const KisFixedPaintDeviceSP srcDev,const KisFixedPaintDeviceSP selection,qint32 selX,qint32 selY,qint32 srcX,qint32 srcY,quint32 srcWidth,quint32 srcHeight)1038 void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
1039                                             const KisFixedPaintDeviceSP srcDev,
1040                                             const KisFixedPaintDeviceSP selection,
1041                                             qint32 selX, qint32 selY,
1042                                             qint32 srcX, qint32 srcY,
1043                                             quint32 srcWidth, quint32 srcHeight)
1044 {
1045     // TODO: get selX and selY working as intended
1046 
1047     /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
1048     initializing they perform some dummy passes with those parameters, and it must not crash */
1049     if (srcWidth == 0 || srcHeight == 0) return;
1050     if (srcDev.isNull()) return;
1051     if (d->device.isNull()) return;
1052 
1053      // Check that selection has an alpha colorspace, crash if false
1054     Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8());
1055 
1056     QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
1057     QRect selRect = QRect(selX, selY, srcWidth, srcHeight);
1058 
1059     QRect srcBounds = srcDev->bounds();
1060     QRect selBounds = selection->bounds();
1061 
1062     /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
1063     so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */
1064     Q_ASSERT(srcBounds.contains(srcRect));
1065     Q_UNUSED(srcRect); // only used in above assertion
1066     Q_ASSERT(selBounds.contains(selRect));
1067     Q_UNUSED(selRect); // only used in above assertion
1068 
1069     /* Create an intermediate byte array to hold information before it is written
1070     to the current paint device (aka: d->device) */
1071     quint8* dstBytes = 0;
1072     try {
1073         dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
1074     } catch (const std::bad_alloc&) {
1075         warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
1076         return;
1077     }
1078     d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
1079 
1080     const quint8 *srcRowStart = srcDev->data() +
1081         (srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize();
1082     const quint8 *selRowStart = selection->data() +
1083         (selBounds.width() * (selY - selBounds.top()) + (selX - selBounds.left())) * selection->pixelSize();
1084 
1085     if (!d->selection) {
1086         /* As there's nothing selected, blit to dstBytes (intermediary bit array),
1087           ignoring d->selection (the user selection)*/
1088         d->paramInfo.dstRowStart   = dstBytes;
1089         d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
1090         d->paramInfo.srcRowStart   = srcRowStart;
1091         d->paramInfo.srcRowStride  = srcBounds.width() * srcDev->pixelSize();
1092         d->paramInfo.maskRowStart  = selRowStart;
1093         d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize();
1094         d->paramInfo.rows          = srcHeight;
1095         d->paramInfo.cols          = srcWidth;
1096         d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
1097     }
1098     else {
1099         /* Read the user selection (d->selection) bytes into an array, ready
1100         to merge in the next block*/
1101         quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize();
1102         quint8 * mergedSelectionBytes = 0;
1103         try {
1104             mergedSelectionBytes = new quint8[ totalBytes ];
1105         } catch (const std::bad_alloc&) {
1106             warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << totalBytes << "total bytes";
1107             delete[] dstBytes;
1108             return;
1109         }
1110         d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight);
1111 
1112         KoCompositeOp::ParameterInfo multiplyParamInfo;
1113         multiplyParamInfo.opacity = 1.0f;
1114         multiplyParamInfo.flow = 1.0f;
1115 
1116         // Merge selections here by multiplying them - compositeOp(COMPOSITE_MULT)
1117         multiplyParamInfo.dstRowStart   = mergedSelectionBytes;
1118         multiplyParamInfo.dstRowStride  = srcWidth * selection->pixelSize();
1119         multiplyParamInfo.srcRowStart   = selRowStart;
1120         multiplyParamInfo.srcRowStride  = selBounds.width() * selection->pixelSize();
1121         multiplyParamInfo.maskRowStart  = 0;
1122         multiplyParamInfo.maskRowStride = 0;
1123         multiplyParamInfo.rows          = srcHeight;
1124         multiplyParamInfo.cols          = srcWidth;
1125         KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(multiplyParamInfo);
1126 
1127         // Blit to dstBytes (intermediary bit array)
1128         d->paramInfo.dstRowStart   = dstBytes;
1129         d->paramInfo.dstRowStride  = srcWidth * d->device->pixelSize();
1130         d->paramInfo.srcRowStart   = srcRowStart;
1131         d->paramInfo.srcRowStride  = srcBounds.width() * srcDev->pixelSize();
1132         d->paramInfo.maskRowStart  = mergedSelectionBytes;
1133         d->paramInfo.maskRowStride = srcWidth * selection->pixelSize();
1134         d->paramInfo.rows          = srcHeight;
1135         d->paramInfo.cols          = srcWidth;
1136         d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
1137 
1138         delete[] mergedSelectionBytes;
1139     }
1140 
1141     d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
1142 
1143     delete[] dstBytes;
1144 
1145     addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
1146 }
1147 
bltFixedWithFixedSelection(qint32 dstX,qint32 dstY,const KisFixedPaintDeviceSP srcDev,const KisFixedPaintDeviceSP selection,quint32 srcWidth,quint32 srcHeight)1148 void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
1149                                             const KisFixedPaintDeviceSP srcDev,
1150                                             const KisFixedPaintDeviceSP selection,
1151                                             quint32 srcWidth, quint32 srcHeight)
1152 {
1153     bltFixedWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight);
1154 }
1155 
1156 
1157 
1158 
1159 
paintLine(const KisPaintInformation & pi1,const KisPaintInformation & pi2,KisDistanceInformation * currentDistance)1160 void KisPainter::paintLine(const KisPaintInformation &pi1,
1161                            const KisPaintInformation &pi2,
1162                            KisDistanceInformation *currentDistance)
1163 {
1164     if (d->device && d->paintOp && d->paintOp->canPaint()) {
1165         d->paintOp->paintLine(pi1, pi2, currentDistance);
1166     }
1167 }
1168 
1169 
paintPolyline(const vQPointF & points,int index,int numPoints)1170 void KisPainter::paintPolyline(const vQPointF &points,
1171                                int index, int numPoints)
1172 {
1173     if (d->fillStyle != FillStyleNone) {
1174         fillPolygon(points, d->fillStyle);
1175     }
1176 
1177     if (d->strokeStyle == StrokeStyleNone) return;
1178 
1179     if (index >= points.count())
1180         return;
1181 
1182     if (numPoints < 0)
1183         numPoints = points.count();
1184 
1185     if (index + numPoints > points.count())
1186         numPoints = points.count() - index;
1187 
1188     if (numPoints > 1) {
1189         KisDistanceInformation saveDist(points[0],
1190                 KisAlgebra2D::directionBetweenPoints(points[0], points[1], 0.0));
1191         for (int i = index; i < index + numPoints - 1; i++) {
1192             paintLine(points [i], points [i + 1], &saveDist);
1193         }
1194     }
1195 
1196 }
1197 
getBezierCurvePoints(const KisVector2D & pos1,const KisVector2D & control1,const KisVector2D & control2,const KisVector2D & pos2,vQPointF & points)1198 static void getBezierCurvePoints(const KisVector2D &pos1,
1199                                  const KisVector2D &control1,
1200                                  const KisVector2D &control2,
1201                                  const KisVector2D &pos2,
1202                                  vQPointF& points)
1203 {
1204     LineEquation line = LineEquation::Through(pos1, pos2);
1205     qreal d1 = line.absDistance(control1);
1206     qreal d2 = line.absDistance(control2);
1207 
1208     if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
1209         points.push_back(toQPointF(pos1));
1210     } else {
1211         // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
1212 
1213         KisVector2D l2 = (pos1 + control1) / 2;
1214         KisVector2D h = (control1 + control2) / 2;
1215         KisVector2D l3 = (l2 + h) / 2;
1216         KisVector2D r3 = (control2 + pos2) / 2;
1217         KisVector2D r2 = (h + r3) / 2;
1218         KisVector2D l4 = (l3 + r2) / 2;
1219 
1220         getBezierCurvePoints(pos1, l2, l3, l4, points);
1221         getBezierCurvePoints(l4, r2, r3, pos2, points);
1222     }
1223 }
1224 
getBezierCurvePoints(const QPointF & pos1,const QPointF & control1,const QPointF & control2,const QPointF & pos2,vQPointF & points) const1225 void KisPainter::getBezierCurvePoints(const QPointF &pos1,
1226                                       const QPointF &control1,
1227                                       const QPointF &control2,
1228                                       const QPointF &pos2,
1229                                       vQPointF& points) const
1230 {
1231     ::getBezierCurvePoints(toKisVector2D(pos1), toKisVector2D(control1), toKisVector2D(control2), toKisVector2D(pos2), points);
1232 }
1233 
paintBezierCurve(const KisPaintInformation & pi1,const QPointF & control1,const QPointF & control2,const KisPaintInformation & pi2,KisDistanceInformation * currentDistance)1234 void KisPainter::paintBezierCurve(const KisPaintInformation &pi1,
1235                                   const QPointF &control1,
1236                                   const QPointF &control2,
1237                                   const KisPaintInformation &pi2,
1238                                   KisDistanceInformation *currentDistance)
1239 {
1240     if (d->paintOp && d->paintOp->canPaint()) {
1241         d->paintOp->paintBezierCurve(pi1, control1, control2, pi2, currentDistance);
1242     }
1243 }
1244 
paintRect(const QRectF & rect)1245 void KisPainter::paintRect(const QRectF &rect)
1246 {
1247     QRectF normalizedRect = rect.normalized();
1248 
1249     vQPointF points;
1250 
1251     points.push_back(normalizedRect.topLeft());
1252     points.push_back(normalizedRect.bottomLeft());
1253     points.push_back(normalizedRect.bottomRight());
1254     points.push_back(normalizedRect.topRight());
1255 
1256     paintPolygon(points);
1257 }
1258 
paintRect(const qreal x,const qreal y,const qreal w,const qreal h)1259 void KisPainter::paintRect(const qreal x,
1260                            const qreal y,
1261                            const qreal w,
1262                            const qreal h)
1263 {
1264     paintRect(QRectF(x, y, w, h));
1265 }
1266 
paintEllipse(const QRectF & rect)1267 void KisPainter::paintEllipse(const QRectF &rect)
1268 {
1269     QRectF r = rect.normalized(); // normalize before checking as negative width and height are empty too
1270     if (r.isEmpty()) return;
1271 
1272     // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation.
1273     // kappa = (4/3*(sqrt(2)-1))
1274     const qreal kappa = 0.5522847498;
1275     const qreal lx = (r.width() / 2) * kappa;
1276     const qreal ly = (r.height() / 2) * kappa;
1277 
1278     QPointF center = r.center();
1279 
1280     QPointF p0(r.left(), center.y());
1281     QPointF p1(r.left(), center.y() - ly);
1282     QPointF p2(center.x() - lx, r.top());
1283     QPointF p3(center.x(), r.top());
1284 
1285     vQPointF points;
1286 
1287     getBezierCurvePoints(p0, p1, p2, p3, points);
1288 
1289     QPointF p4(center.x() + lx, r.top());
1290     QPointF p5(r.right(), center.y() - ly);
1291     QPointF p6(r.right(), center.y());
1292 
1293     getBezierCurvePoints(p3, p4, p5, p6, points);
1294 
1295     QPointF p7(r.right(), center.y() + ly);
1296     QPointF p8(center.x() + lx, r.bottom());
1297     QPointF p9(center.x(), r.bottom());
1298 
1299     getBezierCurvePoints(p6, p7, p8, p9, points);
1300 
1301     QPointF p10(center.x() - lx, r.bottom());
1302     QPointF p11(r.left(), center.y() + ly);
1303 
1304     getBezierCurvePoints(p9, p10, p11, p0, points);
1305 
1306     paintPolygon(points);
1307 }
1308 
paintEllipse(const qreal x,const qreal y,const qreal w,const qreal h)1309 void KisPainter::paintEllipse(const qreal x,
1310                               const qreal y,
1311                               const qreal w,
1312                               const qreal h)
1313 {
1314     paintEllipse(QRectF(x, y, w, h));
1315 }
1316 
paintAt(const KisPaintInformation & pi,KisDistanceInformation * savedDist)1317 void KisPainter::paintAt(const KisPaintInformation& pi,
1318                          KisDistanceInformation *savedDist)
1319 {
1320     if (d->paintOp && d->paintOp->canPaint()) {
1321         d->paintOp->paintAt(pi, savedDist);
1322     }
1323 }
1324 
fillPolygon(const vQPointF & points,FillStyle fillStyle)1325 void KisPainter::fillPolygon(const vQPointF& points, FillStyle fillStyle)
1326 {
1327     if (points.count() < 3) {
1328         return;
1329     }
1330 
1331     if (fillStyle == FillStyleNone) {
1332         return;
1333     }
1334 
1335     QPainterPath polygonPath;
1336 
1337     polygonPath.moveTo(points.at(0));
1338 
1339     for (int pointIndex = 1; pointIndex < points.count(); pointIndex++) {
1340         polygonPath.lineTo(points.at(pointIndex));
1341     }
1342 
1343     polygonPath.closeSubpath();
1344 
1345     d->fillStyle = fillStyle;
1346     fillPainterPath(polygonPath);
1347 }
1348 
paintPolygon(const vQPointF & points)1349 void KisPainter::paintPolygon(const vQPointF& points)
1350 {
1351     if (d->fillStyle != FillStyleNone) {
1352         fillPolygon(points, d->fillStyle);
1353     }
1354 
1355     if (d->strokeStyle != StrokeStyleNone) {
1356         if (points.count() > 1) {
1357             KisDistanceInformation distance(points[0],
1358                                             KisAlgebra2D::directionBetweenPoints(points[0], points[1], 0.0));
1359 
1360             for (int i = 0; i < points.count() - 1; i++) {
1361                 paintLine(KisPaintInformation(points[i]), KisPaintInformation(points[i + 1]), &distance);
1362             }
1363             paintLine(points[points.count() - 1], points[0], &distance);
1364         }
1365     }
1366 }
1367 
paintPainterPath(const QPainterPath & path)1368 void KisPainter::paintPainterPath(const QPainterPath& path)
1369 {
1370     if (d->fillStyle != FillStyleNone) {
1371         fillPainterPath(path);
1372     }
1373 
1374     if (d->strokeStyle == StrokeStyleNone) return;
1375 
1376     QPointF lastPoint, nextPoint;
1377     int elementCount = path.elementCount();
1378     KisDistanceInformation saveDist;
1379     for (int i = 0; i < elementCount; i++) {
1380         QPainterPath::Element element = path.elementAt(i);
1381         switch (element.type) {
1382         case QPainterPath::MoveToElement:
1383             lastPoint =  QPointF(element.x, element.y);
1384             break;
1385         case QPainterPath::LineToElement:
1386             nextPoint =  QPointF(element.x, element.y);
1387             paintLine(KisPaintInformation(lastPoint), KisPaintInformation(nextPoint), &saveDist);
1388             lastPoint = nextPoint;
1389             break;
1390         case QPainterPath::CurveToElement:
1391             nextPoint =  QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y);
1392             paintBezierCurve(KisPaintInformation(lastPoint),
1393                              QPointF(path.elementAt(i).x, path.elementAt(i).y),
1394                              QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
1395                              KisPaintInformation(nextPoint), &saveDist);
1396             lastPoint = nextPoint;
1397             break;
1398         default:
1399             continue;
1400         }
1401     }
1402 }
1403 
fillPainterPath(const QPainterPath & path)1404 void KisPainter::fillPainterPath(const QPainterPath& path)
1405 {
1406     fillPainterPath(path, QRect());
1407 }
1408 
fillPainterPath(const QPainterPath & path,const QRect & requestedRect)1409 void KisPainter::fillPainterPath(const QPainterPath& path, const QRect &requestedRect)
1410 {
1411     if (d->mirrorHorizontally || d->mirrorVertically) {
1412         KisLodTransform lod(d->device);
1413         QPointF effectiveAxesCenter = lod.map(d->axesCenter);
1414 
1415         QTransform C1 = QTransform::fromTranslate(-effectiveAxesCenter.x(), -effectiveAxesCenter.y());
1416         QTransform C2 = QTransform::fromTranslate(effectiveAxesCenter.x(), effectiveAxesCenter.y());
1417 
1418         QTransform t;
1419         QPainterPath newPath;
1420         QRect newRect;
1421 
1422         if (d->mirrorHorizontally) {
1423             t = C1 * QTransform::fromScale(-1,1) * C2;
1424             newPath = t.map(path);
1425             newRect = t.mapRect(requestedRect);
1426             d->fillPainterPathImpl(newPath, newRect);
1427         }
1428 
1429         if (d->mirrorVertically) {
1430             t = C1 * QTransform::fromScale(1,-1) * C2;
1431             newPath = t.map(path);
1432             newRect = t.mapRect(requestedRect);
1433             d->fillPainterPathImpl(newPath, newRect);
1434         }
1435 
1436         if (d->mirrorHorizontally && d->mirrorVertically) {
1437             t = C1 * QTransform::fromScale(-1,-1) * C2;
1438             newPath = t.map(path);
1439             newRect = t.mapRect(requestedRect);
1440             d->fillPainterPathImpl(newPath, newRect);
1441         }
1442     }
1443 
1444     d->fillPainterPathImpl(path, requestedRect);
1445 }
1446 
fillPainterPathImpl(const QPainterPath & path,const QRect & requestedRect)1447 void KisPainter::Private::fillPainterPathImpl(const QPainterPath& path, const QRect &requestedRect)
1448 {
1449     if (fillStyle == FillStyleNone) {
1450         return;
1451     }
1452 
1453     // Fill the polygon bounding rectangle with the required contents then we'll
1454     // create a mask for the actual polygon coverage.
1455 
1456     if (!fillPainter) {
1457         polygon = device->createCompositionSourceDevice();
1458         fillPainter = new KisFillPainter(polygon);
1459     } else {
1460         polygon->clear();
1461     }
1462 
1463     Q_CHECK_PTR(polygon);
1464 
1465     QRectF boundingRect = path.boundingRect();
1466     QRect fillRect = boundingRect.toAlignedRect();
1467 
1468     // Expand the rectangle to allow for anti-aliasing.
1469     fillRect.adjust(-1, -1, 1, 1);
1470 
1471     if (requestedRect.isValid()) {
1472         fillRect &= requestedRect;
1473     }
1474 
1475     switch (fillStyle) {
1476     default:
1477         Q_FALLTHROUGH();
1478     case FillStyleForegroundColor:
1479         fillPainter->fillRect(fillRect, q->paintColor(), OPACITY_OPAQUE_U8);
1480         break;
1481     case FillStyleBackgroundColor:
1482         fillPainter->fillRect(fillRect, q->backgroundColor(), OPACITY_OPAQUE_U8);
1483         break;
1484     case FillStylePattern:
1485         if (pattern) { // if the user hasn't got any patterns installed, we shouldn't crash...
1486             fillPainter->fillRectNoCompose(fillRect, pattern, patternTransform);
1487         }
1488         break;
1489     case FillStyleGenerator:
1490         if (generator) { // if the user hasn't got any generators, we shouldn't crash...
1491             fillPainter->fillRect(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), q->generator());
1492         }
1493         break;
1494     }
1495 
1496     if (polygonMaskImage.isNull() || (maskPainter == 0)) {
1497         polygonMaskImage = QImage(maskImageWidth, maskImageHeight, QImage::Format_ARGB32_Premultiplied);
1498         maskPainter = new QPainter(&polygonMaskImage);
1499         maskPainter->setRenderHint(QPainter::Antialiasing, q->antiAliasPolygonFill());
1500     }
1501 
1502     // Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
1503     const QColor black(Qt::black);
1504     const QBrush brush(Qt::white);
1505     for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += maskImageWidth) {
1506         for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += maskImageHeight) {
1507 
1508             polygonMaskImage.fill(black.rgb());
1509             maskPainter->translate(-x, -y);
1510             maskPainter->fillPath(path, brush);
1511             maskPainter->translate(x, y);
1512 
1513             qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, maskImageWidth);
1514             qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, maskImageHeight);
1515 
1516             KisHLineIteratorSP lineIt = polygon->createHLineIteratorNG(x, y, rectWidth);
1517 
1518             quint8 tmp;
1519             for (int row = y; row < y + rectHeight; row++) {
1520                 QRgb* line = reinterpret_cast<QRgb*>(polygonMaskImage.scanLine(row - y));
1521                 do {
1522                     tmp = qRed(line[lineIt->x() - x]);
1523                     polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1);
1524                 } while (lineIt->nextPixel());
1525                 lineIt->nextRow();
1526             }
1527 
1528         }
1529     }
1530 
1531     QRect bltRect = !requestedRect.isEmpty() ? requestedRect : fillRect;
1532     q->bitBlt(bltRect.x(), bltRect.y(), polygon, bltRect.x(), bltRect.y(), bltRect.width(), bltRect.height());
1533 }
1534 
drawPainterPath(const QPainterPath & path,const QPen & pen)1535 void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& pen)
1536 {
1537     drawPainterPath(path, pen, QRect());
1538 }
1539 
drawPainterPath(const QPainterPath & path,const QPen & _pen,const QRect & requestedRect)1540 void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& _pen, const QRect &requestedRect)
1541 {
1542     QPen pen(_pen);
1543     pen.setColor(Qt::white);
1544 
1545     if (!d->fillPainter) {
1546         d->polygon = d->device->createCompositionSourceDevice();
1547         d->fillPainter = new KisFillPainter(d->polygon);
1548     } else {
1549         d->polygon->clear();
1550     }
1551 
1552     Q_CHECK_PTR(d->polygon);
1553 
1554     QRectF boundingRect = path.boundingRect();
1555     QRect fillRect = boundingRect.toAlignedRect();
1556 
1557     // take width of the pen into account
1558     int penWidth = qRound(pen.widthF());
1559     fillRect.adjust(-penWidth, -penWidth, penWidth, penWidth);
1560 
1561     // Expand the rectangle to allow for anti-aliasing.
1562     fillRect.adjust(-1, -1, 1, 1);
1563 
1564     if (!requestedRect.isNull()) {
1565         fillRect &= requestedRect;
1566     }
1567 
1568     d->fillPainter->fillRect(fillRect, paintColor(), OPACITY_OPAQUE_U8);
1569 
1570     if (d->polygonMaskImage.isNull() || (d->maskPainter == 0)) {
1571         d->polygonMaskImage = QImage(d->maskImageWidth, d->maskImageHeight, QImage::Format_ARGB32_Premultiplied);
1572         d->maskPainter = new QPainter(&d->polygonMaskImage);
1573         d->maskPainter->setRenderHint(QPainter::Antialiasing, antiAliasPolygonFill());
1574     }
1575 
1576     // Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
1577     const QColor black(Qt::black);
1578     QPen oldPen = d->maskPainter->pen();
1579     d->maskPainter->setPen(pen);
1580 
1581     for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += d->maskImageWidth) {
1582         for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += d->maskImageHeight) {
1583 
1584             d->polygonMaskImage.fill(black.rgb());
1585             d->maskPainter->translate(-x, -y);
1586             d->maskPainter->drawPath(path);
1587             d->maskPainter->translate(x, y);
1588 
1589             qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, d->maskImageWidth);
1590             qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, d->maskImageHeight);
1591 
1592             KisHLineIteratorSP lineIt = d->polygon->createHLineIteratorNG(x, y, rectWidth);
1593 
1594             quint8 tmp;
1595             for (int row = y; row < y + rectHeight; row++) {
1596                 QRgb* line = reinterpret_cast<QRgb*>(d->polygonMaskImage.scanLine(row - y));
1597                 do {
1598                     tmp = qRed(line[lineIt->x() - x]);
1599                     d->polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1);
1600                 } while (lineIt->nextPixel());
1601                 lineIt->nextRow();
1602             }
1603 
1604         }
1605     }
1606 
1607     d->maskPainter->setPen(oldPen);
1608     QRect r = d->polygon->extent();
1609 
1610     bitBlt(r.x(), r.y(), d->polygon, r.x(), r.y(), r.width(), r.height());
1611 }
1612 
compositeOnePixel(quint8 * dst,const KoColor & color)1613 inline void KisPainter::compositeOnePixel(quint8 *dst, const KoColor &color)
1614 {
1615     d->paramInfo.dstRowStart = dst;
1616     d->paramInfo.dstRowStride = 0;
1617     d->paramInfo.srcRowStart = color.data();
1618     d->paramInfo.srcRowStride = 0;
1619     d->paramInfo.maskRowStart = 0;
1620     d->paramInfo.maskRowStride = 0;
1621     d->paramInfo.rows = 1;
1622     d->paramInfo.cols = 1;
1623 
1624     d->colorSpace->bitBlt(color.colorSpace(), d->paramInfo, d->compositeOp,
1625                           d->renderingIntent,
1626                           d->conversionFlags);
1627 }
1628 
1629 /**/
drawLine(const QPointF & start,const QPointF & end,qreal width,bool antialias)1630 void KisPainter::drawLine(const QPointF& start, const QPointF& end, qreal width, bool antialias){
1631     int x1 = qFloor(start.x());
1632     int y1 = qFloor(start.y());
1633     int x2 = qFloor(end.x());
1634     int y2 = qFloor(end.y());
1635 
1636     if ((x2 == x1 ) && (y2 == y1)) return;
1637 
1638     int dstX = x2-x1;
1639     int dstY = y2-y1;
1640 
1641     qreal uniC = dstX*y1 - dstY*x1;
1642     qreal projectionDenominator = 1.0 / (pow((double)dstX, 2) + pow((double)dstY, 2));
1643 
1644     qreal subPixel;
1645     if (qAbs(dstX) > qAbs(dstY)){
1646         subPixel = start.x() - x1;
1647     }else{
1648         subPixel = start.y() - y1;
1649     }
1650 
1651     qreal halfWidth = width * 0.5 + subPixel;
1652     int W_ = qRound(halfWidth) + 1;
1653 
1654     // save the state
1655     int X1_ = x1;
1656     int Y1_ = y1;
1657     int X2_ = x2;
1658     int Y2_ = y2;
1659 
1660     if (x2<x1) std::swap(x1,x2);
1661     if (y2<y1) std::swap(y1,y2);
1662 
1663     qreal denominator = sqrt(pow((double)dstY,2) + pow((double)dstX,2));
1664     if (denominator == 0.0) {
1665         denominator = 1.0;
1666     }
1667     denominator = 1.0/denominator;
1668 
1669     qreal projection,scanX,scanY,AA_;
1670     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
1671     KisRandomConstAccessorSP selectionAccessor;
1672     if (d->selection) {
1673         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
1674     }
1675 
1676     for (int y = y1-W_; y < y2+W_ ; y++){
1677         for (int x = x1-W_; x < x2+W_; x++){
1678 
1679             projection = ( (x-X1_)* dstX + (y-Y1_)*dstY ) * projectionDenominator;
1680             scanX = X1_ + projection * dstX;
1681             scanY = Y1_ + projection * dstY;
1682 
1683             if (((scanX < x1) || (scanX > x2)) || ((scanY < y1) || (scanY > y2))) {
1684                 AA_ = qMin( sqrt( pow((double)x - X1_, 2) + pow((double)y - Y1_, 2) ),
1685                             sqrt( pow((double)x - X2_, 2) + pow((double)y - Y2_, 2) ));
1686             }else{
1687                 AA_ = qAbs(dstY*x - dstX*y + uniC) * denominator;
1688             }
1689 
1690             if (AA_>halfWidth) {
1691                 continue;
1692             }
1693 
1694             accessor->moveTo(x, y);
1695             if (selectionAccessor) selectionAccessor->moveTo(x,y);
1696 
1697             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1698                 KoColor mycolor = d->paintColor;
1699 
1700                 if (antialias && AA_ > halfWidth-1.0) {
1701                     mycolor.colorSpace()->multiplyAlpha(mycolor.data(), 1.0 - (AA_-(halfWidth-1.0)), 1);
1702                 }
1703 
1704                 compositeOnePixel(accessor->rawData(), mycolor);
1705             }
1706         }
1707     }
1708 }
1709 
1710 /**/
1711 
drawLine(const QPointF & start,const QPointF & end)1712 void KisPainter::drawLine(const QPointF & start, const QPointF & end)
1713 {
1714     drawThickLine(start, end, 1, 1);
1715 }
1716 
1717 
drawDDALine(const QPointF & start,const QPointF & end)1718 void KisPainter::drawDDALine(const QPointF & start, const QPointF & end)
1719 {
1720     int x = qFloor(start.x());
1721     int y = qFloor(start.y());
1722 
1723     int x2 = qFloor(end.x());
1724     int y2 = qFloor(end.y());
1725 
1726     // Width and height of the line
1727     int xd = x2 - x;
1728     int yd = y2 - y;
1729 
1730     float m = 0;
1731     bool lockAxis = true;
1732 
1733     if (xd == 0) {
1734         m = 2.0;
1735     } else if ( yd != 0) {
1736         lockAxis = false;
1737         m = (float)yd / (float)xd;
1738     }
1739 
1740     float fx = x;
1741     float fy = y;
1742     int inc;
1743 
1744     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
1745     KisRandomConstAccessorSP selectionAccessor;
1746     if (d->selection) {
1747         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
1748     }
1749 
1750 
1751     accessor->moveTo(x, y);
1752     if (selectionAccessor) selectionAccessor->moveTo(x,y);
1753 
1754     if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1755         compositeOnePixel(accessor->rawData(), d->paintColor);
1756     }
1757 
1758     if (fabs(m) > 1.0f) {
1759         inc = (yd > 0) ? 1 : -1;
1760         m = (lockAxis)? 0 : 1.0f / m;
1761         m *= inc;
1762         while (y != y2) {
1763             y = y + inc;
1764             fx = fx + m;
1765             x = qRound(fx);
1766 
1767             accessor->moveTo(x, y);
1768             if (selectionAccessor) selectionAccessor->moveTo(x, y);
1769 
1770             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1771                 compositeOnePixel(accessor->rawData(), d->paintColor);
1772             }
1773         }
1774     } else {
1775         inc = (xd > 0) ? 1 : -1;
1776         m *= inc;
1777         while (x != x2) {
1778             x = x + inc;
1779             fy = fy + m;
1780             y = qRound(fy);
1781 
1782             accessor->moveTo(x, y);
1783             if (selectionAccessor) selectionAccessor->moveTo(x, y);
1784 
1785             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1786                 compositeOnePixel(accessor->rawData(), d->paintColor);
1787             }
1788         }
1789     }
1790 }
1791 
drawWobblyLine(const QPointF & start,const QPointF & end)1792 void KisPainter::drawWobblyLine(const QPointF & start, const QPointF & end)
1793 {
1794     KoColor mycolor(d->paintColor);
1795 
1796     int x1 = qFloor(start.x());
1797     int y1 = qFloor(start.y());
1798     int x2 = qFloor(end.x());
1799     int y2 = qFloor(end.y());
1800 
1801     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
1802     KisRandomConstAccessorSP selectionAccessor;
1803     if (d->selection) {
1804         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
1805     }
1806 
1807     // Width and height of the line
1808     int xd = (x2 - x1);
1809     int yd = (y2 - y1);
1810 
1811     int x;
1812     int y;
1813     float fx = (x = x1);
1814     float fy = (y = y1);
1815     float m = (float)yd / (float)xd;
1816     int inc;
1817 
1818     if (fabs(m) > 1) {
1819         inc = (yd > 0) ? 1 : -1;
1820         m = 1.0f / m;
1821         m *= inc;
1822         while (y != y2) {
1823             fx = fx + m;
1824             y = y + inc;
1825             x = qRound(fx);
1826 
1827             float br1 = qFloor(fx + 1) - fx;
1828             float br2 = fx - qFloor(fx);
1829 
1830             accessor->moveTo(x, y);
1831             if (selectionAccessor) selectionAccessor->moveTo(x, y);
1832 
1833             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1834                 mycolor.setOpacity((quint8)(255*br1));
1835                 compositeOnePixel(accessor->rawData(), mycolor);
1836             }
1837 
1838             accessor->moveTo(x + 1, y);
1839             if (selectionAccessor) selectionAccessor->moveTo(x + 1, y);
1840 
1841             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1842                 mycolor.setOpacity((quint8)(255*br2));
1843                 compositeOnePixel(accessor->rawData(), mycolor);
1844             }
1845         }
1846     } else {
1847         inc = (xd > 0) ? 1 : -1;
1848         m *= inc;
1849         while (x != x2) {
1850             fy = fy + m;
1851             x = x + inc;
1852             y = qRound(fy);
1853 
1854             float br1 = qFloor(fy + 1) - fy;
1855             float br2 = fy - qFloor(fy);
1856 
1857             accessor->moveTo(x, y);
1858             if (selectionAccessor) selectionAccessor->moveTo(x, y);
1859 
1860             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1861                 mycolor.setOpacity((quint8)(255*br1));
1862                 compositeOnePixel(accessor->rawData(), mycolor);
1863             }
1864 
1865             accessor->moveTo(x, y + 1);
1866             if (selectionAccessor) selectionAccessor->moveTo(x, y + 1);
1867 
1868             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1869                 mycolor.setOpacity((quint8)(255*br2));
1870                 compositeOnePixel(accessor->rawData(), mycolor);
1871             }
1872         }
1873     }
1874 
1875 }
1876 
drawWuLine(const QPointF & start,const QPointF & end)1877 void KisPainter::drawWuLine(const QPointF & start, const QPointF & end)
1878 {
1879     KoColor lineColor(d->paintColor);
1880 
1881     int x1 = qFloor(start.x());
1882     int y1 = qFloor(start.y());
1883     int x2 = qFloor(end.x());
1884     int y2 = qFloor(end.y());
1885 
1886     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
1887     KisRandomConstAccessorSP selectionAccessor;
1888     if (d->selection) {
1889         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
1890     }
1891 
1892     float grad, xd, yd;
1893     float xgap, ygap, xend, yend, yf, xf;
1894     float brightness1, brightness2;
1895 
1896     int ix1, ix2, iy1, iy2;
1897     quint8 c1, c2;
1898 
1899     // gradient of line
1900     xd = (x2 - x1);
1901     yd = (y2 - y1);
1902 
1903     if (yd == 0) {
1904         /* Horizontal line */
1905         int incr = (x1 < x2) ? 1 : -1;
1906         ix1 = x1;
1907         ix2 = x2;
1908         iy1 = y1;
1909         while (ix1 != ix2) {
1910             ix1 = ix1 + incr;
1911 
1912             accessor->moveTo(ix1, iy1);
1913             if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
1914 
1915             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1916                 compositeOnePixel(accessor->rawData(), lineColor);
1917             }
1918         }
1919         return;
1920     }
1921 
1922     if (xd == 0) {
1923         /* Vertical line */
1924         int incr = (y1 < y2) ? 1 : -1;
1925         iy1 = y1;
1926         iy2 = y2;
1927         ix1 = x1;
1928         while (iy1 != iy2) {
1929             iy1 = iy1 + incr;
1930 
1931             accessor->moveTo(ix1, iy1);
1932             if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
1933 
1934             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1935                 compositeOnePixel(accessor->rawData(), lineColor);
1936             }
1937         }
1938         return;
1939     }
1940 
1941     if (fabs(xd) > fabs(yd)) {
1942         // horizontal line
1943         // line have to be paint from left to right
1944         if (x1 > x2) {
1945             std::swap(x1, x2);
1946             std::swap(y1, y2);
1947             xd = (x2 - x1);
1948             yd = (y2 - y1);
1949         }
1950         grad = yd / xd;
1951         // nearest X,Y integer coordinates
1952         xend = x1;
1953         yend = y1 + grad * (xend - x1);
1954 
1955         xgap = invertFrac(x1 + 0.5f);
1956 
1957         ix1 = x1;
1958         iy1 = qFloor(yend);
1959 
1960         // calc the intensity of the other end point pixel pair.
1961         brightness1 = invertFrac(yend) * xgap;
1962         brightness2 =       frac(yend) * xgap;
1963 
1964         c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
1965         c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
1966 
1967         accessor->moveTo(ix1, iy1);
1968         if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
1969 
1970         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1971             lineColor.setOpacity(c1);
1972             compositeOnePixel(accessor->rawData(), lineColor);
1973         }
1974 
1975         accessor->moveTo(ix1, iy1 + 1);
1976         if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1 + 1);
1977 
1978         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
1979             lineColor.setOpacity(c2);
1980             compositeOnePixel(accessor->rawData(), lineColor);
1981         }
1982 
1983         // calc first Y-intersection for main loop
1984         yf = yend + grad;
1985 
1986         xend = x2;
1987         yend = y2 + grad * (xend - x2);
1988 
1989         xgap = invertFrac(x2 - 0.5f);
1990 
1991         ix2 = x2;
1992         iy2 = qFloor(yend);
1993 
1994         brightness1 = invertFrac(yend) * xgap;
1995         brightness2 =    frac(yend) * xgap;
1996 
1997         c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
1998         c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
1999 
2000         accessor->moveTo(ix2, iy2);
2001         if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2);
2002 
2003         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2004             lineColor.setOpacity(c1);
2005             compositeOnePixel(accessor->rawData(), lineColor);
2006         }
2007 
2008         accessor->moveTo(ix2, iy2 + 1);
2009         if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2 + 1);
2010 
2011         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2012             lineColor.setOpacity(c2);
2013             compositeOnePixel(accessor->rawData(), lineColor);
2014         }
2015 
2016         // main loop
2017         for (int x = ix1 + 1; x <= ix2 - 1; x++) {
2018             brightness1 = invertFrac(yf);
2019             brightness2 =    frac(yf);
2020             c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2021             c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2022 
2023             accessor->moveTo(x, qFloor(yf));
2024             if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yf));
2025 
2026             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2027                 lineColor.setOpacity(c1);
2028                 compositeOnePixel(accessor->rawData(), lineColor);
2029             }
2030 
2031             accessor->moveTo(x, qFloor(yf) + 1);
2032             if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yf) + 1);
2033 
2034             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2035                 lineColor.setOpacity(c2);
2036                 compositeOnePixel(accessor->rawData(), lineColor);
2037             }
2038 
2039             yf = yf + grad;
2040         }
2041     } else {
2042         //vertical
2043         // line have to be painted from left to right
2044         if (y1 > y2) {
2045             std::swap(x1, x2);
2046             std::swap(y1, y2);
2047             xd = (x2 - x1);
2048             yd = (y2 - y1);
2049         }
2050 
2051         grad = xd / yd;
2052 
2053         // nearest X,Y integer coordinates
2054         yend = y1;
2055         xend = x1 + grad * (yend - y1);
2056 
2057         ygap = y1;
2058 
2059         ix1 = qFloor(xend);
2060         iy1 = y1;
2061 
2062         // calc the intensity of the other end point pixel pair.
2063         brightness1 = invertFrac(xend) * ygap;
2064         brightness2 =       frac(xend) * ygap;
2065 
2066         c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2067         c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2068 
2069         accessor->moveTo(ix1, iy1);
2070         if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
2071 
2072         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2073             lineColor.setOpacity(c1);
2074             compositeOnePixel(accessor->rawData(), lineColor);
2075         }
2076 
2077         accessor->moveTo(x1 + 1, y1);
2078         if (selectionAccessor) selectionAccessor->moveTo(x1 + 1, y1);
2079 
2080         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2081             lineColor.setOpacity(c2);
2082             compositeOnePixel(accessor->rawData(), lineColor);
2083         }
2084 
2085         // calc first Y-intersection for main loop
2086         xf = xend + grad;
2087 
2088         yend = y2;
2089         xend = x2 + grad * (yend - y2);
2090 
2091         ygap = invertFrac(y2 - 0.5f);
2092 
2093         ix2 = qFloor(xend);
2094         iy2 = y2;
2095 
2096         brightness1 = invertFrac(xend) * ygap;
2097         brightness2 =    frac(xend) * ygap;
2098 
2099         c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2100         c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2101 
2102         accessor->moveTo(ix2, iy2);
2103         if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2);
2104 
2105         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2106             lineColor.setOpacity(c1);
2107             compositeOnePixel(accessor->rawData(), lineColor);
2108         }
2109 
2110         accessor->moveTo(ix2 + 1, iy2);
2111         if (selectionAccessor) selectionAccessor->moveTo(ix2 + 1, iy2);
2112 
2113         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2114             lineColor.setOpacity(c2);
2115             compositeOnePixel(accessor->rawData(), lineColor);
2116         }
2117 
2118         // main loop
2119         for (int y = iy1 + 1; y <= iy2 - 1; y++) {
2120             brightness1 = invertFrac(xf);
2121             brightness2 =    frac(xf);
2122             c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
2123             c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
2124 
2125             accessor->moveTo(qFloor(xf), y);
2126             if (selectionAccessor) selectionAccessor->moveTo(qFloor(xf), y);
2127 
2128             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2129                 lineColor.setOpacity(c1);
2130                 compositeOnePixel(accessor->rawData(), lineColor);
2131             }
2132 
2133             accessor->moveTo(qFloor(xf) + 1, y);
2134             if (selectionAccessor) selectionAccessor->moveTo(qFloor(xf) + 1, y);
2135 
2136             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2137                 lineColor.setOpacity(c2);
2138                 compositeOnePixel(accessor->rawData(), lineColor);
2139             }
2140 
2141             xf = xf + grad;
2142         }
2143     }//end-of-else
2144 
2145 }
2146 
drawThickLine(const QPointF & start,const QPointF & end,int startWidth,int endWidth)2147 void KisPainter::drawThickLine(const QPointF & start, const QPointF & end, int startWidth, int endWidth)
2148 {
2149 
2150     KisRandomAccessorSP accessor = d->device->createRandomAccessorNG();
2151     KisRandomConstAccessorSP selectionAccessor;
2152     if (d->selection) {
2153         selectionAccessor = d->selection->projection()->createRandomConstAccessorNG();
2154     }
2155 
2156     const KoColorSpace *cs = d->device->colorSpace();
2157 
2158     KoColor c1(d->paintColor);
2159     KoColor c2(d->paintColor);
2160     KoColor c3(d->paintColor);
2161     KoColor col1(c1);
2162     KoColor col2(c1);
2163 
2164     float grada, gradb, dxa, dxb, dya, dyb, fraca, fracb,
2165     xfa, yfa, xfb, yfb, b1a, b2a, b1b, b2b, dstX, dstY;
2166     int x, y, ix1, ix2, iy1, iy2;
2167 
2168     int x0a, y0a, x1a, y1a, x0b, y0b, x1b, y1b;
2169     int tp0, tn0, tp1, tn1;
2170 
2171     int horizontal = 0;
2172     float opacity = 1.0;
2173 
2174     tp0 = startWidth / 2;
2175     tn0 = startWidth / 2;
2176     if (startWidth % 2 == 0) // even width startWidth
2177         tn0--;
2178 
2179     tp1 = endWidth / 2;
2180     tn1 = endWidth / 2;
2181     if (endWidth % 2 == 0) // even width endWidth
2182         tn1--;
2183 
2184     int x0 = qRound(start.x());
2185     int y0 = qRound(start.y());
2186     int x1 = qRound(end.x());
2187     int y1 = qRound(end.y());
2188 
2189     dstX = x1 - x0; // run of general line
2190     dstY = y1 - y0; // rise of general line
2191 
2192     if (dstY < 0) dstY = -dstY;
2193     if (dstX < 0) dstX = -dstX;
2194 
2195     if (dstX > dstY) { // horizontalish
2196         horizontal = 1;
2197         x0a = x0;   y0a = y0 - tn0;
2198         x0b = x0;   y0b = y0 + tp0;
2199         x1a = x1;   y1a = y1 - tn1;
2200         x1b = x1;   y1b = y1 + tp1;
2201     } else {
2202         x0a = x0 - tn0;   y0a = y0;
2203         x0b = x0 + tp0;   y0b = y0;
2204         x1a = x1 - tn1;   y1a = y1;
2205         x1b = x1 + tp1;   y1b = y1;
2206     }
2207 
2208     if (horizontal) { // draw endpoints
2209         for (int i = y0a; i <= y0b; i++) {
2210 
2211             accessor->moveTo(x0, i);
2212             if (selectionAccessor) selectionAccessor->moveTo(x0, i);
2213 
2214             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2215                 compositeOnePixel(accessor->rawData(), c1);
2216             }
2217         }
2218         for (int i = y1a; i <= y1b; i++) {
2219 
2220             accessor->moveTo(x1, i);
2221             if (selectionAccessor) selectionAccessor->moveTo(x1, i);
2222 
2223             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2224                 compositeOnePixel(accessor->rawData(), c1);
2225             }
2226         }
2227 
2228     } else {
2229         for (int i = x0a; i <= x0b; i++) {
2230 
2231             accessor->moveTo(i, y0);
2232             if (selectionAccessor) selectionAccessor->moveTo(i, y0);
2233 
2234             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2235                 compositeOnePixel(accessor->rawData(), c1);
2236             }
2237         }
2238         for (int i = x1a; i <= x1b; i++) {
2239             accessor->moveTo(i, y1);
2240             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2241                 compositeOnePixel(accessor->rawData(), c1);
2242             }
2243         }
2244     }
2245 
2246     //antialias endpoints
2247     if (x1 != x0 && y1 != y0) {
2248         if (horizontal) {
2249 
2250             accessor->moveTo(x0a, y0a - 1);
2251             if (selectionAccessor) selectionAccessor->moveTo(x0a, y0a - 1);
2252 
2253             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2254                 qreal alpha = cs->opacityF(accessor->rawData());
2255                 opacity = .25 * c1.opacityF() + (1 - .25) * alpha;
2256                 col1.setOpacity(opacity);
2257                 compositeOnePixel(accessor->rawData(), col1);
2258             }
2259 
2260             accessor->moveTo(x1b, y1b + 1);
2261             if (selectionAccessor) selectionAccessor->moveTo(x1b, y1b + 1);
2262 
2263             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2264                 qreal alpha = cs->opacityF(accessor->rawData());
2265                 opacity = .25 * c2.opacityF() + (1 - .25) * alpha;
2266                 col1.setOpacity(opacity);
2267                 compositeOnePixel(accessor->rawData(), col1);
2268             }
2269 
2270         } else {
2271 
2272             accessor->moveTo(x0a - 1, y0a);
2273             if (selectionAccessor) selectionAccessor->moveTo(x0a - 1, y0a);
2274 
2275             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2276                 qreal alpha = cs->opacityF(accessor->rawData());
2277                 opacity = .25 * c1.opacityF() + (1 - .25) * alpha;
2278                 col1.setOpacity(opacity);
2279                 compositeOnePixel(accessor->rawData(), col1);
2280             }
2281 
2282             accessor->moveTo(x1b + 1, y1b);
2283             if (selectionAccessor) selectionAccessor->moveTo(x1b + 1, y1b);
2284 
2285             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2286                 qreal alpha = cs->opacityF(accessor->rawData());
2287                 opacity = .25 * c2.opacityF() + (1 - .25) * alpha;
2288                 col1.setOpacity(opacity);
2289                 compositeOnePixel(accessor->rawData(), col1);
2290             }
2291         }
2292     }
2293 
2294     dxa = x1a - x0a; // run of a
2295     dya = y1a - y0a; // rise of a
2296     dxb = x1b - x0b; // run of b
2297     dyb = y1b - y0b; // rise of b
2298 
2299     if (horizontal) { // horizontal-ish lines
2300         if (x1 < x0) {
2301             int xt, yt, wt;
2302             KoColor tmp;
2303             xt = x1a;     x1a = x0a;    x0a = xt;
2304             yt = y1a;     y1a = y0a;    y0a = yt;
2305             xt = x1b;     x1b = x0b;    x0b = xt;
2306             yt = y1b;     y1b = y0b;    y0b = yt;
2307             xt = x1;      x1 = x0;      x0 = xt;
2308             yt = y1;      y1 = y0;      y0 = yt;
2309 
2310             tmp = c1; c1 = c2; c2 = tmp;
2311             wt = startWidth;      startWidth = endWidth;      endWidth = wt;
2312         }
2313 
2314         grada = dya / dxa;
2315         gradb = dyb / dxb;
2316 
2317         ix1 = x0;   iy1 = y0;
2318         ix2 = x1;   iy2 = y1;
2319 
2320         yfa = y0a + grada;
2321         yfb = y0b + gradb;
2322 
2323         for (x = ix1 + 1; x <= ix2 - 1; x++) {
2324             fraca = yfa - qFloor(yfa);
2325             b1a = 1 - fraca;
2326             b2a = fraca;
2327 
2328             fracb = yfb - qFloor(yfb);
2329             b1b = 1 - fracb;
2330             b2b = fracb;
2331 
2332             // color first pixel of bottom line
2333             opacity = ((x - ix1) / dstX) * c2.opacityF() + (1 - (x - ix1) / dstX) * c1.opacityF();
2334             c3.setOpacity(opacity);
2335 
2336             accessor->moveTo(x, qFloor(yfa));
2337             if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfa));
2338 
2339             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2340                 qreal alpha = cs->opacityF(accessor->rawData());
2341                 opacity = b1a * c3.opacityF() + (1 - b1a) * alpha;
2342                 col1.setOpacity(opacity);
2343                 compositeOnePixel(accessor->rawData(), col1);
2344             }
2345 
2346             // color first pixel of top line
2347             if (!(startWidth == 1 && endWidth == 1)) {
2348                 accessor->moveTo(x, qFloor(yfb));
2349                 if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfb));
2350 
2351                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2352                     qreal alpha = cs->opacityF(accessor->rawData());
2353                     opacity = b1b * c3.opacityF() + (1 - b1b) * alpha;
2354                     col1.setOpacity(opacity);
2355                     compositeOnePixel(accessor->rawData(), col1);
2356                 }
2357             }
2358 
2359             // color second pixel of bottom line
2360             if (grada != 0 && grada != 1) { // if not flat or exact diagonal
2361 
2362                 accessor->moveTo(x, qFloor(yfa) + 1);
2363                 if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfa) + 1);
2364 
2365                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2366                     qreal alpha = cs->opacityF(accessor->rawData());
2367                     opacity = b2a * c3.opacityF() + (1 - b2a)  * alpha;
2368                     col2.setOpacity(opacity);
2369                     compositeOnePixel(accessor->rawData(), col2);
2370                 }
2371 
2372             }
2373 
2374             // color second pixel of top line
2375             if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) {
2376 
2377                 accessor->moveTo(x, qFloor(yfb) + 1);
2378                 if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfb) + 1);
2379 
2380                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2381                     qreal alpha = cs->opacityF(accessor->rawData());
2382                     opacity = b2b * c3.opacityF() + (1 - b2b) * alpha;
2383                     col2.setOpacity(opacity);
2384                     compositeOnePixel(accessor->rawData(), col2);
2385                 }
2386 
2387             }
2388 
2389             // fill remaining pixels
2390             if (!(startWidth == 1 && endWidth == 1)) {
2391                 if (yfa < yfb)
2392                     for (int i = qFloor(yfa) + 1; i <= qFloor(yfb); i++) {
2393 
2394                         accessor->moveTo(x, i);
2395                         if (selectionAccessor) selectionAccessor->moveTo(x, i);
2396 
2397                         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2398                             compositeOnePixel(accessor->rawData(), c3);
2399                         }
2400                     }
2401                 else
2402                     for (int i = qFloor(yfa) + 1; i >= qFloor(yfb); i--) {
2403 
2404                         accessor->moveTo(x, i);
2405                         if (selectionAccessor) selectionAccessor->moveTo(x, i);
2406 
2407                         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2408                             compositeOnePixel(accessor->rawData(), c3);
2409                         }
2410                     }
2411 
2412             }
2413 
2414             yfa += grada;
2415             yfb += gradb;
2416         }
2417     } else { // vertical-ish lines
2418         if (y1 < y0) {
2419             int xt, yt, wt;
2420             xt = x1a;     x1a = x0a;    x0a = xt;
2421             yt = y1a;     y1a = y0a;    y0a = yt;
2422             xt = x1b;     x1b = x0b;    x0b = xt;
2423             yt = y1b;     y1b = y0b;    y0b = yt;
2424             xt = x1;      x1 = x0;      x0 = xt;
2425             yt = y1;      y1 = y0;      y0 = yt;
2426 
2427             KoColor tmp;
2428             tmp = c1; c1 = c2; c2 = tmp;
2429             wt = startWidth;      startWidth = endWidth;      endWidth = wt;
2430         }
2431 
2432         grada = dxa / dya;
2433         gradb = dxb / dyb;
2434 
2435         ix1 = x0;   iy1 = y0;
2436         ix2 = x1;   iy2 = y1;
2437 
2438         xfa = x0a + grada;
2439         xfb = x0b + gradb;
2440 
2441         for (y = iy1 + 1; y <= iy2 - 1; y++) {
2442             fraca = xfa - qFloor(xfa);
2443             b1a = 1 - fraca;
2444             b2a = fraca;
2445 
2446             fracb = xfb - qFloor(xfb);
2447             b1b = 1 - fracb;
2448             b2b = fracb;
2449 
2450             // color first pixel of left line
2451             opacity = ((y - iy1) / dstY) * c2.opacityF() + (1 - (y - iy1) / dstY) * c1.opacityF();
2452             c3.setOpacity(opacity);
2453 
2454             accessor->moveTo(qFloor(xfa), y);
2455             if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfa), y);
2456 
2457             if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2458                 qreal alpha = cs->opacityF(accessor->rawData());
2459                 opacity = b1a * c3.opacityF() + (1 - b1a) * alpha;
2460                 col1.setOpacity(opacity);
2461                 compositeOnePixel(accessor->rawData(), col1);
2462             }
2463 
2464             // color first pixel of right line
2465             if (!(startWidth == 1 && endWidth == 1)) {
2466 
2467                 accessor->moveTo(qFloor(xfb), y);
2468                 if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfb), y);
2469 
2470                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2471                     qreal alpha = cs->opacityF(accessor->rawData());
2472                     opacity = b1b * c3.opacityF() + (1 - b1b)  * alpha;
2473                     col1.setOpacity(opacity);
2474                     compositeOnePixel(accessor->rawData(), col1);
2475                 }
2476             }
2477 
2478             // color second pixel of left line
2479             if (grada != 0 && grada != 1) { // if not flat or exact diagonal
2480 
2481                 accessor->moveTo(qFloor(xfa) + 1, y);
2482                 if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfa) + 1, y);
2483 
2484                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2485                     qreal alpha = cs->opacityF(accessor->rawData());
2486                     opacity = b2a * c3.opacityF() + (1 - b2a) * alpha;
2487                     col2.setOpacity(opacity);
2488                     compositeOnePixel(accessor->rawData(), col2);
2489                 }
2490 
2491             }
2492 
2493             // color second pixel of right line
2494             if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) {
2495 
2496                 accessor->moveTo(qFloor(xfb) + 1, y);
2497                 if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfb) + 1, y);
2498 
2499                 if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2500                     qreal alpha = cs->opacityF(accessor->rawData());
2501                     opacity = b2b * c3.opacityF() + (1 - b2b) * alpha;
2502                     col2.setOpacity(opacity);
2503                     compositeOnePixel(accessor->rawData(), col2);
2504                 }
2505             }
2506 
2507             // fill remaining pixels between current xfa,xfb
2508             if (!(startWidth == 1 && endWidth == 1)) {
2509                 if (xfa < xfb)
2510                     for (int i = qFloor(xfa) + 1; i <= qFloor(xfb); i++) {
2511 
2512                         accessor->moveTo(i, y);
2513                         if (selectionAccessor) selectionAccessor->moveTo(i, y);
2514 
2515                         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2516                             compositeOnePixel(accessor->rawData(), c3);
2517                         }
2518                     }
2519                 else
2520                     for (int i = qFloor(xfb); i <= qFloor(xfa) + 1; i++) {
2521 
2522                         accessor->moveTo(i, y);
2523                         if (selectionAccessor) selectionAccessor->moveTo(i, y);
2524 
2525                         if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
2526                             compositeOnePixel(accessor->rawData(), c3);
2527                         }
2528                     }
2529             }
2530 
2531             xfa += grada;
2532             xfb += gradb;
2533         }
2534     }
2535 
2536 }
2537 
2538 
2539 
setProgress(KoUpdater * progressUpdater)2540 void KisPainter::setProgress(KoUpdater * progressUpdater)
2541 {
2542     d->progressUpdater = progressUpdater;
2543 }
2544 
device() const2545 const KisPaintDeviceSP KisPainter::device() const
2546 {
2547     return d->device;
2548 }
device()2549 KisPaintDeviceSP KisPainter::device()
2550 {
2551     return d->device;
2552 }
2553 
setChannelFlags(QBitArray channelFlags)2554 void KisPainter::setChannelFlags(QBitArray channelFlags)
2555 {
2556     // Q_ASSERT(channelFlags.isEmpty() || quint32(channelFlags.size()) == d->colorSpace->channelCount());
2557     // Now, if all bits in the channelflags are true, pass an empty channel flags bitarray
2558     // because otherwise the compositeops cannot optimize.
2559     d->paramInfo.channelFlags = channelFlags;
2560 
2561     if (!channelFlags.isEmpty() && channelFlags == QBitArray(channelFlags.size(), true)) {
2562         d->paramInfo.channelFlags = QBitArray();
2563     }
2564 }
2565 
channelFlags()2566 QBitArray KisPainter::channelFlags()
2567 {
2568     return d->paramInfo.channelFlags;
2569 }
2570 
setPattern(const KoPattern * pattern)2571 void KisPainter::setPattern(const KoPattern * pattern)
2572 {
2573     d->pattern = pattern;
2574 }
2575 
pattern() const2576 const KoPattern * KisPainter::pattern() const
2577 {
2578     return d->pattern;
2579 }
2580 
setPaintColor(const KoColor & color)2581 void KisPainter::setPaintColor(const KoColor& color)
2582 {
2583     d->paintColor = color;
2584     if (d->device) {
2585         d->paintColor.convertTo(d->device->compositionSourceColorSpace());
2586     }
2587 }
2588 
paintColor() const2589 const KoColor &KisPainter::paintColor() const
2590 {
2591     return d->paintColor;
2592 }
2593 
setBackgroundColor(const KoColor & color)2594 void KisPainter::setBackgroundColor(const KoColor& color)
2595 {
2596     d->backgroundColor = color;
2597     if (d->device) {
2598         d->backgroundColor.convertTo(d->device->compositionSourceColorSpace());
2599     }
2600 }
2601 
backgroundColor() const2602 const KoColor &KisPainter::backgroundColor() const
2603 {
2604     return d->backgroundColor;
2605 }
2606 
setGenerator(KisFilterConfigurationSP generator)2607 void KisPainter::setGenerator(KisFilterConfigurationSP  generator)
2608 {
2609     d->generator = generator;
2610 }
2611 
generator() const2612 const KisFilterConfigurationSP  KisPainter::generator() const
2613 {
2614     return d->generator;
2615 }
2616 
setFillStyle(FillStyle fillStyle)2617 void KisPainter::setFillStyle(FillStyle fillStyle)
2618 {
2619     d->fillStyle = fillStyle;
2620 }
2621 
fillStyle() const2622 KisPainter::FillStyle KisPainter::fillStyle() const
2623 {
2624     return d->fillStyle;
2625 }
2626 
setPatternTransform(QTransform transform)2627 void KisPainter::setPatternTransform(QTransform transform)
2628 {
2629     d->patternTransform = transform;
2630 }
2631 
patternTransform()2632 QTransform KisPainter::patternTransform()
2633 {
2634     return d->patternTransform;
2635 }
2636 
setAntiAliasPolygonFill(bool antiAliasPolygonFill)2637 void KisPainter::setAntiAliasPolygonFill(bool antiAliasPolygonFill)
2638 {
2639     d->antiAliasPolygonFill = antiAliasPolygonFill;
2640 }
2641 
antiAliasPolygonFill()2642 bool KisPainter::antiAliasPolygonFill()
2643 {
2644     return d->antiAliasPolygonFill;
2645 }
2646 
setStrokeStyle(KisPainter::StrokeStyle strokeStyle)2647 void KisPainter::setStrokeStyle(KisPainter::StrokeStyle strokeStyle)
2648 {
2649     d->strokeStyle = strokeStyle;
2650 }
strokeStyle() const2651 KisPainter::StrokeStyle KisPainter::strokeStyle() const
2652 {
2653     return d->strokeStyle;
2654 }
2655 
setFlow(quint8 flow)2656 void KisPainter::setFlow(quint8 flow)
2657 {
2658     d->paramInfo.flow = float(flow) / 255.0f;
2659 }
2660 
flow() const2661 quint8 KisPainter::flow() const
2662 {
2663     return quint8(d->paramInfo.flow * 255.0f);
2664 }
2665 
setOpacityUpdateAverage(quint8 opacity)2666 void KisPainter::setOpacityUpdateAverage(quint8 opacity)
2667 {
2668     d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8;
2669     d->paramInfo.updateOpacityAndAverage(float(opacity) / 255.0f);
2670 }
2671 
setAverageOpacity(qreal averageOpacity)2672 void KisPainter::setAverageOpacity(qreal averageOpacity)
2673 {
2674     d->paramInfo.setOpacityAndAverage(d->paramInfo.opacity, averageOpacity);
2675 }
2676 
blendAverageOpacity(qreal opacity,qreal averageOpacity)2677 qreal KisPainter::blendAverageOpacity(qreal opacity, qreal averageOpacity)
2678 {
2679     const float exponent = 0.1;
2680 
2681     return averageOpacity < opacity ?
2682         opacity :
2683         exponent * opacity + (1.0 - exponent) * (averageOpacity);
2684 }
2685 
setOpacity(quint8 opacity)2686 void KisPainter::setOpacity(quint8 opacity)
2687 {
2688     d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8;
2689     d->paramInfo.opacity = float(opacity) / 255.0f;
2690 }
2691 
opacity() const2692 quint8 KisPainter::opacity() const
2693 {
2694     return quint8(d->paramInfo.opacity * 255.0f);
2695 }
2696 
setCompositeOp(const KoCompositeOp * op)2697 void KisPainter::setCompositeOp(const KoCompositeOp * op)
2698 {
2699     d->compositeOp = op;
2700 }
2701 
compositeOp()2702 const KoCompositeOp * KisPainter::compositeOp()
2703 {
2704     return d->compositeOp;
2705 }
2706 
2707 /**
2708  * TODO: Rename this setCompositeOpId().  See KoCompositeOpRegistry.h
2709  */
setCompositeOp(const QString & op)2710 void KisPainter::setCompositeOp(const QString& op)
2711 {
2712     d->compositeOp = d->colorSpace->compositeOp(op);
2713 }
2714 
setSelection(KisSelectionSP selection)2715 void KisPainter::setSelection(KisSelectionSP selection)
2716 {
2717     d->selection = selection;
2718 }
2719 
selection()2720 KisSelectionSP KisPainter::selection()
2721 {
2722     return d->selection;
2723 }
2724 
progressUpdater()2725 KoUpdater * KisPainter::progressUpdater()
2726 {
2727     return d->progressUpdater;
2728 }
2729 
setGradient(const KoAbstractGradient * gradient)2730 void KisPainter::setGradient(const KoAbstractGradient* gradient)
2731 {
2732     d->gradient = gradient;
2733 }
2734 
gradient() const2735 const KoAbstractGradient* KisPainter::gradient() const
2736 {
2737     return d->gradient;
2738 }
2739 
setPaintOpPreset(KisPaintOpPresetSP preset,KisNodeSP node,KisImageSP image)2740 void KisPainter::setPaintOpPreset(KisPaintOpPresetSP preset, KisNodeSP node, KisImageSP image)
2741 {
2742     d->paintOpPreset = preset;
2743     KisPaintOp *paintop = KisPaintOpRegistry::instance()->paintOp(preset, this, node, image);
2744     Q_ASSERT(paintop);
2745     if (paintop) {
2746         delete d->paintOp;
2747         d->paintOp = paintop;
2748     }
2749     else {
2750         warnKrita << "Could not create paintop for preset " << preset->name();
2751     }
2752 }
2753 
preset() const2754 KisPaintOpPresetSP KisPainter::preset() const
2755 {
2756     return d->paintOpPreset;
2757 }
2758 
paintOp() const2759 KisPaintOp* KisPainter::paintOp() const
2760 {
2761     return d->paintOp;
2762 }
2763 
setMirrorInformation(const QPointF & axesCenter,bool mirrorHorizontally,bool mirrorVertically)2764 void KisPainter::setMirrorInformation(const QPointF& axesCenter, bool mirrorHorizontally, bool mirrorVertically)
2765 {
2766     d->axesCenter = axesCenter;
2767     d->mirrorHorizontally = mirrorHorizontally;
2768     d->mirrorVertically = mirrorVertically;
2769 }
2770 
copyMirrorInformationFrom(const KisPainter * other)2771 void KisPainter::copyMirrorInformationFrom(const KisPainter *other)
2772 {
2773     d->axesCenter = other->d->axesCenter;
2774     d->mirrorHorizontally = other->d->mirrorHorizontally;
2775     d->mirrorVertically = other->d->mirrorVertically;
2776 }
2777 
hasMirroring() const2778 bool KisPainter::hasMirroring() const
2779 {
2780     return d->mirrorHorizontally || d->mirrorVertically;
2781 }
2782 
hasHorizontalMirroring() const2783 bool KisPainter::hasHorizontalMirroring() const
2784 {
2785     return d->mirrorHorizontally;
2786 }
2787 
hasVerticalMirroring() const2788 bool KisPainter::hasVerticalMirroring() const
2789 {
2790     return d->mirrorVertically;
2791 }
2792 
setMaskImageSize(qint32 width,qint32 height)2793 void KisPainter::setMaskImageSize(qint32 width, qint32 height)
2794 {
2795 
2796     d->maskImageWidth = qBound(1, width, 256);
2797     d->maskImageHeight = qBound(1, height, 256);
2798     d->fillPainter = 0;
2799     d->polygonMaskImage = QImage();
2800 }
2801 
2802 //void KisPainter::setLockAlpha(bool protect)
2803 //{
2804 //    if(d->paramInfo.channelFlags.isEmpty()) {
2805 //        d->paramInfo.channelFlags = d->colorSpace->channelFlags(true, true);
2806 //    }
2807 
2808 //    QBitArray switcher =
2809 //        d->colorSpace->channelFlags(protect, !protect);
2810 
2811 //    if(protect) {
2812 //        d->paramInfo.channelFlags &= switcher;
2813 //    }
2814 //    else {
2815 //        d->paramInfo.channelFlags |= switcher;
2816 //    }
2817 
2818 //    Q_ASSERT(quint32(d->paramInfo.channelFlags.size()) == d->colorSpace->channelCount());
2819 //}
2820 
2821 //bool KisPainter::alphaLocked() const
2822 //{
2823 //    QBitArray switcher = d->colorSpace->channelFlags(false, true);
2824 //    return !(d->paramInfo.channelFlags & switcher).count(true);
2825 //}
2826 
setRenderingIntent(KoColorConversionTransformation::Intent intent)2827 void KisPainter::setRenderingIntent(KoColorConversionTransformation::Intent intent)
2828 {
2829     d->renderingIntent = intent;
2830 }
2831 
setColorConversionFlags(KoColorConversionTransformation::ConversionFlags conversionFlags)2832 void KisPainter::setColorConversionFlags(KoColorConversionTransformation::ConversionFlags conversionFlags)
2833 {
2834     d->conversionFlags = conversionFlags;
2835 }
2836 
setRunnableStrokeJobsInterface(KisRunnableStrokeJobsInterface * interface)2837 void KisPainter::setRunnableStrokeJobsInterface(KisRunnableStrokeJobsInterface *interface)
2838 {
2839     d->runnableStrokeJobsInterface = interface;
2840 }
2841 
runnableStrokeJobsInterface() const2842 KisRunnableStrokeJobsInterface *KisPainter::runnableStrokeJobsInterface() const
2843 {
2844     if (!d->runnableStrokeJobsInterface) {
2845         if (!d->fakeRunnableStrokeJobsInterface) {
2846             d->fakeRunnableStrokeJobsInterface.reset(new KisFakeRunnableStrokeJobsExecutor());
2847         }
2848         return d->fakeRunnableStrokeJobsInterface.data();
2849     }
2850 
2851     return d->runnableStrokeJobsInterface;
2852 }
2853 
renderMirrorMaskSafe(QRect rc,KisFixedPaintDeviceSP dab,bool preserveDab)2854 void KisPainter::renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, bool preserveDab)
2855 {
2856     if (!d->mirrorHorizontally && !d->mirrorVertically) return;
2857 
2858     KisFixedPaintDeviceSP dabToProcess = dab;
2859     if (preserveDab) {
2860         dabToProcess = new KisFixedPaintDevice(*dab);
2861     }
2862     renderMirrorMask(rc, dabToProcess);
2863 }
2864 
renderMirrorMaskSafe(QRect rc,KisPaintDeviceSP dab,int sx,int sy,KisFixedPaintDeviceSP mask,bool preserveMask)2865 void KisPainter::renderMirrorMaskSafe(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask, bool preserveMask)
2866 {
2867     if (!d->mirrorHorizontally && !d->mirrorVertically) return;
2868 
2869     KisFixedPaintDeviceSP maskToProcess = mask;
2870     if (preserveMask) {
2871         maskToProcess = new KisFixedPaintDevice(*mask);
2872     }
2873     renderMirrorMask(rc, dab, sx, sy, maskToProcess);
2874 }
2875 
renderMirrorMask(QRect rc,KisFixedPaintDeviceSP dab)2876 void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab)
2877 {
2878     int x = rc.topLeft().x();
2879     int y = rc.topLeft().y();
2880 
2881     KisLodTransform t(d->device);
2882     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
2883 
2884     int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
2885     int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
2886 
2887     if (d->mirrorHorizontally && d->mirrorVertically){
2888         dab->mirror(true, false);
2889         bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
2890         dab->mirror(false,true);
2891         bltFixed(mirrorX, mirrorY, dab, 0,0,rc.width(),rc.height());
2892         dab->mirror(true, false);
2893         bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height());
2894 
2895     }
2896     else if (d->mirrorHorizontally){
2897         dab->mirror(true, false);
2898         bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
2899     }
2900     else if (d->mirrorVertically){
2901         dab->mirror(false, true);
2902         bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height());
2903     }
2904 
2905 }
2906 
renderMirrorMask(QRect rc,KisFixedPaintDeviceSP dab,KisFixedPaintDeviceSP mask)2907 void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab, KisFixedPaintDeviceSP mask)
2908 {
2909     int x = rc.topLeft().x();
2910     int y = rc.topLeft().y();
2911 
2912     KisLodTransform t(d->device);
2913     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
2914 
2915     int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
2916     int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
2917 
2918     if (d->mirrorHorizontally && d->mirrorVertically){
2919         dab->mirror(true, false);
2920         mask->mirror(true, false);
2921         bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
2922 
2923         dab->mirror(false,true);
2924         mask->mirror(false, true);
2925         bltFixedWithFixedSelection(mirrorX,mirrorY, dab, mask, rc.width() ,rc.height() );
2926 
2927         dab->mirror(true, false);
2928         mask->mirror(true, false);
2929         bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() );
2930 
2931     }else if (d->mirrorHorizontally){
2932         dab->mirror(true, false);
2933         mask->mirror(true, false);
2934         bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
2935 
2936     }else if (d->mirrorVertically){
2937         dab->mirror(false, true);
2938         mask->mirror(false, true);
2939         bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() );
2940     }
2941 
2942 }
2943 
2944 
renderMirrorMask(QRect rc,KisPaintDeviceSP dab)2945 void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab){
2946     if (d->mirrorHorizontally || d->mirrorVertically){
2947         KisFixedPaintDeviceSP mirrorDab(new KisFixedPaintDevice(dab->colorSpace()));
2948         QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) );
2949         mirrorDab->setRect(dabRc);
2950         mirrorDab->lazyGrowBufferWithoutInitialization();
2951 
2952         dab->readBytes(mirrorDab->data(),rc);
2953 
2954         renderMirrorMask( QRect(rc.topLeft(),dabRc.size()), mirrorDab);
2955     }
2956 }
2957 
renderMirrorMask(QRect rc,KisPaintDeviceSP dab,int sx,int sy,KisFixedPaintDeviceSP mask)2958 void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask)
2959 {
2960     if (d->mirrorHorizontally || d->mirrorVertically){
2961         KisFixedPaintDeviceSP mirrorDab(new KisFixedPaintDevice(dab->colorSpace()));
2962         QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) );
2963         mirrorDab->setRect(dabRc);
2964         mirrorDab->lazyGrowBufferWithoutInitialization();
2965         dab->readBytes(mirrorDab->data(),QRect(QPoint(sx,sy),rc.size()));
2966         renderMirrorMask(rc, mirrorDab, mask);
2967     }
2968 }
2969 
renderDabWithMirroringNonIncremental(QRect rc,KisPaintDeviceSP dab)2970 void KisPainter::renderDabWithMirroringNonIncremental(QRect rc, KisPaintDeviceSP dab)
2971 {
2972     QVector<QRect> rects;
2973 
2974     int x = rc.topLeft().x();
2975     int y = rc.topLeft().y();
2976 
2977     KisLodTransform t(d->device);
2978     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
2979 
2980     int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
2981     int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
2982 
2983     rects << rc;
2984 
2985     if (d->mirrorHorizontally && d->mirrorVertically){
2986         rects << QRect(mirrorX, y, rc.width(), rc.height());
2987         rects << QRect(mirrorX, mirrorY, rc.width(), rc.height());
2988         rects << QRect(x, mirrorY, rc.width(), rc.height());
2989     } else if (d->mirrorHorizontally) {
2990         rects << QRect(mirrorX, y, rc.width(), rc.height());
2991     } else if (d->mirrorVertically) {
2992         rects << QRect(x, mirrorY, rc.width(), rc.height());
2993     }
2994 
2995     Q_FOREACH (const QRect &rc, rects) {
2996         d->device->clear(rc);
2997     }
2998 
2999     QRect resultRect = dab->extent() | rc;
3000     bool intersects = false;
3001 
3002     for (int i = 1; i < rects.size(); i++) {
3003         if (rects[i].intersects(resultRect)) {
3004             intersects = true;
3005             break;
3006         }
3007     }
3008 
3009     /**
3010      * If there are no cross-intersections, we can use a fast path
3011      * and do no cycling recompositioning
3012      */
3013     if (!intersects) {
3014         rects.resize(1);
3015     }
3016 
3017     Q_FOREACH (const QRect &rc, rects) {
3018         bitBlt(rc.topLeft(), dab, rc);
3019     }
3020 
3021     Q_FOREACH (const QRect &rc, rects) {
3022         renderMirrorMask(rc, dab);
3023     }
3024 }
3025 
hasDirtyRegion() const3026 bool KisPainter::hasDirtyRegion() const
3027 {
3028     return !d->dirtyRects.isEmpty();
3029 }
3030 
mirrorRect(Qt::Orientation direction,QRect * rc) const3031 void KisPainter::mirrorRect(Qt::Orientation direction, QRect *rc) const
3032 {
3033     KisLodTransform t(d->device);
3034     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
3035 
3036     KritaUtils::mirrorRect(direction, effectiveAxesCenter, rc);
3037 }
3038 
mirrorDab(Qt::Orientation direction,KisRenderedDab * dab,bool skipMirrorPixels) const3039 void KisPainter::mirrorDab(Qt::Orientation direction, KisRenderedDab *dab, bool skipMirrorPixels) const
3040 {
3041     KisLodTransform t(d->device);
3042     QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
3043 
3044     KritaUtils::mirrorDab(direction, effectiveAxesCenter, dab, skipMirrorPixels);
3045 }
3046 
3047 namespace {
3048 
mirrorOneObject(Qt::Orientation dir,const QPoint & center,QRect * rc)3049 inline void mirrorOneObject(Qt::Orientation dir, const QPoint &center, QRect *rc) {
3050     KritaUtils::mirrorRect(dir, center, rc);
3051 }
3052 
mirrorOneObject(Qt::Orientation dir,const QPoint & center,QPointF * pt)3053 inline void mirrorOneObject(Qt::Orientation dir, const QPoint &center, QPointF *pt) {
3054     KritaUtils::mirrorPoint(dir, center, pt);
3055 }
3056 
mirrorOneObject(Qt::Orientation dir,const QPoint & center,QPair<QPointF,QPointF> * pair)3057 inline void mirrorOneObject(Qt::Orientation dir, const QPoint &center, QPair<QPointF, QPointF> *pair) {
3058     KritaUtils::mirrorPoint(dir, center, &pair->first);
3059     KritaUtils::mirrorPoint(dir, center, &pair->second);
3060 }
3061 }
3062 
calculateMirroredObjects(const T & object)3063 template<class T> QVector<T> KisPainter::Private::calculateMirroredObjects(const T &object)
3064 {
3065     QVector<T> result;
3066 
3067     KisLodTransform t(this->device);
3068     const QPoint effectiveAxesCenter = t.map(this->axesCenter).toPoint();
3069 
3070     T baseObject = object;
3071     result << baseObject;
3072 
3073     if (this->mirrorHorizontally && this->mirrorVertically){
3074         mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
3075         result << baseObject;
3076         mirrorOneObject(Qt::Vertical, effectiveAxesCenter, &baseObject);
3077         result << baseObject;
3078         mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
3079         result << baseObject;
3080     } else if (this->mirrorHorizontally) {
3081         mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
3082         result << baseObject;
3083     } else if (this->mirrorVertically) {
3084         mirrorOneObject(Qt::Vertical, effectiveAxesCenter, &baseObject);
3085         result << baseObject;
3086     }
3087 
3088     return result;
3089 }
3090 
calculateAllMirroredRects(const QRect & rc)3091 const QVector<QRect> KisPainter::calculateAllMirroredRects(const QRect &rc)
3092 {
3093     return d->calculateMirroredObjects(rc);
3094 }
3095 
calculateAllMirroredPoints(const QPointF & pos)3096 const QVector<QPointF> KisPainter::calculateAllMirroredPoints(const QPointF &pos)
3097 {
3098     return d->calculateMirroredObjects(pos);
3099 }
3100 
calculateAllMirroredPoints(const QPair<QPointF,QPointF> & pair)3101 const QVector<QPair<QPointF, QPointF>> KisPainter::calculateAllMirroredPoints(const QPair<QPointF, QPointF> &pair)
3102 {
3103     return d->calculateMirroredObjects(pair);
3104 }
3105