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 ¢er, 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 ¢er, 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 ¢er, 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