Coverage Report

Created: 2026-04-01 07:17

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
        output->alpha = xps_alloc(ctx, (size_t)output->width * output->height);
158
0
        if (!output->alpha) {
159
0
            gs_throw(gs_error_VMerror, "out of memory: output->alpha.\n");
160
0
            return;
161
0
        }
162
0
    }
163
164
0
    depth = jxr_get_OUTPUT_BITDEPTH(image);
165
166
0
    my = my * 16;
167
0
    mx = mx * 16;
168
169
0
    for (y = 0; y < 16; y++)
170
0
    {
171
0
        if (my + y >= output->height)
172
0
            return;
173
0
        p = output->alpha + (my + y) * (size_t)output->width + mx;
174
0
        for (x = 0; x < 16; x++)
175
0
        {
176
0
            if (mx + x >= output->width)
177
0
                data ++;
178
0
            else
179
0
                *p++ = scale_bits(depth, *data++);
180
0
        }
181
0
    }
182
0
}
183
184
int
185
xps_decode_jpegxr(xps_context_t *ctx, byte *buf, int len, xps_image_t *output)
186
0
{
187
0
    gp_file *file;
188
0
    char *name = xps_alloc(ctx, gp_file_name_sizeof);
189
0
    struct state state;
190
0
    jxr_container_t container;
191
0
    jxr_image_t image;
192
0
    int offset, alpha_offset;
193
0
    int rc;
194
195
0
    if (!name) {
196
0
        return gs_throw(gs_error_VMerror, "cannot allocate scratch file name buffer");
197
0
    }
198
199
0
    memset(output, 0, sizeof(*output));
200
201
0
    file = gp_open_scratch_file(ctx->memory, "jpegxr-scratch-", name, "wb+");
202
0
    if (!file) {
203
0
        xps_free(ctx, name);
204
0
        return gs_throw(gs_error_invalidfileaccess, "cannot open scratch file");
205
0
    }
206
0
    rc = gp_fwrite(buf, 1, len, file);
207
0
    if (rc != len) {
208
0
        xps_free(ctx, name);
209
0
        return gs_throw(gs_error_invalidfileaccess, "cannot write to scratch file");
210
0
    }
211
0
    rc = xps_fseek(file, 0, SEEK_SET);
212
0
    if (rc != 0) {
213
0
        xps_free(ctx, name);
214
0
        return gs_throw(gs_error_invalidfileaccess, "cannot write to scratch file");
215
0
    }
216
217
0
    container = jxr_create_container();
218
0
    rc = jxr_read_image_container(container, gp_get_file(file));
219
0
    if (rc < 0) {
220
0
        xps_free(ctx, name);
221
0
        jxr_destroy_container(container);
222
0
        return gs_throw1(-1, "jxr_read_image_container: %s", jxr_error_string(rc));
223
0
    }
224
225
0
    offset = jxrc_image_offset(container, 0);
226
0
    alpha_offset = jxrc_alpha_offset(container, 0);
227
228
0
    output->xres = (int)jxrc_width_resolution(container, 0);
229
0
    output->yres = (int)jxrc_height_resolution(container, 0);
230
0
    output->invert_decode = false;
231
232
0
    image = jxr_create_input();
233
0
    if (image == NULL) {
234
0
        xps_free(ctx, name);
235
0
        jxr_destroy_container(container);
236
0
        return gs_throw(-1, "jxr creation failed");
237
0
    }
238
0
    jxr_set_PROFILE_IDC(image, 111);
239
0
    jxr_set_LEVEL_IDC(image, 255);
240
0
    jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0));
241
0
    jxr_set_container_parameters(image,
242
0
        jxrc_image_pixelformat(container, 0),
243
0
        jxrc_image_width(container, 0),
244
0
        jxrc_image_height(container, 0),
245
0
        jxrc_alpha_offset(container, 0),
246
0
        jxrc_image_band_presence(container, 0),
247
0
        jxrc_alpha_band_presence(container, 0), 0);
248
249
0
    jxr_set_block_output(image, xps_decode_jpegxr_block);
250
0
    state.ctx = ctx;
251
0
    state.output = output;
252
0
    jxr_set_user_data(image, &state);
253
254
0
    rc = xps_fseek(file, offset, SEEK_SET);
255
0
    if (rc != 0) {
256
0
        xps_free(ctx, name);
257
0
        jxr_destroy_container(container);
258
0
        jxr_destroy(image);
259
0
        return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
260
0
    }
261
262
0
    rc = jxr_read_image_bitstream(image, gp_get_file(file));
263
0
    if (rc < 0) {
264
0
        xps_free(ctx, name);
265
0
        jxr_destroy_container(container);
266
0
        jxr_destroy(image);
267
0
        return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
268
0
    }
269
270
0
    jxr_destroy(image);
271
272
0
    if (alpha_offset > 0)
273
0
    {
274
0
        image = jxr_create_input();
275
0
        if (image == NULL) {
276
0
            xps_free(ctx, name);
277
0
            jxr_destroy_container(container);
278
0
            return gs_throw(-1, "jxr creation failed");
279
0
        }
280
0
        jxr_set_PROFILE_IDC(image, 111);
281
0
        jxr_set_LEVEL_IDC(image, 255);
282
0
        jxr_set_pixel_format(image, jxrc_image_pixelformat(container, 0));
283
0
        jxr_set_container_parameters(image,
284
0
            jxrc_image_pixelformat(container, 0),
285
0
            jxrc_image_width(container, 0),
286
0
            jxrc_image_height(container, 0),
287
0
            jxrc_alpha_offset(container, 0),
288
0
            jxrc_image_band_presence(container, 0),
289
0
            jxrc_alpha_band_presence(container, 0), 0);
290
291
0
        jxr_set_block_output(image, xps_decode_jpegxr_alpha_block);
292
0
        state.ctx = ctx;
293
0
        state.output = output;
294
0
        jxr_set_user_data(image, &state);
295
296
0
        rc = xps_fseek(file, alpha_offset, SEEK_SET);
297
0
        if (rc != 0) {
298
0
            xps_free(ctx, name);
299
0
            jxr_destroy_container(container);
300
0
            jxr_destroy(image);
301
0
            return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
302
0
        }
303
304
0
        rc = jxr_read_image_bitstream(image, gp_get_file(file));
305
0
        if (rc < 0) {
306
0
            xps_free(ctx, name);
307
0
            jxr_destroy_container(container);
308
0
            jxr_destroy(image);
309
0
            return gs_throw1(-1, "jxr_read_image_bitstream: %s", jxr_error_string(rc));
310
0
        }
311
312
0
        jxr_destroy(image);
313
0
    }
314
315
0
    jxr_destroy_container(container);
316
317
0
    gp_fclose(file);
318
0
    gp_unlink(ctx->memory, name);
319
0
    xps_free(ctx, name);
320
321
0
    return gs_okay;
322
0
}
323
324
int
325
xps_jpegxr_has_alpha(xps_context_t *ctx, byte *buf, int len)
326
0
{
327
0
    return 1;
328
0
}