Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/gpdl/jpgtop.c
Line
Count
Source
1
/* Copyright (C) 2019-2025 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
/* jpgtop.c */
17
/* Top-level API implementation of "JPG" Language Interface */
18
19
#include "pltop.h"
20
#include "gserrors.h"
21
#include "gxdevice.h"
22
#include "gsstate.h"
23
#include "strimpl.h"
24
#include "gscoord.h"
25
#include "gsicc_manage.h"
26
#include "gspaint.h"
27
#include "plmain.h"
28
#include "jpeglib.h"
29
#include "setjmp_.h"
30
#include "sjpeg.h"
31
32
/* Forward decls */
33
34
/************************************************************/
35
/******** Language wrapper implementation (see pltop.h) *****/
36
/************************************************************/
37
38
typedef enum
39
{
40
    ii_state_identifying = 0,
41
    ii_state_jpeg,
42
    ii_state_jpeg_header,
43
    ii_state_jpeg_start,
44
    ii_state_jpeg_rows,
45
    ii_state_jpeg_finish,
46
    ii_state_flush
47
} ii_state;
48
49
typedef struct {
50
    struct jpeg_error_mgr pub;
51
    jmp_buf *setjmp_buffer;
52
} jpg_error_mgr;
53
54
/*
55
 * Jpg interpreter instance
56
 */
57
typedef struct jpg_interp_instance_s {
58
    gs_memory_t       *memory;
59
    gs_memory_t       *cmemory;
60
    gx_device         *dev;
61
    gx_device         *nulldev;
62
63
    gs_color_space    *gray;
64
    gs_color_space    *rgb;
65
    gs_color_space    *cmyk;
66
67
    /* Jpg parser state machine */
68
    ii_state           state;
69
70
    int                pages;
71
72
    uint8_t            bpp;
73
    uint8_t            cs;
74
    uint32_t           width;
75
    uint32_t           height;
76
    uint32_t           xresolution;
77
    uint32_t           yresolution;
78
79
    uint32_t           num_comps;
80
    uint32_t           byte_width;
81
    uint32_t           y;
82
83
    uint32_t           bytes_available_on_entry;
84
85
    gs_image_t         image;
86
    gs_image_enum     *penum;
87
    gs_gstate         *pgs;
88
89
    struct jpeg_decompress_struct cinfo;
90
    struct jpeg_source_mgr        jsrc;
91
    size_t             bytes_to_skip;
92
    jpg_error_mgr      jerr;
93
94
    struct
95
    {
96
        jmp_buf setjmp_buffer;
97
        unsigned char pad[16];
98
    } aligned_jmpbuf;
99
100
101
    byte              *samples;
102
103
} jpg_interp_instance_t;
104
105
static int
106
jpg_detect_language(const char *s, int len)
107
28.0k
{
108
28.0k
    const byte *hdr = (const byte *)s;
109
28.0k
    if (len >= 11) {
110
27.3k
        if (hdr[0] == 0xFF && hdr[1] == 0xd8 && hdr[2] == 0xff && hdr[3] == 0xe0 &&
111
4
            strncmp("JFIF", s+6, 5) == 0)
112
2
            return 100;
113
27.3k
    }
114
115
28.0k
    return 0;
116
28.0k
}
117
118
static const pl_interp_characteristics_t jpg_characteristics = {
119
    "JPG",
120
    jpg_detect_language,
121
};
122
123
/* GS's fakakta jpeg integration insists on putting a
124
 * memory structure pointer in the decompress structs client data.
125
 * This is no good to find our instance from. We therefore find
126
 * it by offsetting back from the address of the cinfo. This is
127
 * a nasty bit of casting, so wrap it in a macro. */
128
#define JPG_FROM_CINFO(CINFO) \
129
1
    (jpg_interp_instance_t *)(((char *)(CINFO))-offsetof(jpg_interp_instance_t,cinfo))
