Coverage Report

Created: 2026-04-09 07:06

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
        if (check_size_multiply(output->stride, output->height, &z))
105
0
            gs_throw(gs_error_VMerror, "image too large\n");
106
0
        output->samples = xps_alloc(ctx, z);
107
0
        if (!output->samples) {
108
0
            gs_throw(gs_error_VMerror, "out of memory: output->samples.\n");
109
0
            return;
110
0
        }
111
112
0
        old_cs = output->colorspace;
113
0
        switch (output->comps)
114
0
        {
115
0
        default:
116
0
        case 1: output->colorspace = ctx->gray; break;
117
0
        case 3: output->colorspace = ctx->srgb; break;
118
0
        case 4: output->colorspace = ctx->cmyk; break;
119
0
        }
120
0
        rc_increment(output->colorspace);
121
0
        rc_decrement(old_cs, "xps_decode_jpegxr_block");
122
0
    }
123
124
0
    depth = jxr_get_OUTPUT_BITDEPTH(image);
125
126
0
    my = my * 16;
127
0
    mx = mx * 16;
128
129
0
    for (y = 0; y < 16; y++)
130
0
    {
131
0
        if (my + y >= output->height)
132
0
            return;
133
0
        p = output->samples + (my + y) * (size_t)output->stride + mx * output->comps;
134
0
        for (x = 0; x < 16; x++)
135
0
        {
136
0
            if (mx + x >= output->width)
137
0
                data += output->comps;
138
0
            else
139
0
                for (k = 0; k < output->comps; k++)
140
0
                    *p++ = scale_bits(depth, *data++);
141
0
        }
142
0
    }
