JP2: Disable ICC profile writing when saving an image with OpenJPEG V2.5.3 or lesser and improve the format detection on reading.

This commit is contained in:
Mirco Miranda 2025-02-12 06:45:21 +00:00
parent 36a6ef7d78
commit e9fa4b6610
2 changed files with 42 additions and 5 deletions

View File

@ -314,6 +314,7 @@ in your cmake options.**
JP2 plugin has the following limitations due to the lack of support by OpenJPEG: JP2 plugin has the following limitations due to the lack of support by OpenJPEG:
- Metadata are not supported. - Metadata are not supported.
- Image resolution is not supported. - Image resolution is not supported.
- To write ICC profiles you need OpenJPEG V2.5.4 or higher
### The JXL plugin ### The JXL plugin

View File

@ -101,11 +101,20 @@ public:
JP2HandlerPrivate() JP2HandlerPrivate()
: m_jp2_stream(nullptr) : m_jp2_stream(nullptr)
, m_jp2_image(nullptr) , m_jp2_image(nullptr)
, m_jp2_version(0)
, m_jp2_codec(nullptr) , m_jp2_codec(nullptr)
, m_quality(-1) , m_quality(-1)
, m_subtype(JP2_SUBTYPE) , m_subtype(JP2_SUBTYPE)
{ {
auto sver = QString::fromLatin1(QByteArray(opj_version())).split(QChar(u'.'));
if (sver.size() == 3) {
bool ok1, ok2, ok3;
auto v1 = sver.at(0).toInt(&ok1);
auto v2 = sver.at(1).toInt(&ok2);
auto v3 = sver.at(2).toInt(&ok3);
if (ok1 && ok2 && ok3)
m_jp2_version = QT_VERSION_CHECK(v1, v2, v3);
}
} }
~JP2HandlerPrivate() ~JP2HandlerPrivate()
{ {
@ -363,6 +372,17 @@ public:
prec = 0; prec = 0;
} }
auto jp2cs = m_jp2_image->color_space; auto jp2cs = m_jp2_image->color_space;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) {
auto cs = colorSpace();
if (cs.colorModel() == QColorSpace::ColorModel::Cmyk)
jp2cs = OPJ_CLRSPC_CMYK;
else if (cs.colorModel() == QColorSpace::ColorModel::Rgb)
jp2cs = OPJ_CLRSPC_SRGB;
else if (cs.colorModel() == QColorSpace::ColorModel::Gray)
jp2cs = OPJ_CLRSPC_GRAY;
}
#endif
if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) { if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) {
if (m_jp2_image->numcomps == 1) if (m_jp2_image->numcomps == 1)
jp2cs = OPJ_CLRSPC_GRAY; jp2cs = OPJ_CLRSPC_GRAY;
@ -453,13 +473,28 @@ public:
return subType() == J2K_SUBTYPE ? OPJ_CODEC_J2K : OPJ_CODEC_JP2; return subType() == J2K_SUBTYPE ? OPJ_CODEC_J2K : OPJ_CODEC_JP2;
} }
/*!
* \brief opjVersion
* \return The runtime library version.
*/
qint32 opjVersion() const
{
return m_jp2_version;
}
bool imageToJp2(const QImage &image) bool imageToJp2(const QImage &image)
{ {
auto ncomp = image.hasAlphaChannel() ? 4 : 3; auto ncomp = image.hasAlphaChannel() ? 4 : 3;
auto prec = 8; auto prec = 8;
auto cs = OPJ_CLRSPC_SRGB;
auto convFormat = image.format(); auto convFormat = image.format();
auto isFloat = false; auto isFloat = false;
auto cs = OPJ_CLRSPC_SRGB;
if (opjVersion() >= QT_VERSION_CHECK(2, 5, 4)) {
auto ics = image.colorSpace();
if (!(ics.isValid() && ics.primaries() == QColorSpace::Primaries::SRgb && ics.transferFunction() == QColorSpace::TransferFunction::SRgb)) {
cs = OPJ_CLRSPC_UNKNOWN;
}
}
switch (image.format()) { switch (image.format()) {
case QImage::Format_Mono: case QImage::Format_Mono:
@ -527,7 +562,7 @@ public:
break; break;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QImage::Format_CMYK8888: // requires OpenJPEG 2.5.3+ case QImage::Format_CMYK8888: // requires OpenJPEG 2.5.3+
if (strcmp(opj_version(), "2.5.3") >= 0) { if (opjVersion() >= QT_VERSION_CHECK(2, 5, 3)) {
ncomp = 4; ncomp = 4;
cs = OPJ_CLRSPC_CMYK; cs = OPJ_CLRSPC_CMYK;
break; break;
@ -613,8 +648,7 @@ public:
} }
} }
// With SRGB, Gray and CMYK, writing the colorspace gives an assert if (opjVersion() >= QT_VERSION_CHECK(2, 5, 4)) {
if (cs == OPJ_CLRSPC_UNKNOWN || cs == OPJ_CLRSPC_UNSPECIFIED) {
auto colorSpace = scl.targetColorSpace().iccProfile(); auto colorSpace = scl.targetColorSpace().iccProfile();
if (!colorSpace.isEmpty()) { if (!colorSpace.isEmpty()) {
m_jp2_image->icc_profile_buf = reinterpret_cast<OPJ_BYTE *>(malloc(colorSpace.size())); m_jp2_image->icc_profile_buf = reinterpret_cast<OPJ_BYTE *>(malloc(colorSpace.size()));
@ -674,6 +708,8 @@ private:
opj_image_t *m_jp2_image; opj_image_t *m_jp2_image;
qint32 m_jp2_version;
// read // read
opj_codec_t *m_jp2_codec; opj_codec_t *m_jp2_codec;