130
131
132
/* Get implementation's characteristics */
133
static const pl_interp_characteristics_t * /* always returns a descriptor */
134
jpg_impl_characteristics(const pl_interp_implementation_t *impl)     /* implementation of interpreter to alloc */
135
59.1k
{
136
59.1k
  return &jpg_characteristics;
137
59.1k
}
138
139
static void
140
jpg_deallocate(jpg_interp_instance_t *jpg)
141
16.1k
{
142
16.1k
    if (jpg == NULL)
143
0
        return;
144
145
16.1k
    rc_decrement_cs(jpg->gray, "jpg_deallocate");
146
16.1k
    rc_decrement_cs(jpg->rgb, "jpg_deallocate");
147
16.1k
    rc_decrement_cs(jpg->cmyk, "jpg_deallocate");
148
149
16.1k
    if (jpg->pgs != NULL)
150
16.1k
        gs_gstate_free_chain(jpg->pgs);
151
16.1k
    gs_free_object(jpg->memory, jpg, "jpg_impl_allocate_interp_instance");
152
16.1k
}
153
154
/* Deallocate a interpreter instance */
155
static int
156
jpg_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
157
16.1k
{
158
16.1k
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
159
160
16.1k
    jpg_deallocate(jpg);
161
16.1k
    impl->interp_client_data = NULL;
162
163
16.1k
    return 0;
164
16.1k
}
165
166
/* Do per-instance interpreter allocation/init. */
167
static int
168
jpg_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
169
16.1k
{
170
16.1k
    int code;
171
16.1k
    jpg_interp_instance_t *jpg
172
16.1k
        = (jpg_interp_instance_t *)gs_alloc_bytes(mem,
173
16.1k
                                                  sizeof(jpg_interp_instance_t),
174
16.1k
                                                  "jpg_impl_allocate_interp_instance");
175
16.1k
    if (!jpg)
176
0
        return_error(gs_error_VMerror);
177
16.1k
    memset(jpg, 0, sizeof(*jpg));
178
179
16.1k
    jpg->memory = mem;
180
16.1k
    jpg->pgs = gs_gstate_alloc(mem);
181
16.1k
    if (jpg->pgs == NULL)
182
0
        goto failVM;
183
184
    /* Push one save level onto the stack to assuage the memory handling */
185
16.1k
    code = gs_gsave(jpg->pgs);
186
16.1k
    if (code < 0)
187
0
        goto fail;
188
189
16.1k
    code = gsicc_init_iccmanager(jpg->pgs);
190
16.1k
    if (code < 0)
191
0
        goto fail;
192
193
16.1k
    jpg->gray = gs_cspace_new_ICC(mem, jpg->pgs, 1);
194
16.1k
    jpg->rgb  = gs_cspace_new_ICC(mem, jpg->pgs, 3);
195
16.1k
    jpg->cmyk = gs_cspace_new_ICC(mem, jpg->pgs, 4);
196
197
16.1k
    impl->interp_client_data = jpg;
198
199
16.1k
    return 0;
200
201
0
failVM:
202
0
    code = gs_note_error(gs_error_VMerror);
203
0
fail:
204
0
    (void)jpg_deallocate(jpg);
205
0
    return code;
206
0
}
207
208
/*
209
 * Get the allocator with which to allocate a device
210
 */
211
static gs_memory_t *
212
jpg_impl_get_device_memory(pl_interp_implementation_t *impl)
213
0
{
214
0
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
215
216
0
    return jpg->dev ? jpg->dev->memory : NULL;
217
0
}
218
219
#if 0 /* UNUSED */
220
static int
221
jpg_impl_set_param(pl_interp_implementation_t *impl,
222
                   pl_set_param_type           type,
223
                   const char                 *param,
224
                   const void                 *val)
225
{
226
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
227
228
    /* No params set here */
229
    return 0;
230
}
231
232
static int
233
jpg_impl_add_path(pl_interp_implementation_t *impl,
234
                  const char                 *path)
235
{
236
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
237
238
    /* No paths to add */
239
    return 0;
240
}
241
242
static int
243
jpg_impl_post_args_init(pl_interp_implementation_t *impl)
244
{
245
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
246
247
    /* No post args processing */
248
    return 0;
249
}
250
#endif
251
252
/* Prepare interp instance for the next "job" */
253
static int
254
jpg_impl_init_job(pl_interp_implementation_t *impl,
255
                  gx_device                  *device)
