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