File indexing completed on 2024-06-16 04:16:32

0001 /*
0002  * SPDX-FileCopyrightText: 2013 Lukáš Tvrdý <lukast.dev@gmail.com>
0003  * SPDX-FileCopyrightText: 2020-2022 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_qmic_simple_convertor.h"
0009 
0010 #include <array>
0011 #include <cstddef>
0012 #include <map>
0013 #include <memory>
0014 #include <vector>
0015 
0016 #include <kis_debug.h>
0017 #include <kis_random_accessor_ng.h>
0018 
0019 #include <KoColorModelStandardIds.h>
0020 #include <KoColorSpace.h>
0021 #include <KoColorSpaceRegistry.h>
0022 #include <KoColorSpaceTraits.h>
0023 #include <KoCompositeOpRegistry.h>
0024 
0025 #define SCALE_TO_FLOAT(v) KoColorSpaceMaths<_channel_type_, float>::scaleToA(v)
0026 #define SCALE_FROM_FLOAT(v)                                                    \
0027     KoColorSpaceMaths<float, _channel_type_>::scaleToA(v)
0028 
0029 using KoColorTransformationSP = std::shared_ptr<KoColorTransformation>;
0030 
0031 template<typename _channel_type_, typename traits>
0032 class KisColorToFloatConvertor : public KoColorTransformation
0033 {
0034     using RGBTrait = traits;
0035     using RGBPixel = typename RGBTrait::Pixel;
0036 
0037 public:
0038     KisColorToFloatConvertor(float gmicUnitValue = 255.0f)
0039         : m_gmicUnitValue(gmicUnitValue)
0040     {
0041     }
0042 
0043     float m_gmicUnitValue;
0044 
0045     void
0046     transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
0047     {
0048         const float gmicUnitValue2KritaUnitValue =
0049             m_gmicUnitValue / KoColorSpaceMathsTraits<float>::unitValue;
0050 
0051         const auto *srcPixel = reinterpret_cast<const RGBPixel *>(src);
0052         auto *dstPixel = reinterpret_cast<KoRgbF32Traits::Pixel *>(dst);
0053 
0054         while (nPixels > 0) {
0055             dstPixel->red =
0056                 SCALE_TO_FLOAT(srcPixel->red) * gmicUnitValue2KritaUnitValue;
0057             dstPixel->green =
0058                 SCALE_TO_FLOAT(srcPixel->green) * gmicUnitValue2KritaUnitValue;
0059             dstPixel->blue =
0060                 SCALE_TO_FLOAT(srcPixel->blue) * gmicUnitValue2KritaUnitValue;
0061             dstPixel->alpha =
0062                 SCALE_TO_FLOAT(srcPixel->alpha) * gmicUnitValue2KritaUnitValue;
0063 
0064             --nPixels;
0065             ++srcPixel;
0066             ++dstPixel;
0067         }
0068     }
0069 };
0070 
0071 template<typename _channel_type_, typename traits>
0072 class KisColorFromFloat : public KoColorTransformation
0073 {
0074     using RGBTrait = traits;
0075     using RGBPixel = typename RGBTrait::Pixel;
0076 
0077 public:
0078     KisColorFromFloat(float gmicUnitValue = 255.0f)
0079         : m_gmicUnitValue(gmicUnitValue)
0080     {
0081     }
0082 
0083     void
0084     transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
0085     {
0086         const auto *srcPixel =
0087             reinterpret_cast<const KoRgbF32Traits::Pixel *>(src);
0088         auto *dstPixel = reinterpret_cast<RGBPixel *>(dst);
0089 
0090         const float gmicUnitValue2KritaUnitValue =
0091             KoColorSpaceMathsTraits<float>::unitValue / m_gmicUnitValue;
0092 
0093         while (nPixels > 0) {
0094             dstPixel->red =
0095                 SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
0096             dstPixel->green = SCALE_FROM_FLOAT(srcPixel->green
0097                                                * gmicUnitValue2KritaUnitValue);
0098             dstPixel->blue =
0099                 SCALE_FROM_FLOAT(srcPixel->blue * gmicUnitValue2KritaUnitValue);
0100             dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->alpha
0101                                                * gmicUnitValue2KritaUnitValue);
0102 
0103             --nPixels;
0104             ++srcPixel;
0105             ++dstPixel;
0106         }
0107     }
0108 
0109 private:
0110     float m_gmicUnitValue;
0111 };
0112 
0113 template<typename _channel_type_, typename traits>
0114 class KisColorFromGrayScaleFloat : public KoColorTransformation
0115 {
0116     using RGBTrait = traits;
0117     using RGBPixel = typename RGBTrait::Pixel;
0118 
0119 public:
0120     KisColorFromGrayScaleFloat(float gmicUnitValue = 255.0f)
0121         : m_gmicUnitValue(gmicUnitValue)
0122     {
0123     }
0124 
0125     void
0126     transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
0127     {
0128         const auto *srcPixel =
0129             reinterpret_cast<const KoRgbF32Traits::Pixel *>(src);
0130         auto *dstPixel = reinterpret_cast<RGBPixel *>(dst);
0131 
0132         const float gmicUnitValue2KritaUnitValue =
0133             KoColorSpaceMathsTraits<float>::unitValue / m_gmicUnitValue;
0134         // warning: green and blue channels on input contain random data!!! see
0135         // that we copy only one channel when gmic image has grayscale
0136         // colorspace
0137         while (nPixels > 0) {
0138             dstPixel->red = dstPixel->green = dstPixel->blue =
0139                 SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
0140             dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->alpha
0141                                                * gmicUnitValue2KritaUnitValue);
0142 
0143             --nPixels;
0144             ++srcPixel;
0145             ++dstPixel;
0146         }
0147     }
0148 
0149 private:
0150     float m_gmicUnitValue;
0151 };
0152 
0153 template<typename _channel_type_, typename traits>
0154 class KisColorFromGrayScaleAlphaFloat : public KoColorTransformation
0155 {
0156     using RGBTrait = traits;
0157     using RGBPixel = typename RGBTrait::Pixel;
0158 
0159 public:
0160     KisColorFromGrayScaleAlphaFloat(float gmicUnitValue = 255.0f)
0161         : m_gmicUnitValue(gmicUnitValue)
0162     {
0163     }
0164 
0165     void
0166     transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override
0167     {
0168         const auto *srcPixel =
0169             reinterpret_cast<const KoRgbF32Traits::Pixel *>(src);
0170         auto *dstPixel = reinterpret_cast<RGBPixel *>(dst);
0171 
0172         const float gmicUnitValue2KritaUnitValue =
0173             KoColorSpaceMathsTraits<float>::unitValue / m_gmicUnitValue;
0174         // warning: green and blue channels on input contain random data!!! see
0175         // that we copy only one channel when gmic image has grayscale
0176         // colorspace
0177         while (nPixels > 0) {
0178             dstPixel->red = dstPixel->green = dstPixel->blue =
0179                 SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
0180             dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->green
0181                                                * gmicUnitValue2KritaUnitValue);
0182 
0183             --nPixels;
0184             ++srcPixel;
0185             ++dstPixel;
0186         }
0187     }
0188 
0189 private:
0190     float m_gmicUnitValue;
0191 };
0192 
0193 const std::map<QString, QString> blendingModeMap = {
0194     {"add", COMPOSITE_ADD},
0195     //   {"alpha", ""}, // XXX
0196     {"and", COMPOSITE_AND},
0197     //   {"average", ""}, // XXX
0198     {"blue", COMPOSITE_COPY_BLUE},
0199     {"burn", COMPOSITE_BURN},
0200     {"darken", COMPOSITE_DARKEN},
0201     {"difference", COMPOSITE_DIFF},
0202     {"divide", COMPOSITE_DIVIDE},
0203     {"dodge", COMPOSITE_DODGE},
0204     //   {"edges", ""}, // XXX
0205     {"exclusion", COMPOSITE_EXCLUSION},
0206     {"freeze", COMPOSITE_FREEZE},
0207     {"grainextract", COMPOSITE_GRAIN_EXTRACT},
0208     {"grainmerge", COMPOSITE_GRAIN_MERGE},
0209     {"green", COMPOSITE_COPY_GREEN},
0210     {"hardlight", COMPOSITE_HARD_LIGHT},
0211     {"hardmix", COMPOSITE_HARD_MIX},
0212     {"hue", COMPOSITE_HUE},
0213     {"interpolation", COMPOSITE_INTERPOLATION},
0214     {"lighten", COMPOSITE_LIGHTEN},
0215     {"lightness", COMPOSITE_LIGHTNESS},
0216     {"linearburn", COMPOSITE_LINEAR_BURN},
0217     {"linearlight", COMPOSITE_LINEAR_LIGHT},
0218     {"luminance", COMPOSITE_LUMINIZE},
0219     {"multiply", COMPOSITE_MULT},
0220     {"negation", COMPOSITE_NEGATION},
0221     {"or", COMPOSITE_OR},
0222     {"overlay", COMPOSITE_OVERLAY},
0223     {"pinlight", COMPOSITE_PIN_LIGHT},
0224     {"red", COMPOSITE_COPY_RED},
0225     {"reflect", COMPOSITE_REFLECT},
0226     {"saturation", COMPOSITE_SATURATION},
0227     //   {"seamless", ""},       // XXX
0228     //   {"seamless_mixed", ""}, // XXX
0229     {"screen", COMPOSITE_SCREEN},
0230     //   {"shapeareamax", ""},                    // XXX
0231     //   {"shapeareamax0", ""},                   // XXX
0232     //   {"shapeareamin", ""},                    // XXX
0233     //   {"shapeareamin0", ""},                   // XXX
0234     //   {"shapeaverage", ""},                    // XXX
0235     //   {"shapeaverage0", ""},                   // XXX
0236     //   {"shapemedian", ""},                     // XXX
0237     //   {"shapemedian0", ""},                    // XXX
0238     //   {"shapemin", ""},                        // XXX
0239     //   {"shapemin0", ""},                       // XXX
0240     //   {"shapemax", ""},                        // XXX
0241     //   {"shapemax0", ""},                       // XXX
0242     //   {"softburn", ""},                        // XXX
0243     //   {"softdodge", ""},                       // XXX
0244     {"softlight", COMPOSITE_SOFT_LIGHT_SVG}, // XXX Is this correct?
0245     //   {"stamp", ""}, // XXX
0246     {"subtract", COMPOSITE_SUBTRACT},
0247     {"value", COMPOSITE_VALUE},
0248     {"vividlight", COMPOSITE_VIVID_LIGHT},
0249     {"xor", COMPOSITE_XOR}};
0250 
0251 static KoColorTransformation *
0252 createTransformationFromGmic(const KoColorSpace *colorSpace,
0253                              int gmicSpectrum,
0254                              float gmicUnitValue)
0255 {
0256     KoColorTransformation *colorTransformation = nullptr;
0257     if (colorSpace->colorModelId() != RGBAColorModelID) {
0258         dbgKrita << "Unsupported color space for fast pixel transformation to "
0259                     "gmic pixel format"
0260                  << colorSpace->id();
0261         return nullptr;
0262     }
0263 
0264     if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
0265         if (gmicSpectrum == 3 || gmicSpectrum == 4) {
0266             colorTransformation =
0267                 new KisColorFromFloat<float, KoRgbTraits<float>>(gmicUnitValue);
0268         } else if (gmicSpectrum == 1) {
0269             colorTransformation =
0270                 new KisColorFromGrayScaleFloat<float, KoRgbTraits<float>>(
0271                     gmicUnitValue);
0272         } else if (gmicSpectrum == 2) {
0273             colorTransformation =
0274                 new KisColorFromGrayScaleAlphaFloat<float, KoRgbTraits<float>>(
0275                     gmicUnitValue);
0276         }
0277     }
0278 #ifdef HAVE_OPENEXR
0279     else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
0280         if (gmicSpectrum == 3 || gmicSpectrum == 4) {
0281             colorTransformation =
0282                 new KisColorFromFloat<half, KoRgbTraits<half>>(gmicUnitValue);
0283         } else if (gmicSpectrum == 1) {
0284             colorTransformation =
0285                 new KisColorFromGrayScaleFloat<half, KoRgbTraits<half>>(
0286                     gmicUnitValue);
0287         } else if (gmicSpectrum == 2) {
0288             colorTransformation =
0289                 new KisColorFromGrayScaleAlphaFloat<half, KoRgbTraits<half>>(
0290                     gmicUnitValue);
0291         }
0292     }
0293 #endif
0294     else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
0295         if (gmicSpectrum == 3 || gmicSpectrum == 4) {
0296             colorTransformation =
0297                 new KisColorFromFloat<quint16, KoBgrTraits<quint16>>(
0298                     gmicUnitValue);
0299         } else if (gmicSpectrum == 1) {
0300             colorTransformation =
0301                 new KisColorFromGrayScaleFloat<quint16, KoBgrTraits<quint16>>(
0302                     gmicUnitValue);
0303         } else if (gmicSpectrum == 2) {
0304             colorTransformation =
0305                 new KisColorFromGrayScaleAlphaFloat<quint16,
0306                                                     KoBgrTraits<quint16>>(
0307                     gmicUnitValue);
0308         }
0309     } else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
0310         if (gmicSpectrum == 3 || gmicSpectrum == 4) {
0311             colorTransformation =
0312                 new KisColorFromFloat<quint8, KoBgrTraits<quint8>>(
0313                     gmicUnitValue);
0314         } else if (gmicSpectrum == 1) {
0315             colorTransformation =
0316                 new KisColorFromGrayScaleFloat<quint8, KoBgrTraits<quint8>>(
0317                     gmicUnitValue);
0318         } else if (gmicSpectrum == 2) {
0319             colorTransformation =
0320                 new KisColorFromGrayScaleAlphaFloat<quint8,
0321                                                     KoBgrTraits<quint8>>(
0322                     gmicUnitValue);
0323         }
0324     } else {
0325         dbgKrita << "Unsupported color space " << colorSpace->id()
0326                  << " for fast pixel transformation to gmic pixel format";
0327         return nullptr;
0328     }
0329 
0330     return colorTransformation;
0331 }
0332 
0333 static KoColorTransformation *
0334 createTransformation(const KoColorSpace *colorSpace)
0335 {
0336     KoColorTransformation *colorTransformation = nullptr;
0337     if (colorSpace->colorModelId() != RGBAColorModelID) {
0338         dbgKrita << "Unsupported color space for fast pixel transformation to "
0339                     "gmic pixel format"
0340                  << colorSpace->id();
0341         return nullptr;
0342     }
0343 
0344     if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
0345         colorTransformation =
0346             new KisColorToFloatConvertor<float, KoRgbTraits<float>>();
0347     }
0348 #ifdef HAVE_OPENEXR
0349     else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
0350         colorTransformation =
0351             new KisColorToFloatConvertor<half, KoRgbTraits<half>>();
0352     }
0353 #endif
0354     else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
0355         colorTransformation =
0356             new KisColorToFloatConvertor<quint16, KoBgrTraits<quint16>>();
0357     } else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
0358         colorTransformation =
0359             new KisColorToFloatConvertor<quint8, KoBgrTraits<quint8>>();
0360     } else {
0361         dbgKrita << "Unsupported color space " << colorSpace->id()
0362                  << " for fast pixel transformation to gmic pixel format";
0363         return nullptr;
0364     }
0365     return colorTransformation;
0366 }
0367 
0368 void KisQmicSimpleConvertor::convertFromGmicFast(const KisQMicImage &gmicImage,
0369                                                  KisPaintDeviceSP dst,
0370                                                  float gmicUnitValue)
0371 {
0372     dbgPlugins << "convertFromGmicFast";
0373     const KoColorSpace *dstColorSpace = dst->colorSpace();
0374     const KoColorTransformationSP gmicToDstPixelFormat(
0375         createTransformationFromGmic(dstColorSpace,
0376                                      gmicImage.m_spectrum,
0377                                      gmicUnitValue));
0378     if (!gmicToDstPixelFormat) {
0379         dbgPlugins << "Fall-back to slow color conversion";
0380         convertFromGmicImage(gmicImage, dst, gmicUnitValue);
0381         return;
0382     }
0383 
0384     dst->clear();
0385     dst->moveTo(0, 0);
0386 
0387     const qint32 x = 0;
0388     const qint32 y = 0;
0389     const auto width = static_cast<size_t>(gmicImage.m_width);
0390     const auto height = static_cast<size_t>(gmicImage.m_height);
0391 
0392     const auto *rgbaFloat32bitcolorSpace =
0393         KoColorSpaceRegistry::instance()->colorSpace(
0394             RGBAColorModelID.id(),
0395             Float32BitsColorDepthID.id(),
0396             KoColorSpaceRegistry::instance()->rgb8()->profile());
0397     // this function always convert to rgba or rgb with various color depth
0398     const quint32 dstNumChannels = rgbaFloat32bitcolorSpace->channelCount();
0399 
0400     // number of channels that we will copy
0401     const int numChannels = gmicImage.m_spectrum;
0402 
0403     // gmic image has 4, 3, 2, 1 channel
0404     std::vector<float *> planes(dstNumChannels);
0405     const size_t channelOffset = width * height;
0406     for (int channelIndex = 0; channelIndex < gmicImage.m_spectrum;
0407          channelIndex++) {
0408         planes[channelIndex] = gmicImage.m_data + channelOffset * channelIndex;
0409     }
0410 
0411     for (int channelIndex = gmicImage.m_spectrum;
0412          channelIndex < (int)dstNumChannels;
0413          channelIndex++) {
0414         planes[channelIndex] = 0; // turn off
0415     }
0416 
0417     size_t dataY = 0;
0418     int imageY = y;
0419     size_t rowsRemaining = height;
0420 
0421     const auto floatPixelSize = rgbaFloat32bitcolorSpace->pixelSize();
0422 
0423     KisRandomAccessorSP it = dst->createRandomAccessorNG();
0424     const auto tileWidth =
0425         static_cast<size_t>(it->numContiguousColumns(dst->x()));
0426     const auto tileHeight =
0427         static_cast<size_t>(it->numContiguousRows(dst->y()));
0428     Q_ASSERT(tileWidth == 64);
0429     Q_ASSERT(tileHeight == 64);
0430     std::vector<quint8> convertedTile(
0431         static_cast<size_t>(rgbaFloat32bitcolorSpace->pixelSize()) * tileWidth
0432         * tileHeight);
0433 
0434     // grayscale and rgb case does not have alpha, so let's fill 4th channel of
0435     // rgba tile with opacity opaque
0436     if (gmicImage.m_spectrum == 1 || gmicImage.m_spectrum == 3) {
0437         const auto nPixels = tileWidth * tileHeight;
0438         auto *srcPixel =
0439             reinterpret_cast<KoRgbF32Traits::Pixel *>(convertedTile.data());
0440         for (size_t pixelIndex = 0; pixelIndex < nPixels;
0441              pixelIndex++, srcPixel++) {
0442             srcPixel->alpha = gmicUnitValue;
0443         }
0444     }
0445 
0446     while (rowsRemaining > 0) {
0447         size_t dataX = 0;
0448         qint32 imageX = x;
0449         size_t columnsRemaining = width;
0450         const auto numContiguousImageRows =
0451             static_cast<size_t>(it->numContiguousRows(imageY));
0452 
0453         size_t rowsToWork = qMin(numContiguousImageRows, rowsRemaining);
0454 
0455         while (columnsRemaining > 0) {
0456             const auto numContiguousImageColumns =
0457                 static_cast<size_t>(it->numContiguousColumns(imageX));
0458             auto columnsToWork =
0459                 qMin(numContiguousImageColumns, columnsRemaining);
0460 
0461             const auto dataIdx = dataX + dataY * width;
0462             const auto tileRowStride =
0463                 (tileWidth - columnsToWork) * floatPixelSize;
0464 
0465             auto *tileItStart = convertedTile.data();
0466             // copy gmic channels to float tile
0467             const auto channelSize = sizeof(float);
0468             for (int i = 0; i < numChannels; i++) {
0469                 float *planeIt = planes[i] + dataIdx;
0470                 const auto dataStride = width - columnsToWork;
0471                 quint8 *tileIt = tileItStart;
0472 
0473                 for (size_t row = 0; row < rowsToWork; row++) {
0474                     for (size_t col = 0; col < columnsToWork; col++) {
0475                         memcpy(tileIt, planeIt, channelSize);
0476                         tileIt += floatPixelSize;
0477                         planeIt += 1;
0478                     }
0479 
0480                     tileIt += tileRowStride;
0481                     planeIt += dataStride;
0482                 }
0483                 tileItStart += channelSize;
0484             }
0485 
0486             it->moveTo(imageX, imageY);
0487             quint8 *dstTileItStart = it->rawData();
0488             tileItStart =
0489                 convertedTile.data(); // back to the start of the converted tile
0490             // copy float tile to dst colorspace based on input colorspace (rgb
0491             // or grayscale)
0492             for (size_t row = 0; row < rowsToWork; row++) {
0493                 gmicToDstPixelFormat->transform(
0494                     tileItStart,
0495                     dstTileItStart,
0496                     static_cast<int>(columnsToWork));
0497                 dstTileItStart += dstColorSpace->pixelSize() * tileWidth;
0498                 tileItStart += floatPixelSize * tileWidth;
0499             }
0500 
0501             imageX += static_cast<int>(columnsToWork);
0502             dataX += columnsToWork;
0503             columnsRemaining -= columnsToWork;
0504         }
0505 
0506         imageY += static_cast<int>(rowsToWork);
0507         dataY += rowsToWork;
0508         rowsRemaining -= rowsToWork;
0509     }
0510 
0511     dst->crop(x, y, static_cast<qint32>(width), static_cast<qint32>(height));
0512 }
0513 
0514 void KisQmicSimpleConvertor::convertToGmicImageFast(KisPaintDeviceSP dev,
0515                                                     KisQMicImage &gmicImage,
0516                                                     QRect rc)
0517 {
0518     KoColorTransformationSP pixelToGmicPixelFormat(
0519         createTransformation(dev->colorSpace()));
0520     if (!pixelToGmicPixelFormat) {
0521         dbgPlugins << "Fall-back to slow color conversion method";
0522         convertToGmicImage(dev, gmicImage, rc);
0523         return;
0524     }
0525 
0526     if (rc.isEmpty()) {
0527         dbgPlugins
0528             << "Image rectangle is empty! Using supplied gmic layer dimension";
0529         rc = QRect(0,
0530                    0,
0531                    static_cast<int>(gmicImage.m_width),
0532                    static_cast<int>(gmicImage.m_height));
0533     }
0534 
0535     const auto x = rc.x();
0536     const auto y = rc.y();
0537     const size_t width = rc.width() < 0 ? 0 : static_cast<size_t>(rc.width());
0538     const size_t height =
0539         rc.height() < 0 ? 0 : static_cast<size_t>(rc.height());
0540 
0541     const qint32 numChannels = 4;
0542 
0543     const size_t greenOffset =
0544         static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
0545     const size_t blueOffset = greenOffset * 2;
0546     const size_t alphaOffset = greenOffset * 3;
0547 
0548     const std::array<float *, 4> planes = {gmicImage.m_data,
0549                                            gmicImage.m_data + greenOffset,
0550                                            gmicImage.m_data + blueOffset,
0551                                            gmicImage.m_data + alphaOffset};
0552 
0553     KisRandomConstAccessorSP it = dev->createRandomConstAccessorNG();
0554     const auto tileWidth = it->numContiguousColumns(dev->x());
0555     const auto tileHeight = it->numContiguousRows(dev->y());
0556 
0557     Q_ASSERT(tileWidth == 64);
0558     Q_ASSERT(tileHeight == 64);
0559 
0560     const KoColorSpace *rgbaFloat32bitcolorSpace =
0561         KoColorSpaceRegistry::instance()->colorSpace(
0562             RGBAColorModelID.id(),
0563             Float32BitsColorDepthID.id(),
0564             KoColorSpaceRegistry::instance()->rgb8()->profile());
0565     Q_CHECK_PTR(rgbaFloat32bitcolorSpace);
0566     const auto dstPixelSize = rgbaFloat32bitcolorSpace->pixelSize();
0567     const auto srcPixelSize = dev->pixelSize();
0568 
0569     std::vector<quint8> dstTile(dstPixelSize * static_cast<size_t>(tileWidth)
0570                                 * static_cast<size_t>(tileHeight));
0571 
0572     size_t dataY = 0;
0573     int imageY = y;
0574     int imageX = x;
0575     it->moveTo(imageX, imageY);
0576     size_t rowsRemaining = height;
0577 
0578     while (rowsRemaining > 0) {
0579         size_t dataX = 0;
0580         imageX = x;
0581         size_t columnsRemaining = width;
0582         const auto numContiguousImageRows = it->numContiguousRows(imageY);
0583 
0584         const auto rowsToWork =
0585             qMin(numContiguousImageRows, static_cast<qint32>(rowsRemaining));
0586         const auto convertedTileY = tileHeight - rowsToWork;
0587         Q_ASSERT(convertedTileY >= 0);
0588 
0589         while (columnsRemaining > 0) {
0590             const auto numContiguousImageColumns =
0591                 static_cast<size_t>(it->numContiguousColumns(imageX));
0592             const auto columnsToWork =
0593                 qMin(numContiguousImageColumns, columnsRemaining);
0594             const auto convertedTileX =
0595                 tileWidth - static_cast<qint32>(columnsToWork);
0596             Q_ASSERT(convertedTileX >= 0);
0597 
0598             const auto dataIdx = dataX + dataY * width;
0599             const auto dstTileIndex =
0600                 convertedTileX + convertedTileY * tileWidth;
0601             const auto tileRowStride =
0602                 (static_cast<size_t>(tileWidth) - columnsToWork) * dstPixelSize;
0603             const auto srcTileRowStride =
0604                 (static_cast<size_t>(tileWidth) - columnsToWork) * srcPixelSize;
0605 
0606             it->moveTo(imageX, imageY);
0607             quint8 *tileItStart = dstTile.data() + dstTileIndex * dstPixelSize;
0608 
0609             // transform tile row by row
0610             auto *dstTileIt = tileItStart;
0611             const auto *srcTileIt = it->rawDataConst();
0612 
0613             auto row = rowsToWork;
0614             while (row > 0) {
0615                 pixelToGmicPixelFormat->transform(
0616                     srcTileIt,
0617                     dstTileIt,
0618                     static_cast<qint32>(columnsToWork));
0619                 srcTileIt += columnsToWork * srcPixelSize;
0620                 srcTileIt += srcTileRowStride;
0621 
0622                 dstTileIt += columnsToWork * dstPixelSize;
0623                 dstTileIt += tileRowStride;
0624 
0625                 row--;
0626             }
0627 
0628             // here we want to copy floats to dstTile, so tileItStart has to
0629             // point to float buffer
0630             const auto channelSize = sizeof(float);
0631             for (size_t i = 0; i < numChannels; i++) {
0632                 float *planeIt = planes.at(i) + dataIdx;
0633                 const auto dataStride = (width - columnsToWork);
0634                 quint8 *tileIt = tileItStart;
0635 
0636                 for (int row = 0; row < rowsToWork; row++) {
0637                     for (size_t col = 0; col < columnsToWork; col++) {
0638                         memcpy(planeIt, tileIt, channelSize);
0639                         tileIt += dstPixelSize;
0640                         planeIt += 1;
0641                     }
0642 
0643                     tileIt += tileRowStride;
0644                     planeIt += dataStride;
0645                 }
0646                 // skip channel in tile: red, green, blue, alpha
0647                 tileItStart += channelSize;
0648             }
0649 
0650             imageX += static_cast<int>(columnsToWork);
0651             dataX += columnsToWork;
0652             columnsRemaining -= columnsToWork;
0653         }
0654 
0655         imageY += static_cast<int>(rowsToWork);
0656         dataY += rowsToWork;
0657         rowsRemaining -= rowsToWork;
0658     }
0659 }
0660 
0661 // gmic assumes float rgba in 0.0 - 255.0
0662 void KisQmicSimpleConvertor::convertToGmicImage(KisPaintDeviceSP dev,
0663                                                 KisQMicImage &gmicImage,
0664                                                 QRect rc)
0665 {
0666     Q_ASSERT(!dev.isNull());
0667     Q_ASSERT(gmicImage.m_spectrum == 4); // rgba
0668 
0669     if (rc.isEmpty()) {
0670         rc = QRect(0,
0671                    0,
0672                    static_cast<int>(gmicImage.m_width),
0673                    static_cast<int>(gmicImage.m_height));
0674     }
0675 
0676     const KoColorSpace *rgbaFloat32bitcolorSpace =
0677         KoColorSpaceRegistry::instance()->colorSpace(
0678             RGBAColorModelID.id(),
0679             Float32BitsColorDepthID.id(),
0680             KoColorSpaceRegistry::instance()->rgb8()->profile());
0681     Q_CHECK_PTR(rgbaFloat32bitcolorSpace);
0682 
0683     const KoColorTransformationSP pixelToGmicPixelFormat(
0684         createTransformation(rgbaFloat32bitcolorSpace));
0685 
0686     const size_t greenOffset =
0687         static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
0688     const size_t blueOffset = greenOffset * 2;
0689     const size_t alphaOffset = greenOffset * 3;
0690 
0691     const auto renderingIntent =
0692         KoColorConversionTransformation::internalRenderingIntent();
0693     const auto conversionFlags =
0694         KoColorConversionTransformation::internalConversionFlags();
0695 
0696     const KoColorSpace *colorSpace = dev->colorSpace();
0697     KisRandomConstAccessorSP it = dev->createRandomConstAccessorNG();
0698 
0699     const size_t optimalBufferSize =
0700         64; // most common numContiguousColumns, tile size?
0701     std::vector<quint8> floatRGBApixelStorage(
0702         rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize);
0703     quint8 *floatRGBApixel = floatRGBApixelStorage.data();
0704 
0705     const auto pixelSize = rgbaFloat32bitcolorSpace->pixelSize();
0706     for (int y = 0; y < rc.height(); y++) {
0707         int x = 0;
0708         while (x < rc.width()) {
0709             it->moveTo(rc.x() + x, rc.y() + y);
0710             auto numContiguousColumns =
0711                 qMin(static_cast<size_t>(it->numContiguousColumns(rc.x() + x)),
0712                      optimalBufferSize);
0713             numContiguousColumns =
0714                 qMin(numContiguousColumns, static_cast<size_t>(rc.width() - x));
0715 
0716             colorSpace->convertPixelsTo(
0717                 it->rawDataConst(),
0718                 floatRGBApixel,
0719                 rgbaFloat32bitcolorSpace,
0720                 static_cast<quint32>(numContiguousColumns),
0721                 renderingIntent,
0722                 conversionFlags);
0723             pixelToGmicPixelFormat->transform(
0724                 floatRGBApixel,
0725                 floatRGBApixel,
0726                 static_cast<qint32>(numContiguousColumns));
0727 
0728             auto pos = static_cast<size_t>(y) * gmicImage.m_width
0729                 + static_cast<size_t>(x);
0730             for (size_t bx = 0; bx < numContiguousColumns; bx++) {
0731                 memcpy(gmicImage.m_data + pos,
0732                        floatRGBApixel + bx * pixelSize,
0733                        4);
0734                 memcpy(gmicImage.m_data + pos + greenOffset,
0735                        floatRGBApixel + bx * pixelSize + 4,
0736                        4);
0737                 memcpy(gmicImage.m_data + pos + blueOffset,
0738                        floatRGBApixel + bx * pixelSize + 8,
0739                        4);
0740                 memcpy(gmicImage.m_data + pos + alphaOffset,
0741                        floatRGBApixel + bx * pixelSize + 12,
0742                        4);
0743                 pos++;
0744             }
0745 
0746             x += static_cast<int>(numContiguousColumns);
0747         }
0748     }
0749 }
0750 
0751 void KisQmicSimpleConvertor::convertFromGmicImage(const KisQMicImage &gmicImage,
0752                                                   KisPaintDeviceSP dst,
0753                                                   float gmicMaxChannelValue)
0754 {
0755     dbgPlugins << "convertFromGmicSlow";
0756     Q_ASSERT(!dst.isNull());
0757     const KoColorSpace *rgbaFloat32bitcolorSpace =
0758         KoColorSpaceRegistry::instance()->colorSpace(
0759             RGBAColorModelID.id(),
0760             Float32BitsColorDepthID.id(),
0761             KoColorSpaceRegistry::instance()->rgb8()->profile());
0762     const KoColorSpace *dstColorSpace = dst->colorSpace();
0763 
0764     KisPaintDeviceSP dev = dst;
0765     const size_t greenOffset =
0766         static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
0767     const size_t blueOffset = greenOffset * 2;
0768     const size_t alphaOffset = greenOffset * 3;
0769     QRect rc(0,
0770              0,
0771              static_cast<int>(gmicImage.m_width),
0772              static_cast<int>(gmicImage.m_height));
0773 
0774     KisRandomAccessorSP it = dev->createRandomAccessorNG();
0775     float r = 0;
0776     float g = 0;
0777     float b = 0;
0778     float a = 1.0;
0779 
0780     const size_t optimalBufferSize =
0781         64; // most common numContiguousColumns, tile size?
0782     std::vector<quint8> floatRGBApixelStorage(
0783         rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize);
0784     quint8 *floatRGBApixel = floatRGBApixelStorage.data();
0785     const auto pixelSize = rgbaFloat32bitcolorSpace->pixelSize();
0786 
0787     const auto renderingIntent =
0788         KoColorConversionTransformation::internalRenderingIntent();
0789     const auto conversionFlags =
0790         KoColorConversionTransformation::internalConversionFlags();
0791 
0792     // Krita needs rgba in 0.0...1.0
0793     const float multiplied =
0794         KoColorSpaceMathsTraits<float>::unitValue / gmicMaxChannelValue;
0795 
0796     switch (gmicImage.m_spectrum) {
0797     case 1: {
0798         // convert grayscale to rgba
0799         for (int y = 0; y < rc.height(); y++) {
0800             int x = 0;
0801             while (x < rc.width()) {
0802                 it->moveTo(x, y);
0803                 auto numContiguousColumns =
0804                     qMin(static_cast<size_t>(it->numContiguousColumns(x)),
0805                          optimalBufferSize);
0806                 numContiguousColumns =
0807                     qMin(numContiguousColumns,
0808                          static_cast<size_t>(rc.width() - x));
0809 
0810                 size_t pos = static_cast<size_t>(y) * gmicImage.m_width
0811                     + static_cast<size_t>(x);
0812                 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
0813                     r = g = b = gmicImage.m_data[pos] * multiplied;
0814                     a = KoColorSpaceMathsTraits<float>::unitValue;
0815 
0816                     memcpy(floatRGBApixel + bx * pixelSize, &r, 4);
0817                     memcpy(floatRGBApixel + bx * pixelSize + 4, &g, 4);
0818                     memcpy(floatRGBApixel + bx * pixelSize + 8, &b, 4);
0819                     memcpy(floatRGBApixel + bx * pixelSize + 12, &a, 4);
0820                     pos++;
0821                 }
0822                 rgbaFloat32bitcolorSpace->convertPixelsTo(
0823                     floatRGBApixel,
0824                     it->rawData(),
0825                     dstColorSpace,
0826                     static_cast<quint32>(numContiguousColumns),
0827                     renderingIntent,
0828                     conversionFlags);
0829                 x += static_cast<int>(numContiguousColumns);
0830             }
0831         }
0832         break;
0833     }
0834     case 2: {
0835         // convert grayscale alpha to rgba
0836         for (int y = 0; y < rc.height(); y++) {
0837             int x = 0;
0838             while (x < rc.width()) {
0839                 it->moveTo(x, y);
0840                 auto numContiguousColumns =
0841                     qMin(static_cast<size_t>(it->numContiguousColumns(x)),
0842                          optimalBufferSize);
0843                 numContiguousColumns =
0844                     qMin(numContiguousColumns,
0845                          static_cast<size_t>(rc.width() - x));
0846 
0847                 size_t pos = static_cast<size_t>(y) * gmicImage.m_width
0848                     + static_cast<size_t>(x);
0849                 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
0850                     r = g = b = gmicImage.m_data[pos] * multiplied;
0851                     a = gmicImage.m_data[pos + greenOffset] * multiplied;
0852 
0853                     memcpy(floatRGBApixel + bx * pixelSize, &r, 4);
0854                     memcpy(floatRGBApixel + bx * pixelSize + 4, &g, 4);
0855                     memcpy(floatRGBApixel + bx * pixelSize + 8, &b, 4);
0856                     memcpy(floatRGBApixel + bx * pixelSize + 12, &a, 4);
0857                     pos++;
0858                 }
0859                 rgbaFloat32bitcolorSpace->convertPixelsTo(
0860                     floatRGBApixel,
0861                     it->rawData(),
0862                     dstColorSpace,
0863                     static_cast<quint32>(numContiguousColumns),
0864                     renderingIntent,
0865                     conversionFlags);
0866                 x += static_cast<int>(numContiguousColumns);
0867             }
0868         }
0869         break;
0870     }
0871     case 3: {
0872         // convert rgb -> rgba
0873         for (int y = 0; y < rc.height(); y++) {
0874             int x = 0;
0875             while (x < rc.width()) {
0876                 it->moveTo(x, y);
0877                 auto numContiguousColumns =
0878                     qMin(static_cast<size_t>(it->numContiguousColumns(x)),
0879                          optimalBufferSize);
0880                 numContiguousColumns =
0881                     qMin(numContiguousColumns,
0882                          static_cast<size_t>(rc.width() - x));
0883 
0884                 size_t pos = static_cast<size_t>(y) * gmicImage.m_width
0885                     + static_cast<size_t>(x);
0886                 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
0887                     r = gmicImage.m_data[pos] * multiplied;
0888                     g = gmicImage.m_data[pos + greenOffset] * multiplied;
0889                     b = gmicImage.m_data[pos + blueOffset] * multiplied;
0890                     a = gmicMaxChannelValue * multiplied;
0891 
0892                     memcpy(floatRGBApixel + bx * pixelSize, &r, 4);
0893                     memcpy(floatRGBApixel + bx * pixelSize + 4, &g, 4);
0894                     memcpy(floatRGBApixel + bx * pixelSize + 8, &b, 4);
0895                     memcpy(floatRGBApixel + bx * pixelSize + 12, &a, 4);
0896                     pos++;
0897                 }
0898                 rgbaFloat32bitcolorSpace->convertPixelsTo(
0899                     floatRGBApixel,
0900                     it->rawData(),
0901                     dstColorSpace,
0902                     static_cast<quint32>(numContiguousColumns),
0903                     renderingIntent,
0904                     conversionFlags);
0905                 x += static_cast<int>(numContiguousColumns);
0906             }
0907         }
0908         break;
0909     }
0910     case 4: {
0911         for (int y = 0; y < rc.height(); y++) {
0912             int x = 0;
0913             while (x < rc.width()) {
0914                 it->moveTo(x, y);
0915                 auto numContiguousColumns =
0916                     qMin(static_cast<size_t>(it->numContiguousColumns(x)),
0917                          optimalBufferSize);
0918                 numContiguousColumns =
0919                     qMin(numContiguousColumns,
0920                          static_cast<size_t>(rc.width() - x));
0921 
0922                 size_t pos = static_cast<size_t>(y) * gmicImage.m_width
0923                     + static_cast<size_t>(x);
0924                 for (size_t bx = 0; bx < numContiguousColumns; bx++) {
0925                     r = gmicImage.m_data[pos] * multiplied;
0926                     g = gmicImage.m_data[pos + greenOffset] * multiplied;
0927                     b = gmicImage.m_data[pos + blueOffset] * multiplied;
0928                     a = gmicImage.m_data[pos + alphaOffset] * multiplied;
0929 
0930                     memcpy(floatRGBApixel + bx * pixelSize, &r, 4);
0931                     memcpy(floatRGBApixel + bx * pixelSize + 4, &g, 4);
0932                     memcpy(floatRGBApixel + bx * pixelSize + 8, &b, 4);
0933                     memcpy(floatRGBApixel + bx * pixelSize + 12, &a, 4);
0934                     pos++;
0935                 }
0936                 rgbaFloat32bitcolorSpace->convertPixelsTo(
0937                     floatRGBApixel,
0938                     it->rawData(),
0939                     dstColorSpace,
0940                     static_cast<quint32>(numContiguousColumns),
0941                     renderingIntent,
0942                     conversionFlags);
0943                 x += static_cast<int>(numContiguousColumns);
0944             }
0945         }
0946         break;
0947     }
0948 
0949     default: {
0950         dbgPlugins << "Unsupported gmic output format : " << gmicImage.m_width
0951                    << gmicImage.m_height << gmicImage.m_spectrum;
0952     }
0953     }
0954 }
0955 
0956 QImage KisQmicSimpleConvertor::convertToQImage(const KisQMicImage &gmicImage,
0957                                                float gmicActualMaxChannelValue)
0958 {
0959     QImage image = QImage(static_cast<int>(gmicImage.m_width),
0960                           static_cast<int>(gmicImage.m_height),
0961                           QImage::Format_ARGB32);
0962 
0963     dbgPlugins << image.format() << "first pixel:" << gmicImage.m_data[0]
0964                << gmicImage.m_width << gmicImage.m_height
0965                << gmicImage.m_spectrum;
0966 
0967     const size_t greenOffset =
0968         static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
0969     const size_t blueOffset = greenOffset * 2;
0970 
0971     // always put 255 to qimage
0972     const float multiplied = 255.0f / gmicActualMaxChannelValue;
0973 
0974     for (int y = 0; y < gmicImage.m_height; y++) {
0975         QRgb *pixel =
0976             reinterpret_cast<QRgb *>(image.scanLine(static_cast<int>(y)));
0977         for (int x = 0; x < gmicImage.m_width; x++) {
0978             const auto pos = y * gmicImage.m_width + x;
0979             const float r = gmicImage.m_data[pos] * multiplied;
0980             const float g = gmicImage.m_data[pos + greenOffset] * multiplied;
0981             const float b = gmicImage.m_data[pos + blueOffset] * multiplied;
0982             pixel[x] = qRgb(int(r), int(g), int(b));
0983         }
0984     }
0985     return image;
0986 }
0987 
0988 void KisQmicSimpleConvertor::convertFromQImage(const QImage &image,
0989                                                KisQMicImage &gmicImage,
0990                                                float gmicUnitValue)
0991 {
0992     const auto greenOffset =
0993         static_cast<size_t>(gmicImage.m_width) * gmicImage.m_height;
0994     const size_t blueOffset = greenOffset * 2;
0995     const size_t alphaOffset = greenOffset * 3;
0996 
0997     // QImage has 0..255
0998     const float qimageUnitValue = 255.0f;
0999     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 
1078 std::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 
1087 QString KisQmicSimpleConvertor::blendingModeToString(QString blendMode)
1088 {
1089     static auto reverseModeMap = reverseMap();
1090     if (reverseModeMap.find(blendMode) != reverseModeMap.end())
1091         return reverseModeMap.at(blendMode);
1092     return "alpha";
1093 }
1094 
1095 QString KisQmicSimpleConvertor::stringToBlendingMode(QString blendMode)
1096 {
1097     if (blendingModeMap.find(blendMode) != blendingModeMap.end())
1098         return blendingModeMap.at(blendMode);
1099     return COMPOSITE_OVER;
1100 }