File indexing completed on 2024-05-19 04:26:43
0001 /* 0002 * SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisOverlayPaintDeviceWrapper.h" 0008 0009 #include "kis_paint_device.h" 0010 #include <KoColor.h> 0011 #include "KisRectsGrid.h" 0012 #include "kis_painter.h" 0013 #include "KoColorModelStandardIds.h" 0014 #include "KisFastDeviceProcessingUtils.h" 0015 #include "KisRegion.h" 0016 #include "kis_wrapped_rect.h" 0017 #include <KoOptimizedPixelDataScalerU8ToU16Factory.h> 0018 #include <kundo2command.h> 0019 #include <memory> 0020 #include <kis_transaction.h> 0021 #include "kis_command_utils.h" 0022 #include "KoColorProfile.h" 0023 0024 struct KisChangeOverlayWrapperCommand; 0025 0026 struct KisOverlayPaintDeviceWrapper::Private 0027 { 0028 KisPaintDeviceSP source; 0029 QVector<KisPaintDeviceSP> overlays; 0030 KisRectsGrid grid; 0031 bool usePreciseMode = false; 0032 QScopedPointer<KoOptimizedPixelDataScalerU8ToU16Base> scaler; 0033 KisPaintDeviceSP externalDestination; 0034 0035 QScopedPointer<KUndo2Command> rootTransactionData; 0036 KisChangeOverlayWrapperCommand *changeOverlayCommand; 0037 std::vector<std::unique_ptr<KisTransaction>> overlayTransactions; 0038 QSharedPointer<KisRectsGrid> previousGrid; 0039 }; 0040 0041 struct KisChangeOverlayWrapperCommand : public KUndo2Command 0042 { 0043 KisChangeOverlayWrapperCommand(KisOverlayPaintDeviceWrapper::Private *d, KUndo2Command *parent = 0) 0044 : KUndo2Command(parent) 0045 , m_d(d) 0046 { 0047 } 0048 0049 void undo() override { 0050 KUndo2Command::undo(); 0051 m_d->grid = *m_oldRectsGrid; 0052 m_d->previousGrid = m_oldRectsGrid; 0053 } 0054 0055 void redo() override { 0056 KUndo2Command::redo(); 0057 m_d->grid = *m_newRectsGrid; 0058 m_d->previousGrid = m_newRectsGrid; 0059 } 0060 0061 QSharedPointer<KisRectsGrid> m_oldRectsGrid; 0062 QSharedPointer<KisRectsGrid> m_newRectsGrid; 0063 KisOverlayPaintDeviceWrapper::Private *m_d; 0064 }; 0065 0066 KisOverlayPaintDeviceWrapper::KisOverlayPaintDeviceWrapper(KisPaintDeviceSP source, int numOverlays, KisOverlayPaintDeviceWrapper::OverlayMode mode, const KoColorSpace *forcedOverlayColorSpace) 0067 : m_d(new Private()) 0068 { 0069 m_d->source = source; 0070 0071 const KoColorSpace *overlayColorSpace = source->compositionSourceColorSpace(); 0072 0073 if (forcedOverlayColorSpace) { 0074 overlayColorSpace = forcedOverlayColorSpace; 0075 } else { 0076 const bool usePreciseMode = mode == PreciseMode || mode == LazyPreciseMode; 0077 0078 if (usePreciseMode && 0079 overlayColorSpace->colorDepthId() == Integer8BitsColorDepthID) { 0080 0081 overlayColorSpace = 0082 KoColorSpaceRegistry::instance()->colorSpace( 0083 overlayColorSpace->colorModelId().id(), 0084 Integer16BitsColorDepthID.id(), 0085 overlayColorSpace->profile()); 0086 } 0087 } 0088 0089 m_d->usePreciseMode = *source->colorSpace() != *overlayColorSpace; 0090 0091 if (source->colorSpace()->colorModelId() == RGBAColorModelID && 0092 source->colorSpace()->colorDepthId() == Integer8BitsColorDepthID && 0093 overlayColorSpace->colorModelId() == RGBAColorModelID && 0094 overlayColorSpace->colorDepthId() == Integer16BitsColorDepthID && 0095 *source->colorSpace()->profile() == *overlayColorSpace->profile()) { 0096 0097 m_d->scaler.reset(KoOptimizedPixelDataScalerU8ToU16Factory::createRgbaScaler()); 0098 0099 } else if (source->colorSpace()->colorModelId() == CMYKAColorModelID && 0100 source->colorSpace()->colorDepthId() == Integer8BitsColorDepthID && 0101 overlayColorSpace->colorModelId() == CMYKAColorModelID && 0102 overlayColorSpace->colorDepthId() == Integer16BitsColorDepthID && 0103 *source->colorSpace()->profile() == *overlayColorSpace->profile()) { 0104 0105 m_d->scaler.reset(KoOptimizedPixelDataScalerU8ToU16Factory::createCmykaScaler()); 0106 0107 } else if (source->colorSpace()->colorModelId() == YCbCrAColorModelID && 0108 source->colorSpace()->colorDepthId() == Integer8BitsColorDepthID && 0109 overlayColorSpace->colorModelId() == YCbCrAColorModelID && 0110 overlayColorSpace->colorDepthId() == Integer16BitsColorDepthID && 0111 *source->colorSpace()->profile() == *overlayColorSpace->profile()) { 0112 0113 m_d->scaler.reset(KoOptimizedPixelDataScalerU8ToU16Factory::createRgbaScaler()); 0114 0115 } 0116 0117 if (!m_d->usePreciseMode && mode == LazyPreciseMode && numOverlays == 1) { 0118 return; 0119 } 0120 0121 for (int i = 0; i < numOverlays; i++) { 0122 KisPaintDeviceSP overlay = new KisPaintDevice(overlayColorSpace); 0123 overlay->setDefaultPixel(source->defaultPixel().convertedTo(overlayColorSpace)); 0124 overlay->setDefaultBounds(source->defaultBounds()); 0125 overlay->setSupportsWraparoundMode(source->supportsWraproundMode()); 0126 overlay->moveTo(source->offset()); 0127 0128 m_d->overlays.append(overlay); 0129 } 0130 } 0131 0132 KisOverlayPaintDeviceWrapper::~KisOverlayPaintDeviceWrapper() 0133 { 0134 } 0135 0136 void KisOverlayPaintDeviceWrapper::setExternalDestination(KisPaintDeviceSP device) 0137 { 0138 m_d->externalDestination = device; 0139 } 0140 0141 KisPaintDeviceSP KisOverlayPaintDeviceWrapper::externalDestination() const 0142 { 0143 return m_d->externalDestination; 0144 } 0145 0146 KisPaintDeviceSP KisOverlayPaintDeviceWrapper::source() const 0147 { 0148 return m_d->source; 0149 } 0150 0151 KisPaintDeviceSP KisOverlayPaintDeviceWrapper::overlay(int index) const 0152 { 0153 return !m_d->overlays.isEmpty() ? m_d->overlays[index] : m_d->source; 0154 } 0155 0156 void KisOverlayPaintDeviceWrapper::readRect(const QRect &rc) 0157 { 0158 readRects({rc}); 0159 } 0160 0161 void KisOverlayPaintDeviceWrapper::writeRect(const QRect &rc, int index) 0162 { 0163 writeRects({rc}, index); 0164 } 0165 0166 void KisOverlayPaintDeviceWrapper::readRects(const QVector<QRect> &rects) 0167 { 0168 if (rects.isEmpty()) return; 0169 if (m_d->overlays.isEmpty()) return; 0170 0171 QRect cropRect = m_d->source->extent(); 0172 QVector<QRect> rectsToRead; 0173 0174 Q_FOREACH (const QRect &rc, rects) { 0175 if (m_d->source->defaultBounds()->wrapAroundMode()) { 0176 const QRect wrapRect = m_d->source->defaultBounds()->imageBorderRect(); 0177 KisWrappedRect wrappedRect(rc, wrapRect, m_d->source->defaultBounds()->wrapAroundModeAxis()); 0178 Q_FOREACH (const QRect &wrc, wrappedRect) { 0179 rectsToRead += m_d->grid.addRect(wrc); 0180 } 0181 cropRect &= wrapRect; 0182 } else { 0183 rectsToRead += m_d->grid.addRect(rc); 0184 } 0185 } 0186 0187 KisRegion::makeGridLikeRectsUnique(rectsToRead); 0188 0189 //TODO: implement synchronization of the offset between the grid and devices 0190 0191 if (!m_d->scaler) { 0192 Q_FOREACH (KisPaintDeviceSP overlay, m_d->overlays) { 0193 Q_FOREACH (const QRect &rect, rectsToRead) { 0194 const QRect croppedRect = rect & cropRect; 0195 if (croppedRect.isEmpty()) continue; 0196 0197 KisPainter::copyAreaOptimized(croppedRect.topLeft(), m_d->source, overlay, croppedRect); 0198 } 0199 } 0200 } else { 0201 KisPaintDeviceSP overlay = m_d->overlays.first(); 0202 0203 KisRandomConstAccessorSP srcIt = m_d->source->createRandomConstAccessorNG(); 0204 KisRandomAccessorSP dstIt = overlay->createRandomAccessorNG(); 0205 0206 auto rectIter = rectsToRead.begin(); 0207 while (rectIter != rectsToRead.end()) { 0208 const QRect croppedRect = *rectIter & cropRect; 0209 0210 if (!croppedRect.isEmpty()) { 0211 0212 KritaUtils::processTwoDevicesWithStrides(croppedRect, 0213 srcIt, dstIt, 0214 [this] (const quint8 *src, int srcRowStride, 0215 quint8 *dst, int dstRowStride, 0216 int numRows, int numColumns) { 0217 0218 m_d->scaler->convertU8ToU16(src, srcRowStride, 0219 dst, dstRowStride, 0220 numRows, numColumns); 0221 }); 0222 0223 for (auto it = std::next(m_d->overlays.begin()); it != m_d->overlays.end(); ++it) { 0224 KisPaintDeviceSP otherOverlay = *it; 0225 KisPainter::copyAreaOptimized(croppedRect.topLeft(), overlay, otherOverlay, croppedRect); 0226 } 0227 } 0228 0229 rectIter++; 0230 } 0231 } 0232 } 0233 0234 void KisOverlayPaintDeviceWrapper::writeRects(const QVector<QRect> &rects, int index) 0235 { 0236 if (rects.isEmpty()) return; 0237 if (m_d->overlays.isEmpty()) return; 0238 0239 KisPaintDeviceSP destinationDevice = 0240 m_d->externalDestination ? m_d->externalDestination : m_d->source; 0241 0242 if (!m_d->scaler || 0243 (destinationDevice != m_d->source && 0244 *destinationDevice->colorSpace() != *m_d->source->colorSpace())) { 0245 0246 Q_FOREACH (const QRect &rc, rects) { 0247 KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->grid.contains(rc)); 0248 KisPainter::copyAreaOptimized(rc.topLeft(), m_d->overlays[index], destinationDevice, rc); 0249 } 0250 } else { 0251 KisPaintDeviceSP overlay = m_d->overlays[index]; 0252 0253 0254 KisRandomConstAccessorSP srcIt = overlay->createRandomConstAccessorNG(); 0255 KisRandomAccessorSP dstIt = destinationDevice->createRandomAccessorNG(); 0256 0257 Q_FOREACH (const QRect &rc, rects) { 0258 KritaUtils::processTwoDevicesWithStrides(rc, 0259 srcIt, dstIt, 0260 [this] (const quint8 *src, int srcRowStride, 0261 quint8 *dst, int dstRowStride, 0262 int numRows, int numColumns) { 0263 0264 m_d->scaler->convertU16ToU8(src, srcRowStride, 0265 dst, dstRowStride, 0266 numRows, numColumns); 0267 }); 0268 } 0269 } 0270 } 0271 0272 const KoColorSpace *KisOverlayPaintDeviceWrapper::overlayColorSpace() const 0273 { 0274 return !m_d->overlays.isEmpty() ? m_d->overlays.first()->colorSpace() : m_d->source->colorSpace(); 0275 } 0276 0277 KisPaintDeviceSP KisOverlayPaintDeviceWrapper::createPreciseCompositionSourceDevice() 0278 { 0279 /** 0280 * TODO: this function has rather vague meaning when forcedOverlayColorSpace 0281 * feature is used 0282 */ 0283 0284 KisPaintDeviceSP result; 0285 0286 if (!m_d->usePreciseMode) { 0287 result = source()->createCompositionSourceDevice(); 0288 } else { 0289 const KoColorSpace *compositionColorSpace = 0290 m_d->source->compositionSourceColorSpace(); 0291 0292 const KoColorSpace *preciseCompositionColorSpace = 0293 KoColorSpaceRegistry::instance()->colorSpace( 0294 compositionColorSpace->colorModelId().id(), 0295 Integer16BitsColorDepthID.id(), 0296 compositionColorSpace->profile()); 0297 0298 KisPaintDeviceSP device = new KisPaintDevice(preciseCompositionColorSpace); 0299 device->setDefaultBounds(m_d->source->defaultBounds()); 0300 result = device; 0301 } 0302 0303 return result; 0304 } 0305 0306 void KisOverlayPaintDeviceWrapper::beginTransaction(KUndo2Command *parent) 0307 { 0308 KIS_SAFE_ASSERT_RECOVER(!m_d->rootTransactionData) { 0309 m_d->rootTransactionData.reset(); 0310 } 0311 0312 if (!m_d->previousGrid) { 0313 m_d->previousGrid.reset(new KisRectsGrid(m_d->grid)); 0314 } 0315 0316 m_d->rootTransactionData.reset(new KUndo2Command(parent)); 0317 0318 m_d->changeOverlayCommand = new KisChangeOverlayWrapperCommand(m_d.data()); 0319 (void) new KisCommandUtils::SkipFirstRedoWrapper(m_d->changeOverlayCommand, m_d->rootTransactionData.data()); 0320 m_d->changeOverlayCommand->m_oldRectsGrid = m_d->previousGrid; 0321 0322 for (const auto &overlayDevice : m_d->overlays) { 0323 m_d->overlayTransactions.emplace_back(new KisTransaction(overlayDevice, m_d->rootTransactionData.data())); 0324 } 0325 } 0326 0327 KUndo2Command *KisOverlayPaintDeviceWrapper::endTransaction() 0328 { 0329 KUndo2Command *result = nullptr; 0330 0331 KIS_SAFE_ASSERT_RECOVER(m_d->rootTransactionData) { 0332 m_d->overlayTransactions.clear(); 0333 return result; 0334 } 0335 0336 m_d->previousGrid.reset(new KisRectsGrid(m_d->grid)); 0337 0338 m_d->changeOverlayCommand->m_newRectsGrid = m_d->previousGrid; 0339 result = m_d->rootTransactionData.take(); 0340 0341 for (auto &transactionPtr : m_d->overlayTransactions) { 0342 // the transactions are assigned as children to m_d->changeOverlayCommand 0343 (void) transactionPtr->endAndTake(); 0344 } 0345 m_d->overlayTransactions.clear(); 0346 0347 return result; 0348 }