1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
6 #include "tools/wasm_demo/jxl_decoder.h"
8 #include <jxl/decode.h>
9 #include <jxl/decode_cxx.h>
10 #include <jxl/thread_parallel_runner_cxx.h>
21 struct DecoderInstancePrivate {
22 // Due to "Standard Layout" rules it is guaranteed that address of the entity
23 // and its first non-static member are the same.
26 size_t pixels_size = 0;
28 uint32_t display_nits;
29 JxlPixelFormat format;
30 JxlDecoderPtr decoder;
31 JxlThreadParallelRunnerPtr thread_pool;
33 std::vector<uint8_t> tail;
38 DecoderInstance* jxlCreateInstance(bool want_sdr, uint32_t display_nits) {
39 DecoderInstancePrivate* self = new DecoderInstancePrivate();
45 self->want_sdr = want_sdr;
46 self->display_nits = display_nits;
47 JxlDataType storageFormat = want_sdr ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16;
48 self->format = {4, storageFormat, JXL_NATIVE_ENDIAN, 0};
49 self->decoder = JxlDecoderMake(nullptr);
51 JxlDecoder* dec = self->decoder.get();
53 auto report_error = [&](uint32_t code, const char* text) {
54 fprintf(stderr, "%s\n", text);
56 return reinterpret_cast<DecoderInstance*>(code);
59 self->thread_pool = JxlThreadParallelRunnerMake(nullptr, 4);
60 void* runner = self->thread_pool.get();
63 JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner);
65 if (status != JXL_DEC_SUCCESS) {
66 return report_error(1, "JxlDecoderSetParallelRunner failed");
69 status = JxlDecoderSubscribeEvents(
70 dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE |
71 JXL_DEC_FRAME_PROGRESSION);
72 if (JXL_DEC_SUCCESS != status) {
73 return report_error(2, "JxlDecoderSubscribeEvents failed");
76 status = JxlDecoderSetProgressiveDetail(dec, kPasses);
77 if (JXL_DEC_SUCCESS != status) {
78 return report_error(3, "JxlDecoderSetProgressiveDetail failed");
83 void jxlDestroyInstance(DecoderInstance* instance) {
84 if (instance == nullptr) return;
85 DecoderInstancePrivate* self =
86 reinterpret_cast<DecoderInstancePrivate*>(instance);
87 if (instance->pixels) {
88 free(instance->pixels);
93 uint32_t jxlProcessInput(DecoderInstance* instance, const uint8_t* input,
95 if (instance == nullptr) return static_cast<uint32_t>(-1);
96 DecoderInstancePrivate* self =
97 reinterpret_cast<DecoderInstancePrivate*>(instance);
98 JxlDecoder* dec = self->decoder.get();
100 auto report_error = [&](int code, const char* text) {
101 fprintf(stderr, "%s\n", text);
102 return static_cast<uint32_t>(code);
105 std::vector<uint8_t>& tail = self->tail;
107 tail.reserve(tail.size() + input_size);
108 tail.insert(tail.end(), input, input + input_size);
110 input_size = tail.size();
113 auto status = JxlDecoderSetInput(dec, input, input_size);
114 if (JXL_DEC_SUCCESS != status) {
115 return report_error(-2, "JxlDecoderSetInput failed");
118 auto release_input = [&]() {
119 size_t unused_input = JxlDecoderReleaseInput(dec);
120 if (unused_input == 0) {
125 tail.insert(tail.end(), input + input_size - unused_input,
128 memmove(tail.data(), tail.data() + tail.size() - unused_input,
130 tail.resize(unused_input);
135 status = JxlDecoderProcessInput(dec);
136 if (JXL_DEC_SUCCESS == status) {
138 return 0; // ¯\_(ツ)_/¯
139 } else if (JXL_DEC_FRAME_PROGRESSION == status) {
141 return 1; // ready to flush; client will decide whether it is necessary
142 } else if (JXL_DEC_NEED_MORE_INPUT == status) {
145 } else if (JXL_DEC_FULL_IMAGE == status) {
147 return 0; // final image is ready
148 } else if (JXL_DEC_BASIC_INFO == status) {
150 status = JxlDecoderGetBasicInfo(dec, &info);
151 if (status != JXL_DEC_SUCCESS) {
153 return report_error(-4, "JxlDecoderGetBasicInfo failed");
155 instance->width = info.xsize;
156 instance->height = info.ysize;
158 JxlDecoderImageOutBufferSize(dec, &self->format, &self->pixels_size);
159 if (status != JXL_DEC_SUCCESS) {
161 return report_error(-6, "JxlDecoderImageOutBufferSize failed");
163 if (instance->pixels) {
165 return report_error(-7, "Tried to realloc pixels");
167 instance->pixels = reinterpret_cast<uint8_t*>(malloc(self->pixels_size));
168 } else if (JXL_DEC_NEED_IMAGE_OUT_BUFFER == status) {
169 if (!self->info.pixels) {
171 return report_error(-8, "Out buffer not allocated");
173 status = JxlDecoderSetImageOutBuffer(dec, &self->format, instance->pixels,
175 if (status != JXL_DEC_SUCCESS) {
177 return report_error(-9, "JxlDecoderSetImageOutBuffer failed");
179 } else if (JXL_DEC_COLOR_ENCODING == status) {
180 JxlColorEncoding color_encoding;
181 color_encoding.color_space = JXL_COLOR_SPACE_RGB;
182 color_encoding.white_point = JXL_WHITE_POINT_D65;
183 color_encoding.primaries =
184 self->want_sdr ? JXL_PRIMARIES_SRGB : JXL_PRIMARIES_2100;
185 color_encoding.transfer_function = self->want_sdr
186 ? JXL_TRANSFER_FUNCTION_SRGB
187 : JXL_TRANSFER_FUNCTION_PQ;
188 color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL;
189 status = JxlDecoderSetPreferredColorProfile(dec, &color_encoding);
190 if (status != JXL_DEC_SUCCESS) {
192 return report_error(-5, "JxlDecoderSetPreferredColorProfile failed");
196 return report_error(-3, "Unexpected decoder status");
204 uint32_t jxlFlush(DecoderInstance* instance) {
205 if (instance == nullptr) return static_cast<uint32_t>(-1);
206 DecoderInstancePrivate* self =
207 reinterpret_cast<DecoderInstancePrivate*>(instance);
208 JxlDecoder* dec = self->decoder.get();
210 auto report_error = [&](int code, const char* text) {
211 fprintf(stderr, "%s\n", text);
212 // self->result = code;
213 return static_cast<uint32_t>(code);
216 if (!instance->pixels) {
217 return report_error(-2, "Not ready to flush");
220 auto status = JxlDecoderFlushImage(dec);
221 if (status != JXL_DEC_SUCCESS) {
222 return report_error(-3, "Failed to flush");