256
2
{
257
2
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
258
259
2
    jpg->dev = device;
260
2
    jpg->state = ii_state_identifying;
261
262
2
    return 0;
263
2
}
264
265
#if 0 /* UNUSED */
266
static int
267
jpg_impl_run_prefix_commands(pl_interp_implementation_t *impl,
268
                             const char                 *prefix)
269
{
270
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
271
272
    return 0;
273
}
274
275
static int
276
jpg_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
277
{
278
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
279
280
    return 0;
281
}
282
#endif
283
284
/* Do any setup for parser per-cursor */
285
static int                      /* ret 0 or +ve if ok, else -ve error code */
286
jpg_impl_process_begin(pl_interp_implementation_t * impl)
287
2
{
288
2
    return 0;
289
2
}
290
291
/* Ensure we have 'required' bytes to read, and further ensure
292
 * that we have no UEL's within those bytes. */
293
static int
294
ensure_bytes(jpg_interp_instance_t *jpg, stream_cursor_read *pr, int required)
295
2
{
296
2
    int n;
297
2
    const uint8_t *p = pr->ptr+1;
298
2
    const uint8_t *q;
299
2
    int avail;
300
301
    /* Find out how many bytes we need to check */
302
2
    n = pr->limit - pr->ptr;
303
2
    if (n > required)
304
2
        n = required;
305
306
    /* Make sure there are no UELs in that block */
307
2
    q = p + n;
308
2
    while (p != q) {
309
24
        while (p != q && *p != '\033')
310
22
            p++;
311
2
        if (p == q)
312
2
            break;
313
0
        avail = pr->limit - pr->ptr;
314
0
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
315
            /* At least a partial match to a UEL */
316
0
            return avail < 9 ? gs_error_NeedInput : gs_error_InterpreterExit;
317
0
        }
318
0
        p++;
319
0
    }
320
321
    /* If we have enough bytes, great, if not, get some more */
322
2
    return (n < required) ? gs_error_NeedInput : 0;
323
2
}
324
325
static int
326
flush_to_uel(stream_cursor_read *pr)
327
5
{
328
5
    const uint8_t *p = pr->ptr+1;
329
5
    const uint8_t *q = pr->limit+1;
330
5
    int avail;
331
332
47
    while (p != q) {
333
8.67k
        while (p != q && *p != '\033')
334
8.62k
            p++;
335
47
        if (p == q)
336
5
            break;
337
42
        avail = pr->limit - pr->ptr;
338
42
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
339
            /* At least a partial match to a UEL. Bin everything to
340
             * the start of the match. */
341
0
            pr->ptr = p-1;
342
0
            if (avail == 9) /* Complete match. Exit! */
343
0
                return gs_error_InterpreterExit;
344
            /* Partial match. Get more data. */
345
0
            return gs_error_NeedInput;
346
0
        }
347
42
        p++;
348
42
    }
349
350
5
    pr->ptr = pr->limit;
351
352
5
    return 0;
353
5
}
354
355
static int
356
bytes_until_uel(const stream_cursor_read *pr)
357
4
{
358
4
    const uint8_t *p = pr->ptr+1;
359
4
    const uint8_t *q = pr->limit+1;
360
4
    int avail;
361
362
14
    while (p != q) {
363
5.61k
        while (p != q && *p != '\033')
364
5.60k
            p++;
365
14
        if (p == q)
366
4
            break;
367
10
        avail = q - p;
368
10
        if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
369
            /* At least a partial match to a UEL. Everything up to
370
             * the start of the match is up for grabs. */
371
0
            return p - (pr->ptr+1);
372
0
        }
373
10
        p++;
374
10
    }
375
376
4
    return pr->limit - pr->ptr;
