15#include <jxl/encode.h>
16#include <jxl/thread_parallel_runner.h>
20#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))
21#ifndef JXL_QT_AUTOTRANSFORM
22#define JXL_QT_AUTOTRANSFORM
26QJpegXLHandler::QJpegXLHandler()
27 : m_parseState(ParseJpegXLNotParsed)
29 , m_currentimage_index(0)
30 , m_previousimage_index(-1)
34 , m_next_image_delay(0)
35 , m_input_image_format(
QImage::Format_Invalid)
36 , m_target_image_format(
QImage::Format_Invalid)
41QJpegXLHandler::~QJpegXLHandler()
44 JxlThreadParallelRunnerDestroy(m_runner);
47 JxlDecoderDestroy(m_decoder);
51bool QJpegXLHandler::canRead()
const
53 if (m_parseState == ParseJpegXLNotParsed && !canRead(device())) {
57 if (m_parseState != ParseJpegXLError) {
60 if (m_parseState == ParseJpegXLFinished) {
69bool QJpegXLHandler::canRead(
QIODevice *device)
75 if (header.
size() < 12) {
79 JxlSignature signature = JxlSignatureCheck(
reinterpret_cast<const uint8_t *
>(header.
constData()), header.
size());
80 if (signature == JXL_SIG_CODESTREAM || signature == JXL_SIG_CONTAINER) {
86bool QJpegXLHandler::ensureParsed()
const
88 if (m_parseState == ParseJpegXLSuccess || m_parseState == ParseJpegXLBasicInfoParsed || m_parseState == ParseJpegXLFinished) {
91 if (m_parseState == ParseJpegXLError) {
95 QJpegXLHandler *that =
const_cast<QJpegXLHandler *
>(
this);
97 return that->ensureDecoder();
100bool QJpegXLHandler::ensureALLCounted()
const
102 if (!ensureParsed()) {
106 if (m_parseState == ParseJpegXLSuccess || m_parseState == ParseJpegXLFinished) {
110 QJpegXLHandler *that =
const_cast<QJpegXLHandler *
>(
this);
112 return that->countALLFrames();
115bool QJpegXLHandler::ensureDecoder()
121 m_rawData = device()->
readAll();
123 if (m_rawData.isEmpty()) {
127 JxlSignature signature = JxlSignatureCheck(
reinterpret_cast<const uint8_t *
>(m_rawData.constData()), m_rawData.size());
128 if (signature != JXL_SIG_CODESTREAM && signature != JXL_SIG_CONTAINER) {
129 m_parseState = ParseJpegXLError;
133 m_decoder = JxlDecoderCreate(
nullptr);
135 qWarning(
"ERROR: JxlDecoderCreate failed");
136 m_parseState = ParseJpegXLError;
140#ifdef JXL_QT_AUTOTRANSFORM
142 JxlDecoderSetKeepOrientation(m_decoder,
true);
146 if (!m_runner && num_worker_threads >= 4) {
149 num_worker_threads = num_worker_threads / 2;
150 num_worker_threads = qBound(2, num_worker_threads, 64);
151 m_runner = JxlThreadParallelRunnerCreate(
nullptr, num_worker_threads);
153 if (JxlDecoderSetParallelRunner(m_decoder, JxlThreadParallelRunner, m_runner) != JXL_DEC_SUCCESS) {
154 qWarning(
"ERROR: JxlDecoderSetParallelRunner failed");
155 m_parseState = ParseJpegXLError;
160 if (JxlDecoderSetInput(m_decoder,
reinterpret_cast<const uint8_t *
>(m_rawData.constData()), m_rawData.size()) != JXL_DEC_SUCCESS) {
161 qWarning(
"ERROR: JxlDecoderSetInput failed");
162 m_parseState = ParseJpegXLError;
166 JxlDecoderCloseInput(m_decoder);
168 JxlDecoderStatus
status = JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME);
169 if (
status == JXL_DEC_ERROR) {
170 qWarning(
"ERROR: JxlDecoderSubscribeEvents failed");
171 m_parseState = ParseJpegXLError;
175 status = JxlDecoderProcessInput(m_decoder);
176 if (
status == JXL_DEC_ERROR) {
177 qWarning(
"ERROR: JXL decoding failed");
178 m_parseState = ParseJpegXLError;
181 if (
status == JXL_DEC_NEED_MORE_INPUT) {
182 qWarning(
"ERROR: JXL data incomplete");
183 m_parseState = ParseJpegXLError;
187 status = JxlDecoderGetBasicInfo(m_decoder, &m_basicinfo);
188 if (
status != JXL_DEC_SUCCESS) {
189 qWarning(
"ERROR: JXL basic info not available");
190 m_parseState = ParseJpegXLError;
194 if (m_basicinfo.xsize == 0 || m_basicinfo.ysize == 0) {
195 qWarning(
"ERROR: JXL image has zero dimensions");
196 m_parseState = ParseJpegXLError;
200 if (m_basicinfo.xsize > 65535 || m_basicinfo.ysize > 65535) {
201 qWarning(
"JXL image (%dx%d) is too large", m_basicinfo.xsize, m_basicinfo.ysize);
202 m_parseState = ParseJpegXLError;
206 if (
sizeof(
void *) <= 4) {
210 if (m_basicinfo.xsize > ((8192 * 8192) / m_basicinfo.ysize)) {
211 qWarning(
"JXL image (%dx%d) is too large for 32bit build of the plug-in", m_basicinfo.xsize, m_basicinfo.ysize);
212 m_parseState = ParseJpegXLError;
219 if (m_basicinfo.xsize > ((16384 * 16384) / m_basicinfo.ysize)) {
220 qWarning(
"JXL image (%dx%d) is bigger than security limit 256 megapixels", m_basicinfo.xsize, m_basicinfo.ysize);
221 m_parseState = ParseJpegXLError;
226 m_parseState = ParseJpegXLBasicInfoParsed;
230bool QJpegXLHandler::countALLFrames()
232 if (m_parseState != ParseJpegXLBasicInfoParsed) {
236 JxlDecoderStatus
status = JxlDecoderProcessInput(m_decoder);
237 if (
status != JXL_DEC_COLOR_ENCODING) {
238 qWarning(
"Unexpected event %d instead of JXL_DEC_COLOR_ENCODING",
status);
239 m_parseState = ParseJpegXLError;
243 JxlColorEncoding color_encoding;
244 if (m_basicinfo.uses_original_profile == JXL_FALSE) {
245 JxlColorEncodingSetToSRGB(&color_encoding, JXL_FALSE);
246 JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
251 if (m_basicinfo.alpha_bits > 0) {
257 m_input_pixel_format.endianness = JXL_NATIVE_ENDIAN;
258 m_input_pixel_format.align = 0;
259 m_input_pixel_format.num_channels = 4;
261 if (m_basicinfo.bits_per_sample > 8) {
262 m_input_pixel_format.data_type = JXL_TYPE_UINT16;
263 m_buffer_size = 8 * (size_t)m_basicinfo.xsize * (
size_t)m_basicinfo.ysize;
272 m_input_pixel_format.data_type = JXL_TYPE_UINT8;
273 m_buffer_size = 4 * (size_t)m_basicinfo.xsize * (
size_t)m_basicinfo.ysize;
283 status = JxlDecoderGetColorAsEncodedProfile(m_decoder,
284#
if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
285 &m_input_pixel_format,
287 JXL_COLOR_PROFILE_TARGET_DATA,
290 if (
status == JXL_DEC_SUCCESS && color_encoding.color_space == JXL_COLOR_SPACE_RGB && color_encoding.white_point == JXL_WHITE_POINT_D65
291 && color_encoding.primaries == JXL_PRIMARIES_SRGB && color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_SRGB) {
295 if (JxlDecoderGetICCProfileSize(m_decoder,
296#
if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
297 &m_input_pixel_format,
299 JXL_COLOR_PROFILE_TARGET_DATA,
301 == JXL_DEC_SUCCESS) {
304 if (JxlDecoderGetColorAsICCProfile(m_decoder,
305#
if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
306 &m_input_pixel_format,
308 JXL_COLOR_PROFILE_TARGET_DATA,
309 reinterpret_cast<uint8_t *
>(icc_data.data()),
311 == JXL_DEC_SUCCESS) {
314 if (!m_colorspace.isValid()) {
315 qWarning(
"JXL image has Qt-unsupported or invalid ICC profile!");
318 qWarning(
"Failed to obtain data from JPEG XL decoder");
321 qWarning(
"Empty ICC data");
324 qWarning(
"no ICC, other color profile");
328 if (m_basicinfo.have_animation) {
329 JxlFrameHeader frame_header;
332 for (
status = JxlDecoderProcessInput(m_decoder);
status != JXL_DEC_SUCCESS;
status = JxlDecoderProcessInput(m_decoder)) {
333 if (
status != JXL_DEC_FRAME) {
336 qWarning(
"ERROR: JXL decoding failed");
338 case JXL_DEC_NEED_MORE_INPUT:
339 qWarning(
"ERROR: JXL data incomplete");
342 qWarning(
"Unexpected event %d instead of JXL_DEC_FRAME",
status);
345 m_parseState = ParseJpegXLError;
349 if (JxlDecoderGetFrameHeader(m_decoder, &frame_header) != JXL_DEC_SUCCESS) {
350 qWarning(
"ERROR: JxlDecoderGetFrameHeader failed");
351 m_parseState = ParseJpegXLError;
355 if (m_basicinfo.animation.tps_denominator > 0 && m_basicinfo.animation.tps_numerator > 0) {
356 delay = (int)(0.5 + 1000.0 * frame_header.duration * m_basicinfo.animation.tps_denominator / m_basicinfo.animation.tps_numerator);
361 m_framedelays.append(delay);
364 if (m_framedelays.isEmpty()) {
365 qWarning(
"no frames loaded by the JXL plug-in");
366 m_parseState = ParseJpegXLError;
370 if (m_framedelays.count() == 1) {
371 qWarning(
"JXL file was marked as animation but it has only one frame.");
372 m_basicinfo.have_animation = JXL_FALSE;
375 m_framedelays.resize(1);
376 m_framedelays[0] = 0;
383 m_next_image_delay = m_framedelays[0];
384 m_parseState = ParseJpegXLSuccess;
388bool QJpegXLHandler::decode_one_frame()
390 JxlDecoderStatus
status = JxlDecoderProcessInput(m_decoder);
391 if (
status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
392 qWarning(
"Unexpected event %d instead of JXL_DEC_NEED_IMAGE_OUT_BUFFER",
status);
393 m_parseState = ParseJpegXLError;
397 m_current_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, m_input_image_format);
398 if (m_current_image.isNull()) {
399 qWarning(
"Memory cannot be allocated");
400 m_parseState = ParseJpegXLError;
404 m_current_image.setColorSpace(m_colorspace);
406 if (JxlDecoderSetImageOutBuffer(m_decoder, &m_input_pixel_format, m_current_image.bits(), m_buffer_size) != JXL_DEC_SUCCESS) {
407 qWarning(
"ERROR: JxlDecoderSetImageOutBuffer failed");
408 m_parseState = ParseJpegXLError;
412 status = JxlDecoderProcessInput(m_decoder);
413 if (
status != JXL_DEC_FULL_IMAGE) {
414 qWarning(
"Unexpected event %d instead of JXL_DEC_FULL_IMAGE",
status);
415 m_parseState = ParseJpegXLError;
419 if (m_target_image_format != m_input_image_format) {
420 m_current_image.convertTo(m_target_image_format);
423 m_next_image_delay = m_framedelays[m_currentimage_index];
424 m_previousimage_index = m_currentimage_index;
426 if (m_framedelays.count() > 1) {
427 m_currentimage_index++;
429 if (m_currentimage_index >= m_framedelays.count()) {
435 m_parseState = ParseJpegXLFinished;
437 m_parseState = ParseJpegXLSuccess;
441 m_parseState = ParseJpegXLFinished;
447bool QJpegXLHandler::read(
QImage *image)
449 if (!ensureALLCounted()) {
453 if (m_currentimage_index == m_previousimage_index) {
454 *image = m_current_image;
455 return jumpToNextImage();
458 if (decode_one_frame()) {
459 *image = m_current_image;
466bool QJpegXLHandler::write(
const QImage &image)
469 qWarning(
"No image data to save");
474 if ((image.
width() > 65535) || (image.
height() > 65535)) {
475 qWarning(
"Image (%dx%d) is too large to save!", image.
width(), image.
height());
479 if (
sizeof(
void *) <= 4) {
480 if (image.
width() > ((8192 * 8192) / image.
height())) {
481 qWarning(
"Image (%dx%d) is too large save via 32bit build of JXL plug-in", image.
width(), image.
height());
485 if (image.
width() > ((16384 * 16384) / image.
height())) {
486 qWarning(
"Image (%dx%d) will not be saved because it has more than 256 megapixels", image.
width(), image.
height());
491 qWarning(
"Image has zero dimension!");
518 if (image.
depth() > 32) {
526 JxlEncoder *encoder = JxlEncoderCreate(
nullptr);
528 qWarning(
"Failed to create Jxl encoder");
532 if (m_quality > 100) {
534 }
else if (m_quality < 0) {
538 JxlBasicInfo output_info;
539 JxlEncoderInitBasicInfo(&output_info);
541 bool convert_color_profile;
546 convert_color_profile =
true;
548 convert_color_profile =
false;
551 convert_color_profile =
false;
553 if (iccprofile.
size() > 0 || m_quality == 100) {
554 output_info.uses_original_profile = JXL_TRUE;
558 if (save_depth == 16 && (image.
hasAlphaChannel() || output_info.uses_original_profile)) {
559 output_info.have_container = JXL_TRUE;
560 JxlEncoderUseContainer(encoder, JXL_TRUE);
561 JxlEncoderSetCodestreamLevel(encoder, 10);
564 void *runner =
nullptr;
567 if (num_worker_threads > 1) {
568 runner = JxlThreadParallelRunnerCreate(
nullptr, num_worker_threads);
569 if (JxlEncoderSetParallelRunner(encoder, JxlThreadParallelRunner, runner) != JXL_ENC_SUCCESS) {
570 qWarning(
"JxlEncoderSetParallelRunner failed");
571 JxlThreadParallelRunnerDestroy(runner);
572 JxlEncoderDestroy(encoder);
577 JxlPixelFormat pixel_format;
581 pixel_format.endianness = JXL_NATIVE_ENDIAN;
582 pixel_format.align = 0;
584 output_info.num_color_channels = 3;
585 output_info.animation.tps_numerator = 10;
586 output_info.animation.tps_denominator = 1;
587 output_info.orientation = JXL_ORIENT_IDENTITY;
589 output_info.orientation = JXL_ORIENT_FLIP_HORIZONTAL;
591 output_info.orientation = JXL_ORIENT_ROTATE_180;
593 output_info.orientation = JXL_ORIENT_FLIP_VERTICAL;
595 output_info.orientation = JXL_ORIENT_TRANSPOSE;
597 output_info.orientation = JXL_ORIENT_ROTATE_90_CW;
599 output_info.orientation = JXL_ORIENT_ANTI_TRANSPOSE;
601 output_info.orientation = JXL_ORIENT_ROTATE_90_CCW;
604 if (save_depth > 8) {
605 pixel_format.data_type = JXL_TYPE_UINT16;
607 output_info.bits_per_sample = 16;
611 pixel_format.num_channels = 4;
612 output_info.alpha_bits = 16;
613 output_info.num_extra_channels = 1;
616 pixel_format.num_channels = 3;
617 output_info.alpha_bits = 0;
620 pixel_format.data_type = JXL_TYPE_UINT8;
622 output_info.bits_per_sample = 8;
626 pixel_format.num_channels = 4;
627 output_info.alpha_bits = 8;
628 output_info.num_extra_channels = 1;
631 pixel_format.num_channels = 3;
632 output_info.alpha_bits = 0;
639 const size_t xsize = tmpimage.
width();
640 const size_t ysize = tmpimage.
height();
641 const size_t buffer_size = (save_depth > 8) ? (2 * pixel_format.num_channels * xsize * ysize) : (pixel_format.num_channels * xsize * ysize);
643 if (xsize == 0 || ysize == 0 || tmpimage.
isNull()) {
644 qWarning(
"Unable to allocate memory for output image");
646 JxlThreadParallelRunnerDestroy(runner);
648 JxlEncoderDestroy(encoder);
652 output_info.xsize = tmpimage.
width();
653 output_info.ysize = tmpimage.
height();
655 status = JxlEncoderSetBasicInfo(encoder, &output_info);
656 if (
status != JXL_ENC_SUCCESS) {
657 qWarning(
"JxlEncoderSetBasicInfo failed!");
659 JxlThreadParallelRunnerDestroy(runner);
661 JxlEncoderDestroy(encoder);
665 if (!convert_color_profile && iccprofile.
size() > 0) {
666 status = JxlEncoderSetICCProfile(encoder,
reinterpret_cast<const uint8_t *
>(iccprofile.
constData()), iccprofile.
size());
667 if (
status != JXL_ENC_SUCCESS) {
668 qWarning(
"JxlEncoderSetICCProfile failed!");
670 JxlThreadParallelRunnerDestroy(runner);
672 JxlEncoderDestroy(encoder);
676 JxlColorEncoding color_profile;
677 JxlColorEncodingSetToSRGB(&color_profile, JXL_FALSE);
679 status = JxlEncoderSetColorEncoding(encoder, &color_profile);
680 if (
status != JXL_ENC_SUCCESS) {
681 qWarning(
"JxlEncoderSetColorEncoding failed!");
683 JxlThreadParallelRunnerDestroy(runner);
685 JxlEncoderDestroy(encoder);
690 JxlEncoderFrameSettings *encoder_options = JxlEncoderFrameSettingsCreate(encoder,
nullptr);
692 JxlEncoderSetFrameDistance(encoder_options, (100.0f - m_quality) / 10.0f);
694 JxlEncoderSetFrameLossless(encoder_options, (m_quality == 100) ? JXL_TRUE : JXL_FALSE);
696 if (image.
hasAlphaChannel() || ((save_depth == 8) && (xsize % 4 == 0))) {
697 status = JxlEncoderAddImageFrame(encoder_options, &pixel_format,
static_cast<const void *
>(tmpimage.
constBits()), buffer_size);
699 if (save_depth > 8) {
700 uint16_t *tmp_buffer =
new (std::nothrow) uint16_t[3 * xsize * ysize];
702 qWarning(
"Memory allocation error");
704 JxlThreadParallelRunnerDestroy(runner);
706 JxlEncoderDestroy(encoder);
710 uint16_t *dest_pixels = tmp_buffer;
711 for (
int y = 0; y < tmpimage.
height(); y++) {
712 const uint16_t *src_pixels =
reinterpret_cast<const uint16_t *
>(tmpimage.
constScanLine(y));
713 for (
int x = 0; x < tmpimage.
width(); x++) {
715 *dest_pixels = *src_pixels;
719 *dest_pixels = *src_pixels;
723 *dest_pixels = *src_pixels;
728 status = JxlEncoderAddImageFrame(encoder_options, &pixel_format,
static_cast<const void *
>(tmp_buffer), buffer_size);
731 uchar *tmp_buffer8 =
new (std::nothrow) uchar[3 * xsize * ysize];
733 qWarning(
"Memory allocation error");
735 JxlThreadParallelRunnerDestroy(runner);
737 JxlEncoderDestroy(encoder);
741 uchar *dest_pixels8 = tmp_buffer8;
742 const size_t rowbytes = 3 * xsize;
743 for (
int y = 0; y < tmpimage.
height(); y++) {
745 dest_pixels8 += rowbytes;
747 status = JxlEncoderAddImageFrame(encoder_options, &pixel_format,
static_cast<const void *
>(tmp_buffer8), buffer_size);
748 delete[] tmp_buffer8;
752 if (
status == JXL_ENC_ERROR) {
753 qWarning(
"JxlEncoderAddImageFrame failed!");
755 JxlThreadParallelRunnerDestroy(runner);
757 JxlEncoderDestroy(encoder);
761 JxlEncoderCloseInput(encoder);
763 std::vector<uint8_t> compressed;
764 compressed.resize(4096);
769 next_out = compressed.data() + offset;
770 avail_out = compressed.size() - offset;
771 status = JxlEncoderProcessOutput(encoder, &next_out, &avail_out);
773 if (
status == JXL_ENC_NEED_MORE_OUTPUT) {
774 offset = next_out - compressed.data();
775 compressed.resize(compressed.size() * 2);
776 }
else if (
status == JXL_ENC_ERROR) {
777 qWarning(
"JxlEncoderProcessOutput failed!");
779 JxlThreadParallelRunnerDestroy(runner);
781 JxlEncoderDestroy(encoder);
784 }
while (
status != JXL_ENC_SUCCESS);
787 JxlThreadParallelRunnerDestroy(runner);
789 JxlEncoderDestroy(encoder);
791 compressed.resize(next_out - compressed.data());
793 if (compressed.size() > 0) {
794 qint64 write_status = device()->
write(
reinterpret_cast<const char *
>(compressed.data()), compressed.size());
796 if (write_status > 0) {
798 }
else if (write_status == -1) {
799 qWarning(
"Write error: %s\n", qUtf8Printable(device()->errorString()));
806QVariant QJpegXLHandler::option(ImageOption option)
const
808 if (!supportsOption(option)) {
812 if (option == Quality) {
816 if (!ensureParsed()) {
817#ifdef JXL_QT_AUTOTRANSFORM
818 if (option == ImageTransformation) {
819 return int(m_transformations);
828 return QSize(m_basicinfo.xsize, m_basicinfo.ysize);
830 if (m_basicinfo.have_animation) {
835#ifdef JXL_QT_AUTOTRANSFORM
836 case ImageTransformation:
837 if (m_basicinfo.orientation == JXL_ORIENT_IDENTITY) {
839 }
else if (m_basicinfo.orientation == JXL_ORIENT_FLIP_HORIZONTAL) {
841 }
else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_180) {
843 }
else if (m_basicinfo.orientation == JXL_ORIENT_FLIP_VERTICAL) {
845 }
else if (m_basicinfo.orientation == JXL_ORIENT_TRANSPOSE) {
847 }
else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_90_CW) {
849 }
else if (m_basicinfo.orientation == JXL_ORIENT_ANTI_TRANSPOSE) {
851 }
else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_90_CCW) {
862void QJpegXLHandler::setOption(ImageOption option,
const QVariant &value)
866 m_quality = value.
toInt();
867 if (m_quality > 100) {
869 }
else if (m_quality < 0) {
873#ifdef JXL_QT_AUTOTRANSFORM
874 case ImageTransformation:
875 if (
auto t = value.
toInt()) {
887bool QJpegXLHandler::supportsOption(ImageOption option)
const
889 auto supported = option == Quality || option == Size || option ==
Animation;
890#ifdef JXL_QT_AUTOTRANSFORM
891 supported = supported || option == ImageTransformation;
896int QJpegXLHandler::imageCount()
const
898 if (!ensureParsed()) {
902 if (m_parseState == ParseJpegXLBasicInfoParsed) {
903 if (!m_basicinfo.have_animation) {
907 if (!ensureALLCounted()) {
912 if (!m_framedelays.isEmpty()) {
913 return m_framedelays.count();
918int QJpegXLHandler::currentImageNumber()
const
920 if (m_parseState == ParseJpegXLNotParsed) {
924 if (m_parseState == ParseJpegXLError || m_parseState == ParseJpegXLBasicInfoParsed || !m_decoder) {
928 return m_currentimage_index;
931bool QJpegXLHandler::jumpToNextImage()
933 if (!ensureALLCounted()) {
937 if (m_framedelays.count() > 1) {
938 m_currentimage_index++;
940 if (m_currentimage_index >= m_framedelays.count()) {
945 JxlDecoderSkipFrames(m_decoder, 1);
949 m_parseState = ParseJpegXLSuccess;
953bool QJpegXLHandler::jumpToImage(
int imageNumber)
955 if (!ensureALLCounted()) {
959 if (imageNumber < 0 || imageNumber >= m_framedelays.count()) {
963 if (imageNumber == m_currentimage_index) {
964 m_parseState = ParseJpegXLSuccess;
968 if (imageNumber > m_currentimage_index) {
969 JxlDecoderSkipFrames(m_decoder, imageNumber - m_currentimage_index);
970 m_currentimage_index = imageNumber;
971 m_parseState = ParseJpegXLSuccess;
979 if (imageNumber > 0) {
980 JxlDecoderSkipFrames(m_decoder, imageNumber);
982 m_currentimage_index = imageNumber;
983 m_parseState = ParseJpegXLSuccess;
987int QJpegXLHandler::nextImageDelay()
const
989 if (!ensureALLCounted()) {
993 if (m_framedelays.count() < 2) {
997 return m_next_image_delay;
1000int QJpegXLHandler::loopCount()
const
1002 if (!ensureParsed()) {
1006 if (m_basicinfo.have_animation) {
1007 return (m_basicinfo.animation.num_loops > 0) ? m_basicinfo.animation.num_loops - 1 : -1;
1013bool QJpegXLHandler::rewind()
1015 m_currentimage_index = 0;
1017 JxlDecoderReleaseInput(m_decoder);
1018 JxlDecoderRewind(m_decoder);
1020 if (JxlDecoderSetParallelRunner(m_decoder, JxlThreadParallelRunner, m_runner) != JXL_DEC_SUCCESS) {
1021 qWarning(
"ERROR: JxlDecoderSetParallelRunner failed");
1022 m_parseState = ParseJpegXLError;
1027 if (JxlDecoderSetInput(m_decoder,
reinterpret_cast<const uint8_t *
>(m_rawData.constData()), m_rawData.size()) != JXL_DEC_SUCCESS) {
1028 qWarning(
"ERROR: JxlDecoderSetInput failed");
1029 m_parseState = ParseJpegXLError;
1033 JxlDecoderCloseInput(m_decoder);
1035 if (m_basicinfo.uses_original_profile) {
1036 if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
1037 qWarning(
"ERROR: JxlDecoderSubscribeEvents failed");
1038 m_parseState = ParseJpegXLError;
1042 if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
1043 qWarning(
"ERROR: JxlDecoderSubscribeEvents failed");
1044 m_parseState = ParseJpegXLError;
1048 JxlDecoderStatus
status = JxlDecoderProcessInput(m_decoder);
1049 if (
status != JXL_DEC_COLOR_ENCODING) {
1050 qWarning(
"Unexpected event %d instead of JXL_DEC_COLOR_ENCODING",
status);
1051 m_parseState = ParseJpegXLError;
1055 JxlColorEncoding color_encoding;
1056 JxlColorEncodingSetToSRGB(&color_encoding, JXL_FALSE);
1057 JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
1065 if (format ==
"jxl") {
1077 if (device->
isReadable() && QJpegXLHandler::canRead(device)) {
1096#include "moc_jxl_p.cpp"
Q_SCRIPTABLE CaptureState status()
QFlags< Capability > Capabilities
const char * constData() const const
bool isEmpty() const const
qsizetype size() const const
QColorSpace fromIccProfile(const QByteArray &iccProfile)
QByteArray iccProfile() const const
bool isValid() const const
Primaries primaries() const const
TransferFunction transferFunction() const const
QColorSpace colorSpace() const const
const uchar * constBits() const const
const uchar * constScanLine(int i) const const
QImage convertedToColorSpace(const QColorSpace &colorSpace) const const
bool hasAlphaChannel() const const
bool isNull() const const
void setDevice(QIODevice *device)
virtual void setOption(ImageOption option, const QVariant &value)
bool isOpen() const const
bool isReadable() const const
bool isWritable() const const
QByteArray peek(qint64 maxSize)
qint64 write(const QByteArray &data)
int toInt(bool *ok) const const