143
0
}
144
145
static void
146
xps_decode_jpegxr_alpha_block(jxr_image_t image, int mx, int my, int *data)
147
0
{
148
0
    struct state *state = jxr_get_user_data(image);
149
0
    xps_context_t *ctx = state->ctx;
150
0
    xps_image_t *output = state->output;
151
0
    int depth;
152
0
    unsigned char *p;
153
0
    int x, y;
154
155
0
    if (!output->alpha)
156
0
    {
157
0
        uint32_t size;
158
159
0
        if (check_uint32_multiply((uint32_t)output->width, (uint32_t)output->height, &size) != 0) {
160
0
            gs_throw(gs_error_limitcheck, "image alpha is too large");
161
0
            return;
162
0
        }
163
164
0
        output->alpha = xps_alloc(ctx, size);
165
0
        if (!output->alpha) {
166
0
            gs_throw(gs_error_VMerror, "out of memory: output->alpha.\n");
167
0
            return;
168
0
        }
169
0
    }
170
171
0
    depth = jxr_get_OUTPUT_BITDEPTH(image);
172
173
0
    my = my * 16;
174
0
    mx = mx * 16;
175
176
0
    for (y = 0; y < 16; y++)
177
0
    {
178
0
        if (my + y >= output->height)
179
0
            return;
180
0
        p = output->alpha + (my + y) * (size_t)output->width + mx;
181
0
        for (x = 0; x < 16; x++)
182
0
        {
183
0
            if (mx + x >= output->width)
184
0
                data ++;
185
0
            else
186
0
                *p++ = scale_bits(depth, *data++);
187
0
        }
188
0
    }
189
0
}
190
191
#ifdef JXR_REDIRECTED_MALLOCS
192
static void *
193
my_jxr_malloc(void *handle, size_t z)
194
0
{
195
0
    xps_context_t *ctx = (xps_context_t *)handle;
196
0
    return xps_alloc(ctx, z);
197
0
}
198
199
static void *
200
my_jxr_calloc(void *handle, size_t z, size_t n)
201
0
{
202
0
    void *p;
203
0
    size_t zn;
204
0
    xps_context_t *ctx = (xps_context_t *)handle;
205
0
    if (check_size_multiply(z, n, &zn))
206
0
        return NULL;
207
0
    p = xps_alloc(ctx, zn);
208
0
    if (p)
209
0
        memset(p, 0, zn);
210
0
    return p;
211
0
}
212
213
static void *
214
my_jxr_realloc(void *handle, void *ptr, size_t z)
215
0
{
216
0
    xps_context_t *ctx = (xps_context_t *)handle;
217
0
    return xps_realloc(ctx, ptr, z);
218
0
}
219
220
static void
221
my_jxr_free(void *handle, void *ptr)
222
0
{
223
0
    xps_context_t *ctx = (xps_context_t *)handle;
224
0
    xps_free(ctx, ptr);
225
0
}
226
#endif
227
228
int
229
xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *output)
230
0
{
231
0
    gp_file *file;
232
0
    char *name = xps_alloc(ctx, gp_file_name_sizeof);
233
0
    struct state state;
234
0
    jxr_container_t container;
235
0
    jxr_image_t image;
236
0
    int offset, alpha_offset;
237
0
    int rc;
238
0
#ifdef JXR_REDIRECTED_MALLOCS
239
0
    jxr_alloc alloc = { 0 };
240
241
0
    alloc.handle = ctx;
242
0
    alloc.malloc = my_jxr_malloc;
243
0
    alloc.calloc = my_jxr_calloc;
244
0
    alloc.realloc = my_jxr_realloc;
245
0
    alloc.free = my_jxr_free;
246
0
#endif
247
248
0
    if (!name) {
249
0
        return gs_throw(gs_error_VMerror, "cannot allocate scratch file name buffer");
250
0
    }
251
252
0
    memset(output, 0, sizeof(*output));
253
254
0
    file = gp_open_scratch_file(ctx->memory, "jpegxr-scratch-", name, "wb+");
255
0
    if (!file) {
256
0
        xps_free(ctx, name);
257
0
        return gs_throw(gs_error_invalidfileaccess, "cannot open scratch file");
258
0
    }
259
0
    rc = gp_fwrite(buf, 1, len, file);
260
0
    if (rc != len) {
261
0
        xps_free(ctx, name);
262
0
        return gs_throw(gs_error_invalidfileaccess, "cannot write to scratch file");
263
0
    }
264
0
    rc = xps_fseek(file, 0, SEEK_SET);
265
0
    if (rc != 0) {
266
0
        xps_free(ctx, name);
267
0
        return gs_throw(gs_error_invalidfileaccess, "cannot write to scratch file");
268
0
    }
269
270
0
#ifdef JXR_REDIRECTED_MALLOCS
271
0
    container = jxr_create_container_alloc(&alloc);
272
#else
273
    container = jxr_create_container();
274
#endif
275
0
    rc = jxr_read_image_container(container, gp_get_file(file));
276
0
    if (rc < 0) {
277
0
        xps_free(ctx, name);
278
0
        jxr_destroy_container(container);
279
0
        return gs_throw1(-1, "jxr_read_image_container: %s", jxr_error_string(rc));
280
0
    }
281
282
0
    offset = jxrc_image_offset(container, 0);
283
0
    alpha_offset = jxrc_alpha_offset(container, 0);
284
285
0
    output->xres = (int)jxrc_width_resolution(container, 0);
286
0
    output->yres = (int)jxrc_height_resolution(container, 0);
287
0
    output->invert_decode = false;
288
289
0
#ifdef JXR_REDIRECTED_MALLOCS
290
0
    image = jxr_create_input_alloc(&alloc);
291
#else
292
    image = jxr_create_input();
293
#endif
294
0
    if (image == NULL) {
295
0
        xps_free(ctx, name);
296
0
        jxr_destroy_container(container);
297
0
        return gs_throw(-1, "jxr creation failed");
298
0
    }
299
0
    jxr_set_PROFILE_IDC(image, 111);
300
0
    jxr_set_LEVEL_IDC(image, 255);
301
    //jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0));
302
    //jxr_set_container_parameters(image,
303
    //    _jxrc_image_pixelformat(container, 0),
304
    //    jxrc_image_width(container, 0),
305
    //    jxrc_image_height(container, 0),
306
    //    jxrc_alpha_offset(container, 0),
307
    //    jxrc_image_band_presence(container, 0),
308
    //    jxrc_alpha_band_presence(container, 0), 0);
309
310
0
    jxr_set_block_output(image, xps_decode_jpegxr_block);
311
0
    state.ctx = ctx;
312
0
    state.output = output;
313
0
    jxr_set_user_data(image, &state);
314
315
0
    rc = xps_fseek(file, offset, SEEK_SET);
316
0
    if (rc != 0) {
317
0
        xps_free(ctx, name);
318
0
        jxr_destroy_container(container);
319
0
        jxr_destroy(image);
320
0
        return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
321
0
    }
322
323
0
    rc = jxr_read_image_bitstream(image, gp_get_file(file));
324
0
    if (rc < 0) {
325
0
        xps_free(ctx, name);
326
0
        jxr_destroy_container(container);
327
0
        jxr_destroy(image);
328
0
        return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
329
0
    }
330
331
0
    jxr_destroy(image);
332
333
0
    if (alpha_offset > 0)
334
0
    {
335
0
        image = jxr_create_input();
336
0
        if (image == NULL) {
337
0
            xps_free(ctx, name);
338
0
            jxr_destroy_container(container);
339
0
            return gs_throw(-1, "jxr creation failed");
340
0
        }
341
0
        jxr_set_PROFILE_IDC(image, 111);
342
0
        jxr_set_LEVEL_IDC(image, 255);
343
        //jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0));
344
        //jxr_set_container_parameters(image,
345
        //    _jxrc_image_pixelformat(container, 0),
346
        //    jxrc_image_width(container, 0),
347
        //    jxrc_image_height(container, 0),
348
        //    jxrc_alpha_offset(container, 0),
349
        //    jxrc_image_band_presence(container, 0),
350
        //    jxrc_alpha_band_presence(container, 0), 0);
351
352
0
        jxr_set_block_output(image, xps_decode_jpegxr_alpha_block);
353
0
        state.ctx = ctx;
354
0
        state.output = output;
355
0
        jxr_set_user_data(image, &state);
356
357
0
        rc = xps_fseek(file, alpha_offset, SEEK_SET);
358
0
        if (rc != 0) {
359
0
            xps_free(ctx, name);
360
0
            jxr_destroy_container(container);
361
0
            jxr_destroy(image);
362
0
            return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
363
0
        }
364
365
0
        rc = jxr_read_image_bitstream(image, gp_get_file(file));
366
0
        if (rc < 0) {
367
0
            xps_free(ctx, name);
368
0
            jxr_destroy_container(container);
369
0
            jxr_destroy(image);
370
0
            return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
371
0
        }
372
373
0
        jxr_destroy(image);
374
0
    }
375
376
0
    jxr_destroy_container(container);
377
378
0
    gp_fclose(file);
379
0
    gp_unlink(ctx->memory, name);
380
0
    xps_free(ctx, name);
381
382
0
    return gs_okay;
383
0
}
384
385
int
386
xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len)
387
0
{
388
0
    return 1;
389
0
}