377
4
}
378
379
static void
380
jpg_jpeg_init_source(j_decompress_ptr cinfo)
381
2
{
382
    /* We've already inited the source */
383
2
}
384
385
static boolean
386
jpg_fill_input_buffer(j_decompress_ptr cinfo)
387
0
{
388
0
    return FALSE; /* We've filled the buffer already */
389
0
}
390
391
static void
392
jpg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
393
1
{
394
1
    jpg_interp_instance_t *jpg = JPG_FROM_CINFO(cinfo);
395
1
    size_t n = cinfo->src->bytes_in_buffer;
396
397
1
    if (n > num_bytes)
398
1
        n = num_bytes;
399
1
    jpg->jsrc.next_input_byte += num_bytes;
400
1
    jpg->jsrc.bytes_in_buffer -= n;
401
1
}
402
403
static boolean
404
jpg_resync_to_restart(j_decompress_ptr cinfo, int desired)
405
0
{
406
0
    return FALSE;
407
0
}
408
409
static void
410
jpg_term_source(j_decompress_ptr cinfo)
411
0
{
412
0
}
413
414
OPTIMIZE_SETJMP
415
static void
416
jpg_error_exit(j_common_ptr cinfo)
417
2
{
418
2
    jpg_error_mgr *jerr = (jpg_error_mgr *)cinfo->err;
419
420
2
    longjmp(*jerr->setjmp_buffer, 1);
421
2
}
422
423
static int
424
fill_jpeg_source(jpg_interp_instance_t *jpg, stream_cursor_read * pr)
425
4
{
426
4
    size_t n = bytes_until_uel(pr);
427
4
    size_t skip = jpg->bytes_to_skip;
428
429
    /* Skip any bytes we are supposed to be skipping. */
430
4
    if (skip > 0) {
431
0
        if (skip > n)
432
0
            skip = n;
433
0
        pr->ptr += skip;
434
0
        n -= skip;
435
0
        jpg->bytes_to_skip -= skip;
436
0
        if (jpg->bytes_to_skip != 0)
437
0
            return 1; /* Still more to skip */
438
0
    }
439
440
    /* Set up for the call into the jpeg lib */
441
4
    jpg->jsrc.next_input_byte = pr->ptr+1;
442
4
    jpg->jsrc.bytes_in_buffer = pr->limit - pr->ptr;
443
4
    jpg->bytes_available_on_entry = jpg->jsrc.bytes_in_buffer;
444
445
4
    return 0;
446
4
}
447
448
static int
449
consume_jpeg_data(jpg_interp_instance_t *jpg, stream_cursor_read *pr)
450
2
{
451
2
    size_t bytes_read = jpg->jsrc.next_input_byte - (pr->ptr+1);
452
2
    size_t n = pr->limit - pr->ptr;
453
454
2
    if (n > bytes_read)
455
2
        n = bytes_read;
456
2
    pr->ptr += n;
457
2
    bytes_read -= n;
458
459
    /* We need to skip the next bytes_read bytes */
460
2
    jpg->bytes_to_skip = bytes_read;
461
462
2
    return (pr->limit - pr->ptr == jpg->bytes_available_on_entry);
463
2
}
464
465
/* Horrible hack. Windows appears to expect the jmpbuf to be 16 byte
466
 * aligned. Most 64bit platforms appear to return blocks from malloc
467
 * that are aligned to 16 byte boundaries. Our malloc routines only
468
 * return things to be 8 byte aligned on 64bit platforms, so we may
469
 * fail. Accordingly, we allocate the jmpbuf in a larger structure,
470
 * and align it.
471
 *
472
 * This is only a calculation we have to do once, so we just do it on
473
 * all platforms. */
474
static jmp_buf *
475
align_setjmp_buffer(void *ptr)
476
2
{
477
2
    intptr_t offset = (intptr_t)ptr;
478
2
    char *aligned;
479
2
    offset &= 15;
480
2
    offset = (16-offset) & 15;
481
482
2
    aligned = ((char *)ptr) + offset;
483
484
2
    return (jmp_buf *)(void *)aligned;
485
2
}
486
487
OPTIMIZE_SETJMP
488
static int
489
jpg_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
490
5
{
491
5
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
492
5
    int code = 0;
493
5
    int need_more_data;
494
495
5
    do
496
13
    {
497
13
        need_more_data = 0;
498
13
        switch(jpg->state)
499
13
        {
500
2
        case ii_state_identifying:
501
2
        {
502
2
            const byte *hdr;
503
504
2
            jpg->jerr.setjmp_buffer = align_setjmp_buffer(&jpg->aligned_jmpbuf);
505
506
            /* Try and get us 11 bytes */
507
2
            code = ensure_bytes(jpg, pr, 11);
508
2
            if (code < 0)
509
0
                return code;
510
2
            hdr = pr->ptr+1;
511
2
            if (hdr[0] == 0xFF && hdr[1] == 0xd8 && hdr[2] == 0xff && hdr[3] == 0xe0 &&
512
2
                memcmp("JFIF", hdr+6, 5) == 0)
513
2
            {
514
2
                jpg->state = ii_state_jpeg;
515
2
                break;
516
2
            }
517
0
            jpg->state = ii_state_flush;
518
0
            break;
519
2
        }
520
2
        case ii_state_jpeg:
521
2
            code = gs_jpeg_mem_init(jpg->memory, (j_common_ptr)&jpg->cinfo);
522
2
            if (code < 0) {
523
0
                jpg->state = ii_state_flush;
524
0
                break;
525
0
            }
526
527
2
            jpg->cinfo.err = jpeg_std_error(&jpg->jerr.pub);
528
2
            jpg->jerr.pub.error_exit = jpg_error_exit;
529
2
            if (setjmp(*jpg->jerr.setjmp_buffer)) {
530
0
                jpg->state = ii_state_flush;
531
0
                break;
532
0
            }
533
534
2
            jpeg_create_decompress(&jpg->cinfo);
535
536
2
            jpg->cinfo.src = &jpg->jsrc;
537
2
            jpg->jsrc.init_source = jpg_jpeg_init_source;
538
2
            jpg->jsrc.fill_input_buffer = jpg_fill_input_buffer;
539
2
            jpg->jsrc.skip_input_data = jpg_skip_input_data;
540
2
            jpg->jsrc.resync_to_restart = jpg_resync_to_restart;
541
2
            jpg->jsrc.term_source = jpg_term_source;
542
543
2
            jpg->state = ii_state_jpeg_header;
544
2
            jpg->y = 0;
545
2
            break;
546
2
        case ii_state_jpeg_header:
547
2
        {
548
2
            int ok;
549
2
            gs_color_space *cs;
550
2
            float scale, xext, yext, xoffset, yoffset;
551
552
2
            if (fill_jpeg_source(jpg, pr)) {
553
0
                need_more_data = 1;
554
0
                break; /* No bytes left after skipping */
555
0
            }
556
2
            if (setjmp(*jpg->jerr.setjmp_buffer)) {
557
0
                jpeg_destroy_decompress(&jpg->cinfo);
558
0
                jpg->state = ii_state_flush;
559
0
                break;
560
0
            }
561
2
            ok = jpeg_read_header(&jpg->cinfo, TRUE);
562
2
            need_more_data = consume_jpeg_data(jpg, pr);
563
2
            if (ok == JPEG_SUSPENDED)
564
0
                break;
565
2
            need_more_data = 0;
566
567
2
            jpg->width = jpg->cinfo.image_width;
568
2
            jpg->height = jpg->cinfo.image_height;
569
2
            jpg->num_comps = jpg->cinfo.num_components;
570
2
            jpg->bpp = 8 * jpg->num_comps;
571
2
            switch(jpg->num_comps) {
572
0
            default:
573
0
            case 1:
574
0
                cs = jpg->gray;
575
0
                jpg->cinfo.out_color_space = JCS_GRAYSCALE;
576
0
                break;
577
2
            case 3:
578
2
                cs = jpg->rgb;
579
2
                jpg->cinfo.out_color_space = JCS_RGB;
580
2
                break;
581
0
            case 4:
582
0
                cs = jpg->cmyk;
583
0
                jpg->cinfo.out_color_space = JCS_CMYK;
584
0
                break;
585
2
            }
586
587
            /* Find us some X and Y resolutions */
588
2
            jpg->xresolution = jpg->cinfo.X_density;
589
2
            jpg->yresolution = jpg->cinfo.Y_density;
590
2
            if (jpg->xresolution == 0)
591
0
                jpg->xresolution = jpg->yresolution;
592
2
            if (jpg->yresolution == 0)
593
0
                jpg->yresolution = jpg->xresolution;
594
2
            if (jpg->xresolution == 0)
595
0
                jpg->xresolution = 72;
596
2
            if (jpg->yresolution == 0)
597
0
                jpg->yresolution = 72;
598
599
            /* Scale to fit, if too large. */
600
2
            scale = 1.0f;
601
2
            if (jpg->width * jpg->dev->HWResolution[0] > jpg->dev->width * jpg->xresolution)
602
1
                scale = ((float)jpg->dev->width * jpg->xresolution) / (jpg->width * jpg->dev->HWResolution[0]);
603
2
            if (scale * jpg->height * jpg->dev->HWResolution[1] > jpg->dev->height * jpg->yresolution)
604
0
                scale = ((float)jpg->dev->height * jpg->yresolution) / (jpg->height * jpg->dev->HWResolution[1]);
605
606
            /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
607
2
            xext = ((float)jpg->width * 72 * scale / jpg->xresolution);
608
2
            xoffset = (jpg->dev->width * 72 / jpg->dev->HWResolution[0] - xext)/2;
609
2
            yext = ((float)jpg->height * 72 * scale / jpg->yresolution);
610
2
            yoffset = (jpg->dev->height * 72 / jpg->dev->HWResolution[1] - yext)/2;
611
612
            /* Now we've got the data from the image header, we can
613
             * make the gs image instance */
614
2
            jpg->byte_width = (jpg->bpp>>3)*jpg->width;
615
2
            if (jpg->width != 0 && jpg->byte_width / (jpg->bpp>>3) != jpg->width)
616
0
                return gs_note_error(gs_error_undefinedresult);
617
618
2
            jpg->nulldev = gs_currentdevice(jpg->pgs);
619
2
            rc_increment(jpg->nulldev);
620
2
            code = gs_setdevice_no_erase(jpg->pgs, jpg->dev);
621
2
            if (code < 0) {
622
0
                jpg->state = ii_state_flush;
623
0
                break;
624
0
            }
625
2
            gs_initmatrix(jpg->pgs);
626
627
            /* By default the ctm is set to:
628
             *   xres/72   0
629
             *   0         -yres/72
630
             *   0         dev->height * yres/72
631
             * i.e. it moves the origin from being top right to being bottom left.
632
             * We want to move it back, as without this, the image will be displayed
633
             * upside down.
634
             */
635
2
            code = gs_translate(jpg->pgs, 0.0, jpg->dev->height * 72 / jpg->dev->HWResolution[1]);
636
2
            if (code >= 0)
637
2
                code = gs_translate(jpg->pgs, xoffset, -yoffset);
638
2
            if (code >= 0)
639
2
                code = gs_scale(jpg->pgs, scale, -scale);
640
            /* At this point, the ctm is set to:
641
             *   xres/72  0
642
             *   0        yres/72
643
             *   0        0
644
             */
645
2
            if (code >= 0)
646
2
                code = gs_erasepage(jpg->pgs);
647
2
            if (code < 0) {
648
0
                jpg->state = ii_state_flush;
649
0
                break;
650
0
            }
651
652
2
            jpg->samples = gs_alloc_bytes(jpg->memory, jpg->byte_width, "jpg_impl_process(samples)");
653
2
            if (jpg->samples == NULL) {
654
0
                jpg->state = ii_state_flush;
655
0
                break;
656
0
            }
657
658
2
            memset(&jpg->image, 0, sizeof(jpg->image));
659
2
            gs_image_t_init(&jpg->image, cs);
660
2
            jpg->image.BitsPerComponent = jpg->bpp/jpg->num_comps;
661
2
            jpg->image.Width = jpg->width;
662
2
            jpg->image.Height = jpg->height;
663
664
2
            jpg->image.ImageMatrix.xx = jpg->xresolution / 72.0f;
665
2
            jpg->image.ImageMatrix.yy = jpg->yresolution / 72.0f;
666
667
2
            jpg->penum = gs_image_enum_alloc(jpg->memory, "jpg_impl_process(penum)");
668
2
            if (jpg->penum == NULL) {
669
0
                code = gs_note_error(gs_error_VMerror);
670
0
                jpg->state = ii_state_flush;
671
0
                return code;
672
0
            }
673
674
2
            code = gs_image_init(jpg->penum,
675
2
                                 &jpg->image,
676
2
                                 false,
677
2
                                 false,
678
2
                                 jpg->pgs);
679
2
            if (code < 0) {
680
0
                jpg->state = ii_state_flush;
681
0
                return code;
682
0
            }
683
684
2
            jpg->state = ii_state_jpeg_start;
685
2
            break;
686
2
        }
687
2
        case ii_state_jpeg_start:
688
2
        {
689
2
            int ok;
690
2
            if (fill_jpeg_source(jpg, pr)) {
691
0
                need_more_data = 1;
692
0
                break; /* No bytes left after skipping */
693
0
            }
694
2
            if (setjmp(*jpg->jerr.setjmp_buffer)) {
695
2
                jpeg_destroy_decompress(&jpg->cinfo);
696
2
                jpg->state = ii_state_flush;
697
2
                break;
698
2
            }
699
0
            ok = jpeg_start_decompress(&jpg->cinfo);
700
0
            (void)consume_jpeg_data(jpg, pr);
701
0
            if (ok == FALSE)
702
0
                break;
703
0
            jpg->state = ii_state_jpeg_rows;
704
0
            break;
705
0
        }
706
0
        case ii_state_jpeg_rows:
707
0
        {
708
0
            int rows_decoded;
709
0
            unsigned int used;
710
711
0
            if (fill_jpeg_source(jpg, pr)) {
712
0
                need_more_data = 1;
713
0
                break; /* No bytes left after skipping */
714
0
            }
715
0
            if (setjmp(*jpg->jerr.setjmp_buffer)) {
716
0
                jpeg_destroy_decompress(&jpg->cinfo);
717
0
                jpg->state = ii_state_flush;
718
0
                break;
719
0
            }
720
0
            rows_decoded = jpeg_read_scanlines(&jpg->cinfo, &jpg->samples, 1);
721
0
            need_more_data = consume_jpeg_data(jpg, pr);
722
0
            if (rows_decoded == 0)
723
0
                break; /* Not enough data for a scanline yet */
724
0
            need_more_data = 0;
725
726
0
            code = gs_image_next(jpg->penum, jpg->samples, jpg->byte_width, &used);
727
0
            if (code < 0) {
728
0
                jpg->state = ii_state_flush;
729
0
                break;
730
0
            }
731
0
            jpg->y++;
732
0
            if (jpg->y == jpg->height) {
733
0
                code = gs_image_cleanup_and_free_enum(jpg->penum, jpg->pgs);
734
0
                jpg->penum = NULL;
735
0
                if (code < 0) {
736
0
                    jpg->state = ii_state_flush;
737
0
                    break;
738
0
                }
739
0
                code = pl_finish_page(jpg->memory->gs_lib_ctx->top_of_system,
740
0
                                      jpg->pgs, 1, true);
741
0
                if (code < 0) {
742
0
                    jpg->state = ii_state_flush;
743
0
                    break;
744
0
                }
745
0
                jpg->state = ii_state_jpeg_finish;
746
0
            }
747
0
            break;
748
0
        }
749
0
        case ii_state_jpeg_finish:
750
0
        {
751
0
            int ok;
752
753
0
            if (fill_jpeg_source(jpg, pr)) {
754
0
                need_more_data = 1;
755
0
                break; /* No bytes left after skipping */
756
0
            }
757
0
            if (setjmp(*jpg->jerr.setjmp_buffer)) {
758
0
                jpeg_destroy_decompress(&jpg->cinfo);
759
0
                jpg->state = ii_state_flush;
760
0
                break;
761
0
            }
762
0
            ok = jpeg_finish_decompress(&jpg->cinfo);
763
0
            need_more_data = consume_jpeg_data(jpg, pr);
764
0
            if (ok == FALSE)
765
0
                break;
766
0
            need_more_data = 0;
767
0
            jpg->state = ii_state_flush;
768
0
            break;
769
0
        }
770
0
        default:
771
5
        case ii_state_flush:
772
5
            if (setjmp(*jpg->jerr.setjmp_buffer))
773
0
                break;
774
5
            jpeg_destroy_decompress(&jpg->cinfo);
775
776
5
            gs_jpeg_mem_term((j_common_ptr)&jpg->cinfo);
777
778
5
            if (jpg->penum) {
779
2
                (void)gs_image_cleanup_and_free_enum(jpg->penum, jpg->pgs);
780
2
                jpg->penum = NULL;
781
2
            }
782
783
5
            gs_free_object(jpg->memory, jpg->samples, "jpg_impl_process(samples)");
784
5
            jpg->samples = NULL;
785
            /* We want to bin any data we get up to, but not including
786
             * a UEL. */
787
5
            return flush_to_uel(pr);
788
13
        }
789
13
    } while (!need_more_data);
790
791
0
    return code;
792
5
}
793
794
static int
795
jpg_impl_process_end(pl_interp_implementation_t * impl)
796
2
{
797
2
    return 0;
798
2
}
799
800
/* Not implemented */
801
static int
802
jpg_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
803
0
{
804
0
    const byte *p = cursor->ptr;
805
0
    const byte *rlimit = cursor->limit;
806
807
    /* Skip to, but leave UEL in buffer for PJL to find later */
808
0
    for (; p < rlimit; ++p)
809
0
        if (p[1] == '\033') {
810
0
            uint avail = rlimit - p;
811
812
0
            if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
813
0
                continue;
814
0
            if (avail < 9)
815
0
                break;
816
0
            cursor->ptr = p;
817
0
            return 1;           /* found eoj */
818
0
        }
819
0
    cursor->ptr = p;
820
0
    return 0;                   /* need more */
821
0
}
822
823
/* Parser action for end-of-file */
824
static int
825
jpg_impl_process_eof(pl_interp_implementation_t *impl)
826
2
{
827
2
    return 0;
828
2
}
829
830
/* Report any errors after running a job */
831
static int
832
jpg_impl_report_errors(pl_interp_implementation_t *impl,          /* interp instance to wrap up job in */
833
                       int                         code,          /* prev termination status */
834
                       long                        file_position, /* file position of error, -1 if unknown */
835
                       bool                        force_to_cout  /* force errors to cout */
836
)
837
0
{
838
0
    return 0;
839
0
}
840
841
/* Wrap up interp instance after a "job" */
842
static int
843
jpg_impl_dnit_job(pl_interp_implementation_t *impl)
844
2
{
845
2
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
846
847
2
    if (jpg->nulldev) {
848
2
        int code = gs_setdevice(jpg->pgs, jpg->nulldev);
849
2
        jpg->dev = NULL;
850
2
        rc_decrement(jpg->nulldev, "jpg_impl_dnit_job(nulldevice)");
851
2
        jpg->nulldev = NULL;
852
2
        return code;
853
2
    }
854
0
    return 0;
855
2
}
856
857
/* Parser implementation descriptor */
858
const pl_interp_implementation_t jpg_implementation = {
859
  jpg_impl_characteristics,
860
  jpg_impl_allocate_interp_instance,
861
  jpg_impl_get_device_memory,
862
  NULL, /* jpg_impl_set_param */
863
  NULL, /* jpg_impl_add_path */
864
  NULL, /* jpg_impl_post_args_init */
865
  jpg_impl_init_job,
866
  NULL, /* jpg_impl_run_prefix_commands */
867
  NULL, /* jpg_impl_process_file */
868
  jpg_impl_process_begin,
869
  jpg_impl_process,
870
  jpg_impl_process_end,
871
  jpg_impl_flush_to_eoj,
872
  jpg_impl_process_eof,
873
  jpg_impl_report_errors,
874
  jpg_impl_dnit_job,
875
  jpg_impl_deallocate_interp_instance,
876
  NULL, /* jpg_impl_reset */
877
  NULL  /* interp_client_data */
878
};