Coverage Report

Created: 2026-04-01 07:17

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
17.7k
{
108
17.7k
    const byte *hdr = (const byte *)s;
109
17.7k
    if (len >= 11) {
110
17.1k
        if (hdr[0] == 0xFF && hdr[1] == 0xd8 && hdr[2] == 0xff && hdr[3] == 0xe0 &&
111
1
            strncmp("JFIF", s+6, 5) == 0)
112
0
            return 100;
113
17.1k
    }
114
115
17.7k
    return 0;
116
17.7k
}
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
0
    (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
37.5k
{
136
37.5k
  return &jpg_characteristics;
137
37.5k
}
138
139
static void
140
jpg_deallocate(jpg_interp_instance_t *jpg)
141
8.09k
{
142
8.09k
    if (jpg == NULL)
143
0
        return;
144
145
8.09k
    rc_decrement_cs(jpg->gray, "jpg_deallocate");
146
8.09k
    rc_decrement_cs(jpg->rgb, "jpg_deallocate");
147
8.09k
    rc_decrement_cs(jpg->cmyk, "jpg_deallocate");
148
149
8.09k
    if (jpg->pgs != NULL)
150
8.09k
        gs_gstate_free_chain(jpg->pgs);
151
8.09k
    gs_free_object(jpg->memory, jpg, "jpg_impl_allocate_interp_instance");
152
8.09k
}
153
154
/* Deallocate a interpreter instance */
155
static int
156
jpg_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
157
8.09k
{
158
8.09k
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
159
160
8.09k
    jpg_deallocate(jpg);
161
8.09k
    impl->interp_client_data = NULL;
162
163
8.09k
    return 0;
164
8.09k
}
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
8.09k
{
170
8.09k
    int code;
171
8.09k
    jpg_interp_instance_t *jpg
172
8.09k
        = (jpg_interp_instance_t *)gs_alloc_bytes(mem,
173
8.09k
                                                  sizeof(jpg_interp_instance_t),
174
8.09k
                                                  "jpg_impl_allocate_interp_instance");
175
8.09k
    if (!jpg)
176
0
        return_error(gs_error_VMerror);
177
8.09k
    memset(jpg, 0, sizeof(*jpg));
178
179
8.09k
    jpg->memory = mem;
180
8.09k
    jpg->pgs = gs_gstate_alloc(mem);
181
8.09k
    if (jpg->pgs == NULL)
182
0
        goto failVM;
183
184
    /* Push one save level onto the stack to assuage the memory handling */
185
8.09k
    code = gs_gsave(jpg->pgs);
186
8.09k
    if (code < 0)
187
0
        goto fail;
188
189
8.09k
    code = gsicc_init_iccmanager(jpg->pgs);
190
8.09k
    if (code < 0)
191
0
        goto fail;
192
193
8.09k
    jpg->gray = gs_cspace_new_ICC(mem, jpg->pgs, 1);
194
8.09k
    jpg->rgb  = gs_cspace_new_ICC(mem, jpg->pgs, 3);
195
8.09k
    jpg->cmyk = gs_cspace_new_ICC(mem, jpg->pgs, 4);
196
197
8.09k
    impl->interp_client_data = jpg;
198
199
8.09k
    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
0
{
257
0
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
258
259
0
    jpg->dev = device;
260
0
    jpg->state = ii_state_identifying;
261
262
0
    return 0;
263
0
}
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
0
{
288
0
    return 0;
289
0
}
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
0
{
296
0
    int n;
297
0
    const uint8_t *p = pr->ptr+1;
298
0
    const uint8_t *q;
299
0
    int avail;
300
301
    /* Find out how many bytes we need to check */
302
0
    n = pr->limit - pr->ptr;
303
0
    if (n > required)
304
0
        n = required;
305
306
    /* Make sure there are no UELs in that block */
307
0
    q = p + n;
308
0
    while (p != q) {
309
0
        while (p != q && *p != '\033')
310
0
            p++;
311
0
        if (p == q)
312
0
            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
0
    return (n < required) ? gs_error_NeedInput : 0;
323
0
}
324
325
static int
326
flush_to_uel(stream_cursor_read *pr)
327
0
{
328
0
    const uint8_t *p = pr->ptr+1;
329
0
    const uint8_t *q = pr->limit+1;
330
0
    int avail;
331
332
0
    while (p != q) {
333
0
        while (p != q && *p != '\033')
334
0
            p++;
335
0
        if (p == q)
336
0
            break;
337
0
        avail = pr->limit - pr->ptr;
338
0
        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
0
        p++;
348
0
    }
349
350
0
    pr->ptr = pr->limit;
351
352
0
    return 0;
353
0
}
354
355
static int
356
bytes_until_uel(const stream_cursor_read *pr)
357
0
{
358
0
    const uint8_t *p = pr->ptr+1;
359
0
    const uint8_t *q = pr->limit+1;
360
0
    int avail;
361
362
0
    while (p != q) {
363
0
        while (p != q && *p != '\033')
364
0
            p++;
365
0
        if (p == q)
366
0
            break;
367
0
        avail = q - p;
368
0
        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
0
        p++;
374
0
    }
375
376
0
    return pr->limit - pr->ptr;
377
0
}
378
379
static void
380
jpg_jpeg_init_source(j_decompress_ptr cinfo)
381
0
{
382
    /* We've already inited the source */
383
0
}
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
0
{
394
0
    jpg_interp_instance_t *jpg = JPG_FROM_CINFO(cinfo);
395
0
    size_t n = cinfo->src->bytes_in_buffer;
396
397
0
    if (n > num_bytes)
398
0
        n = num_bytes;
399
0
    jpg->jsrc.next_input_byte += num_bytes;
400
0
    jpg->jsrc.bytes_in_buffer -= n;
401
0
}
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
0
{
418
0
    jpg_error_mgr *jerr = (jpg_error_mgr *)cinfo->err;
419
420
0
    longjmp(*jerr->setjmp_buffer, 1);
421
0
}
422
423
static int
424
fill_jpeg_source(jpg_interp_instance_t *jpg, stream_cursor_read * pr)
425
0
{
426
0
    size_t n = bytes_until_uel(pr);
427
0
    size_t skip = jpg->bytes_to_skip;
428
429
    /* Skip any bytes we are supposed to be skipping. */
430
0
    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
0
    jpg->jsrc.next_input_byte = pr->ptr+1;
442
0
    jpg->jsrc.bytes_in_buffer = pr->limit - pr->ptr;
443
0
    jpg->bytes_available_on_entry = jpg->jsrc.bytes_in_buffer;
444
445
0
    return 0;
446
0
}
447
448
static int
449
consume_jpeg_data(jpg_interp_instance_t *jpg, stream_cursor_read *pr)
450
0
{
451
0
    size_t bytes_read = jpg->jsrc.next_input_byte - (pr->ptr+1);
452
0
    size_t n = pr->limit - pr->ptr;
453
454
0
    if (n > bytes_read)
455
0
        n = bytes_read;
456
0
    pr->ptr += n;
457
0
    bytes_read -= n;
458
459
    /* We need to skip the next bytes_read bytes */
460
0
    jpg->bytes_to_skip = bytes_read;
461
462
0
    return (pr->limit - pr->ptr == jpg->bytes_available_on_entry);
463
0
}
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
0
{
477
0
    intptr_t offset = (intptr_t)ptr;
478
0
    char *aligned;
479
0
    offset &= 15;
480
0
    offset = (16-offset) & 15;
481
482
0
    aligned = ((char *)ptr) + offset;
483
484
0
    return (jmp_buf *)(void *)aligned;
485
0
}
486
487
OPTIMIZE_SETJMP
488
static int
489
jpg_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
490
0
{
491
0
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
492
0
    int code = 0;
493
0
    int need_more_data;
494
495
0
    do
496
0
    {
497
0
        need_more_data = 0;
498
0
        switch(jpg->state)
499
0
        {
500
0
        case ii_state_identifying:
501
0
        {
502
0
            const byte *hdr;
503
504
0
            jpg->jerr.setjmp_buffer = align_setjmp_buffer(&jpg->aligned_jmpbuf);
505
506
            /* Try and get us 11 bytes */
507
0
            code = ensure_bytes(jpg, pr, 11);
508
0
            if (code < 0)
509
0
                return code;
510
0
            hdr = pr->ptr+1;
511
0
            if (hdr[0] == 0xFF && hdr[1] == 0xd8 && hdr[2] == 0xff && hdr[3] == 0xe0 &&
512
0
                memcmp("JFIF", hdr+6, 5) == 0)
513
0
            {
514
0
                jpg->state = ii_state_jpeg;
515
0
                break;
516
0
            }
517
0
            jpg->state = ii_state_flush;
518
0
            break;
519
0
        }
520
0
        case ii_state_jpeg:
521
0
            code = gs_jpeg_mem_init(jpg->memory, (j_common_ptr)&jpg->cinfo);
522
0
            if (code < 0) {
523
0
                jpg->state = ii_state_flush;
524
0
                break;
525
0
            }
526
527
0
            jpg->cinfo.err = jpeg_std_error(&jpg->jerr.pub);
528
0
            jpg->jerr.pub.error_exit = jpg_error_exit;
529
0
            if (setjmp(*jpg->jerr.setjmp_buffer)) {
530
0
                jpg->state = ii_state_flush;
531
0
                break;
532
0
            }
533
534
0
            jpeg_create_decompress(&jpg->cinfo);
535
536
0
            jpg->cinfo.src = &jpg->jsrc;
537
0
            jpg->jsrc.init_source = jpg_jpeg_init_source;
538
0
            jpg->jsrc.fill_input_buffer = jpg_fill_input_buffer;
539
0
            jpg->jsrc.skip_input_data = jpg_skip_input_data;
540
0
            jpg->jsrc.resync_to_restart = jpg_resync_to_restart;
541
0
            jpg->jsrc.term_source = jpg_term_source;
542
543
0
            jpg->state = ii_state_jpeg_header;
544
0
            jpg->y = 0;
545
0
            break;
546
0
        case ii_state_jpeg_header:
547
0
        {
548
0
            int ok;
549
0
            gs_color_space *cs;
550
0
            float scale, xext, yext, xoffset, yoffset;
551
552
0
            if (fill_jpeg_source(jpg, pr)) {
553
0
                need_more_data = 1;
554
0
                break; /* No bytes left after skipping */
555
0
            }
556
0
            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
0
            ok = jpeg_read_header(&jpg->cinfo, TRUE);
562
0
            need_more_data = consume_jpeg_data(jpg, pr);
563
0
            if (ok == JPEG_SUSPENDED)
564
0
                break;
565
0
            need_more_data = 0;
566
567
0
            jpg->width = jpg->cinfo.image_width;
568
0
            jpg->height = jpg->cinfo.image_height;
569
0
            jpg->num_comps = jpg->cinfo.num_components;
570
0
            jpg->bpp = 8 * jpg->num_comps;
571
0
            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
0
            case 3:
578
0
                cs = jpg->rgb;
579
0
                jpg->cinfo.out_color_space = JCS_RGB;
580
0
                break;
581
0
            case 4:
582
0
                cs = jpg->cmyk;
583
0
                jpg->cinfo.out_color_space = JCS_CMYK;
584
0
                break;
585
0
            }
586
587
            /* Find us some X and Y resolutions */
588
0
            jpg->xresolution = jpg->cinfo.X_density;
589
0
            jpg->yresolution = jpg->cinfo.Y_density;
590
0
            if (jpg->xresolution == 0)
591
0
                jpg->xresolution = jpg->yresolution;
592
0
            if (jpg->yresolution == 0)
593
0
                jpg->yresolution = jpg->xresolution;
594
0
            if (jpg->xresolution == 0)
595
0
                jpg->xresolution = 72;
596
0
            if (jpg->yresolution == 0)
597
0
                jpg->yresolution = 72;
598
599
            /* Scale to fit, if too large. */
600
0
            scale = 1.0f;
601
0
            if (jpg->width * jpg->dev->HWResolution[0] > jpg->dev->width * jpg->xresolution)
602
0
                scale = ((float)jpg->dev->width * jpg->xresolution) / (jpg->width * jpg->dev->HWResolution[0]);
603
0
            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
0
            xext = ((float)jpg->width * 72 * scale / jpg->xresolution);
608
0
            xoffset = (jpg->dev->width * 72 / jpg->dev->HWResolution[0] - xext)/2;
609
0
            yext = ((float)jpg->height * 72 * scale / jpg->yresolution);
610
0
            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
0
            jpg->byte_width = (jpg->bpp>>3)*jpg->width;
615
0
            if (jpg->width != 0 && jpg->byte_width / (jpg->bpp>>3) != jpg->width)
616
0
                return gs_note_error(gs_error_undefinedresult);
617
618
0
            jpg->nulldev = gs_currentdevice(jpg->pgs);
619
0
            rc_increment(jpg->nulldev);
620
0
            code = gs_setdevice_no_erase(jpg->pgs, jpg->dev);
621
0
            if (code < 0) {
622
0
                jpg->state = ii_state_flush;
623
0
                break;
624
0
            }
625
0
            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
0
            code = gs_translate(jpg->pgs, 0.0, jpg->dev->height * 72 / jpg->dev->HWResolution[1]);
636
0
            if (code >= 0)
637
0
                code = gs_translate(jpg->pgs, xoffset, -yoffset);
638
0
            if (code >= 0)
639
0
                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
0
            if (code >= 0)
646
0
                code = gs_erasepage(jpg->pgs);
647
0
            if (code < 0) {
648
0
                jpg->state = ii_state_flush;
649
0
                break;
650
0
            }
651
652
0
            jpg->samples = gs_alloc_bytes(jpg->memory, jpg->byte_width, "jpg_impl_process(samples)");
653
0
            if (jpg->samples == NULL) {
654
0
                jpg->state = ii_state_flush;
655
0
                break;
656
0
            }
657
658
0
            memset(&jpg->image, 0, sizeof(jpg->image));
659
0
            gs_image_t_init(&jpg->image, cs);
660
0
            jpg->image.BitsPerComponent = jpg->bpp/jpg->num_comps;
661
0
            jpg->image.Width = jpg->width;
662
0
            jpg->image.Height = jpg->height;
663
664
0
            jpg->image.ImageMatrix.xx = jpg->xresolution / 72.0f;
665
0
            jpg->image.ImageMatrix.yy = jpg->yresolution / 72.0f;
666
667
0
            jpg->penum = gs_image_enum_alloc(jpg->memory, "jpg_impl_process(penum)");
668
0
            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
0
            code = gs_image_init(jpg->penum,
675
0
                                 &jpg->image,
676
0
                                 false,
677
0
                                 false,
678
0
                                 jpg->pgs);
679
0
            if (code < 0) {
680
0
                jpg->state = ii_state_flush;
681
0
                return code;
682
0
            }
683
684
0
            jpg->state = ii_state_jpeg_start;
685
0
            break;
686
0
        }
687
0
        case ii_state_jpeg_start:
688
0
        {
689
0
            int ok;
690
0
            if (fill_jpeg_source(jpg, pr)) {
691
0
                need_more_data = 1;
692
0
                break; /* No bytes left after skipping */
693
0
            }
694
0
            if (setjmp(*jpg->jerr.setjmp_buffer)) {
695
0
                jpeg_destroy_decompress(&jpg->cinfo);
696
0
                jpg->state = ii_state_flush;
697
0
                break;
698
0
            }
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
0
        case ii_state_flush:
772
0
            if (setjmp(*jpg->jerr.setjmp_buffer))
773
0
                break;
774
0
            jpeg_destroy_decompress(&jpg->cinfo);
775
776
0
            gs_jpeg_mem_term((j_common_ptr)&jpg->cinfo);
777
778
0
            if (jpg->penum) {
779
0
                (void)gs_image_cleanup_and_free_enum(jpg->penum, jpg->pgs);
780
0
                jpg->penum = NULL;
781
0
            }
782
783
0
            gs_free_object(jpg->memory, jpg->samples, "jpg_impl_process(samples)");
784
0
            jpg->samples = NULL;
785
            /* We want to bin any data we get up to, but not including
786
             * a UEL. */
787
0
            return flush_to_uel(pr);
788
0
        }
789
0
    } while (!need_more_data);
790
791
0
    return code;
792
0
}
793
794
static int
795
jpg_impl_process_end(pl_interp_implementation_t * impl)
796
0
{
797
0
    return 0;
798
0
}
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
0
{
827
0
    return 0;
828
0
}
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
0
{
845
0
    jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
846
847
0
    if (jpg->nulldev) {
848
0
        int code = gs_setdevice(jpg->pgs, jpg->nulldev);
849
0
        jpg->dev = NULL;
850
0
        rc_decrement(jpg->nulldev, "jpg_impl_dnit_job(nulldevice)");
851
0
        jpg->nulldev = NULL;
852
0
        return code;
853
0
    }
854
0
    return 0;
855
0
}
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
};