File indexing completed on 2024-05-12 15:58:52
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->moveTo(source->offset()); 0126 0127 m_d->overlays.append(overlay); 0128 } 0129 } 0130 0131 KisOverlayPaintDeviceWrapper::~KisOverlayPaintDeviceWrapper() 0132 { 0133 } 0134 0135 void KisOverlayPaintDeviceWrapper::setExternalDestination(KisPaintDeviceSP device) 0136 { 0137 m_d->externalDestination = device; 0138 } 0139 0140 KisPaintDeviceSP KisOverlayPaintDeviceWrapper::externalDestination() const 0141 { 0142 return m_d->externalDestination; 0143 } 0144 0145 KisPaintDeviceSP KisOverlayPaintDeviceWrapper::source() const 0146 { 0147 return m_d->source; 0148 } 0149 0150 KisPaintDeviceSP KisOverlayPaintDeviceWrapper::overlay(int index) const 0151 { 0152 return !m_d->overlays.isEmpty() ? m_d->overlays[index] : m_d->source; 0153 } 0154 0155 void KisOverlayPaintDeviceWrapper::readRect(const QRect &rc) 0156 { 0157 readRects({rc}); 0158 } 0159 0160 void KisOverlayPaintDeviceWrapper::writeRect(const QRect &rc, int index) 0161 { 0162 writeRects({rc}, index); 0163 } 0164 0165 void KisOverlayPaintDeviceWrapper::readRects(const QVector<QRect> &rects) 0166 { 0167 if (rects.isEmpty()) return; 0168 if (m_d->overlays.isEmpty()) return; 0169 0170 QRect cropRect = m_d->source->extent(); 0171 QVector<QRect> rectsToRead; 0172 0173 Q_FOREACH (const QRect &rc, rects) { 0174 if (m_d->source->defaultBounds()->wrapAroundMode()) { 0175 const QRect wrapRect = m_d->source->defaultBounds()->imageBorderRect(); 0176 KisWrappedRect wrappedRect(rc, wrapRect); 0177 Q_FOREACH (const QRect &wrc, wrappedRect) { 0178 rectsToRead += m_d->grid.addRect(wrc); 0179 } 0180 cropRect &= wrapRect; 0181 } else { 0182 rectsToRead += m_d->grid.addRect(rc); 0183 } 0184 } 0185 0186 KisRegion::makeGridLikeRectsUnique(rectsToRead); 0187 0188 //TODO: implement synchronization of the offset between the grid and devices 0189 0190 if (!m_d->scaler) { 0191 Q_FOREACH (KisPaintDeviceSP overlay, m_d->overlays) { 0192 Q_FOREACH (const QRect &rect, rectsToRead) { 0193 const QRect croppedRect = rect & cropRect; 0194 if (croppedRect.isEmpty()) continue; 0195 0196 KisPainter::copyAreaOptimized(croppedRect.topLeft(), m_d->source, overlay, croppedRect); 0197 } 0198 } 0199 } else { 0200 KisPaintDeviceSP overlay = m_d->overlays.first(); 0201 0202 KisRandomConstAccessorSP srcIt = m_d->source->createRandomConstAccessorNG(); 0203 KisRandomAccessorSP dstIt = overlay->createRandomAccessorNG(); 0204 0205 auto rectIter = rectsToRead.begin(); 0206 while (rectIter != rectsToRead.end()) { 0207 const QRect croppedRect = *rectIter & cropRect; 0208 0209 if (!croppedRect.isEmpty()) { 0210 0211 KritaUtils::processTwoDevicesWithStrides(croppedRect, 0212 srcIt, dstIt, 0213 [this] (const quint8 *src, int srcRowStride, 0214 quint8 *dst, int dstRowStride, 0215 int numRows, int numColumns) { 0216 0217 m_d->scaler->convertU8ToU16(src, srcRowStride, 0218 dst, dstRowStride, 0219 numRows, numColumns); 0220 }); 0221 0222 for (auto it = std::next(m_d->overlays.begin()); it != m_d->overlays.end(); ++it) { 0223 KisPaintDeviceSP otherOverlay = *it; 0224 KisPainter::copyAreaOptimized(croppedRect.topLeft(), overlay, otherOverlay, croppedRect); 0225 } 0226 } 0227 0228 rectIter++; 0229 } 0230 } 0231 } 0232 0233 void KisOverlayPaintDeviceWrapper::writeRects(const QVector<QRect> &rects, int index) 0234 { 0235 if (rects.isEmpty()) return; 0236 if (m_d->overlays.isEmpty()) return; 0237 0238 KisPaintDeviceSP destinationDevice = 0239 m_d->externalDestination ? m_d->externalDestination : m_d->source; 0240 0241 if (!m_d->scaler || 0242 (destinationDevice != m_d->source && 0243 *destinationDevice->colorSpace() != *m_d->source->colorSpace())) { 0244 0245 Q_FOREACH (const QRect &rc, rects) { 0246 KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->grid.contains(rc)); 0247 KisPainter::copyAreaOptimized(rc.topLeft(), m_d->overlays[index], destinationDevice, rc); 0248 } 0249 } else { 0250 KisPaintDeviceSP overlay = m_d->overlays[index]; 0251 0252 0253 KisRandomConstAccessorSP srcIt = overlay->createRandomConstAccessorNG(); 0254 KisRandomAccessorSP dstIt = destinationDevice->createRandomAccessorNG(); 0255 0256 Q_FOREACH (const QRect &rc, rects) { 0257 KritaUtils::processTwoDevicesWithStrides(rc, 0258 srcIt, dstIt, 0259 [this] (const quint8 *src, int srcRowStride, 0260 quint8 *dst, int dstRowStride, 0261 int numRows, int numColumns) { 0262 0263 m_d->scaler->convertU16ToU8(src, srcRowStride, 0264 dst, dstRowStride, 0265 numRows, numColumns); 0266 }); 0267 } 0268 } 0269 } 0270 0271 const KoColorSpace *KisOverlayPaintDeviceWrapper::overlayColorSpace() const 0272 { 0273 return !m_d->overlays.isEmpty() ? m_d->overlays.first()->colorSpace() : m_d->source->colorSpace(); 0274 } 0275 0276 KisPaintDeviceSP KisOverlayPaintDeviceWrapper::createPreciseCompositionSourceDevice() 0277 { 0278 /** 0279 * TODO: this funciton has rather vague meaning when forcedOverlayColorSpace 0280 * feature is used 0281 */ 0282 0283 KisPaintDeviceSP result; 0284 0285 if (!m_d->usePreciseMode) { 0286 result = source()->createCompositionSourceDevice(); 0287 } else { 0288 const KoColorSpace *compositionColorSpace = 0289 m_d->source->compositionSourceColorSpace(); 0290 0291 const KoColorSpace *preciseCompositionColorSpace = 0292 KoColorSpaceRegistry::instance()->colorSpace( 0293 compositionColorSpace->colorModelId().id(), 0294 Integer16BitsColorDepthID.id(), 0295 compositionColorSpace->profile()); 0296 0297 KisPaintDeviceSP device = new KisPaintDevice(preciseCompositionColorSpace); 0298 device->setDefaultBounds(m_d->source->defaultBounds()); 0299 result = device; 0300 } 0301 0302 return result; 0303 } 0304 0305 void KisOverlayPaintDeviceWrapper::beginTransaction(KUndo2Command *parent) 0306 { 0307 KIS_SAFE_ASSERT_RECOVER(!m_d->rootTransactionData) { 0308 m_d->rootTransactionData.reset(); 0309 } 0310 0311 if (!m_d->previousGrid) { 0312 m_d->previousGrid.reset(new KisRectsGrid(m_d->grid)); 0313 } 0314 0315 m_d->rootTransactionData.reset(new KUndo2Command(parent)); 0316 0317 m_d->changeOverlayCommand = new KisChangeOverlayWrapperCommand(m_d.data()); 0318 (void) new KisCommandUtils::SkipFirstRedoWrapper(m_d->changeOverlayCommand, m_d->rootTransactionData.data()); 0319 m_d->changeOverlayCommand->m_oldRectsGrid = m_d->previousGrid; 0320 0321 for (const auto &overlayDevice : m_d->overlays) { 0322 m_d->overlayTransactions.emplace_back(new KisTransaction(overlayDevice, m_d->rootTransactionData.data())); 0323 } 0324 } 0325 0326 KUndo2Command *KisOverlayPaintDeviceWrapper::endTransaction() 0327 { 0328 KUndo2Command *result = nullptr; 0329 0330 KIS_SAFE_ASSERT_RECOVER(m_d->rootTransactionData) { 0331 m_d->overlayTransactions.clear(); 0332 return result; 0333 } 0334 0335 m_d->previousGrid.reset(new KisRectsGrid(m_d->grid)); 0336 0337 m_d->changeOverlayCommand->m_newRectsGrid = m_d->previousGrid; 0338 result = m_d->rootTransactionData.take(); 0339 0340 for (auto &transactionPtr : m_d->overlayTransactions) { 0341 // the transactions are assigned as children to m_d->changeOverlayCommand 0342 (void) transactionPtr->endAndTake(); 0343 } 0344 m_d->overlayTransactions.clear(); 0345 0346 return result; 0347 }