Imported Upstream version 0.9.0
[platform/upstream/libjxl.git] / tools / wasm_demo / jxl_decoder.cc
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
5
6 #include "tools/wasm_demo/jxl_decoder.h"
7
8 #include <jxl/decode.h>
9 #include <jxl/decode_cxx.h>
10 #include <jxl/thread_parallel_runner_cxx.h>
11
12 #include <cstdio>
13 #include <cstring>
14 #include <memory>
15 #include <vector>
16
17 extern "C" {
18
19 namespace {
20
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.
24   DecoderInstance info;
25
26   size_t pixels_size = 0;
27   bool want_sdr;
28   uint32_t display_nits;
29   JxlPixelFormat format;
30   JxlDecoderPtr decoder;
31   JxlThreadParallelRunnerPtr thread_pool;
32
33   std::vector<uint8_t> tail;
34 };
35
36 }  // namespace
37
38 DecoderInstance* jxlCreateInstance(bool want_sdr, uint32_t display_nits) {
39   DecoderInstancePrivate* self = new DecoderInstancePrivate();
40
41   if (!self) {
42     return nullptr;
43   }
44
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);
50
51   JxlDecoder* dec = self->decoder.get();
52
53   auto report_error = [&](uint32_t code, const char* text) {
54     fprintf(stderr, "%s\n", text);
55     delete self;
56     return reinterpret_cast<DecoderInstance*>(code);
57   };
58
59   self->thread_pool = JxlThreadParallelRunnerMake(nullptr, 4);
60   void* runner = self->thread_pool.get();
61
62   auto status =
63       JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner);
64
65   if (status != JXL_DEC_SUCCESS) {
66     return report_error(1, "JxlDecoderSetParallelRunner failed");
67   }
68
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");
74   }
75
76   status = JxlDecoderSetProgressiveDetail(dec, kPasses);
77   if (JXL_DEC_SUCCESS != status) {
78     return report_error(3, "JxlDecoderSetProgressiveDetail failed");
79   }
80   return &self->info;
81 }
82
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);
89   }
90   delete self;
91 }
92
93 uint32_t jxlProcessInput(DecoderInstance* instance, const uint8_t* input,
94                          size_t input_size) {
95   if (instance == nullptr) return static_cast<uint32_t>(-1);
96   DecoderInstancePrivate* self =
97       reinterpret_cast<DecoderInstancePrivate*>(instance);
98   JxlDecoder* dec = self->decoder.get();
99
100   auto report_error = [&](int code, const char* text) {
101     fprintf(stderr, "%s\n", text);
102     return static_cast<uint32_t>(code);
103   };
104
105   std::vector<uint8_t>& tail = self->tail;
106   if (!tail.empty()) {
107     tail.reserve(tail.size() + input_size);
108     tail.insert(tail.end(), input, input + input_size);
109     input = tail.data();
110     input_size = tail.size();
111   }
112
113   auto status = JxlDecoderSetInput(dec, input, input_size);
114   if (JXL_DEC_SUCCESS != status) {
115     return report_error(-2, "JxlDecoderSetInput failed");
116   }
117
118   auto release_input = [&]() {
119     size_t unused_input = JxlDecoderReleaseInput(dec);
120     if (unused_input == 0) {
121       tail.clear();
122       return;
123     }
124     if (tail.empty()) {
125       tail.insert(tail.end(), input + input_size - unused_input,
126                   input + input_size);
127     } else {
128       memmove(tail.data(), tail.data() + tail.size() - unused_input,
129               unused_input);
130       tail.resize(unused_input);
131     }
132   };
133
134   while (true) {
135     status = JxlDecoderProcessInput(dec);
136     if (JXL_DEC_SUCCESS == status) {
137       release_input();
138       return 0;  // ¯\_(ツ)_/¯
139     } else if (JXL_DEC_FRAME_PROGRESSION == status) {
140       release_input();
141       return 1;  // ready to flush; client will decide whether it is necessary
142     } else if (JXL_DEC_NEED_MORE_INPUT == status) {
143       release_input();
144       return 2;
145     } else if (JXL_DEC_FULL_IMAGE == status) {
146       release_input();
147       return 0;  // final image is ready
148     } else if (JXL_DEC_BASIC_INFO == status) {
149       JxlBasicInfo info;
150       status = JxlDecoderGetBasicInfo(dec, &info);
151       if (status != JXL_DEC_SUCCESS) {
152         release_input();
153         return report_error(-4, "JxlDecoderGetBasicInfo failed");
154       }
155       instance->width = info.xsize;
156       instance->height = info.ysize;
157       status =
158           JxlDecoderImageOutBufferSize(dec, &self->format, &self->pixels_size);
159       if (status != JXL_DEC_SUCCESS) {
160         release_input();
161         return report_error(-6, "JxlDecoderImageOutBufferSize failed");
162       }
163       if (instance->pixels) {
164         release_input();
165         return report_error(-7, "Tried to realloc pixels");
166       }
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) {
170         release_input();
171         return report_error(-8, "Out buffer not allocated");
172       }
173       status = JxlDecoderSetImageOutBuffer(dec, &self->format, instance->pixels,
174                                            self->pixels_size);
175       if (status != JXL_DEC_SUCCESS) {
176         release_input();
177         return report_error(-9, "JxlDecoderSetImageOutBuffer failed");
178       }
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) {
191         release_input();
192         return report_error(-5, "JxlDecoderSetPreferredColorProfile failed");
193       }
194     } else {
195       release_input();
196       return report_error(-3, "Unexpected decoder status");
197     }
198   }
199
200   release_input();
201   return 0;
202 }
203
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();
209
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);
214   };
215
216   if (!instance->pixels) {
217     return report_error(-2, "Not ready to flush");
218   }
219
220   auto status = JxlDecoderFlushImage(dec);
221   if (status != JXL_DEC_SUCCESS) {
222     return report_error(-3, "Failed to flush");
223   }
224
225   return 0;
226 }
227
228 }  // extern "C"