// gmic assumes float rgba in 0.0 - 255.0, thus default value void KisGmicSimpleConvertor::convertToGmicImage(KisPaintDeviceSP dev, gmic_image<float>& gmicImage, QRect rc) { Q_ASSERT(!dev.isNull()); Q_ASSERT(gmicImage._spectrum == 4); // rgba if (rc.isEmpty()) { rc = QRect(0,0,gmicImage._width, gmicImage._height); } const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), KoColorSpaceRegistry::instance()->rgb8()->profile()); Q_CHECK_PTR(rgbaFloat32bitcolorSpace); int greenOffset = gmicImage._width * gmicImage._height; int blueOffset = greenOffset * 2; int alphaOffset = greenOffset * 3; KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(); KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags(); const KoColorSpace * colorSpace = dev->colorSpace(); KisRandomConstAccessorSP it = dev->createRandomConstAccessorNG(0,0); int optimalBufferSize = 64; // most common numContiguousColumns, tile size? quint8 * floatRGBApixel = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize]; quint32 pixelSize = rgbaFloat32bitcolorSpace->pixelSize(); int pos = 0; for (int y = 0; y < rc.height(); y++) { int x = 0; while (x < rc.width()) { it->moveTo(x, y); qint32 numContiguousColumns = qMin(it->numContiguousColumns(x), optimalBufferSize); numContiguousColumns = qMin(numContiguousColumns, rc.width() - x); colorSpace->convertPixelsTo(it->rawDataConst(), floatRGBApixel, rgbaFloat32bitcolorSpace, numContiguousColumns, renderingIntent, conversionFlags); pos = y * gmicImage._width + x; for (qint32 bx = 0; bx < numContiguousColumns; bx++) { memcpy(gmicImage._data + pos ,floatRGBApixel + bx * pixelSize , 4); memcpy(gmicImage._data + pos + greenOffset ,floatRGBApixel + bx * pixelSize + 4, 4); memcpy(gmicImage._data + pos + blueOffset ,floatRGBApixel + bx * pixelSize + 8, 4); memcpy(gmicImage._data + pos + alphaOffset ,floatRGBApixel + bx * pixelSize + 12, 4); pos++; } x += numContiguousColumns; } } delete [] floatRGBApixel; }
void KisGmicSimpleConvertor::convertToGmicImageFast(KisPaintDeviceSP dev, CImg< float >& gmicImage, QRect rc) { KoColorTransformation * pixelToGmicPixelFormat = createTransformation(dev->colorSpace()); if (pixelToGmicPixelFormat == 0) { dbgPlugins << "Fall-back to slow color conversion method"; convertToGmicImage(dev, gmicImage, rc); return; } if (rc.isEmpty()) { dbgPlugins << "Image rectangle is empty! Using supplied gmic layer dimension"; rc = QRect(0,0,gmicImage._width, gmicImage._height); } qint32 x = rc.x(); qint32 y = rc.y(); qint32 width = rc.width(); qint32 height = rc.height(); width = width < 0 ? 0 : width; height = height < 0 ? 0 : height; const qint32 numChannels = 4; int greenOffset = gmicImage._width * gmicImage._height; int blueOffset = greenOffset * 2; int alphaOffset = greenOffset * 3; QVector<float *> planes; planes.append(gmicImage._data); planes.append(gmicImage._data + greenOffset); planes.append(gmicImage._data + blueOffset); planes.append(gmicImage._data + alphaOffset); KisRandomConstAccessorSP it = dev->createRandomConstAccessorNG(dev->x(), dev->y()); int tileWidth = it->numContiguousColumns(dev->x()); int tileHeight = it->numContiguousRows(dev->y()); Q_ASSERT(tileWidth == 64); Q_ASSERT(tileHeight == 64); const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), KoColorSpaceRegistry::instance()->rgb8()->profile()); Q_CHECK_PTR(rgbaFloat32bitcolorSpace); const qint32 dstPixelSize = rgbaFloat32bitcolorSpace->pixelSize(); const qint32 srcPixelSize = dev->pixelSize(); quint8 * dstTile = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * tileWidth * tileHeight]; qint32 dataY = 0; qint32 imageX = x; qint32 imageY = y; it->moveTo(imageX, imageY); qint32 rowsRemaining = height; while (rowsRemaining > 0) { qint32 dataX = 0; imageX = x; qint32 columnsRemaining = width; qint32 numContiguousImageRows = it->numContiguousRows(imageY); qint32 rowsToWork = qMin(numContiguousImageRows, rowsRemaining); qint32 convertedTileY = tileHeight - rowsToWork; Q_ASSERT(convertedTileY >= 0); while (columnsRemaining > 0) { qint32 numContiguousImageColumns = it->numContiguousColumns(imageX); qint32 columnsToWork = qMin(numContiguousImageColumns, columnsRemaining); qint32 convertedTileX = tileWidth - columnsToWork; Q_ASSERT(convertedTileX >= 0); const qint32 dataIdx = dataX + dataY * width; const qint32 dstTileIndex = convertedTileX + convertedTileY * tileWidth; const qint32 tileRowStride = (tileWidth - columnsToWork) * dstPixelSize; const qint32 srcTileRowStride = (tileWidth - columnsToWork) * srcPixelSize; it->moveTo(imageX, imageY); quint8 *tileItStart = dstTile + dstTileIndex * dstPixelSize; // transform tile row by row quint8 *dstTileIt = tileItStart; quint8 *srcTileIt = const_cast<quint8*>(it->rawDataConst()); qint32 row = rowsToWork; while (row > 0) { pixelToGmicPixelFormat->transform(srcTileIt, dstTileIt , columnsToWork); srcTileIt += columnsToWork * srcPixelSize; srcTileIt += srcTileRowStride; dstTileIt += columnsToWork * dstPixelSize; dstTileIt += tileRowStride; row--; } // here we want to copy floats to dstTile, so tileItStart has to point to float buffer qint32 channelSize = sizeof(float); for(qint32 i=0; i<numChannels;i++) { float * planeIt = planes[i] + dataIdx; qint32 dataStride = (width - columnsToWork); quint8* tileIt = tileItStart; for (qint32 row = 0; row < rowsToWork; row++) { for (int col = 0; col < columnsToWork; col++) { memcpy(planeIt, tileIt, channelSize); tileIt += dstPixelSize; planeIt += 1; } tileIt += tileRowStride; planeIt += dataStride; } // skip channel in tile: red, green, blue, alpha tileItStart += channelSize; } imageX += columnsToWork; dataX += columnsToWork; columnsRemaining -= columnsToWork; } imageY += rowsToWork; dataY += rowsToWork; rowsRemaining -= rowsToWork; } delete [] dstTile; delete pixelToGmicPixelFormat; }