Coverage Report

Created: 2026-09-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpsjxr.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* JPEG-XR (formerly HD-Photo (formerly Windows Media Photo)) support */
18
19
#include "ghostxps.h"
20
21
#ifdef _MSC_VER
22
#undef _MSC_VER
23
#endif
24
25
#include "jpegxr.h"
26
#include "gxdevice.h"
27
28
struct state { xps_context_t *ctx; xps_image_t *output; };
29
30
static const char *
31
jxr_error_string(int code)
32
0
{
33
0
    switch (code)
34
0
    {
35
0
    case JXR_EC_OK: return "No error";
36
0
    default:
37
0
    case JXR_EC_ERROR: return "Unspecified error";
38
0
    case JXR_EC_BADMAGIC: return "Stream lacks proper magic number";
39
0
    case JXR_EC_FEATURE_NOT_IMPLEMENTED: return "Feature not implemented";
40
0
    case JXR_EC_IO: return "Error reading/writing data";
41
0
    case JXR_EC_BADFORMAT: return "Bad file format";
42
0
    }
43
0
}
44
45
0
#define CLAMP(v, mn, mx) (v < mn ? mn : v > mx ? mx : v)
46
47
static inline int
48
scale_bits(int depth, int value)
49
0
{
50
0
    union { int iv; float fv; } bd32f;
51
52
0
    switch (depth)
53
0
    {
54
0
    case JXR_BD1WHITE1:
55
0
        return value * 255;
56
0
    case JXR_BD1BLACK1:
57
0
        return value ? 0 : 255;
58
0
    case JXR_BD8:
59
0
        return value;
60
0
    case JXR_BD16:
61
0
        return value >> 8;
62
0
    case JXR_BD16S: /* -4 .. 4 ; 8192 = 1.0 */
63
0
        value = value >> 5;
64
0
        return CLAMP(value, 0, 255);
65
0
    case JXR_BD32S: /* -128 .. 128 ; 16777216 = 1.0 */
66
0
        value = value >> 16;
67
0
        return CLAMP(value, 0, 255);
68
0
    case JXR_BD32F:
69
0
        bd32f.iv = value;
70
0
        value = (int)(bd32f.fv * 255);
71
0
        return CLAMP(value, 0, 255);
72
#if 0
73
    case JXR_BDRESERVED: return value;
74
    case JXR_BD16F: return value;
75
    case JXR_BD5: return value;
76
    case JXR_BD10: return value;
77
    case JXR_BD565: return value;
78
#endif
79
0
    }
80
0
    return value;
81
0
}
82
83
static void
84
xps_decode_jpegxr_block(jxr_image_t image, int mx, int my, int *data)
85
0
{
86
0
    struct state *state = jxr_get_user_data(image);
87
0
    xps_context_t *ctx = state->ctx;
88
0
    xps_image_t *output = state->output;
89
0
    int depth;
90
0
    unsigned char *p;
91
0
    int x, y, k;
92
93
0
    if (!output->samples)
94
0
    {
95
0
        size_t z;
96
0
        gs_color_space *old_cs;
97
0
        output->width = jxr_get_IMAGE_WIDTH(image);
98
0
        output->height = jxr_get_IMAGE_HEIGHT(image);
99
0
        output->comps = jxr_get_IMAGE_CHANNELS(image);
100
0
        output->hasalpha = jxr_get_ALPHACHANNEL_FLAG(image);
101
0
        output->bits = 8;
102
0
        if (check_int_multiply(output->width, output->comps, &output->stride)) {
103
0
            gs_throw(gs_error_VMerror, "image too large\n");
104
0
            return;
105
0
        }
106
0
        if (check_size_multiply(output->stride, output->height, &z)) {
107
0
            gs_throw(gs_error_VMerror, "image too large\n");
108
0
            return;
109
0
        }
110
0
        output->samples = xps_alloc(ctx, z);
111
0
        if (!output->samples) {
112
0
            gs_throw(gs_error_VMerror, "out of memory: output->samples.\n");
113
0
            return;
114
0
        }
115
116
0
        old_cs = output->colorspace;
117
0
        switch (output->comps)
118
0
        {
119
0
        default:
120
0
        case 1: output->colorspace = ctx->gray; break;
121
0
        case 3: output->colorspace = ctx->srgb; break;
122
0
        case 4: output->colorspace = ctx->cmyk; break;
123
0
        }
124
0
        rc_increment(output->colorspace);
125
0
        rc_decrement(old_cs, "xps_decode_jpegxr_block");
126
0
    }
127
128
0
    depth = jxr_get_OUTPUT_BITDEPTH(image);
129
130
0
    my = my * 16;
131
0
    mx = mx * 16;
132
133
0
    for (y = 0; y < 16; y++)
134
0
    {
135
0
        if (my + y >= output->height)
136
0
            return;
137
0
        p = output->samples + (my + y) * (size_t)output->stride + mx * output->comps;
138
0
        for (x = 0; x < 16; x++)
139
0
        {
140
0
            if (mx + x >= output->width)
141
0
                data += output->comps;
142
0
            else
143
0
                for (k = 0; k < output->comps; k++)
144
0
                    *p++ = scale_bits(depth, *data++);
145
0
        }
146
0
    }
147
0
}
148
149
static void
150
xps_decode_jpegxr_alpha_block(jxr_image_t image, int mx, int my, int *data)
151
0
{
152
0
    struct state *state = jxr_get_user_data(image);
153
0
    xps_context_t *ctx = state->ctx;
154
0
    xps_image_t *output = state->output;
155
0
    int depth;
156
0
    unsigned char *p;
157
0
    int x, y;
158
159
0
    if (!output->alpha)
160
0
    {
161
0
        uint32_t size;
162
163
0
        if (check_uint32_multiply((uint32_t)output->width, (uint32_t)output->height, &size) != 0) {
164
0
            gs_throw(gs_error_limitcheck, "image alpha is too large");
165
0
            return;
166
0
        }
167
168
0
        output->alpha = xps_alloc(ctx, size);
169
0
        if (!output->alpha) {
170
0
            gs_throw(gs_error_VMerror, "out of memory: output->alpha.\n");
171
0
            return;
172
0
        }
173
0
    }
174
175
0
    depth = jxr_get_OUTPUT_BITDEPTH(image);
176
177
0
    my = my * 16;
178
0
    mx = mx * 16;
179
180
0
    for (y = 0; y < 16; y++)
181
0
    {
182
0
        if (my + y >= output->height)
183
0
            return;
184
0
        p = output->alpha + (my + y) * (size_t)output->width + mx;
185
0
        for (x = 0; x < 16; x++)
186
0
        {
187
0
            if (mx + x >= output->width)
188
0
                data ++;
189
0
            else
190
0
                *p++ = scale_bits(depth, *data++);
191
0
        }
192
0
    }
193
0
}
194
195
#ifdef JXR_REDIRECTED_MALLOCS
196
static void *
197
my_jxr_malloc(void *handle, size_t z)
198
0
{
199
0
    xps_context_t *ctx = (xps_context_t *)handle;
200
0
    return xps_alloc(ctx, z);
201
0
}
202
203
static void *
204
my_jxr_calloc(void *handle, size_t z, size_t n)
205
0
{
206
0
    void *p;
207
0
    size_t zn;
208
0
    xps_context_t *ctx = (xps_context_t *)handle;
209
0
    if (check_size_multiply(z, n, &zn))
210
0
        return NULL;
211
0
    p = xps_alloc(ctx, zn);
212
0
    if (p)
213
0
        memset(p, 0, zn);
214
0
    return p;
215
0
}
216
217
static void *
218
my_jxr_realloc(void *handle, void *ptr, size_t z)
219
0
{
220
0
    xps_context_t *ctx = (xps_context_t *)handle;
221
0
    return xps_realloc(ctx, ptr, z);
222
0
}
223
224
static void
225
my_jxr_free(void *handle, void *ptr)
226
0
{
227
0
    xps_context_t *ctx = (xps_context_t *)handle;
228
0
    xps_free(ctx, ptr);
229
0
}
230
#endif
231
232
int
233
xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *output)
234
0
{
235
0
    gp_file *file;
236
0
    char *name = xps_alloc(ctx, gp_file_name_sizeof);
237
0
    struct state state;
238
0
    jxr_container_t container;
239
0
    jxr_image_t image;
240
0
    int offset, alpha_offset;
241
0
    int rc;
242
0
#ifdef JXR_REDIRECTED_MALLOCS
243
0
    jxr_alloc alloc = { 0 };
244
245
0
    alloc.handle = ctx;
246
0
    alloc.malloc = my_jxr_malloc;
247
0
    alloc.calloc = my_jxr_calloc;
248
0
    alloc.realloc = my_jxr_realloc;
249
0
    alloc.free = my_jxr_free;
250
0
#endif
251
252
0
    if (!name) {
253
0
        return gs_throw(gs_error_VMerror, "cannot allocate scratch file name buffer");
254
0
    }
255
256
0
    memset(output, 0, sizeof(*output));
257
258
0
    file = gp_open_scratch_file(ctx->memory, "jpegxr-scratch-", name, "wb+");
259
0
    if (!file) {
260
0
        xps_free(ctx, name);
261
0
        return gs_throw(gs_error_invalidfileaccess, "cannot open scratch file");
262
0
    }
263
0
    rc = gp_fwrite(buf, 1, len, file);
264
0
    if (rc != len) {
265
0
        xps_free(ctx, name);
266
0
        return gs_throw(gs_error_invalidfileaccess, "cannot write to scratch file");
267
0
    }
268
0
    rc = xps_fseek(file, 0, SEEK_SET);
269
0
    if (rc != 0) {
270
0
        xps_free(ctx, name);
271
0
        return gs_throw(gs_error_invalidfileaccess, "cannot write to scratch file");
272
0
    }
273
274
0
#ifdef JXR_REDIRECTED_MALLOCS
275
0
    container = jxr_create_container_alloc(&alloc);
276
#else
277
    container = jxr_create_container();
278
#endif
279
0
    rc = jxr_read_image_container(container, gp_get_file(file));
280
0
    if (rc < 0) {
281
0
        xps_free(ctx, name);
282
0
        jxr_destroy_container(container);
283
0
        return gs_throw1(-1, "jxr_read_image_container: %s", jxr_error_string(rc));
284
0
    }
285
286
0
    offset = jxrc_image_offset(container, 0);
287
0
    alpha_offset = jxrc_alpha_offset(container, 0);
288
289
0
    output->xres = (int)jxrc_width_resolution(container, 0);
290
0
    output->yres = (int)jxrc_height_resolution(container, 0);
291
0
    output->invert_decode = false;
292
293
0
#ifdef JXR_REDIRECTED_MALLOCS
294
0
    image = jxr_create_input_alloc(&alloc);
295
#else
296
    image = jxr_create_input();
297
#endif
298
0
    if (image == NULL) {
299
0
        xps_free(ctx, name);
300
0
        jxr_destroy_container(container);
301
0
        return gs_throw(-1, "jxr creation failed");
302
0
    }
303
0
    jxr_set_PROFILE_IDC(image, 111);
304
0
    jxr_set_LEVEL_IDC(image, 255);
305
    //jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0));
306
    //jxr_set_container_parameters(image,
307
    //    _jxrc_image_pixelformat(container, 0),
308
    //    jxrc_image_width(container, 0),
309
    //    jxrc_image_height(container, 0),
310
    //    jxrc_alpha_offset(container, 0),
311
    //    jxrc_image_band_presence(container, 0),
312
    //    jxrc_alpha_band_presence(container, 0), 0);
313
314
0
    jxr_set_block_output(image, xps_decode_jpegxr_block);
315
0
    state.ctx = ctx;
316
0
    state.output = output;
317
0
    jxr_set_user_data(image, &state);
318
319
0
    rc = xps_fseek(file, offset, SEEK_SET);
320
0
    if (rc != 0) {
321
0
        xps_free(ctx, name);
322
0
        jxr_destroy_container(container);
323
0
        jxr_destroy(image);
324
0
        return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
325
0
    }
326
327
0
    rc = jxr_read_image_bitstream(image, gp_get_file(file));
328
0
    if (rc < 0) {
329
0
        xps_free(ctx, name);
330
0
        jxr_destroy_container(container);
331
0
        jxr_destroy(image);
332
0
        return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
333
0
    }
334
335
0
    jxr_destroy(image);
336
337
0
    if (alpha_offset > 0)
338
0
    {
339
0
        image = jxr_create_input();
340
0
        if (image == NULL) {
341
0
            xps_free(ctx, name);
342
0
            jxr_destroy_container(container);
343
0
            return gs_throw(-1, "jxr creation failed");
344
0
        }
345
0
        jxr_set_PROFILE_IDC(image, 111);
346
0
        jxr_set_LEVEL_IDC(image, 255);
347
        //jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0));
348
        //jxr_set_container_parameters(image,
349
        //    _jxrc_image_pixelformat(container, 0),
350
        //    jxrc_image_width(container, 0),
351
        //    jxrc_image_height(container, 0),
352
        //    jxrc_alpha_offset(container, 0),
353
        //    jxrc_image_band_presence(container, 0),
354
        //    jxrc_alpha_band_presence(container, 0), 0);
355
356
0
        jxr_set_block_output(image, xps_decode_jpegxr_alpha_block);
357
0
        state.ctx = ctx;
358
0
        state.output = output;
359
0
        jxr_set_user_data(image, &state);
360
361
0
        rc = xps_fseek(file, alpha_offset, SEEK_SET);
362
0
        if (rc != 0) {
363
0
            xps_free(ctx, name);
364
0
            jxr_destroy_container(container);
365
0
            jxr_destroy(image);
366
0
            return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
367
0
        }
368
369
0
        rc = jxr_read_image_bitstream(image, gp_get_file(file));
370
0
        if (rc < 0) {
371
0
            xps_free(ctx, name);
372
0
            jxr_destroy_container(container);
373
0
            jxr_destroy(image);
374
0
            return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
375
0
        }
376
377
0
        jxr_destroy(image);
378
0
    }
379
380
0
    jxr_destroy_container(container);
381
382
0
    gp_fclose(file);
383
0
    gp_unlink(ctx->memory, name);
384
0
    xps_free(ctx, name);
385
386
0
    return gs_okay;
387
0
}
388
389
int
390
xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len)
391
0
{
392
0
    return 1;
393
0
}