void KisPaintDeviceTest::testMakeClone() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP srcDev = new KisPaintDevice(cs); srcDev->convertFromQImage(image, 0); srcDev->move(10,10); const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->lab16(); KisPaintDeviceSP dstDev = new KisPaintDevice(weirdCS); dstDev->move(1000,1000); QVERIFY(!dstDev->fastBitBltPossible(srcDev)); QRect cloneRect(100,100,200,200); QPoint errpoint; dstDev->makeCloneFrom(srcDev, cloneRect); QVERIFY(*dstDev->colorSpace() == *srcDev->colorSpace()); QCOMPARE(dstDev->pixelSize(), srcDev->pixelSize()); QCOMPARE(dstDev->x(), srcDev->x()); QCOMPARE(dstDev->y(), srcDev->y()); QCOMPARE(dstDev->exactBounds(), cloneRect); QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); if (!TestUtil::compareQImages(errpoint, dstImage, srcImage)) { QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } }
void KisPaintDeviceTest::testCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->objectName().isEmpty()); dev = new KisPaintDevice(cs); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->x() == 0); QVERIFY(dev->y() == 0); QVERIFY(dev->pixelSize() == cs->pixelSize()); QVERIFY(dev->channelCount() == cs->channelCount()); QVERIFY(dev->dataManager() != 0); KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test"); KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125); dev = new KisPaintDevice(layer.data(), cs); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->x() == 0); QVERIFY(dev->y() == 0); QVERIFY(dev->pixelSize() == cs->pixelSize()); QVERIFY(dev->channelCount() == cs->channelCount()); QVERIFY(dev->dataManager() != 0); // Let the layer go out of scope and see what happens { KisPaintLayerSP l2 = new KisPaintLayer(image, "blabla", 250); dev = new KisPaintDevice(l2.data(), cs); } }
void KisClipboardTest::testRoundTrip() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPaintDeviceSP newDev; QPoint errorPoint; QRect fillRect(10,10,20,20); KoColor pixel(Qt::red, cs); dev->fill(fillRect.x(),fillRect.y(), fillRect.width(), fillRect.height(), pixel.data()); QCOMPARE(dev->exactBounds(), fillRect); KisClipboard::instance()->setClip(dev, QPoint()); newDev = KisClipboard::instance()->clip(QPoint()); QCOMPARE(newDev->exactBounds().size(), fillRect.size()); newDev->setX(dev->x()); newDev->setY(dev->y()); QVERIFY(TestUtil::comparePaintDevices(errorPoint, dev, newDev)); QPoint offset(100,100); dev->setX(offset.x()); dev->setY(offset.y()); QCOMPARE(dev->exactBounds(), fillRect.translated(offset)); KisClipboard::instance()->setClip(dev, QPoint()); newDev = KisClipboard::instance()->clip(QPoint()); QCOMPARE(newDev->exactBounds().size(), fillRect.translated(offset).size()); newDev->setX(dev->x()); newDev->setY(dev->y()); QVERIFY(TestUtil::comparePaintDevices(errorPoint, dev, newDev)); }
void KisKraLoaderTest::testLoadAnimated() { KisDocument *doc = KisPart::instance()->createDocument(); doc->loadNativeFormat(QString(FILES_DATA_DIR) + QDir::separator() + "load_test_animation.kra"); KisImageSP image = doc->image(); KisNodeSP node1 = image->root()->firstChild(); KisNodeSP node2 = node1->nextSibling(); QVERIFY(node1->inherits("KisPaintLayer")); QVERIFY(node2->inherits("KisPaintLayer")); KisPaintLayerSP layer1 = qobject_cast<KisPaintLayer*>(node1.data()); KisPaintLayerSP layer2 = qobject_cast<KisPaintLayer*>(node2.data()); KisKeyframeChannel *channel1 = layer1->getKeyframeChannel(KisKeyframeChannel::Content.id()); KisKeyframeChannel *channel2 = layer2->getKeyframeChannel(KisKeyframeChannel::Content.id()); QCOMPARE(channel1->keyframeCount(), 3); QCOMPARE(channel2->keyframeCount(), 1); QCOMPARE(image->animationInterface()->framerate(), 17); QCOMPARE(image->animationInterface()->fullClipRange(), KisTimeRange::fromTime(15, 45)); QCOMPARE(image->animationInterface()->currentTime(), 19); KisPaintDeviceSP dev = layer1->paintDevice(); const KoColorSpace *cs = dev->colorSpace(); KoColor transparent(Qt::transparent, cs); KoColor white(Qt::white, cs); KoColor red(Qt::red, cs); image->animationInterface()->switchCurrentTimeAsync(0); image->waitForDone(); QCOMPARE(dev->exactBounds(), QRect(506, 378, 198, 198)); QCOMPARE(dev->x(), -26); QCOMPARE(dev->y(), -128); QCOMPARE(dev->defaultPixel(), transparent); image->animationInterface()->switchCurrentTimeAsync(20); image->waitForDone(); QCOMPARE(dev->nonDefaultPixelArea(), QRect(615, 416, 129, 129)); QCOMPARE(dev->x(), 502); QCOMPARE(dev->y(), 224); QCOMPARE(dev->defaultPixel(), white); image->animationInterface()->switchCurrentTimeAsync(30); image->waitForDone(); QCOMPARE(dev->nonDefaultPixelArea(), QRect(729, 452, 45, 44)); QCOMPARE(dev->x(), 645); QCOMPARE(dev->y(), -10); QCOMPARE(dev->defaultPixel(), red); }
void MoveSelectionStrokeStrategy::initStrokeCallback() { KisStrokeStrategyUndoCommandBased::initStrokeCallback(); KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); KisPaintDeviceSP movedDevice = new KisPaintDevice(m_paintLayer.data(), paintDevice->colorSpace()); QRect copyRect = m_selection->selectedRect(); KisPainter gc(movedDevice); gc.setSelection(m_selection); gc.bitBlt(copyRect.topLeft(), paintDevice, copyRect); gc.end(); KisTransaction cutTransaction(name(), paintDevice); paintDevice->clearSelection(m_selection); runAndSaveCommand(KUndo2CommandSP(cutTransaction.endAndTake()), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); KisIndirectPaintingSupport *indirect = static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data()); indirect->setTemporaryTarget(movedDevice); indirect->setTemporaryCompositeOp(COMPOSITE_OVER); indirect->setTemporaryOpacity(OPACITY_OPAQUE_U8); m_initialDeviceOffset = QPoint(movedDevice->x(), movedDevice->y()); m_selection->setVisible(false); }
void MoveSelectionStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { MoveStrokeStrategy::Data *d = dynamic_cast<MoveStrokeStrategy::Data*>(data); if (d) { KisIndirectPaintingSupport *indirect = static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data()); KisPaintDeviceSP movedDevice = indirect->temporaryTarget(); QRegion dirtyRegion = movedDevice->region(); QPoint currentDeviceOffset(movedDevice->x(), movedDevice->y()); QPoint newDeviceOffset(m_initialDeviceOffset + d->offset); dirtyRegion |= dirtyRegion.translated(newDeviceOffset - currentDeviceOffset); movedDevice->setX(newDeviceOffset.x()); movedDevice->setY(newDeviceOffset.y()); m_finalOffset = d->offset; m_paintLayer->setDirty(dirtyRegion); } else { KisStrokeStrategyUndoCommandBased::doStrokeCallback(data); } }
KisTransactionData::KisTransactionData(const QString& name, KisPaintDeviceSP device, KUndo2Command* parent) : KUndo2Command(name, parent) , m_d(new Private()) { m_d->device = device; DEBUG_ACTION("Transaction started"); m_d->memento = device->dataManager()->getMemento(); m_d->oldOffset = QPoint(device->x(), device->y()); m_d->firstRedo = true; m_d->transactionFinished = false; }
KisPaintDeviceSP KisShearVisitor::yShear(KisPaintDeviceSP src, qreal shearY, KoUpdater *progress) { int start = progress->progress(); KisPaintDeviceSP dst = KisPaintDeviceSP(new KisPaintDevice(src->colorSpace())); KoMixColorsOp * mixOp = src->colorSpace()->mixColorsOp(); dst->setX(src->x()); dst->setY(src->y()); QRect r = src->exactBounds(); qreal displacement; qint32 displacementInt; qreal weight; for (qint32 x = r.left(); x <= r.right(); x++) { //calculate displacement displacement = x * shearY; displacementInt = (qint32)(floor(displacement)); weight = displacement - displacementInt; qint16 pixelWeights[2]; pixelWeights[0] = static_cast<quint8>(weight * 255 + 0.5); pixelWeights[1] = 255 - pixelWeights[0]; KisVLineConstIteratorPixel srcIt = src->createVLineIterator(x, r.y(), r.height() + 1); KisVLineIteratorPixel leftSrcIt = src->createVLineIterator(x, r.y() - 1, r.height() + 1); KisVLineIteratorPixel dstIt = dst->createVLineIterator(x, r.y() + displacementInt, r.height() + 1); while (!srcIt.isDone()) { const quint8 *pixelPtrs[2]; pixelPtrs[0] = leftSrcIt.rawData(); pixelPtrs[1] = srcIt.rawData(); mixOp->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData()); ++srcIt; ++leftSrcIt; ++dstIt; } progress->setProgress(start + x); } return dst; }
KisPaintDeviceSP KisShearVisitor::xShear(KisPaintDeviceSP src, qreal shearX, KoUpdater *progress) { KisPaintDeviceSP dst = KisPaintDeviceSP(new KisPaintDevice(src->colorSpace())); dst->setX(src->x()); dst->setY(src->y()); QRect r = src->exactBounds(); qreal displacement; qint32 displacementInt; qreal weight; KoMixColorsOp * mixOp = src->colorSpace()->mixColorsOp(); for (qint32 y = r.top(); y <= r.bottom(); y++) { //calculate displacement displacement = -y * shearX; displacementInt = (qint32)(floor(displacement)); weight = displacement - displacementInt; qint16 pixelWeights[2]; pixelWeights[0] = static_cast<quint8>(weight * 255 + 0.5); pixelWeights[1] = 255 - pixelWeights[0]; KisHLineConstIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width() + 1); KisHLineConstIteratorPixel leftSrcIt = src->createHLineIterator(r.x() - 1, y, r.width() + 1); KisHLineIteratorPixel dstIt = dst->createHLineIterator(r.x() + displacementInt, y, r.width() + 1); while (!srcIt.isDone()) { const quint8 *pixelPtrs[2]; pixelPtrs[0] = leftSrcIt.rawData(); pixelPtrs[1] = srcIt.rawData(); mixOp->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData()); ++srcIt; ++leftSrcIt; ++dstIt; } progress->setProgress(y); } return dst; }
void KisPaintDeviceTest::testSharedDataManager() { QRect fillRect(0,0,100,100); quint8 fillPixel[4]={255,255,255,255}; QRect clearRect(10,10,20,20); QImage srcImage; QImage dstImage; const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP srcDevice = new KisPaintDevice(cs); srcDevice->setX(10); srcDevice->setY(20); srcDevice->fill(fillRect.left(), fillRect.top(), fillRect.width(), fillRect.height(),fillPixel); KisPaintDeviceSP dstDevice = new KisPaintDevice(srcDevice->dataManager(), srcDevice); QVERIFY(srcDevice->extent() == dstDevice->extent()); QVERIFY(srcDevice->exactBounds() == dstDevice->exactBounds()); QVERIFY(srcDevice->defaultBounds() == dstDevice->defaultBounds()); QVERIFY(srcDevice->x() == dstDevice->x()); QVERIFY(srcDevice->y() == dstDevice->y()); srcImage = srcDevice->convertToQImage(0); dstImage = dstDevice->convertToQImage(0); QVERIFY(srcImage == dstImage); srcDevice->clear(clearRect); srcImage = srcDevice->convertToQImage(0); dstImage = dstDevice->convertToQImage(0); QVERIFY(srcImage == dstImage); }
void KisPasteActionFactory::run(KisView2 *view) { KisImageWSP image = view->image(); //figure out where to position the clip // XXX: Fix this for internal points & zoom! (BSAR) QWidget * w = view->canvas(); QPoint center = QPoint(w->width() / 2, w->height() / 2); QPoint bottomright = QPoint(w->width(), w->height()); if (bottomright.x() > image->width()) center.setX(image->width() / 2); if (bottomright.y() > image->height()) center.setY(image->height() / 2); const KoCanvasBase* canvasBase = view->canvasBase(); const KoViewConverter* viewConverter = view->canvasBase()->viewConverter(); KisPaintDeviceSP clip = KisClipboard::instance()->clip( QPoint( viewConverter->viewToDocumentX(canvasBase->canvasController()->canvasOffsetX()) + center.x(), viewConverter->viewToDocumentY(canvasBase->canvasController()->canvasOffsetY()) + center.y())); if (clip) { // Pasted layer content could be outside image bounds and invisible, if that is the case move content into the bounds QRect exactBounds = clip->exactBounds(); if (!exactBounds.isEmpty() && !exactBounds.intersects(image->bounds())) { clip->setX(clip->x() - exactBounds.x()); clip->setY(clip->y() - exactBounds.y()); } KisPaintLayer *newLayer = new KisPaintLayer(image.data(), image->nextLayerName() + i18n("(pasted)"), OPACITY_OPAQUE_U8, clip); KisNodeSP aboveNode = view->activeLayer(); KisNodeSP parentNode = aboveNode ? aboveNode->parent() : image->root(); KUndo2Command *cmd = new KisImageLayerAddCommand(image, newLayer, parentNode, aboveNode); KisProcessingApplicator *ap = beginAction(view, cmd->text()); ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); endAction(ap, KisOperationConfiguration(id()).toXML()); } else { #ifdef __GNUC__ #warning "Add saving of XML data for Paste of shapes" #endif view->canvasBase()->toolProxy()->paste(); } }
void KisTransactionTest::testDeviceMove() { KisSurrogateUndoAdapter undoAdapter; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QCOMPARE(dev->x(), 0); QCOMPARE(dev->y(), 0); KisTransaction t1(kundo2_noi18n("move1"), dev, 0); dev->moveTo(10,20); t1.commit(&undoAdapter); QCOMPARE(dev->x(), 10); QCOMPARE(dev->y(), 20); KisTransaction t2(kundo2_noi18n("move2"), dev, 0); dev->moveTo(7,11); t2.commit(&undoAdapter); QCOMPARE(dev->x(), 7); QCOMPARE(dev->y(), 11); undoAdapter.undo(); QCOMPARE(dev->x(), 10); QCOMPARE(dev->y(), 20); undoAdapter.undo(); QCOMPARE(dev->x(), 0); QCOMPARE(dev->y(), 0); undoAdapter.redo(); QCOMPARE(dev->x(), 10); QCOMPARE(dev->y(), 20); undoAdapter.redo(); QCOMPARE(dev->x(), 7); QCOMPARE(dev->y(), 11); }
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; }
void KisGmicSimpleConvertor::convertFromGmicFast(gmic_image<float>& gmicImage, KisPaintDeviceSP dst, float gmicUnitValue) { const KoColorSpace * dstColorSpace = dst->colorSpace(); KoColorTransformation * gmicToDstPixelFormat = createTransformationFromGmic(dstColorSpace,gmicImage._spectrum,gmicUnitValue); if (gmicToDstPixelFormat == 0) { dbgPlugins << "Fall-back to slow color conversion"; convertFromGmicImage(gmicImage, dst, gmicUnitValue); return; } qint32 x = 0; qint32 y = 0; qint32 width = gmicImage._width; qint32 height = gmicImage._height; width = width < 0 ? 0 : width; height = height < 0 ? 0 : height; const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), KoColorSpaceRegistry::instance()->rgb8()->profile()); // this function always convert to rgba or rgb with various color depth quint32 dstNumChannels = rgbaFloat32bitcolorSpace->channelCount(); // number of channels that we will copy quint32 numChannels = gmicImage._spectrum; // gmic image has 4, 3, 2, 1 channel QVector<float *> planes(dstNumChannels); int channelOffset = gmicImage._width * gmicImage._height; for (unsigned int channelIndex = 0; channelIndex < gmicImage._spectrum; channelIndex++) { planes[channelIndex] = gmicImage._data + channelOffset * channelIndex; } for (unsigned int channelIndex = gmicImage._spectrum; channelIndex < dstNumChannels; channelIndex++) { planes[channelIndex] = 0; //turn off } qint32 dataY = 0; qint32 imageY = y; qint32 rowsRemaining = height; const qint32 floatPixelSize = rgbaFloat32bitcolorSpace->pixelSize(); KisRandomAccessorSP it = dst->createRandomAccessorNG(dst->x(), dst->y()); // 0,0 int tileWidth = it->numContiguousColumns(dst->x()); int tileHeight = it->numContiguousRows(dst->y()); Q_ASSERT(tileWidth == 64); Q_ASSERT(tileHeight == 64); quint8 * convertedTile = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * tileWidth * tileHeight]; // grayscale and rgb case does not have alpha, so let's fill 4th channel of rgba tile with opacity opaque if (gmicImage._spectrum == 1 || gmicImage._spectrum == 3) { quint32 nPixels = tileWidth * tileHeight; quint32 pixelIndex = 0; KoRgbF32Traits::Pixel* srcPixel = reinterpret_cast<KoRgbF32Traits::Pixel*>(convertedTile); while (pixelIndex < nPixels) { srcPixel->alpha = gmicUnitValue; ++srcPixel; ++pixelIndex; } } while (rowsRemaining > 0) { qint32 dataX = 0; qint32 imageX = x; qint32 columnsRemaining = width; qint32 numContiguousImageRows = it->numContiguousRows(imageY); qint32 rowsToWork = qMin(numContiguousImageRows, rowsRemaining); while (columnsRemaining > 0) { qint32 numContiguousImageColumns = it->numContiguousColumns(imageX); qint32 columnsToWork = qMin(numContiguousImageColumns, columnsRemaining); const qint32 dataIdx = dataX + dataY * width; const qint32 tileRowStride = (tileWidth - columnsToWork) * floatPixelSize; quint8 *tileItStart = convertedTile; // copy gmic channels to float tile qint32 channelSize = sizeof(float); for(quint32 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(tileIt, planeIt, channelSize); tileIt += floatPixelSize; planeIt += 1; } tileIt += tileRowStride; planeIt += dataStride; } tileItStart += channelSize; } it->moveTo(imageX, imageY); quint8 *dstTileItStart = it->rawData(); tileItStart = convertedTile; // back to the start of the converted tile // copy float tile to dst colorspace based on input colorspace (rgb or grayscale) for (qint32 row = 0; row < rowsToWork; row++) { gmicToDstPixelFormat->transform(tileItStart, dstTileItStart, columnsToWork); dstTileItStart += dstColorSpace->pixelSize() * tileWidth; tileItStart += floatPixelSize * tileWidth; } imageX += columnsToWork; dataX += columnsToWork; columnsRemaining -= columnsToWork; } imageY += rowsToWork; dataY += rowsToWork; rowsRemaining -= rowsToWork; } delete [] convertedTile; delete gmicToDstPixelFormat; }