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