Krita Source Code Documentation
Loading...
Searching...
No Matches
KisOverlayPaintDeviceWrapper.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include "kis_paint_device.h"
10#include <KoColor.h>
11#include "KisRectsGrid.h"
12#include "kis_painter.h"
15#include "KisRegion.h"
16#include "kis_wrapped_rect.h"
18#include <kundo2command.h>
19#include <memory>
20#include <kis_transaction.h>
21#include "kis_command_utils.h"
22#include "KoColorProfile.h"
23
25
40
65
67 : m_d(new Private())
68{
69 m_d->source = source;
70
72
73 if (forcedOverlayColorSpace) {
74 overlayColorSpace = forcedOverlayColorSpace;
75 } else {
76 const bool usePreciseMode = mode == PreciseMode || mode == LazyPreciseMode;
77
78 if (usePreciseMode &&
80
86 }
87 }
88
89 m_d->usePreciseMode = *source->colorSpace() != *overlayColorSpace;
90
96
98
99 } else if (source->colorSpace()->colorModelId() == CMYKAColorModelID &&
104
106
107 } else if (source->colorSpace()->colorModelId() == YCbCrAColorModelID &&
112
114
115 }
116
117 if (!m_d->usePreciseMode && mode == LazyPreciseMode && numOverlays == 1) {
118 return;
119 }
120
121 for (int i = 0; i < numOverlays; i++) {
127
128 m_d->overlays.append(overlay);
129 }
130}
131
135
137{
138 m_d->externalDestination = device;
139}
140
142{
143 return m_d->externalDestination;
144}
145
147{
148 return m_d->source;
149}
150
152{
153 return !m_d->overlays.isEmpty() ? m_d->overlays[index] : m_d->source;
154}
155
157{
158 readRects({rc});
159}
160
161void KisOverlayPaintDeviceWrapper::writeRect(const QRect &rc, int index)
162{
163 writeRects({rc}, index);
164}
165
167{
168 if (rects.isEmpty()) return;
169 if (m_d->overlays.isEmpty()) return;
170
171 QRect cropRect = m_d->source->extent();
172 QVector<QRect> rectsToRead;
173
174 Q_FOREACH (const QRect &rc, rects) {
175 if (m_d->source->defaultBounds()->wrapAroundMode()) {
176 const QRect wrapRect = m_d->source->defaultBounds()->imageBorderRect();
177 KisWrappedRect wrappedRect(rc, wrapRect, m_d->source->defaultBounds()->wrapAroundModeAxis());
178 Q_FOREACH (const QRect &wrc, wrappedRect) {
179 rectsToRead += m_d->grid.addRect(wrc);
180 }
181 cropRect &= wrapRect;
182 } else {
183 rectsToRead += m_d->grid.addRect(rc);
184 }
185 }
186
188
189 //TODO: implement synchronization of the offset between the grid and devices
190
191 if (!m_d->scaler) {
192 Q_FOREACH (KisPaintDeviceSP overlay, m_d->overlays) {
193 Q_FOREACH (const QRect &rect, rectsToRead) {
194 const QRect croppedRect = rect & cropRect;
195 if (croppedRect.isEmpty()) continue;
196
197 KisPainter::copyAreaOptimized(croppedRect.topLeft(), m_d->source, overlay, croppedRect);
198 }
199 }
200 } else {
201 KisPaintDeviceSP overlay = m_d->overlays.first();
202
203 KisRandomConstAccessorSP srcIt = m_d->source->createRandomConstAccessorNG();
205
206 auto rectIter = rectsToRead.begin();
207 while (rectIter != rectsToRead.end()) {
208 const QRect croppedRect = *rectIter & cropRect;
209
210 if (!croppedRect.isEmpty()) {
211
213 srcIt, dstIt,
214 [this] (const quint8 *src, int srcRowStride,
215 quint8 *dst, int dstRowStride,
216 int numRows, int numColumns) {
217
218 m_d->scaler->convertU8ToU16(src, srcRowStride,
219 dst, dstRowStride,
220 numRows, numColumns);
221 });
222
223 for (auto it = std::next(m_d->overlays.begin()); it != m_d->overlays.end(); ++it) {
224 KisPaintDeviceSP otherOverlay = *it;
225 KisPainter::copyAreaOptimized(croppedRect.topLeft(), overlay, otherOverlay, croppedRect);
226 }
227 }
228
229 rectIter++;
230 }
231 }
232}
233
235{
236 if (rects.isEmpty()) return;
237 if (m_d->overlays.isEmpty()) return;
238
239 KisPaintDeviceSP destinationDevice =
240 m_d->externalDestination ? m_d->externalDestination : m_d->source;
241
242 if (!m_d->scaler ||
243 (destinationDevice != m_d->source &&
244 *destinationDevice->colorSpace() != *m_d->source->colorSpace())) {
245
246 Q_FOREACH (const QRect &rc, rects) {
247 KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->grid.contains(rc));
248 KisPainter::copyAreaOptimized(rc.topLeft(), m_d->overlays[index], destinationDevice, rc);
249 }
250 } else {
251 KisPaintDeviceSP overlay = m_d->overlays[index];
252
253
255 KisRandomAccessorSP dstIt = destinationDevice->createRandomAccessorNG();
256
257 Q_FOREACH (const QRect &rc, rects) {
259 srcIt, dstIt,
260 [this] (const quint8 *src, int srcRowStride,
261 quint8 *dst, int dstRowStride,
262 int numRows, int numColumns) {
263
264 m_d->scaler->convertU16ToU8(src, srcRowStride,
265 dst, dstRowStride,
266 numRows, numColumns);
267 });
268 }
269 }
270}
271
273{
274 return !m_d->overlays.isEmpty() ? m_d->overlays.first()->colorSpace() : m_d->source->colorSpace();
275}
276
278{
284 KisPaintDeviceSP result;
285
286 if (!m_d->usePreciseMode) {
288 } else {
289 const KoColorSpace *compositionColorSpace =
290 m_d->source->compositionSourceColorSpace();
291
292 const KoColorSpace *preciseCompositionColorSpace =
294 compositionColorSpace->colorModelId().id(),
296 compositionColorSpace->profile());
297
298 KisPaintDeviceSP device = new KisPaintDevice(preciseCompositionColorSpace);
299 device->setDefaultBounds(m_d->source->defaultBounds());
300 result = device;
301 }
302
303 return result;
304}
305
307{
308 KIS_SAFE_ASSERT_RECOVER(!m_d->rootTransactionData) {
309 m_d->rootTransactionData.reset();
310 }
311
312 if (!m_d->previousGrid) {
313 m_d->previousGrid.reset(new KisRectsGrid(m_d->grid));
314 }
315
316 m_d->rootTransactionData.reset(new KUndo2Command(parent));
317
318 m_d->changeOverlayCommand = new KisChangeOverlayWrapperCommand(m_d.data());
319 (void) new KisCommandUtils::SkipFirstRedoWrapper(m_d->changeOverlayCommand, m_d->rootTransactionData.data());
320 m_d->changeOverlayCommand->m_oldRectsGrid = m_d->previousGrid;
321
322 for (const auto &overlayDevice : m_d->overlays) {
323 m_d->overlayTransactions.emplace_back(new KisTransaction(overlayDevice, m_d->rootTransactionData.data()));
324 }
325}
326
328{
329 KUndo2Command *result = nullptr;
330
331 KIS_SAFE_ASSERT_RECOVER(m_d->rootTransactionData) {
332 m_d->overlayTransactions.clear();
333 return result;
334 }
335
336 m_d->previousGrid.reset(new KisRectsGrid(m_d->grid));
337
338 m_d->changeOverlayCommand->m_newRectsGrid = m_d->previousGrid;
339 result = m_d->rootTransactionData.take();
340
341 for (auto &transactionPtr : m_d->overlayTransactions) {
342 // the transactions are assigned as children to m_d->changeOverlayCommand
343 (void) transactionPtr->endAndTake();
344 }
345 m_d->overlayTransactions.clear();
346
347 return result;
348}
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
const KoID YCbCrAColorModelID("YCbCrA", ki18n("YCbCr/Alpha"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID CMYKAColorModelID("CMYKA", ki18n("CMYK/Alpha"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
virtual void undo()
KUndo2CommandPrivate * d
Definition kundo2stack.h:88
virtual void redo()
void beginTransaction(KUndo2Command *parent=0)
void writeRects(const QVector< QRect > &rects, int index=0)
void setExternalDestination(KisPaintDeviceSP device)
KisPaintDeviceSP overlay(int index=0) const
KisOverlayPaintDeviceWrapper(KisPaintDeviceSP source, int numOverlays=1, OverlayMode mode=NormalMode, const KoColorSpace *forcedOverlayColorSpace=nullptr)
const QScopedPointer< Private > m_d
void readRects(const QVector< QRect > &rects)
const KoColorSpace * overlayColorSpace() const
void writeRect(const QRect &rc, int index=0)
bool supportsWraproundMode() const
void setDefaultPixel(const KoColor &defPixel)
void setDefaultBounds(KisDefaultBoundsBaseSP bounds)
KisPaintDeviceSP createCompositionSourceDevice() const
virtual const KoColorSpace * compositionSourceColorSpace() const
KisRandomConstAccessorSP createRandomConstAccessorNG() const
QRect extent() const
const KoColorSpace * colorSpace() const
void setSupportsWraparoundMode(bool value)
KoColor defaultPixel() const
KisDefaultBoundsBaseSP defaultBounds() const
void moveTo(qint32 x, qint32 y)
QPoint offset() const
KisRandomAccessorSP createRandomAccessorNG()
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
A utility class to maintain a sparse grid of loaded/unloaded rects.
static void makeGridLikeRectsUnique(QVector< QRect > &rects)
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
virtual const KoColorProfile * profile() const =0
KoColor convertedTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
Definition KoColor.cpp:163
QString id() const
Definition KoID.cpp:63
static KoOptimizedPixelDataScalerU8ToU16Base * createCmykaScaler()
static KoOptimizedPixelDataScalerU8ToU16Base * createRgbaScaler()
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
void processTwoDevicesWithStrides(const QRect &rc, KisRandomConstAccessorSP srcIt, KisRandomAccessorSP dstIt, PixelProcessor pixelProcessor)
KisOverlayPaintDeviceWrapper::Private * m_d
QSharedPointer< KisRectsGrid > m_oldRectsGrid
KisChangeOverlayWrapperCommand(KisOverlayPaintDeviceWrapper::Private *d, KUndo2Command *parent=0)
QSharedPointer< KisRectsGrid > m_newRectsGrid
KisChangeOverlayWrapperCommand * changeOverlayCommand
QScopedPointer< KoOptimizedPixelDataScalerU8ToU16Base > scaler
QScopedPointer< KUndo2Command > rootTransactionData
std::vector< std::unique_ptr< KisTransaction > > overlayTransactions
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()