comparison mupdf-source/source/fitz/load-jxr.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24
25 #include "pixmap-imp.h"
26
27 #ifdef HAVE_JPEGXR
28
29 #include <math.h>
30 #include <string.h>
31
32 #include <jpegxr.h>
33
34 struct info
35 {
36 fz_context *ctx;
37
38 float xres, yres;
39 int width, height;
40 int format;
41 int has_alpha;
42 int has_premul;
43
44 int comps, stride;
45 unsigned char *samples;
46 fz_colorspace *cspace;
47 };
48
49 static const char *
50 jxr_error_string(int rc)
51 {
52 switch (rc)
53 {
54 case JXR_EC_OK: return "No error";
55 default:
56 case JXR_EC_ERROR: return "Unspecified error";
57 case JXR_EC_BADMAGIC: return "Stream lacks proper magic number";
58 case JXR_EC_FEATURE_NOT_IMPLEMENTED: return "Feature not implemented";
59 case JXR_EC_IO: return "Error reading/writing data";
60 case JXR_EC_BADFORMAT: return "Bad file format";
61 }
62 }
63
64 struct {
65 jxrc_t_pixelFormat format;
66 int comps;
67 } pixelformats[] = {
68 {JXRC_FMT_BlackWhite, 1},
69 {JXRC_FMT_8bppGray, 1},
70 {JXRC_FMT_16bppGray, 1},
71 {JXRC_FMT_16bppGrayFixedPoint, 1},
72 {JXRC_FMT_16bppGrayHalf, 1},
73 {JXRC_FMT_32bppGrayFixedPoint, 1},
74 {JXRC_FMT_32bppGrayFloat, 1},
75 {JXRC_FMT_16bppBGR555, 3},
76 {JXRC_FMT_16bppBGR565, 3},
77 {JXRC_FMT_24bppBGR, 3},
78 {JXRC_FMT_24bppRGB, 3},
79 {JXRC_FMT_32bppBGR101010, 3},
80 {JXRC_FMT_32bppBGRA, 3},
81 {JXRC_FMT_32bppBGR, 3},
82 {JXRC_FMT_32bppPBGRA, 3},
83 {JXRC_FMT_48bppRGBFixedPoint, 3},
84 {JXRC_FMT_48bppRGBHalf, 3},
85 {JXRC_FMT_48bppRGB, 3},
86 {JXRC_FMT_64bppPRGBA, 3},
87 {JXRC_FMT_64bppRGBAFixedPoint, 3},
88 {JXRC_FMT_64bppRGBAHalf, 3},
89 {JXRC_FMT_64bppRGBA, 3},
90 {JXRC_FMT_64bppRGBFixedPoint, 3},
91 {JXRC_FMT_64bppRGBHalf, 3},
92 {JXRC_FMT_96bppRGBFixedPoint, 3},
93 {JXRC_FMT_128bppPRGBAFloat, 3},
94 {JXRC_FMT_128bppRGBAFixedPoint, 3},
95 {JXRC_FMT_128bppRGBAFloat, 3},
96 {JXRC_FMT_128bppRGBFixedPoint, 3},
97 {JXRC_FMT_128bppRGBFloat, 3},
98 {JXRC_FMT_32bppRGBE, 3},
99 {JXRC_FMT_32bppCMYK, 4},
100 {JXRC_FMT_40bppCMYKAlpha, 4},
101 {JXRC_FMT_64bppCMYK, 4},
102 {JXRC_FMT_80bppCMYKAlpha, 4},
103 {JXRC_FMT_24bpp3Channels, 3},
104 {JXRC_FMT_32bpp3ChannelsAlpha, 3},
105 {JXRC_FMT_32bpp4Channels, 4},
106 {JXRC_FMT_40bpp4ChannelsAlpha, 4},
107 {JXRC_FMT_40bpp5Channels, 5},
108 {JXRC_FMT_48bpp3Channels, 3},
109 {JXRC_FMT_48bpp5ChannelsAlpha, 5},
110 {JXRC_FMT_48bpp6Channels, 6},
111 {JXRC_FMT_56bpp6ChannelsAlpha, 6},
112 {JXRC_FMT_56bpp7Channels, 7},
113 {JXRC_FMT_64bpp3ChannelsAlpha, 3},
114 {JXRC_FMT_64bpp4Channels, 4},
115 {JXRC_FMT_64bpp7ChannelsAlpha, 7},
116 {JXRC_FMT_64bpp8Channels, 8},
117 {JXRC_FMT_72bpp8ChannelsAlpha, 8},
118 {JXRC_FMT_80bpp4ChannelsAlpha, 4},
119 {JXRC_FMT_80bpp5Channels, 5},
120 {JXRC_FMT_96bpp5ChannelsAlpha, 5},
121 {JXRC_FMT_96bpp6Channels, 6},
122 {JXRC_FMT_112bpp6ChannelsAlpha, 6},
123 {JXRC_FMT_112bpp7Channels, 7},
124 {JXRC_FMT_128bpp7ChannelsAlpha, 7},
125 {JXRC_FMT_128bpp8Channels, 8},
126 {JXRC_FMT_144bpp8ChannelsAlpha, 8},
127 };
128
129 static inline float
130 float32_from_int32_bits(int v)
131 {
132 return *((float*) &v);
133 }
134
135 static inline float
136 float32_from_float16(int v)
137 {
138 int s = (v >> 15) & 0x1;
139 int e = (v >> 10) & 0x1f;
140 int m = (v >> 0) & 0x3ff;
141 int i = (s << 31) | ((e - 15 + 127) << 23) | (m << 13);
142 return float32_from_int32_bits(i);
143 }
144
145 static inline float
146 sRGB_from_scRGB(float v)
147 {
148 if (v <= 0.0031308f)
149 return v * 12.92f;
150 return 1.055f * powf(v, 1.0f / 2.4f) - 0.055f;
151 }
152
153 static inline void
154 jxr_unpack_sample(fz_context *ctx, struct info *info, jxr_image_t image, int *sp, unsigned char *dp)
155 {
156 int k, bpc, comps, alpha;
157 float v;
158
159 if (info->format == JXRC_FMT_32bppRGBE)
160 {
161 dp[0] = sRGB_from_scRGB(ldexpf(sp[0], sp[3] - 128 - 8)) * 255 + 0.5f;
162 dp[1] = sRGB_from_scRGB(ldexpf(sp[1], sp[3] - 128 - 8)) * 255 + 0.5f;
163 dp[2] = sRGB_from_scRGB(ldexpf(sp[2], sp[3] - 128 - 8)) * 255 + 0.5f;
164 return;
165 }
166 if (info->format == JXRC_FMT_16bppBGR565)
167 {
168 dp[0] = sp[0] << 3;
169 dp[1] = sp[1] << 2;
170 dp[2] = sp[2] << 3;
171 return;
172 }
173
174 comps = fz_mini(fz_colorspace_n(ctx, info->cspace), jxr_get_IMAGE_CHANNELS(image));
175 alpha = jxr_get_ALPHACHANNEL_FLAG(image);
176 bpc = jxr_get_CONTAINER_BPC(image);
177
178 for (k = 0; k < comps + alpha; k++)
179 {
180 switch (bpc)
181 {
182 default: fz_throw(ctx, FZ_ERROR_FORMAT, "unknown sample type: %d", bpc);
183 case JXR_BD1WHITE1: dp[k] = sp[k] ? 255 : 0; break;
184 case JXR_BD1BLACK1: dp[k] = sp[k] ? 0 : 255; break;
185 case JXR_BD5: dp[k] = sp[k] << 3; break;
186 case JXR_BD8: dp[k] = sp[k]; break;
187 case JXR_BD10: dp[k] = sp[k] >> 2; break;
188 case JXR_BD16: dp[k] = sp[k] >> 8; break;
189
190 case JXR_BD16S:
191 v = sp[k] * (1.0f / (1 << 13));
192 goto decode_float32;
193 case JXR_BD32S:
194 v = sp[k] * (1.0f / (1 << 24));
195 goto decode_float32;
196 case JXR_BD16F:
197 v = float32_from_float16(sp[k]);
198 goto decode_float32;
199 case JXR_BD32F:
200 v = float32_from_int32_bits(sp[k]);
201 goto decode_float32;
202 decode_float32:
203 if (k < comps)
204 dp[k] = sRGB_from_scRGB(fz_clamp(v, 0, 1)) * 255 + 0.5f;
205 else
206 dp[k] = fz_clamp(v, 0, 1) * 255 + 0.5f;
207 break;
208 }
209 }
210 }
211
212 static inline void
213 jxr_unpack_alpha_sample(fz_context *ctx, struct info *info, jxr_image_t image, int *sp, unsigned char *dp)
214 {
215 int bpc = jxr_get_CONTAINER_BPC(image);
216 switch (bpc)
217 {
218 default: fz_throw(ctx, FZ_ERROR_FORMAT, "unknown alpha sample type: %d", bpc);
219 case JXR_BD8: dp[0] = sp[0]; break;
220 case JXR_BD10: dp[0] = sp[0] >> 2; break;
221 case JXR_BD16: dp[0] = sp[0] >> 8; break;
222
223 case JXR_BD16S:
224 dp[0] = fz_clamp(sp[0] * (1.0f / (1 << 13)), 0, 1) * 255 + 0.5f;
225 break;
226 case JXR_BD32S:
227 dp[0] = fz_clamp(sp[0] * (1.0f / (1 << 24)), 0, 1) * 255 + 0.5f;
228 break;
229 case JXR_BD16F:
230 dp[0] = fz_clamp(float32_from_float16(sp[0]), 0, 1) * 255 + 0.5f;
231 break;
232 case JXR_BD32F:
233 dp[0] = fz_clamp(float32_from_int32_bits(sp[0]), 0, 1) * 255 + 0.5f;
234 break;
235 }
236 }
237
238 static void
239 jxr_decode_block(jxr_image_t image, int mx, int my, int *data)
240 {
241 struct info *info = jxr_get_user_data(image);
242 fz_context *ctx = info->ctx;
243 unsigned char *p;
244 int x, y, n1;
245
246 mx *= 16;
247 my *= 16;
248
249 n1 = fz_colorspace_n(ctx, info->cspace) + 1;
250 for (y = 0; y < 16; y++)
251 {
252 if ((my + y) >= info->height)
253 return;
254
255 p = info->samples + (my + y) * info->stride + mx * n1;
256
257 for (x = 0; x < 16; x++)
258 {
259 if ((mx + x) < info->width)
260 {
261 jxr_unpack_sample(ctx, info, image, data, p);
262 p += n1;
263 }
264
265 data += jxr_get_IMAGE_CHANNELS(image) + jxr_get_ALPHACHANNEL_FLAG(image);
266 data += (info->format == JXRC_FMT_32bppRGBE ? 1 : 0);
267 }
268 }
269 }
270
271 static void
272 jxr_decode_block_alpha(jxr_image_t image, int mx, int my, int *data)
273 {
274 struct info *info = jxr_get_user_data(image);
275 fz_context *ctx = info->ctx;
276 unsigned char *p;
277 int x, y, n;
278
279 mx *= 16;
280 my *= 16;
281
282 n = fz_colorspace_n(ctx, info->cspace);
283 for (y = 0; y < 16; y++)
284 {
285 if ((my + y) >= info->height)
286 return;
287
288 p = info->samples + (my + y) * info->stride + mx * (n + 1);
289
290 for (x = 0; x < 16; x++)
291 {
292 if ((mx + x) < info->width)
293 {
294 jxr_unpack_alpha_sample(ctx, info, image, data, p + n);
295 p += n + 1;
296 }
297
298 data++;
299 }
300 }
301 }
302
303 static void
304 jxr_read_image(fz_context *ctx, const unsigned char *data, int size, struct info *info, int only_metadata)
305 {
306 jxr_container_t container;
307 jxr_image_t image = NULL;
308 jxr_image_t alpha = NULL;
309 size_t i;
310 int rc;
311
312 fz_var(image);
313 fz_var(alpha);
314
315 fz_try(ctx)
316 {
317 container = jxr_create_container();
318
319 rc = jxr_read_image_container_memory(container, (unsigned char *)data, size);
320 if (rc < 0)
321 fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot read jxr image container: %s", jxr_error_string(rc));
322
323 info->xres = jxrc_width_resolution(container, 0);
324 info->yres = jxrc_height_resolution(container, 0);
325 info->width = jxrc_image_width(container, 0);
326 info->height = jxrc_image_height(container, 0);
327
328 info->format = (int) jxrc_image_pixelformat(container, 0);
329
330 for (i = 0; i < nelem(pixelformats); i++)
331 if ((int) pixelformats[i].format == info->format)
332 {
333 info->comps = pixelformats[i].comps;
334 break;
335 }
336 if (i == nelem(pixelformats))
337 fz_throw(ctx, FZ_ERROR_FORMAT, "unsupported pixel format: %u", info->format);
338
339 if (info->comps == 1)
340 info->cspace = fz_device_gray(ctx);
341 else if (info->comps == 3)
342 info->cspace = fz_device_rgb(ctx);
343 else if (info->comps >= 4)
344 info->cspace = fz_device_cmyk(ctx);
345
346 info->stride = info->width * (fz_colorspace_n(ctx, info->cspace) + 1);
347
348 if (!only_metadata)
349 {
350 unsigned long image_offset;
351 unsigned char image_band;
352 unsigned long alpha_offset;
353 unsigned char alpha_band;
354
355 info->ctx = ctx;
356 info->samples = Memento_label(fz_malloc(ctx, info->stride * info->height), "jxr_samples");
357 memset(info->samples, 0xff, info->stride * info->height);
358
359 image_offset = jxrc_image_offset(container, 0);
360 image_band = jxrc_image_band_presence(container, 0);
361 alpha_offset = jxrc_alpha_offset(container, 0);
362 alpha_band = jxrc_alpha_band_presence(container, 0);
363
364 image = jxr_create_input();
365
366 jxr_set_PROFILE_IDC(image, 111);
367 jxr_set_LEVEL_IDC(image, 255);
368 jxr_set_pixel_format(image, info->format);
369 jxr_set_container_parameters(image, info->format,
370 info->width, info->height, alpha_offset,
371 image_band, alpha_band, 0);
372
373 jxr_set_user_data(image, info);
374 jxr_set_block_output(image, jxr_decode_block);
375
376 rc = jxr_read_image_bitstream_memory(image, (unsigned char *)data + image_offset, size - image_offset);
377 if (rc < 0)
378 fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot read jxr image: %s", jxr_error_string(rc));
379
380 if (info->format == JXRC_FMT_32bppPBGRA ||
381 info->format == JXRC_FMT_64bppPRGBA ||
382 info->format == JXRC_FMT_128bppPRGBAFloat)
383 info->has_premul = 1;
384
385 if (jxr_get_ALPHACHANNEL_FLAG(image))
386 info->has_alpha = 1;
387
388 if (alpha_offset > 0)
389 {
390 info->has_alpha = 1;
391
392 alpha = jxr_create_input();
393
394 jxr_set_PROFILE_IDC(alpha, 111);
395 jxr_set_LEVEL_IDC(alpha, 255);
396 jxr_set_pixel_format(alpha, info->format);
397 jxr_set_container_parameters(alpha, info->format,
398 info->width, info->height, alpha_offset,
399 image_band, alpha_band, 1);
400
401 jxr_set_user_data(alpha, info);
402 jxr_set_block_output(alpha, jxr_decode_block_alpha);
403
404 rc = jxr_read_image_bitstream_memory(alpha, (unsigned char *)data + alpha_offset, size - alpha_offset);
405 if (rc < 0)
406 fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot read jxr image: %s", jxr_error_string(rc));
407 }
408 }
409 }
410 fz_always(ctx)
411 {
412 if (alpha)
413 jxr_destroy(alpha);
414 if (image)
415 jxr_destroy(image);
416 jxr_destroy_container(container);
417 }
418 fz_catch(ctx)
419 {
420 fz_rethrow(ctx);
421 }
422 }
423
424 fz_pixmap *
425 fz_load_jxr(fz_context *ctx, const unsigned char *data, size_t size)
426 {
427 struct info info = { 0 };
428 fz_pixmap *image = NULL;
429
430 fz_var(image);
431
432 fz_try(ctx)
433 {
434 jxr_read_image(ctx, data, size, &info, 0);
435
436 image = fz_new_pixmap(ctx, info.cspace, info.width, info.height, NULL, 1);
437
438 image->xres = info.xres;
439 image->yres = info.yres;
440
441 fz_unpack_tile(ctx, image, info.samples, fz_colorspace_n(ctx, info.cspace) + 1, 8, info.stride, 0);
442 if (info.has_alpha && !info.has_premul)
443 fz_premultiply_pixmap(ctx, image);
444 }
445 fz_always(ctx)
446 {
447 fz_free(ctx, info.samples);
448 }
449 fz_catch(ctx)
450 {
451 fz_drop_pixmap(ctx, image);
452 fz_rethrow(ctx);
453 }
454
455 return image;
456 }
457
458 void
459 fz_load_jxr_info(fz_context *ctx, const unsigned char *data, size_t size, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
460 {
461 struct info info = { 0 };
462
463 jxr_read_image(ctx, data, size, &info, 1);
464 *cspacep = fz_keep_colorspace(ctx, info.cspace); /* info.cspace is a borrowed device colorspace */
465 *wp = info.width;
466 *hp = info.height;
467 *xresp = info.xres;
468 *yresp = info.yres;
469 }
470 #else /* HAVE_JPEGXR */
471
472 fz_pixmap *
473 fz_load_jxr(fz_context *ctx, const unsigned char *data, size_t size)
474 {
475 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "JPEG-XR codec is not available");
476 }
477
478 void
479 fz_load_jxr_info(fz_context *ctx, const unsigned char *data, size_t size, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
480 {
481 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "JPEG-XR codec is not available");
482 }
483
484 #endif /* HAVE_JPEGXR */