diff --git a/.kde-ci.yml b/.kde-ci.yml index f5064a6..021ac72 100644 --- a/.kde-ci.yml +++ b/.kde-ci.yml @@ -7,3 +7,4 @@ Dependencies: Options: test-before-installing: True require-passing-tests-on: [ 'Linux', 'FreeBSD', 'Windows' ] + cmake-options: "-DKIMAGEFORMATS_JXR=ON" diff --git a/README.md b/README.md index 637a8ad..e7c00a3 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ plugin ('n/a' means no limit, i.e. the limit depends on the format encoding). - HDR: n/a (large image) - HEIF: n/a - JXL: 65,535 x 65,535 pixels, in any case no larger than 256 megapixels -- JXR: n/a +- JXR: n/a, in any case no larger than 4 GB - PCX: 65,535 x 65,535 pixels - PFM: n/a (large image) - PIC: 65,535 x 65,535 pixels diff --git a/src/imageformats/jxr.cpp b/src/imageformats/jxr.cpp index fe023dd..6ed034d 100644 --- a/src/imageformats/jxr.cpp +++ b/src/imageformats/jxr.cpp @@ -484,11 +484,13 @@ public: qi = qi.convertToFormat(alpha ? QImage::Format_RGBA8888 : QImage::Format_RGB888); } #ifndef JXR_DENY_FLOAT_IMAGE - } else if(qi.format() == QImage::Format_RGBA16FPx4 || - qi.format() == QImage::Format_RGBX16FPx4 || - qi.format() == QImage::Format_RGBA32FPx4 || - qi.format() == QImage::Format_RGBA32FPx4_Premultiplied || - qi.format() == QImage::Format_RGBX32FPx4) { + // clang-format off + } else if (qi.format() == QImage::Format_RGBA16FPx4 || + qi.format() == QImage::Format_RGBX16FPx4 || + qi.format() == QImage::Format_RGBA32FPx4 || + qi.format() == QImage::Format_RGBA32FPx4_Premultiplied || + qi.format() == QImage::Format_RGBX32FPx4) { + // clang-format on auto cs = qi.colorSpace(); if (cs.isValid() && cs.transferFunction() != QColorSpace::TransferFunction::Linear) { qi = qi.convertedToColorSpace(QColorSpace(QColorSpace::SRgbLinear)); @@ -670,9 +672,12 @@ private: return false; } QByteArray buff(32768 * 4, char()); - for (; !source->atEnd();) { + for (;;) { auto read = source->read(buff.data(), buff.size()); - if (read < 1) { + if (read == 0) { + break; + } + if (read < 0) { return false; } if (target->write(buff.data(), read) != read) { @@ -868,7 +873,7 @@ bool JXRHandler::read(QImage *outImage) line[x + 3] = hasAlpha ? std::clamp(line[x + 3], float(0), float(1)) : float(1); } } - if(!img.colorSpace().isValid()) { + if (!img.colorSpace().isValid()) { img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear)); } } @@ -880,6 +885,13 @@ bool JXRHandler::read(QImage *outImage) bool JXRHandler::write(const QImage &image) { + // JXR is stored in a TIFF V6 container that is limited to 4GiB. The size + // is limited to 4GB to leave room for IFDs, Metadata, etc... + if (qint64(image.sizeInBytes()) > 4000000000ll) { + qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() image too large: the image cannot exceed 4GB."; + return false; + } + if (!d->initForWriting()) { return false; } @@ -1064,11 +1076,6 @@ bool JXRHandler::canRead(QIODevice *device) return false; } - // Some tests on sequential devices fail: I reject them for now - if (device->isSequential()) { - return false; - } - // JPEG XR image data is stored in TIFF-like container format (II and 0xBC01 version) if (device->peek(4) == QByteArray::fromRawData("\x49\x49\xbc\x01", 4)) { return true;