Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_qmic_simple_convertor.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2013 Lukáš Tvrdý <lukast.dev@gmail.com>
3 * SPDX-FileCopyrightText: 2020-2022 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10#include <array>
11#include <cstddef>
12#include <map>
13#include <memory>
14#include <vector>
15
16#include <kis_debug.h>
18
20#include <KoColorSpace.h>
22#include <KoColorSpaceTraits.h>
24
25#define SCALE_TO_FLOAT(v) KoColorSpaceMaths<_channel_type_, float>::scaleToA(v)
26#define SCALE_FROM_FLOAT(v) \
27 KoColorSpaceMaths<float, _channel_type_>::scaleToA(v)
28
29using KoColorTransformationSP = std::shared_ptr<KoColorTransformation>;
30
31template<typename _channel_type_, typename traits>
33{
34 using RGBTrait = traits;
35 using RGBPixel = typename RGBTrait::Pixel;
36
37public:
38 KisColorToFloatConvertor(float gmicUnitValue = 255.0f)
39 : m_gmicUnitValue(gmicUnitValue)
40 {
41 }
42
44
45 void
46 transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
47 {
48 const float gmicUnitValue2KritaUnitValue =
50
51 const auto *srcPixel = reinterpret_cast<const RGBPixel *>(src);
52 auto *dstPixel = reinterpret_cast<KoRgbF32Traits::Pixel *>(dst);
53
54 while (nPixels > 0) {
55 dstPixel->red =
56 SCALE_TO_FLOAT(srcPixel->red) * gmicUnitValue2KritaUnitValue;
57 dstPixel->green =
58 SCALE_TO_FLOAT(srcPixel->green) * gmicUnitValue2KritaUnitValue;
59 dstPixel->blue =
60 SCALE_TO_FLOAT(srcPixel->blue) * gmicUnitValue2KritaUnitValue;
61 dstPixel->alpha =
62 SCALE_TO_FLOAT(srcPixel->alpha) * gmicUnitValue2KritaUnitValue;
63
64 --nPixels;
65 ++srcPixel;
66 ++dstPixel;
67 }
68 }
69};
70
71template<typename _channel_type_, typename traits>
73{
74 using RGBTrait = traits;
75 using RGBPixel = typename RGBTrait::Pixel;
76
77public:
78 KisColorFromFloat(float gmicUnitValue = 255.0f)
79 : m_gmicUnitValue(gmicUnitValue)
80 {
81 }
82
83 void
84 transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
85 {
86 const auto *srcPixel =
87 reinterpret_cast<const KoRgbF32Traits::Pixel *>(src);
88 auto *dstPixel = reinterpret_cast<RGBPixel *>(dst);
89
90 const float gmicUnitValue2KritaUnitValue =
92
93 while (nPixels > 0) {
94 dstPixel->red =
95 SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
96 dstPixel->green = SCALE_FROM_FLOAT(srcPixel->green
97 * gmicUnitValue2KritaUnitValue);
98 dstPixel->blue =
99 SCALE_FROM_FLOAT(srcPixel->blue * gmicUnitValue2KritaUnitValue);
100 dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->alpha
101 * gmicUnitValue2KritaUnitValue);
102
103 --nPixels;
104 ++srcPixel;
105 ++dstPixel;
106 }
107 }
108
109private:
111};
112
113template<typename _channel_type_, typename traits>
115{
116 using RGBTrait = traits;
117 using RGBPixel = typename RGBTrait::Pixel;
118
119public:
120 KisColorFromGrayScaleFloat(float gmicUnitValue = 255.0f)
121 : m_gmicUnitValue(gmicUnitValue)
122 {
123 }
124
125 void
126 transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
127 {
128 const auto *srcPixel =
129 reinterpret_cast<const KoRgbF32Traits::Pixel *>(src);
130 auto *dstPixel = reinterpret_cast<RGBPixel *>(dst);
131
132 const float gmicUnitValue2KritaUnitValue =
134 // warning: green and blue channels on input contain random data!!! see
135 // that we copy only one channel when gmic image has grayscale
136 // colorspace
137 while (nPixels > 0) {
138 dstPixel->red = dstPixel->green = dstPixel->blue =
139 SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
140 dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->alpha
141 * gmicUnitValue2KritaUnitValue);
142
143 --nPixels;
144 ++srcPixel;
145 ++dstPixel;
146 }
147 }
148
149private:
151};
152
153template<typename _channel_type_, typename traits>
155{
156 using RGBTrait = traits;
157 using RGBPixel = typename RGBTrait::Pixel;
158
159public:
160 KisColorFromGrayScaleAlphaFloat(float gmicUnitValue = 255.0f)
161 : m_gmicUnitValue(gmicUnitValue)
162 {
163 }
164
165 void
166 transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
167 {
168 const auto *srcPixel =
169 reinterpret_cast<const KoRgbF32Traits::Pixel *>(src);
170 auto *dstPixel = reinterpret_cast<RGBPixel *>(dst);
171
172 const float gmicUnitValue2KritaUnitValue =
174 // warning: green and blue channels on input contain random data!!! see
175 // that we copy only one channel when gmic image has grayscale
176 // colorspace
177 while (nPixels > 0) {
178 dstPixel->red = dstPixel->green = dstPixel->blue =
179 SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
180 dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->green
181 * gmicUnitValue2KritaUnitValue);
182
183 --nPixels;
184 ++srcPixel;
185 ++dstPixel;
186 }
187 }
188
189private:
191};
192
193const std::map<QString, QString> blendingModeMap = {
194 {"add", COMPOSITE_ADD},
195 // {"alpha", ""}, // XXX
196 {"and", COMPOSITE_AND},
197 // {"average", ""}, // XXX
198 {"blue", COMPOSITE_COPY_BLUE},
199 {"burn", COMPOSITE_BURN},
200 {"darken", COMPOSITE_DARKEN},
201 {"difference", COMPOSITE_DIFF},
202 {"divide", COMPOSITE_DIVIDE},
203 {"dodge", COMPOSITE_DODGE},
204 // {"edges", ""}, // XXX
205 {"exclusion", COMPOSITE_EXCLUSION},
206 {"freeze", COMPOSITE_FREEZE},
207 {"grainextract", COMPOSITE_GRAIN_EXTRACT},
208 {"grainmerge", COMPOSITE_GRAIN_MERGE},
209 {"green", COMPOSITE_COPY_GREEN},
210 {"hardlight", COMPOSITE_HARD_LIGHT},
211 {"hardmix", COMPOSITE_HARD_MIX},
212 {"hue", COMPOSITE_HUE},
213 {"interpolation", COMPOSITE_INTERPOLATION},
214 {"lighten", COMPOSITE_LIGHTEN},
215 {"lightness", COMPOSITE_LIGHTNESS},
216 {"linearburn", COMPOSITE_LINEAR_BURN},
217 {"linearlight", COMPOSITE_LINEAR_LIGHT},
218 {"luminance", COMPOSITE_LUMINIZE},
219 {"multiply", COMPOSITE_MULT},
220 {"negation", COMPOSITE_NEGATION},
221 {"or", COMPOSITE_OR},
222 {"overlay", COMPOSITE_OVERLAY},
223 {"pinlight", COMPOSITE_PIN_LIGHT},
224 {"red", COMPOSITE_COPY_RED},
225 {"reflect", COMPOSITE_REFLECT},
226 {"saturation", COMPOSITE_SATURATION},
227 // {"seamless", ""}, // XXX
228 // {"seamless_mixed", ""}, // XXX
229 {"screen", COMPOSITE_SCREEN},
230 // {"shapeareamax", ""}, // XXX
231 // {"shapeareamax0", ""}, // XXX
232 // {"shapeareamin", ""}, // XXX
233 // {"shapeareamin0", ""}, // XXX
234 // {"shapeaverage", ""}, // XXX
235 // {"shapeaverage0", ""}, // XXX
236 // {"shapemedian", ""}, // XXX
237 // {"shapemedian0", ""}, // XXX
238 // {"shapemin", ""}, // XXX
239 // {"shapemin0", ""}, // XXX
240 // {"shapemax", ""}, // XXX
241 // {"shapemax0", ""}, // XXX
242 // {"softburn", ""}, // XXX
243 // {"softdodge", ""}, // XXX
244 {"softlight", COMPOSITE_SOFT_LIGHT_SVG}, // XXX Is this correct?
245 // {"stamp", ""}, // XXX
246 {"subtract", COMPOSITE_SUBTRACT},
247 {"value", COMPOSITE_VALUE},
248 {"vividlight", COMPOSITE_VIVID_LIGHT},
249 {"xor", COMPOSITE_XOR}};
250
253 int gmicSpectrum,
254 float gmicUnitValue)
255{
256 KoColorTransformation *colorTransformation = nullptr;
257 if (colorSpace->colorModelId() != RGBAColorModelID) {
258 dbgKrita << "Unsupported color space for fast pixel transformation to "
259 "gmic pixel format"
260 << colorSpace->id();
261 return nullptr;
262 }
263
264 if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
265 if (gmicSpectrum == 3 || gmicSpectrum == 4) {
266 colorTransformation =
268 } else if (gmicSpectrum == 1) {
269 colorTransformation =
271 gmicUnitValue);
272 } else if (gmicSpectrum == 2) {
273 colorTransformation =
275 gmicUnitValue);
276 }
277 }
278#ifdef HAVE_OPENEXR
279 else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
280 if (gmicSpectrum == 3 || gmicSpectrum == 4) {
281 colorTransformation =
283 } else if (gmicSpectrum == 1) {
284 colorTransformation =
286 gmicUnitValue);
287 } else if (gmicSpectrum == 2) {
288 colorTransformation =
290 gmicUnitValue);
291 }
292 }
293#endif
294 else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
295 if (gmicSpectrum == 3 || gmicSpectrum == 4) {
296 colorTransformation =
298 gmicUnitValue);
299 } else if (gmicSpectrum == 1) {
300 colorTransformation =
302 gmicUnitValue);
303 } else if (gmicSpectrum == 2) {
304 colorTransformation =
307 gmicUnitValue);
308 }
309 } else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
310 if (gmicSpectrum == 3 || gmicSpectrum == 4) {
311 colorTransformation =
313 gmicUnitValue);
314 } else if (gmicSpectrum == 1) {
315 colorTransformation =
317 gmicUnitValue);
318 } else if (gmicSpectrum == 2) {
319 colorTransformation =
322 gmicUnitValue);
323 }
324 } else {
325 dbgKrita << "Unsupported color space " << colorSpace->id()
326 << " for fast pixel transformation to gmic pixel format";
327 return nullptr;
328 }
329
330 return colorTransformation;
331}
332
335{
336 KoColorTransformation *colorTransformation = nullptr;
337 if (colorSpace->colorModelId() != RGBAColorModelID) {
338 dbgKrita << "Unsupported color space for fast pixel transformation to "
339 "gmic pixel format"
340 << colorSpace->id();
341 return nullptr;
342 }
343
344 if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
345 colorTransformation =
347 }
348#ifdef HAVE_OPENEXR
349 else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
350 colorTransformation =
352 }
353#endif
354 else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
355 colorTransformation =
357 } else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
358 colorTransformation =
360 } else {
361 dbgKrita << "Unsupported color space " << colorSpace->id()
362 << " for fast pixel transformation to gmic pixel format";
363 return nullptr;
364 }
365 return colorTransformation;
366}
367
370 float gmicUnitValue)
371{
372 dbgPlugins << "convertFromGmicFast";
373 const KoColorSpace *dstColorSpace = dst->colorSpace();
374 const KoColorTransformationSP gmicToDstPixelFormat(
375 createTransformationFromGmic(dstColorSpace,
376 gmicImage.m_spectrum,
377 gmicUnitValue));
378 if (!gmicToDstPixelFormat) {
379 dbgPlugins << "Fall-back to slow color conversion";
380 convertFromGmicImage(gmicImage, dst, gmicUnitValue);
381 return;
382 }
383
384 dst->clear();
385 dst->moveTo(0, 0);
386
387 const qint32 x = 0;
388 const qint32 y = 0;
389 const auto width = static_cast<size_t>(gmicImage.m_width);
390 const auto height = static_cast<size_t>(gmicImage.m_height);
391
392 const auto *rgbaFloat32bitcolorSpace =
397 // this function always convert to rgba or rgb with various color depth
398 const quint32 dstNumChannels = rgbaFloat32bitcolorSpace->channelCount();
399
400 // number of channels that we will copy
401 const int numChannels = gmicImage.m_spectrum;
402
403 // gmic image has 4, 3, 2, 1 channel
404 std::vector<float *> planes(dstNumChannels);
405 const size_t channelOffset = width * height;
406 for (int channelIndex = 0; channelIndex < gmicImage.m_spectrum;
407 channelIndex++) {
408 planes[channelIndex] = gmicImage.m_data + channelOffset * channelIndex;
409 }
410
411 for (int channelIndex = gmicImage.m_spectrum;
412 channelIndex < (int)dstNumChannels;
413 channelIndex++) {
414 planes[channelIndex] = 0; // turn off
415 }
416
417 size_t dataY = 0;
418 int imageY = y;
419 size_t rowsRemaining = height;
420
421 const auto floatPixelSize = rgbaFloat32bitcolorSpace->pixelSize();
422
424 const auto tileWidth =
425 static_cast<size_t>(it->numContiguousColumns(dst->x()));
426 const auto tileHeight =
427 static_cast<size_t>(it->numContiguousRows(dst->y()));
428 Q_ASSERT(tileWidth == 64);
429 Q_ASSERT(tileHeight == 64);
430 std::vector<quint8> convertedTile(
431 static_cast<size_t>(rgbaFloat32bitcolorSpace->pixelSize()) * tileWidth
432 * tileHeight);
433
434 // grayscale and rgb case does not have alpha, so let's fill 4th channel of
435 // rgba tile with opacity opaque
436 if (gmicImage.m_spectrum == 1 || gmicImage.m_spectrum == 3) {
437 const auto nPixels = tileWidth * tileHeight;
438 auto *srcPixel =
439 reinterpret_cast<KoRgbF32Traits::Pixel *>(convertedTile.data());
440 for (size_t pixelIndex = 0; pixelIndex < nPixels;
441 pixelIndex++, srcPixel++) {
442 srcPixel->alpha = gmicUnitValue;
443 }
444 }
445
446 while (rowsRemaining > 0) {
447 size_t dataX = 0;
448 qint32 imageX = x;
449 size_t columnsRemaining = width;
450 const auto numContiguousImageRows =
451 static_cast<size_t>(it->numContiguousRows(imageY));
452
453 size_t rowsToWork = qMin(numContiguousImageRows, rowsRemaining);
454
455 while (columnsRemaining > 0) {
456 const auto numContiguousImageColumns =
457 static_cast<size_t>(it->numContiguousColumns(imageX));
458 auto columnsToWork =
459 qMin(numContiguousImageColumns, columnsRemaining);
460
461 const auto dataIdx = dataX + dataY * width;
462 const auto tileRowStride =
463 (tileWidth - columnsToWork) * floatPixelSize;
464
465 auto *tileItStart = convertedTile.data();
466 // copy gmic channels to float tile
467 const auto channelSize = sizeof(float);
468 for (int i = 0; i < numChannels; i++) {
469 float *planeIt = planes[i] + dataIdx;
470 const auto dataStride = width - columnsToWork;
471 quint8 *tileIt = tileItStart;
472
473 for (size_t row = 0; row < rowsToWork; row++) {
474 for (size_t col = 0; col < columnsToWork; col++) {
475 memcpy(tileIt, planeIt, channelSize);
476 tileIt += floatPixelSize;
477 planeIt += 1;
478 }
479
480 tileIt += tileRowStride;
481 planeIt += dataStride;
482 }
483 tileItStart += channelSize;
484 }
485
486 it->moveTo(imageX, imageY);
487 quint8 *dstTileItStart = it->rawData();
488 tileItStart =
489 convertedTile.data(); // back to the start of the converted tile
490 // copy float tile to dst colorspace based on input colorspace (rgb
491 // or grayscale)
492 for (size_t row = 0; row < rowsToWork; row++) {
493 gmicToDstPixelFormat->transform(
494 tileItStart,
495 dstTileItStart,
496 static_cast<int>(columnsToWork));
497 dstTileItStart += dstColorSpace->pixelSize() * tileWidth;
498 tileItStart += floatPixelSize * tileWidth;
499 }
500
501 imageX += static_cast<int>(columnsToWork);
502 dataX += columnsToWork;
503 columnsRemaining -= columnsToWork;
504 }
505
506 imageY += static_cast<int>(rowsToWork);
507 dataY += rowsToWork;
508 rowsRemaining -= rowsToWork;
509 }
510
511 dst->crop(x, y, static_cast<qint32>(width), static_cast<qint32>(height));
512}
513
515 KisQMicImage &gmicImage,
516 QRect rc)
517{
518 KoColorTransformationSP pixelToGmicPixelFormat(
520 if (!pixelToGmicPixelFormat) {
521 dbgPlugins << "Fall-back to slow color conversion method";
522 convertToGmicImage(dev, gmicImage, rc);
523 return;
524 }
525
526 if (rc.isEmpty()) {
528 << "Image rectangle is empty! Using supplied gmic layer dimension";
529 rc = QRect(0,
530 0,
531 static_cast<int>(gmicImage.m_width),
532 static_cast<int>(gmicImage.m_height));
533 }
534
535 const auto x = rc.x();
536 const auto y = rc.y();
537 const size_t width = rc.width() < 0 ? 0 : static_cast<size_t>(rc.width());
538 const size_t height =
539 rc.height() < 0 ? 0 : static_cast<size_t>(rc.height());
540
541 const qint32 numChannels = 4;
542
543 const size_t greenOffset =
544 static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
545 const size_t blueOffset = greenOffset * 2;
546 const size_t alphaOffset = greenOffset * 3;
547
548 const std::array<float *, 4> planes = {gmicImage.m_data,
549 gmicImage.m_data + greenOffset,
550 gmicImage.m_data + blueOffset,
551 gmicImage.m_data + alphaOffset};
552
554 const auto tileWidth = it->numContiguousColumns(dev->x());
555 const auto tileHeight = it->numContiguousRows(dev->y());
556
557 Q_ASSERT(tileWidth == 64);
558 Q_ASSERT(tileHeight == 64);
559
560 const KoColorSpace *rgbaFloat32bitcolorSpace =
565 Q_CHECK_PTR(rgbaFloat32bitcolorSpace);
566 const auto dstPixelSize = rgbaFloat32bitcolorSpace->pixelSize();
567 const auto srcPixelSize = dev->pixelSize();
568
569 std::vector<quint8> dstTile(dstPixelSize * static_cast<size_t>(tileWidth)
570 * static_cast<size_t>(tileHeight));
571
572 size_t dataY = 0;
573 int imageY = y;
574 int imageX = x;
575 it->moveTo(imageX, imageY);
576 size_t rowsRemaining = height;
577
578 while (rowsRemaining > 0) {
579 size_t dataX = 0;
580 imageX = x;
581 size_t columnsRemaining = width;
582 const auto numContiguousImageRows = it->numContiguousRows(imageY);
583
584 const auto rowsToWork =
585 qMin(numContiguousImageRows, static_cast<qint32>(rowsRemaining));
586 const auto convertedTileY = tileHeight - rowsToWork;
587 Q_ASSERT(convertedTileY >= 0);
588
589 while (columnsRemaining > 0) {
590 const auto numContiguousImageColumns =
591 static_cast<size_t>(it->numContiguousColumns(imageX));
592 const auto columnsToWork =
593 qMin(numContiguousImageColumns, columnsRemaining);
594 const auto convertedTileX =
595 tileWidth - static_cast<qint32>(columnsToWork);
596 Q_ASSERT(convertedTileX >= 0);
597
598 const auto dataIdx = dataX + dataY * width;
599 const auto dstTileIndex =
600 convertedTileX + convertedTileY * tileWidth;
601 const auto tileRowStride =
602 (static_cast<size_t>(tileWidth) - columnsToWork) * dstPixelSize;
603 const auto srcTileRowStride =
604 (static_cast<size_t>(tileWidth) - columnsToWork) * srcPixelSize;
605
606 it->moveTo(imageX, imageY);
607 quint8 *tileItStart = dstTile.data() + dstTileIndex * dstPixelSize;
608
609 // transform tile row by row
610 auto *dstTileIt = tileItStart;
611 const auto *srcTileIt = it->rawDataConst();
612
613 auto row = rowsToWork;
614 while (row > 0) {
615 pixelToGmicPixelFormat->transform(
616 srcTileIt,
617 dstTileIt,
618 static_cast<qint32>(columnsToWork));
619 srcTileIt += columnsToWork * srcPixelSize;
620 srcTileIt += srcTileRowStride;
621
622 dstTileIt += columnsToWork * dstPixelSize;
623 dstTileIt += tileRowStride;
624
625 row--;
626 }
627
628 // here we want to copy floats to dstTile, so tileItStart has to
629 // point to float buffer
630 const auto channelSize = sizeof(float);
631 for (size_t i = 0; i < numChannels; i++) {
632 float *planeIt = planes.at(i) + dataIdx;
633 const auto dataStride = (width - columnsToWork);
634 quint8 *tileIt = tileItStart;
635
636 for (int row = 0; row < rowsToWork; row++) {
637 for (size_t col = 0; col < columnsToWork; col++) {
638 memcpy(planeIt, tileIt, channelSize);
639 tileIt += dstPixelSize;
640 planeIt += 1;
641 }
642
643 tileIt += tileRowStride;
644 planeIt += dataStride;
645 }
646 // skip channel in tile: red, green, blue, alpha
647 tileItStart += channelSize;
648 }
649
650 imageX += static_cast<int>(columnsToWork);
651 dataX += columnsToWork;
652 columnsRemaining -= columnsToWork;
653 }
654
655 imageY += static_cast<int>(rowsToWork);
656 dataY += rowsToWork;
657 rowsRemaining -= rowsToWork;
658 }
659}
660
661// gmic assumes float rgba in 0.0 - 255.0
663 KisQMicImage &gmicImage,
664 QRect rc)
665{
666 Q_ASSERT(!dev.isNull());
667 Q_ASSERT(gmicImage.m_spectrum == 4); // rgba
668
669 if (rc.isEmpty()) {
670 rc = QRect(0,
671 0,
672 static_cast<int>(gmicImage.m_width),
673 static_cast<int>(gmicImage.m_height));
674 }
675
676 const KoColorSpace *rgbaFloat32bitcolorSpace =
681 Q_CHECK_PTR(rgbaFloat32bitcolorSpace);
682
683 const KoColorTransformationSP pixelToGmicPixelFormat(
684 createTransformation(rgbaFloat32bitcolorSpace));
685
686 const size_t greenOffset =
687 static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
688 const size_t blueOffset = greenOffset * 2;
689 const size_t alphaOffset = greenOffset * 3;
690
691 const auto renderingIntent =
693 const auto conversionFlags =
695
696 const KoColorSpace *colorSpace = dev->colorSpace();
698
699 const size_t optimalBufferSize =
700 64; // most common numContiguousColumns, tile size?
701 std::vector<quint8> floatRGBApixelStorage(
702 rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize);
703 quint8 *floatRGBApixel = floatRGBApixelStorage.data();
704
705 const auto pixelSize = rgbaFloat32bitcolorSpace->pixelSize();
706 for (int y = 0; y < rc.height(); y++) {
707 int x = 0;
708 while (x < rc.width()) {
709 it->moveTo(rc.x() + x, rc.y() + y);
710 auto numContiguousColumns =
711 qMin(static_cast<size_t>(it->numContiguousColumns(rc.x() + x)),
712 optimalBufferSize);
713 numContiguousColumns =
714 qMin(numContiguousColumns, static_cast<size_t>(rc.width() - x));
715
716 colorSpace->convertPixelsTo(
717 it->rawDataConst(),
718 floatRGBApixel,
719 rgbaFloat32bitcolorSpace,
720 static_cast<quint32>(numContiguousColumns),
721 renderingIntent,
722 conversionFlags);
723 pixelToGmicPixelFormat->transform(
724 floatRGBApixel,
725 floatRGBApixel,
726 static_cast<qint32>(numContiguousColumns));
727
728 auto pos = static_cast<size_t>(y) * gmicImage.m_width
729 + static_cast<size_t>(x);
730 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
731 memcpy(gmicImage.m_data + pos,
732 floatRGBApixel + bx * pixelSize,
733 4);
734 memcpy(gmicImage.m_data + pos + greenOffset,
735 floatRGBApixel + bx * pixelSize + 4,
736 4);
737 memcpy(gmicImage.m_data + pos + blueOffset,
738 floatRGBApixel + bx * pixelSize + 8,
739 4);
740 memcpy(gmicImage.m_data + pos + alphaOffset,
741 floatRGBApixel + bx * pixelSize + 12,
742 4);
743 pos++;
744 }
745
746 x += static_cast<int>(numContiguousColumns);
747 }
748 }
749}
750
753 float gmicMaxChannelValue)
754{
755 dbgPlugins << "convertFromGmicSlow";
756 Q_ASSERT(!dst.isNull());
757 const KoColorSpace *rgbaFloat32bitcolorSpace =
762 const KoColorSpace *dstColorSpace = dst->colorSpace();
763
764 KisPaintDeviceSP dev = dst;
765 const size_t greenOffset =
766 static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
767 const size_t blueOffset = greenOffset * 2;
768 const size_t alphaOffset = greenOffset * 3;
769 QRect rc(0,
770 0,
771 static_cast<int>(gmicImage.m_width),
772 static_cast<int>(gmicImage.m_height));
773
775 float r = 0;
776 float g = 0;
777 float b = 0;
778 float a = 1.0;
779
780 const size_t optimalBufferSize =
781 64; // most common numContiguousColumns, tile size?
782 std::vector<quint8> floatRGBApixelStorage(
783 rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize);
784 quint8 *floatRGBApixel = floatRGBApixelStorage.data();
785 const auto pixelSize = rgbaFloat32bitcolorSpace->pixelSize();
786
787 const auto renderingIntent =
789 const auto conversionFlags =
791
792 // Krita needs rgba in 0.0...1.0
793 const float multiplied =
794 KoColorSpaceMathsTraits<float>::unitValue / gmicMaxChannelValue;
795
796 switch (gmicImage.m_spectrum) {
797 case 1: {
798 // convert grayscale to rgba
799 for (int y = 0; y < rc.height(); y++) {
800 int x = 0;
801 while (x < rc.width()) {
802 it->moveTo(x, y);
803 auto numContiguousColumns =
804 qMin(static_cast<size_t>(it->numContiguousColumns(x)),
805 optimalBufferSize);
806 numContiguousColumns =
807 qMin(numContiguousColumns,
808 static_cast<size_t>(rc.width() - x));
809
810 size_t pos = static_cast<size_t>(y) * gmicImage.m_width
811 + static_cast<size_t>(x);
812 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
813 r = g = b = gmicImage.m_data[pos] * multiplied;
815
816 memcpy(floatRGBApixel + bx * pixelSize, &r, 4);
817 memcpy(floatRGBApixel + bx * pixelSize + 4, &g, 4);
818 memcpy(floatRGBApixel + bx * pixelSize + 8, &b, 4);
819 memcpy(floatRGBApixel + bx * pixelSize + 12, &a, 4);
820 pos++;
821 }
822 rgbaFloat32bitcolorSpace->convertPixelsTo(
823 floatRGBApixel,
824 it->rawData(),
825 dstColorSpace,
826 static_cast<quint32>(numContiguousColumns),
827 renderingIntent,
828 conversionFlags);
829 x += static_cast<int>(numContiguousColumns);
830 }
831 }
832 break;
833 }
834 case 2: {
835 // convert grayscale alpha to rgba
836 for (int y = 0; y < rc.height(); y++) {
837 int x = 0;
838 while (x < rc.width()) {
839 it->moveTo(x, y);
840 auto numContiguousColumns =
841 qMin(static_cast<size_t>(it->numContiguousColumns(x)),
842 optimalBufferSize);
843 numContiguousColumns =
844 qMin(numContiguousColumns,
845 static_cast<size_t>(rc.width() - x));
846
847 size_t pos = static_cast<size_t>(y) * gmicImage.m_width
848 + static_cast<size_t>(x);
849 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
850 r = g = b = gmicImage.m_data[pos] * multiplied;
851 a = gmicImage.m_data[pos + greenOffset] * multiplied;
852
853 memcpy(floatRGBApixel + bx * pixelSize, &r, 4);
854 memcpy(floatRGBApixel + bx * pixelSize + 4, &g, 4);
855 memcpy(floatRGBApixel + bx * pixelSize + 8, &b, 4);
856 memcpy(floatRGBApixel + bx * pixelSize + 12, &a, 4);
857 pos++;
858 }
859 rgbaFloat32bitcolorSpace->convertPixelsTo(
860 floatRGBApixel,
861 it->rawData(),
862 dstColorSpace,
863 static_cast<quint32>(numContiguousColumns),
864 renderingIntent,
865 conversionFlags);
866 x += static_cast<int>(numContiguousColumns);
867 }
868 }
869 break;
870 }
871 case 3: {
872 // convert rgb -> rgba
873 for (int y = 0; y < rc.height(); y++) {
874 int x = 0;
875 while (x < rc.width()) {
876 it->moveTo(x, y);
877 auto numContiguousColumns =
878 qMin(static_cast<size_t>(it->numContiguousColumns(x)),
879 optimalBufferSize);
880 numContiguousColumns =
881 qMin(numContiguousColumns,
882 static_cast<size_t>(rc.width() - x));
883
884 size_t pos = static_cast<size_t>(y) * gmicImage.m_width
885 + static_cast<size_t>(x);
886 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
887 r = gmicImage.m_data[pos] * multiplied;
888 g = gmicImage.m_data[pos + greenOffset] * multiplied;
889 b = gmicImage.m_data[pos + blueOffset] * multiplied;
890 a = gmicMaxChannelValue * multiplied;
891
892 memcpy(floatRGBApixel + bx * pixelSize, &r, 4);
893 memcpy(floatRGBApixel + bx * pixelSize + 4, &g, 4);
894 memcpy(floatRGBApixel + bx * pixelSize + 8, &b, 4);
895 memcpy(floatRGBApixel + bx * pixelSize + 12, &a, 4);
896 pos++;
897 }
898 rgbaFloat32bitcolorSpace->convertPixelsTo(
899 floatRGBApixel,
900 it->rawData(),
901 dstColorSpace,
902 static_cast<quint32>(numContiguousColumns),
903 renderingIntent,
904 conversionFlags);
905 x += static_cast<int>(numContiguousColumns);
906 }
907 }
908 break;
909 }
910 case 4: {
911 for (int y = 0; y < rc.height(); y++) {
912 int x = 0;
913 while (x < rc.width()) {
914 it->moveTo(x, y);
915 auto numContiguousColumns =
916 qMin(static_cast<size_t>(it->numContiguousColumns(x)),
917 optimalBufferSize);
918 numContiguousColumns =
919 qMin(numContiguousColumns,
920 static_cast<size_t>(rc.width() - x));
921
922 size_t pos = static_cast<size_t>(y) * gmicImage.m_width
923 + static_cast<size_t>(x);
924 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
925 r = gmicImage.m_data[pos] * multiplied;
926 g = gmicImage.m_data[pos + greenOffset] * multiplied;
927 b = gmicImage.m_data[pos + blueOffset] * multiplied;
928 a = gmicImage.m_data[pos + alphaOffset] * multiplied;
929
930 memcpy(floatRGBApixel + bx * pixelSize, &r, 4);
931 memcpy(floatRGBApixel + bx * pixelSize + 4, &g, 4);
932 memcpy(floatRGBApixel + bx * pixelSize + 8, &b, 4);
933 memcpy(floatRGBApixel + bx * pixelSize + 12, &a, 4);
934 pos++;
935 }
936 rgbaFloat32bitcolorSpace->convertPixelsTo(
937 floatRGBApixel,
938 it->rawData(),
939 dstColorSpace,
940 static_cast<quint32>(numContiguousColumns),
941 renderingIntent,
942 conversionFlags);
943 x += static_cast<int>(numContiguousColumns);
944 }
945 }
946 break;
947 }
948
949 default: {
950 dbgPlugins << "Unsupported gmic output format : " << gmicImage.m_width
951 << gmicImage.m_height << gmicImage.m_spectrum;
952 }
953 }
954}
955
957 float gmicActualMaxChannelValue)
958{
959 QImage image = QImage(static_cast<int>(gmicImage.m_width),
960 static_cast<int>(gmicImage.m_height),
961 QImage::Format_ARGB32);
962
963 dbgPlugins << image.format() << "first pixel:" << gmicImage.m_data[0]
964 << gmicImage.m_width << gmicImage.m_height
965 << gmicImage.m_spectrum;
966
967 const size_t greenOffset =
968 static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
969 const size_t blueOffset = greenOffset * 2;
970
971 // always put 255 to qimage
972 const float multiplied = 255.0f / gmicActualMaxChannelValue;
973
974 for (int y = 0; y < gmicImage.m_height; y++) {
975 QRgb *pixel =
976 reinterpret_cast<QRgb *>(image.scanLine(static_cast<int>(y)));
977 for (int x = 0; x < gmicImage.m_width; x++) {
978 const auto pos = y * gmicImage.m_width + x;
979 const float r = gmicImage.m_data[pos] * multiplied;
980 const float g = gmicImage.m_data[pos + greenOffset] * multiplied;
981 const float b = gmicImage.m_data[pos + blueOffset] * multiplied;
982 pixel[x] = qRgb(int(r), int(g), int(b));
983 }
984 }
985 return image;
986}
987
989 KisQMicImage &gmicImage,
990 float gmicUnitValue)
991{
992 const auto greenOffset =
993 static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
994 const size_t blueOffset = greenOffset * 2;
995 const size_t alphaOffset = greenOffset * 3;
996
997 // QImage has 0..255
998 const float qimageUnitValue = 255.0f;
999 const float multiplied = gmicUnitValue / qimageUnitValue;
1000
1001 Q_ASSERT(image.width() == int(gmicImage.m_width));
1002 Q_ASSERT(image.height() == int(gmicImage.m_height));
1003 Q_ASSERT(image.format() == QImage::Format_ARGB32);
1004
1005 switch (gmicImage.m_spectrum) {
1006 case 1: {
1007 for (int y = 0; y < image.height(); y++) {
1008 const QRgb *pixel =
1009 reinterpret_cast<const QRgb *>(image.scanLine(y));
1010 for (int x = 0; x < image.width(); x++) {
1011 const auto pos = static_cast<size_t>(y) * gmicImage.m_width
1012 + static_cast<size_t>(x);
1013 gmicImage.m_data[pos] =
1014 static_cast<float>(qGray(pixel[x])) * multiplied;
1015 }
1016 }
1017 break;
1018 }
1019 case 2: {
1020 for (int y = 0; y < image.height(); y++) {
1021 const QRgb *pixel =
1022 reinterpret_cast<const QRgb *>(image.scanLine(y));
1023 for (int x = 0; x < image.width(); x++) {
1024 const auto pos = static_cast<size_t>(y) * gmicImage.m_width
1025 + static_cast<size_t>(x);
1026 gmicImage.m_data[pos] =
1027 static_cast<float>(qGray(pixel[x])) * multiplied;
1028 gmicImage.m_data[pos + greenOffset] =
1029 static_cast<float>(qAlpha(pixel[x])) * multiplied;
1030 }
1031 }
1032 break;
1033 }
1034 case 3: {
1035 for (int y = 0; y < image.height(); y++) {
1036 const QRgb *pixel =
1037 reinterpret_cast<const QRgb *>(image.scanLine(y));
1038 for (int x = 0; x < image.width(); x++) {
1039 const auto pos = static_cast<size_t>(y) * gmicImage.m_width
1040 + static_cast<size_t>(x);
1041 gmicImage.m_data[pos] =
1042 static_cast<float>(qRed(pixel[x])) * multiplied;
1043 gmicImage.m_data[pos + greenOffset] =
1044 static_cast<float>(qGreen(pixel[x])) * multiplied;
1045 gmicImage.m_data[pos + blueOffset] =
1046 static_cast<float>(qBlue(pixel[x])) * multiplied;
1047 }
1048 }
1049 break;
1050 }
1051 case 4: {
1052 for (int y = 0; y < image.height(); y++) {
1053 const QRgb *pixel =
1054 reinterpret_cast<const QRgb *>(image.scanLine(y));
1055 for (int x = 0; x < image.width(); x++) {
1056 const auto pos = static_cast<size_t>(y) * gmicImage.m_width
1057 + static_cast<size_t>(x);
1058 gmicImage.m_data[pos] =
1059 static_cast<float>(qRed(pixel[x])) * multiplied;
1060 gmicImage.m_data[pos + greenOffset] =
1061 static_cast<float>(qGreen(pixel[x])) * multiplied;
1062 gmicImage.m_data[pos + blueOffset] =
1063 static_cast<float>(qBlue(pixel[x])) * multiplied;
1064 gmicImage.m_data[pos + alphaOffset] =
1065 static_cast<float>(qAlpha(pixel[x])) * multiplied;
1066 }
1067 }
1068 break;
1069 }
1070 default: {
1071 Q_ASSERT(false);
1072 dbgKrita << "Unexpected gmic image format";
1073 break;
1074 }
1075 }
1076}
1077
1078std::map<QString, QString> reverseMap()
1079{
1080 std::map<QString, QString> result{};
1081 for (const auto &pair : blendingModeMap) {
1082 result.emplace(pair.second, pair.first);
1083 }
1084 return result;
1085}
1086
1088{
1089 static auto reverseModeMap = reverseMap();
1090 if (reverseModeMap.find(blendMode) != reverseModeMap.end())
1091 return reverseModeMap.at(blendMode);
1092 return "alpha";
1093}
1094
1096{
1097 if (blendingModeMap.find(blendMode) != blendingModeMap.end())
1098 return blendingModeMap.at(blendMode);
1099 return COMPOSITE_OVER;
1100}
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
const QString COMPOSITE_OVER
const QString COMPOSITE_DARKEN
const QString COMPOSITE_NEGATION
const QString COMPOSITE_OVERLAY
const QString COMPOSITE_DIVIDE
const QString COMPOSITE_LIGHTNESS
const QString COMPOSITE_DODGE
const QString COMPOSITE_SOFT_LIGHT_SVG
const QString COMPOSITE_ADD
const QString COMPOSITE_LIGHTEN
const QString COMPOSITE_COPY_RED
const QString COMPOSITE_LINEAR_BURN
const QString COMPOSITE_FREEZE
const QString COMPOSITE_VALUE
const QString COMPOSITE_GRAIN_MERGE
const QString COMPOSITE_MULT
const QString COMPOSITE_REFLECT
const QString COMPOSITE_OR
const QString COMPOSITE_SATURATION
const QString COMPOSITE_LINEAR_LIGHT
const QString COMPOSITE_COPY_GREEN
const QString COMPOSITE_HARD_LIGHT
const QString COMPOSITE_INTERPOLATION
const QString COMPOSITE_COPY_BLUE
const QString COMPOSITE_SCREEN
const QString COMPOSITE_PIN_LIGHT
const QString COMPOSITE_DIFF
const QString COMPOSITE_HARD_MIX
const QString COMPOSITE_HUE
const QString COMPOSITE_SUBTRACT
const QString COMPOSITE_BURN
const QString COMPOSITE_EXCLUSION
const QString COMPOSITE_AND
const QString COMPOSITE_GRAIN_EXTRACT
const QString COMPOSITE_XOR
const QString COMPOSITE_VIVID_LIGHT
const QString COMPOSITE_LUMINIZE
virtual quint8 * rawData()=0
virtual const quint8 * rawDataConst() const =0
typename RGBTrait::Pixel RGBPixel
void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
KisColorFromFloat(float gmicUnitValue=255.0f)
void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
KisColorFromGrayScaleAlphaFloat(float gmicUnitValue=255.0f)
void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
KisColorFromGrayScaleFloat(float gmicUnitValue=255.0f)
KisColorToFloatConvertor(float gmicUnitValue=255.0f)
void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
typename RGBTrait::Pixel RGBPixel
quint32 pixelSize() const
void crop(qint32 x, qint32 y, qint32 w, qint32 h)
virtual void clear()
KisRandomConstAccessorSP createRandomConstAccessorNG() const
const KoColorSpace * colorSpace() const
void moveTo(qint32 x, qint32 y)
KisRandomAccessorSP createRandomAccessorNG()
static void convertToGmicImage(KisPaintDeviceSP dev, KisQMicImage &gmicImage, QRect rc=QRect())
static void convertToGmicImageFast(KisPaintDeviceSP dev, KisQMicImage &gmicImage, QRect rc=QRect())
static QImage convertToQImage(const KisQMicImage &gmicImage, float gmicMaxChannelValue=255.0)
static void convertFromGmicImage(const KisQMicImage &gmicImage, KisPaintDeviceSP dst, float gmicMaxChannelValue)
static void convertFromQImage(const QImage &image, KisQMicImage &gmicImage, float gmicUnitValue=1.0)
static QString blendingModeToString(QString blendMode)
static void convertFromGmicFast(const KisQMicImage &gmicImage, KisPaintDeviceSP dst, float gmicUnitValue)
Fast versions.
static QString stringToBlendingMode(QString str)
virtual qint32 numContiguousRows(qint32 y) const =0
virtual void moveTo(qint32 x, qint32 y)=0
virtual qint32 numContiguousColumns(qint32 x) const =0
bool isNull() const
virtual quint32 pixelSize() const =0
virtual KoID colorModelId() const =0
virtual quint32 channelCount() const =0
virtual KoID colorDepthId() const =0
virtual bool convertPixelsTo(const quint8 *src, quint8 *dst, const KoColorSpace *dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
virtual const KoColorProfile * profile() const =0
QString id() const
Definition KoID.cpp:63
unsigned int QRgb
#define dbgKrita
Definition kis_debug.h:45
#define dbgPlugins
Definition kis_debug.h:51
std::shared_ptr< KoColorTransformation > KoColorTransformationSP
static KoColorTransformation * createTransformation(const KoColorSpace *colorSpace)
const std::map< QString, QString > blendingModeMap
std::map< QString, QString > reverseMap()
#define SCALE_FROM_FLOAT(v)
static KoColorTransformation * createTransformationFromGmic(const KoColorSpace *colorSpace, int gmicSpectrum, float gmicUnitValue)
#define SCALE_TO_FLOAT(v)
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb8(const QString &profileName=QString())