diff --git a/plugins/impex/exr/exr_converter.h b/plugins/impex/exr/exr_converter.h --- a/plugins/impex/exr/exr_converter.h +++ b/plugins/impex/exr/exr_converter.h @@ -37,7 +37,7 @@ public: KisImageBuilder_Result buildImage(const QString &filename); KisImageBuilder_Result buildFile(const QString &filename, KisPaintLayerSP layer); - KisImageBuilder_Result buildFile(const QString &filename, KisGroupLayerSP layer); + KisImageBuilder_Result buildFile(const QString &filename, KisGroupLayerSP layer, bool flatten=false); /** * Retrieve the constructed image */ diff --git a/plugins/impex/exr/exr_converter.cc b/plugins/impex/exr/exr_converter.cc --- a/plugins/impex/exr/exr_converter.cc +++ b/plugins/impex/exr/exr_converter.cc @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -132,14 +133,14 @@ struct EXRConverter::Private { Private() : doc(0) - , warnedAboutChangedAlpha(false) + , alphaWasModified(false) , showNotifications(false) {} KisImageSP image; KisDocument *doc; - bool warnedAboutChangedAlpha; + bool alphaWasModified; bool showNotifications; QString errorMessage; @@ -167,6 +168,10 @@ { d->doc = doc; d->showNotifications = showNotifications; + + // Set thread count for IlmImf library + Imf::setGlobalThreadCount(QThread::idealThreadCount()); + dbgFile << "EXR Threadcount was set to: " << QThread::idealThreadCount(); } EXRConverter::~EXRConverter() @@ -297,7 +302,6 @@ if (!srcPixel.checkMultipliedColorsConsistent()) { - bool alphaWasModified = false; channel_type newAlpha = srcPixel.alpha(); pixel_type __dstPixelData; @@ -320,30 +324,6 @@ *pixel = dstPixel.pixel; - if (alphaWasModified && - !this->warnedAboutChangedAlpha) { - - QString msg = - i18nc("@info", - "The image contains pixels with zero alpha channel and non-zero " - "color channels. Krita will have to modify those pixels to have " - "at least some alpha. The initial values will not " - "be reverted on saving the image back." - "

" - "This will hardly make any visual difference just keep it in mind." - "

" - "Modified alpha will have a range from %1 to %2", - alphaEpsilon(), - alphaNoiseThreshold()); - - if (this->showNotifications) { - QMessageBox::information(0, i18nc("@title:window", "EXR image will be modified"), msg); - } else { - warnKrita << "WARNING:" << msg; - } - - this->warnedAboutChangedAlpha = true; - } } else if (srcPixel.alpha() > 0.0) { srcPixel.setUnmultiplied(srcPixel.pixel, srcPixel.alpha()); @@ -373,58 +353,55 @@ { typedef Rgba<_T_> Rgba; - QVector pixels(width); + QVector pixels(width * height); bool hasAlpha = info.channelMap.contains("A"); - for (int y = 0; y < height; ++y) { - Imf::FrameBuffer frameBuffer; - Rgba* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; - frameBuffer.insert(info.channelMap["R"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->r, - sizeof(Rgba) * 1, - sizeof(Rgba) * width)); - frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->g, + Imf::FrameBuffer frameBuffer; + Rgba* frameBufferData = (pixels.data()) - xstart - ystart * width; + frameBuffer.insert(info.channelMap["R"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->r, + sizeof(Rgba) * 1, + sizeof(Rgba) * width)); + frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->g, + sizeof(Rgba) * 1, + sizeof(Rgba) * width)); + frameBuffer.insert(info.channelMap["B"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->b, + sizeof(Rgba) * 1, + sizeof(Rgba) * width)); + if (hasAlpha) { + frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->a, sizeof(Rgba) * 1, sizeof(Rgba) * width)); - frameBuffer.insert(info.channelMap["B"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->b, - sizeof(Rgba) * 1, - sizeof(Rgba) * width)); - if (hasAlpha) { - frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->a, - sizeof(Rgba) * 1, - sizeof(Rgba) * width)); - } - - file.setFrameBuffer(frameBuffer); - file.readPixels(ystart + y); - Rgba *rgba = pixels.data(); - KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width); - do { + } - if (hasAlpha) { - unmultiplyAlpha >(rgba); - } + file.setFrameBuffer(frameBuffer); + file.readPixels(ystart, height + ystart - 1); + Rgba *rgba = pixels.data(); - typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast::Pixel*>(it->rawData()); + QRect paintRegion(xstart, ystart, width, height); + KisSequentialIterator it(layer->paintDevice(), paintRegion); + while (it.nextPixel()) { + if (hasAlpha) { + unmultiplyAlpha >(rgba); + } - dst->red = rgba->r; - dst->green = rgba->g; - dst->blue = rgba->b; - if (hasAlpha) { - dst->alpha = rgba->a; - } else { - dst->alpha = 1.0; - } + typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast::Pixel*>(it.rawData()); + dst->red = rgba->r; + dst->green = rgba->g; + dst->blue = rgba->b; + if (hasAlpha) { + dst->alpha = rgba->a; + } else { + dst->alpha = 1.0; + } - ++rgba; - } while (it->nextPixel()); + ++rgba; } - } template @@ -436,50 +413,49 @@ KIS_ASSERT_RECOVER_RETURN( layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID); - QVector pixels(width); + QVector pixels(width * height); Q_ASSERT(info.channelMap.contains("G")); dbgFile << "G -> " << info.channelMap["G"]; bool hasAlpha = info.channelMap.contains("A"); dbgFile << "Has Alpha:" << hasAlpha; - for (int y = 0; y < height; ++y) { - Imf::FrameBuffer frameBuffer; - pixel_type* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; - frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->gray, + Imf::FrameBuffer frameBuffer; + pixel_type* frameBufferData = (pixels.data()) - xstart - ystart * width; + frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->gray, + sizeof(pixel_type) * 1, + sizeof(pixel_type) * width)); + + if (hasAlpha) { + frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->alpha, sizeof(pixel_type) * 1, sizeof(pixel_type) * width)); + } - if (hasAlpha) { - frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->alpha, - sizeof(pixel_type) * 1, - sizeof(pixel_type) * width)); - } - - file.setFrameBuffer(frameBuffer); - file.readPixels(ystart + y); + file.setFrameBuffer(frameBuffer); + file.readPixels(ystart, height + ystart - 1); - pixel_type *srcPtr = pixels.data(); - KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width); - do { + pixel_type *srcPtr = pixels.data(); - if (hasAlpha) { - unmultiplyAlpha >(srcPtr); - } + QRect paintRegion(xstart, ystart, width, height); + KisSequentialIterator it(layer->paintDevice(), paintRegion); + do { - pixel_type* dstPtr = reinterpret_cast(it->rawData()); + if (hasAlpha) { + unmultiplyAlpha >(srcPtr); + } - dstPtr->gray = srcPtr->gray; - dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0); + pixel_type* dstPtr = reinterpret_cast(it.rawData()); - ++srcPtr; - } while (it->nextPixel()); - } + dstPtr->gray = srcPtr->gray; + dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0); + ++srcPtr; + } while (it.nextPixel()); } bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2) @@ -569,6 +545,8 @@ Imf::InputFile file(QFile::encodeName(filename)); Imath::Box2i dw = file.header().dataWindow(); + Imath::Box2i displayWindow = file.header().displayWindow(); + int width = dw.max.x - dw.min.x + 1; int height = dw.max.y - dw.min.y + 1; int dx = dw.min.x; @@ -792,7 +770,11 @@ } // Create the image - d->image = new KisImage(d->doc->createUndoStore(), width, height, colorSpace, ""); + // Make sure the created image is the same size as the displayWindow since + // the dataWindow can be cropped in some cases. + int displayWidth = displayWindow.max.x - displayWindow.min.x + 1; + int displayHeight = displayWindow.max.y - displayWindow.min.y + 1; + d->image = new KisImage(d->doc->createUndoStore(), displayWidth, displayHeight, colorSpace, ""); if (!d->image) { return KisImageBuilder_RESULT_FAILURE; @@ -878,6 +860,25 @@ dbgFile << "No decoding " << info.name << " with " << info.channelMap.size() << " channels, and lack of a color space"; } } + // Set projectionColor to opaque + d->image->setDefaultProjectionColor(KoColor(Qt::transparent, colorSpace)); + + // After reading the image, notify the user about changed alpha. + if (d->alphaWasModified) { + QString msg = + i18nc("@info", + "The image contains pixels with zero alpha channel and non-zero " + "color channels. Krita has modified those pixels to have " + "at least some alpha. The initial values will not " + "be reverted on saving the image back." + "

" + "This will hardly make any visual difference just keep it in mind."); + if (d->showNotifications) { + QMessageBox::information(0, i18nc("@title:window", "EXR image has been modified"), msg); + } else { + warnKrita << "WARNING:" << msg; + } + } if (!extraLayersInfo.isNull()) { KisExrLayersSorter sorter(extraLayersInfo, d->image); @@ -1280,7 +1281,7 @@ return doc.toString(); } -KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisGroupLayerSP layer) +KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisGroupLayerSP layer, bool flatten) { if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; @@ -1305,35 +1306,44 @@ image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } - QList informationObjects; - d->recBuildPaintLayerSaveInfo(informationObjects, "", layer); - - if(informationObjects.isEmpty()) { - return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + if (flatten) { + image->waitForDone(); // This is to make sure we have a full image to project. + KisPaintDeviceSP pd = new KisPaintDevice(*image->projection()); + KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd); + return buildFile(filename, l); } + else { - d->makeLayerNamesUnique(informationObjects); + QList informationObjects; + d->recBuildPaintLayerSaveInfo(informationObjects, "", layer); - QByteArray extraLayersInfo = d->fetchExtraLayersInfo(informationObjects).toUtf8(); - if (!extraLayersInfo.isNull()) { - header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData())); - } - dbgFile << informationObjects.size() << " layers to save"; + if(informationObjects.isEmpty()) { + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } - Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) { - if (info.pixelType < Imf::NUM_PIXELTYPES) { - Q_FOREACH (const QString& channel, info.channels) { - dbgFile << channel << " " << info.pixelType; - header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType)); + d->makeLayerNamesUnique(informationObjects); + + QByteArray extraLayersInfo = d->fetchExtraLayersInfo(informationObjects).toUtf8(); + if (!extraLayersInfo.isNull()) { + header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData())); + } + dbgFile << informationObjects.size() << " layers to save"; + + Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) { + if (info.pixelType < Imf::NUM_PIXELTYPES) { + Q_FOREACH (const QString& channel, info.channels) { + dbgFile << channel << " " << info.pixelType; + header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType)); + } } } - } - // Open file for writing - Imf::OutputFile file(QFile::encodeName(filename), header); + // Open file for writing + Imf::OutputFile file(QFile::encodeName(filename), header); - encodeData(file, informationObjects, width, height); - return KisImageBuilder_RESULT_OK; + encodeData(file, informationObjects, width, height); + return KisImageBuilder_RESULT_OK; + } } void EXRConverter::cancel() diff --git a/plugins/impex/exr/exr_export.cc b/plugins/impex/exr/exr_export.cc --- a/plugins/impex/exr/exr_export.cc +++ b/plugins/impex/exr/exr_export.cc @@ -75,10 +75,7 @@ KisImageBuilder_Result res; if (configuration->getBool("flatten")) { - KisPaintDeviceSP pd = new KisPaintDevice(*image->projection()); - KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd); - - res = exrConverter.buildFile(filename(), l); + res = exrConverter.buildFile(filename(), image->rootLayer(), true); } else { res = exrConverter.buildFile(filename(), image->rootLayer());