mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-05-28 00:30:23 -04:00
JXL: added ImageTransformation option
Let Qt rotate the image when the ImageAutotransform option is set to true. In tests it also solves the image size control with the value returned by the options with certain rotations.
This commit is contained in:
parent
51921e8ee5
commit
219d9cb2c2
BIN
autotests/read/jxl/orientation6_notranfs.jxl
Normal file
BIN
autotests/read/jxl/orientation6_notranfs.jxl
Normal file
Binary file not shown.
19
autotests/read/jxl/orientation6_notranfs.jxl.json
Normal file
19
autotests/read/jxl/orientation6_notranfs.jxl.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.5.7",
|
||||||
|
"maxQtVersion" : "6.5.99",
|
||||||
|
"disableAutoTransform": true,
|
||||||
|
"fileName" : "orientation6_notranfs.png",
|
||||||
|
"comment" : "Test with automatic transformation disabled."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.7.3",
|
||||||
|
"disableAutoTransform": true,
|
||||||
|
"fileName" : "orientation6_notranfs.png",
|
||||||
|
"comment" : "Test with automatic transformation disabled."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unsupportedFormat" : true,
|
||||||
|
"comment" : "It is not possible to disable the transformation with the current version of the plugin."
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/jxl/orientation6_notranfs.png
Normal file
BIN
autotests/read/jxl/orientation6_notranfs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
@ -267,10 +267,13 @@ int main(int argc, char **argv)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skipTest = false;
|
TemplateImage::TestFlags flags = TemplateImage::None;
|
||||||
QFileInfo expFileInfo = timg.compareImage(skipTest);
|
QString comment;
|
||||||
if (skipTest) {
|
QFileInfo expFileInfo = timg.compareImage(flags, comment);
|
||||||
QTextStream(stdout) << "SKIP : " << fi.fileName() << ": image format not supported by current Qt version!\n";
|
if ((flags & TemplateImage::SkipTest) == TemplateImage::SkipTest) {
|
||||||
|
if(comment.isEmpty())
|
||||||
|
comment = QStringLiteral("image format not supported by current Qt version!");
|
||||||
|
QTextStream(stdout) << "SKIP : " << fi.fileName() << QStringLiteral(": %1\n").arg(comment);
|
||||||
++skipped;
|
++skipped;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -291,7 +294,7 @@ int main(int argc, char **argv)
|
|||||||
QImage expImage;
|
QImage expImage;
|
||||||
|
|
||||||
// inputImage is auto-rotated to final orientation
|
// inputImage is auto-rotated to final orientation
|
||||||
inputReader.setAutoTransform(true);
|
inputReader.setAutoTransform((flags & TemplateImage::DisableAutotransform) != TemplateImage::DisableAutotransform);
|
||||||
|
|
||||||
if (!expReader.read(&expImage)) {
|
if (!expReader.read(&expImage)) {
|
||||||
QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n";
|
QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n";
|
||||||
|
@ -28,10 +28,10 @@ bool TemplateImage::isTemplate() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFileInfo TemplateImage::compareImage(bool &skipTest) const
|
QFileInfo TemplateImage::compareImage(TestFlags &flags, QString& comment) const
|
||||||
{
|
{
|
||||||
auto fi = jsonImage(skipTest);
|
auto fi = jsonImage(flags, comment);
|
||||||
if (skipTest) {
|
if ((flags & TestFlag::SkipTest) == TestFlag::SkipTest) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (fi.exists()) {
|
if (fi.exists()) {
|
||||||
@ -58,8 +58,9 @@ QFileInfo TemplateImage::legacyImage() const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QFileInfo TemplateImage::jsonImage(bool &skipTest) const
|
QFileInfo TemplateImage::jsonImage(TestFlags &flags, QString& comment) const
|
||||||
{
|
{
|
||||||
|
flags = TestFlag::None;
|
||||||
auto fi = QFileInfo(QStringLiteral("%1.json").arg(m_fi.filePath()));
|
auto fi = QFileInfo(QStringLiteral("%1.json").arg(m_fi.filePath()));
|
||||||
if (!fi.exists()) {
|
if (!fi.exists()) {
|
||||||
return {};
|
return {};
|
||||||
@ -86,6 +87,10 @@ QFileInfo TemplateImage::jsonImage(bool &skipTest) const
|
|||||||
auto maxQt = QVersionNumber::fromString(obj.value("maxQtVersion").toString());
|
auto maxQt = QVersionNumber::fromString(obj.value("maxQtVersion").toString());
|
||||||
auto name = obj.value("fileName").toString();
|
auto name = obj.value("fileName").toString();
|
||||||
auto unsupportedFormat = obj.value("unsupportedFormat").toBool();
|
auto unsupportedFormat = obj.value("unsupportedFormat").toBool();
|
||||||
|
comment = obj.value("comment").toString();
|
||||||
|
|
||||||
|
if(obj.value("disableAutoTransform").toBool())
|
||||||
|
flags |= TestFlag::DisableAutotransform;
|
||||||
|
|
||||||
// filter
|
// filter
|
||||||
if (name.isEmpty() && !unsupportedFormat)
|
if (name.isEmpty() && !unsupportedFormat)
|
||||||
@ -95,7 +100,7 @@ QFileInfo TemplateImage::jsonImage(bool &skipTest) const
|
|||||||
if (!maxQt.isNull() && currentQt > maxQt)
|
if (!maxQt.isNull() && currentQt > maxQt)
|
||||||
continue;
|
continue;
|
||||||
if (unsupportedFormat) {
|
if (unsupportedFormat) {
|
||||||
skipTest = true;
|
flags |= TestFlag::SkipTest;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return QFileInfo(QStringLiteral("%1/%2").arg(fi.path(), name));
|
return QFileInfo(QStringLiteral("%1/%2").arg(fi.path(), name));
|
||||||
|
@ -16,6 +16,13 @@
|
|||||||
class TemplateImage
|
class TemplateImage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum TestFlag {
|
||||||
|
None = 0x0,
|
||||||
|
SkipTest = 0x1,
|
||||||
|
DisableAutotransform = 0x2
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(TestFlags, TestFlag)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief TemplateImage
|
* \brief TemplateImage
|
||||||
* \param fi The image to test.
|
* \param fi The image to test.
|
||||||
@ -42,10 +49,10 @@ public:
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief compareImage
|
* \brief compareImage
|
||||||
* \param skipTest True if the test should be skipped (e.g. image format not supported by current Qt version).
|
* \param flags Flags for modifying test behavior (e.g. image format not supported by current Qt version).
|
||||||
* \return The template image to use for the comparison.
|
* \return The template image to use for the comparison.
|
||||||
*/
|
*/
|
||||||
QFileInfo compareImage(bool &skipTest) const;
|
QFileInfo compareImage(TestFlags &flags, QString& comment) const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief suffixes
|
* \brief suffixes
|
||||||
@ -62,13 +69,15 @@ private:
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief jsonImage
|
* \brief jsonImage
|
||||||
* \param skipTest True if the test should be skipped (not supported).
|
* \param flags Flags for modifying test behavior.
|
||||||
* \return The template image read from the corresponding JSON.
|
* \return The template image read from the corresponding JSON.
|
||||||
*/
|
*/
|
||||||
QFileInfo jsonImage(bool &skipTest) const;
|
QFileInfo jsonImage(TestFlags &flags, QString& comment) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QFileInfo m_fi;
|
QFileInfo m_fi;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(TemplateImage::TestFlags)
|
||||||
|
|
||||||
#endif // TEMPLATEIMAGE_H
|
#endif // TEMPLATEIMAGE_H
|
||||||
|
@ -16,11 +16,19 @@
|
|||||||
#include <jxl/thread_parallel_runner.h>
|
#include <jxl/thread_parallel_runner.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
// Avoid rotation on buggy Qts (see also https://bugreports.qt.io/browse/QTBUG-126575)
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 7) && QT_VERSION < QT_VERSION_CHECK(6, 6, 0)) || (QT_VERSION >= QT_VERSION_CHECK(6, 7, 3))
|
||||||
|
#ifndef JXL_QT_AUTOTRANSFORM
|
||||||
|
#define JXL_QT_AUTOTRANSFORM
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
QJpegXLHandler::QJpegXLHandler()
|
QJpegXLHandler::QJpegXLHandler()
|
||||||
: m_parseState(ParseJpegXLNotParsed)
|
: m_parseState(ParseJpegXLNotParsed)
|
||||||
, m_quality(90)
|
, m_quality(90)
|
||||||
, m_currentimage_index(0)
|
, m_currentimage_index(0)
|
||||||
, m_previousimage_index(-1)
|
, m_previousimage_index(-1)
|
||||||
|
, m_transformations(QImageIOHandler::TransformationNone)
|
||||||
, m_decoder(nullptr)
|
, m_decoder(nullptr)
|
||||||
, m_runner(nullptr)
|
, m_runner(nullptr)
|
||||||
, m_next_image_delay(0)
|
, m_next_image_delay(0)
|
||||||
@ -129,6 +137,11 @@ bool QJpegXLHandler::ensureDecoder()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
// Let Qt handle the orientation.
|
||||||
|
JxlDecoderSetKeepOrientation(m_decoder, true);
|
||||||
|
#endif
|
||||||
|
|
||||||
int num_worker_threads = QThread::idealThreadCount();
|
int num_worker_threads = QThread::idealThreadCount();
|
||||||
if (!m_runner && num_worker_threads >= 4) {
|
if (!m_runner && num_worker_threads >= 4) {
|
||||||
/* use half of the threads because plug-in is usually used in environment
|
/* use half of the threads because plug-in is usually used in environment
|
||||||
@ -568,10 +581,25 @@ bool QJpegXLHandler::write(const QImage &image)
|
|||||||
pixel_format.endianness = JXL_NATIVE_ENDIAN;
|
pixel_format.endianness = JXL_NATIVE_ENDIAN;
|
||||||
pixel_format.align = 0;
|
pixel_format.align = 0;
|
||||||
|
|
||||||
output_info.orientation = JXL_ORIENT_IDENTITY;
|
|
||||||
output_info.num_color_channels = 3;
|
output_info.num_color_channels = 3;
|
||||||
output_info.animation.tps_numerator = 10;
|
output_info.animation.tps_numerator = 10;
|
||||||
output_info.animation.tps_denominator = 1;
|
output_info.animation.tps_denominator = 1;
|
||||||
|
output_info.orientation = JXL_ORIENT_IDENTITY;
|
||||||
|
if (m_transformations == QImageIOHandler::TransformationMirror) {
|
||||||
|
output_info.orientation = JXL_ORIENT_FLIP_HORIZONTAL;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationRotate180) {
|
||||||
|
output_info.orientation = JXL_ORIENT_ROTATE_180;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationFlip) {
|
||||||
|
output_info.orientation = JXL_ORIENT_FLIP_VERTICAL;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationFlipAndRotate90) {
|
||||||
|
output_info.orientation = JXL_ORIENT_TRANSPOSE;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationRotate90) {
|
||||||
|
output_info.orientation = JXL_ORIENT_ROTATE_90_CW;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationMirrorAndRotate90) {
|
||||||
|
output_info.orientation = JXL_ORIENT_ANTI_TRANSPOSE;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationRotate270) {
|
||||||
|
output_info.orientation = JXL_ORIENT_ROTATE_90_CCW;
|
||||||
|
}
|
||||||
|
|
||||||
if (save_depth > 8) { // 16bit depth
|
if (save_depth > 8) { // 16bit depth
|
||||||
pixel_format.data_type = JXL_TYPE_UINT16;
|
pixel_format.data_type = JXL_TYPE_UINT16;
|
||||||
@ -777,14 +805,24 @@ bool QJpegXLHandler::write(const QImage &image)
|
|||||||
|
|
||||||
QVariant QJpegXLHandler::option(ImageOption option) const
|
QVariant QJpegXLHandler::option(ImageOption option) const
|
||||||
{
|
{
|
||||||
|
if (!supportsOption(option)) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
if (option == Quality) {
|
if (option == Quality) {
|
||||||
return m_quality;
|
return m_quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportsOption(option) || !ensureParsed()) {
|
if (!ensureParsed()) {
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
if (option == ImageTransformation) {
|
||||||
|
return int(m_transformations);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case Size:
|
case Size:
|
||||||
return QSize(m_basicinfo.xsize, m_basicinfo.ysize);
|
return QSize(m_basicinfo.xsize, m_basicinfo.ysize);
|
||||||
@ -794,9 +832,31 @@ QVariant QJpegXLHandler::option(ImageOption option) const
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
case ImageTransformation:
|
||||||
|
if (m_basicinfo.orientation == JXL_ORIENT_IDENTITY) {
|
||||||
|
return int(QImageIOHandler::TransformationNone);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_FLIP_HORIZONTAL) {
|
||||||
|
return int(QImageIOHandler::TransformationMirror);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_180) {
|
||||||
|
return int(QImageIOHandler::TransformationRotate180);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_FLIP_VERTICAL) {
|
||||||
|
return int(QImageIOHandler::TransformationFlip);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_TRANSPOSE) {
|
||||||
|
return int(QImageIOHandler::TransformationFlipAndRotate90);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_90_CW) {
|
||||||
|
return int(QImageIOHandler::TransformationRotate90);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_ANTI_TRANSPOSE) {
|
||||||
|
return int(QImageIOHandler::TransformationMirrorAndRotate90);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_90_CCW) {
|
||||||
|
return int(QImageIOHandler::TransformationRotate270);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
||||||
@ -810,6 +870,14 @@ void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
|||||||
m_quality = 90;
|
m_quality = 90;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
case ImageTransformation:
|
||||||
|
if (auto t = value.toInt()) {
|
||||||
|
if (t > 0 && t < 8)
|
||||||
|
m_transformations = QImageIOHandler::Transformations(t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -818,7 +886,11 @@ void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
|||||||
|
|
||||||
bool QJpegXLHandler::supportsOption(ImageOption option) const
|
bool QJpegXLHandler::supportsOption(ImageOption option) const
|
||||||
{
|
{
|
||||||
return option == Quality || option == Size || option == Animation;
|
auto supported = option == Quality || option == Size || option == Animation;
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
supported = supported || option == ImageTransformation;
|
||||||
|
#endif
|
||||||
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
int QJpegXLHandler::imageCount() const
|
int QJpegXLHandler::imageCount() const
|
||||||
|
@ -64,6 +64,7 @@ private:
|
|||||||
int m_quality;
|
int m_quality;
|
||||||
int m_currentimage_index;
|
int m_currentimage_index;
|
||||||
int m_previousimage_index;
|
int m_previousimage_index;
|
||||||
|
QImageIOHandler::Transformations m_transformations;
|
||||||
|
|
||||||
QByteArray m_rawData;
|
QByteArray m_rawData;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user