Commit bbfb59f5 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix exporting animation frames into EXR format

The patch fixes two problems:

1) DlgAnimationRenderer didn't initialize frames' mimetype correctly,
   so exporting always happened into PNG format.

2) DlgAnimationRenderer should correctly initialize saving format
   settings from the default cfg.exportConfiguration(). Using the
   same settings for both, animation rendering and normal image save
   is a disputable approach, but that  is how we do it for ages.

3) EXRConverter shouldn't call any blocking functions on the image
   (and shouldn't change the state of the image as well). So it should
   convert non-16f/32f layers on the fly.

BUG:406830
parent 3251e720
......@@ -755,9 +755,18 @@ KisDocument* KisDocument::lockAndCloneForSaving()
bool KisDocument::exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration)
{
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return false;
{
/**
* The caller guarantees that noone else uses the document (usually,
* it is a temporary docuent created specifically for exporting), so
* we don't need to copy or lock the document. Instead we should just
* ensure the barrier lock is synced and then released.
*/
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return false;
}
}
d->savingImage = d->image;
......
......@@ -151,6 +151,11 @@ public:
*/
bool exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings = false, KisPropertiesConfigurationSP exportConfiguration = 0);
/**
* Exports he document is a synchronous way. The caller must ensure that the
* image is not accessed by any other actors, because the exporting happens
* without holding the image lock.
*/
bool exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration = 0);
private:
......
......@@ -130,7 +130,7 @@ DlgAnimationRenderer::DlgAnimationRenderer(KisDocument *doc, QWidget *parent)
m_page->videoFilename->setMode(KoFileDialog::SaveFile);
connect(m_page->bnExportOptions, SIGNAL(clicked()), this, SLOT(sequenceMimeTypeSelected()));
connect(m_page->bnExportOptions, SIGNAL(clicked()), this, SLOT(sequenceMimeTypeOptionsClicked()));
connect(m_page->bnRenderOptions, SIGNAL(clicked()), this, SLOT(selectRenderOptions()));
m_page->ffmpegLocation->setMode(KoFileDialog::OpenFile);
......@@ -156,13 +156,6 @@ DlgAnimationRenderer::DlgAnimationRenderer(KisDocument *doc, QWidget *parent)
options.fromProperties(settings);
loadAnimationOptions(options);
/**
* There is already a (modified) frames config in the options themselves,
* but we should better read the one, generated by the config widget, because
* it may have some changes made to the "last use type config".
*/
m_frameExportConfig = cfg.exportConfiguration(options.frameMimeType);
}
......@@ -333,7 +326,7 @@ void DlgAnimationRenderer::selectRenderOptions()
encoderConfigWidget->deleteLater();
}
void DlgAnimationRenderer::sequenceMimeTypeSelected()
void DlgAnimationRenderer::sequenceMimeTypeOptionsClicked()
{
int index = m_page->cmbMimetype->currentIndex();
......@@ -355,8 +348,6 @@ void DlgAnimationRenderer::sequenceMimeTypeSelected()
dlg.setMainWidget(frameExportConfigWidget);
dlg.setButtons(KoDialog::Ok | KoDialog::Cancel);
if (dlg.exec() == QDialog::Accepted) {
m_frameExportConfig = frameExportConfigWidget->configuration();
KisConfig cfg(false);
cfg.setExportConfiguration(mimetype, frameExportConfigWidget->configuration());
}
......@@ -380,6 +371,7 @@ KisAnimationRenderingOptions DlgAnimationRenderer::getEncoderOptions() const
options.lastDocuemntPath = m_doc->localFilePath();
options.videoMimeType = m_page->cmbRenderType->currentData().toString();
options.frameMimeType = m_page->cmbMimetype->currentData().toString();
options.basename = m_page->txtBasename->text();
options.directory = m_page->dirRequester->fileName();
......@@ -399,12 +391,17 @@ KisAnimationRenderingOptions DlgAnimationRenderer::getEncoderOptions() const
options.customFFMpegOptions = m_customFFMpegOptionsString;
// we should create **a copy** of the properties
if (m_frameExportConfig) {
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(*m_frameExportConfig);
{
KisConfig config(true);
KisPropertiesConfigurationSP cfg = config.exportConfiguration(options.frameMimeType);
if (cfg) {
KisImportExportManager::fillStaticExportConfigurationProperties(cfg, m_image);
}
const bool forceHDR = m_forceHDRVideo && !m_page->shouldExportOnlyImageSequence->isChecked();
if (forceHDR) {
KIS_SAFE_ASSERT_RECOVER_NOOP(m_page->cmbMimetype->currentData().toString() == "image/png");
KIS_SAFE_ASSERT_RECOVER_NOOP(options.frameMimeType == "image/png");
cfg->setProperty("forceSRGB", false);
cfg->setProperty("saveAsHDR", true);
}
......
......@@ -66,7 +66,7 @@ private Q_SLOTS:
* @brief sequenceMimeTypeSelected
* calls the dialog for the export widget.
*/
void sequenceMimeTypeSelected();
void sequenceMimeTypeOptionsClicked();
void slotLockAspectRatioDimensionsWidth(int width);
void slotLockAspectRatioDimensionsHeight(int height);
......@@ -96,7 +96,6 @@ private:
KisImageSP m_image;
KisDocument *m_doc;
WdgAnimationRenderer *m_page {0};
KisPropertiesConfigurationSP m_frameExportConfig;
QString m_customFFMpegOptionsString;
bool m_forceHDRVideo = false;
......
......@@ -125,6 +125,7 @@ void ExrPaintLayerInfo::updateImageType(ImageType channelType)
struct ExrPaintLayerSaveInfo {
QString name; ///< name of the layer with a "." at the end (ie "group1.group2.layer1.")
KisPaintDeviceSP layerDevice;
KisPaintLayerSP layer;
QList<QString> channels;
Imf::PixelType pixelType;
......@@ -966,7 +967,7 @@ template<typename _T_, int size, int alphaPos>
void EncoderImpl<_T_, size, alphaPos>::encodeData(int line)
{
ExrPixel *rgba = pixels.data();
KisHLineIteratorSP it = info->layer->paintDevice()->createHLineIteratorNG(0, line, m_width);
KisHLineConstIteratorSP it = info->layerDevice->createHLineConstIteratorNG(0, line, m_width);
do {
const _T_* dst = reinterpret_cast < const _T_* >(it->oldRawData());
......@@ -984,33 +985,33 @@ void EncoderImpl<_T_, size, alphaPos>::encodeData(int line)
Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int width)
{
dbgFile << "Create encoder for" << info.layer->name() << info.channels << info.layer->colorSpace()->channelCount();
switch (info.layer->colorSpace()->channelCount()) {
dbgFile << "Create encoder for" << info.name << info.channels << info.layerDevice->colorSpace()->channelCount();
switch (info.layerDevice->colorSpace()->channelCount()) {
case 1: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl < half, 1, -1 > (&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
} else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl < float, 1, -1 > (&file, &info, width);
}
break;
}
case 2: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl<half, 2, 1>(&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
} else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl<float, 2, 1>(&file, &info, width);
}
break;
}
case 4: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl<half, 4, 3>(&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
} else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl<float, 4, 3>(&file, &info, width);
}
......@@ -1043,6 +1044,30 @@ void encodeData(Imf::OutputFile& file, const QList<ExrPaintLayerSaveInfo>& infor
qDeleteAll(encoders);
}
KisPaintDeviceSP wrapLayerDevice(KisPaintDeviceSP device)
{
const KoColorSpace *cs = device->colorSpace();
if (cs->colorDepthId() != Float16BitsColorDepthID && cs->colorDepthId() != Float32BitsColorDepthID) {
cs = KoColorSpaceRegistry::instance()->colorSpace(
cs->colorModelId() == GrayAColorModelID ?
GrayAColorModelID.id() : RGBAColorModelID.id(),
Float16BitsColorDepthID.id());
} else if (cs->colorModelId() != GrayColorModelID &&
cs->colorModelId() != RGBAColorModelID) {
cs = KoColorSpaceRegistry::instance()->colorSpace(
RGBAColorModelID.id(),
cs->colorDepthId().id());
}
if (*cs != *device->colorSpace()) {
device = new KisPaintDevice(*device);
device->convertTo(cs);
}
return device;
}
KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisPaintLayerSP layer)
{
if (!layer)
......@@ -1057,34 +1082,23 @@ KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisPaint
qint32 width = image->width();
Imf::Header header(width, height);
Imf::PixelType pixelType = Imf::NUM_PIXELTYPES;
ExrPaintLayerSaveInfo info;
info.layer = layer;
info.layerDevice = wrapLayerDevice(layer->paintDevice());
if (layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Imf::PixelType pixelType = Imf::NUM_PIXELTYPES;
if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
pixelType = Imf::HALF;
}
else if(layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
pixelType = Imf::FLOAT;
}
else {
const KoColorSpace *cs = 0;
if (layer->colorSpace()->colorModelId() == GrayAColorModelID) {
cs = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float16BitsColorDepthID.id());
}
else {
cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float16BitsColorDepthID.id());
}
image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
pixelType = Imf::HALF;
}
header.channels().insert("R", Imf::Channel(pixelType));
header.channels().insert("G", Imf::Channel(pixelType));
header.channels().insert("B", Imf::Channel(pixelType));
header.channels().insert("A", Imf::Channel(pixelType));
ExrPaintLayerSaveInfo info;
info.layer = layer;
info.channels.push_back("R");
info.channels.push_back("G");
info.channels.push_back("B");
......@@ -1180,6 +1194,7 @@ void EXRConverter::Private::recBuildPaintLayerSaveInfo(QList<ExrPaintLayerSaveIn
ExrPaintLayerSaveInfo info;
info.name = name + paintLayer->name() + '.';
info.layer = paintLayer;
info.layerDevice = wrapLayerDevice(paintLayer->paintDevice());
if (info.name == QString(HDR_LAYER) + ".") {
info.channels.push_back("R");
......@@ -1309,19 +1324,7 @@ KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisGroup
qint32 width = image->width();
Imf::Header header(width, height);
if (image->colorSpace()->colorDepthId() != Float16BitsColorDepthID && image->colorSpace()->colorDepthId() != Float32BitsColorDepthID) {
const KoColorSpace *cs = 0;
if (layer->colorSpace()->colorModelId() == GrayAColorModelID) {
cs = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float16BitsColorDepthID.id());
}
else {
cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float16BitsColorDepthID.id());
}
image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment