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 }