Coverage Report

Created: 2025-06-10 07:19

/src/ghostpdl/base/gsimage.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* Image setup procedures for Ghostscript library */
18
#include "memory_.h"
19
#include "math_.h"
20
#include "gx.h"
21
#include "gserrors.h"
22
#include "gsstruct.h"
23
#include "gscspace.h"
24
#include "gsmatrix.h"   /* for gsiparam.h */
25
#include "gsimage.h"
26
#include "gxarith.h"    /* for igcd */
27
#include "gxdevice.h"
28
#include "gxiparam.h"
29
#include "gxpath.h"   /* for gx_effective_clip_path */
30
#include "gximask.h"
31
#include "gzstate.h"
32
#include "gsutil.h"
33
#include "gxdevsop.h"
34
#include "gximage.h"
35
36
/*
37
  The main internal invariant for the gs_image machinery is
38
  straightforward.  The state consists primarily of N plane buffers
39
  (planes[]).
40
*/
41
typedef struct image_enum_plane_s {
42
/*
43
  The state of each plane consists of:
44
45
  - A row buffer, aligned and (logically) large enough to hold one scan line
46
    for that plane.  (It may have to be reallocated if the plane width or
47
    depth changes.)  A row buffer is "full" if it holds exactly a full scan
48
    line.
49
*/
50
    gs_string row;
51
/*
52
  - A position within the row buffer, indicating how many initial bytes are
53
    occupied.
54
*/
55
    uint pos;
56
/*
57
  - A (retained) source string, which may be empty (size = 0).
58
*/
59
    gs_const_string source;
60
    /* The gs_string 'orig' is only set if the 'txfer_control' flag was set when
61
     * the 'source' string data was initally passed in. In this case we now control the lifetime
62
     * of the string. So when we empty the source string, free it. We need to know the actual
63
     * address of the string, and that gets modified in the peunum->planes->source and size
64
     * members, so we use 'orig' as both a marker for the control and the original size and location.
65
     */
66
    gs_const_string orig;
67
} image_enum_plane_t;
68
/*
69
  The possible states for each plane do not depend on the state of any other
70
  plane.  Either:
71
72
  - pos = 0, source.size = 0.
73
74
  - If the underlying image processor says the plane is currently wanted,
75
    either:
76
77
    - pos = 0, source.size >= one full row of data for this plane.  This
78
      case allows us to avoid copying the data from the source string to the
79
      row buffer if the client is providing data in blocks of at least one
80
      scan line.
81
82
    - pos = full, source.size may have any value.
83
84
    - pos > 0, pos < full, source.size = 0;
85
86
  - If the underlying image processor says the plane is not currently
87
    wanted:
88
89
    - pos = 0, source.size may have any value.
90
91
  This invariant holds at the beginning and end of each call on
92
  gs_image_next_planes.  Note that for each plane, the "plane wanted" status
93
  and size of a full row may change after each call of plane_data.  As
94
  documented in gxiparam.h, we assume that a call of plane_data can only
95
  change a plane's status from "wanted" to "not wanted", or change the width
96
  or depth of a wanted plane, if data for that plane was actually supplied
97
  (and used).
98
*/
99
100
/* Define the enumeration state for this interface layer. */
101
/*typedef struct gs_image_enum_s gs_image_enum; *//* in gsimage.h */
102
struct gs_image_enum_s {
103
    /* The following are set at initialization time. */
104
    gs_memory_t *memory;
105
    gx_device *dev;   /* if 0, just skip over the data */
106
    gx_image_enum_common_t *info; /* driver bookkeeping structure */
107
    int num_planes;
108
    int height;
109
    bool wanted_varies;
110
    /* The following are updated dynamically. */
111
    int plane_index;    /* index of next plane of data, */
112
                                /* only needed for gs_image_next */
113
    int y;
114
    bool error;
115
    byte wanted[GS_IMAGE_MAX_COMPONENTS]; /* cache gx_image_planes_wanted */
116
    byte client_wanted[GS_IMAGE_MAX_COMPONENTS]; /* see gsimage.h */
117
    image_enum_plane_t planes[GS_IMAGE_MAX_COMPONENTS]; /* see above */
118
    /*
119
     * To reduce setup for transferring complete rows, we maintain a
120
     * partially initialized parameter array for gx_image_plane_data_rows.
121
     * The data member is always set just before calling
122
     * gx_image_plane_data_rows; the data_x and raster members are reset
123
     * when needed.
124
     */
125
    gx_image_plane_t image_planes[GS_IMAGE_MAX_COMPONENTS];
126
};
127
128
gs_private_st_composite(st_gs_image_enum, gs_image_enum, "gs_image_enum",
129
                        gs_image_enum_enum_ptrs, gs_image_enum_reloc_ptrs);
130
144
#define gs_image_enum_num_ptrs 2
131
132
/* GC procedures */
133
static
134
240
ENUM_PTRS_WITH(gs_image_enum_enum_ptrs, gs_image_enum *eptr)
135
144
{
136
    /* Enumerate the data planes. */
137
144
    index -= gs_image_enum_num_ptrs;
138
144
    if (index < eptr->num_planes)
139
48
        ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].source);
140
96
    index -= eptr->num_planes;
141
96
    if (index < eptr->num_planes)
142
48
        ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].row);
143
48
    return 0;
144
96
}
145
96
ENUM_PTR(0, gs_image_enum, dev);
146
240
ENUM_PTR(1, gs_image_enum, info);
147
240
ENUM_PTRS_END
148
48
static RELOC_PTRS_WITH(gs_image_enum_reloc_ptrs, gs_image_enum *eptr)
149
48
{
150
48
    int i;
151
152
48
    RELOC_PTR(gs_image_enum, dev);
153
48
    RELOC_PTR(gs_image_enum, info);
154
96
    for (i = 0; i < eptr->num_planes; i++)
155
48
        RELOC_CONST_STRING_PTR(gs_image_enum, planes[i].source);
156
96
    for (i = 0; i < eptr->num_planes; i++)
157
48
        RELOC_STRING_PTR(gs_image_enum, planes[i].row);
158
48
}
159
48
RELOC_PTRS_END
160
161
static int
162
is_image_visible(const gs_image_common_t * pic, gs_gstate * pgs, gx_clip_path *pcpath)
163
122k
{
164
122k
    gs_rect image_rect = {{0, 0}, {0, 0}};
165
122k
    gs_rect device_rect;
166
122k
    gs_int_rect device_int_rect;
167
122k
    gs_matrix mat;
168
122k
    int code;
169
170
122k
    image_rect.q.x = pic->Width;
171
122k
    image_rect.q.y = pic->Height;
172
122k
    if (pic->ImageMatrix.xx == ctm_only(pgs).xx &&
173
122k
        pic->ImageMatrix.xy == ctm_only(pgs).xy &&
174
122k
        pic->ImageMatrix.yx == ctm_only(pgs).yx &&
175
122k
        pic->ImageMatrix.yy == ctm_only(pgs).yy) {
176
        /* Handle common special case separately to accept singular matrix */
177
5.31k
        mat.xx = mat.yy = 1.;
178
5.31k
        mat.yx = mat.xy = 0.;
179
5.31k
        mat.tx = ctm_only(pgs).tx - pic->ImageMatrix.tx;
180
5.31k
        mat.ty = ctm_only(pgs).ty - pic->ImageMatrix.ty;
181
116k
    } else {
182
116k
        code = gs_matrix_invert(&pic->ImageMatrix, &mat);
183
116k
        if (code < 0)
184
0
            return code;
185
116k
        code = gs_matrix_multiply(&mat, &ctm_only(pgs), &mat);
186
116k
        if (code < 0)
187
0
            return code;
188
116k
    }
189
122k
    code = gs_bbox_transform(&image_rect, &mat, &device_rect);
190
122k
    if (code < 0)
191
0
        return code;
192
122k
    device_int_rect.p.x = (int)floor(device_rect.p.x);
193
122k
    device_int_rect.p.y = (int)floor(device_rect.p.y);
194
122k
    device_int_rect.q.x = (int)ceil(device_rect.q.x);
195
122k
    device_int_rect.q.y = (int)ceil(device_rect.q.y);
196
122k
    if (!gx_cpath_rect_visible(pcpath, &device_int_rect))
197
100k
        return 0;
198
21.8k
    return 1;
199
122k
}
200
201
/* Create an image enumerator given image parameters and a graphics state. */
202
int
203
gs_image_begin_typed(const gs_image_common_t * pic, gs_gstate * pgs,
204
                     bool uses_color, bool image_is_text, gx_image_enum_common_t ** ppie)
205
122k
{
206
122k
    gx_device *dev = gs_currentdevice(pgs);
207
122k
    gx_clip_path *pcpath;
208
122k
    int code = gx_effective_clip_path(pgs, &pcpath);
209
122k
    gx_device *dev2 = dev;
210
122k
    gx_device_color dc_temp, *pdevc = gs_currentdevicecolor_inline(pgs);
211
212
122k
    if (code < 0)
213
0
        return code;
214
    /* Processing an image object operation, but this may be for a text object */
215
122k
    ensure_tag_is_set(pgs, pgs->device, image_is_text ? GS_TEXT_TAG : GS_IMAGE_TAG);  /* NB: may unset_dev_color */
216
217
122k
    if (uses_color) {
218
114k
        code = gx_set_dev_color(pgs);
219
114k
        if (code != 0)
220
1
            return code;
221
114k
        code = gs_gstate_color_load(pgs);
222
114k
        if (code < 0)
223
0
            return code;
224
114k
    }
225
226
122k
    if (pgs->overprint || (!pgs->overprint && dev_proc(pgs->device, dev_spec_op)(pgs->device,
227
122k
        gxdso_overprint_active, NULL, 0))) {
228
11
        gs_overprint_params_t op_params = { 0 };
229
230
11
        if_debug0m(gs_debug_flag_overprint, pgs->memory,
231
11
            "[overprint] Image Overprint\n");
232
11
        code = gs_do_set_overprint(pgs);
233
11
        if (code < 0)
234
0
            return code;
235
236
11
        op_params.op_state = OP_STATE_FILL;
237
11
        gs_gstate_update_overprint(pgs, &op_params);
238
239
11
        dev = gs_currentdevice(pgs);
240
11
        dev2 = dev;
241
11
    }
242
243
    /* Imagemask with shading color needs a special optimization
244
       with converting the image into a clipping.
245
       Check for such case after gs_gstate_color_load is done,
246
       because it can cause interpreter callout.
247
     */
248
122k
    if (pic->type->begin_typed_image == &gx_begin_image1) {
249
122k
        gs_image_t *image = (gs_image_t *)pic;
250
251
122k
        if(image->ImageMask) {
252
114k
            bool transpose = false;
253
114k
            gs_matrix_double mat;
254
255
114k
            if((code = gx_image_compute_mat(pgs, NULL, &(image->ImageMatrix), &mat)) < 0)
256
3
                return code;
257
114k
            if ((any_abs(mat.xy) > any_abs(mat.xx)) && (any_abs(mat.yx) > any_abs(mat.yy)))
258
6.51k
                transpose = true;   /* pure landscape */
259
114k
            code = gx_image_fill_masked_start(dev, gs_currentdevicecolor_inline(pgs), transpose,
260
114k
                                              pcpath, pgs->memory, pgs->log_op, &dev2);
261
114k
            if (code < 0)
262
0
                return code;
263
114k
        }
264
122k
        if (dev->interpolate_control < 0) {   /* Force interpolation before begin_typed_image */
265
0
            ((gs_data_image_t *)pic)->Interpolate = true;
266
0
        }
267
122k
        else if (dev->interpolate_control == 0) {
268
122k
            ((gs_data_image_t *)pic)->Interpolate = false; /* Suppress interpolation */
269
122k
        }
270
122k
        if (dev2 != dev) {
271
11
            set_nonclient_dev_color(&dc_temp, 1);
272
11
            pdevc = &dc_temp;
273
11
        }
274
122k
    }
275
122k
    code = gx_device_begin_typed_image(dev2, (const gs_gstate *)pgs,
276
122k
                NULL, pic, NULL, pdevc, pcpath, pgs->memory, ppie);
277
122k
    if (code < 0)
278
7
        return code;
279
122k
    code = is_image_visible(pic, pgs, pcpath);
280
122k
    if (code < 0)
281
0
        return code;
282
122k
    if (!code)
283
100k
        (*ppie)->skipping = true;
284
122k
    return 0;
285
122k
}
286
287
/* Allocate an image enumerator. */
288
static void
289
image_enum_init(gs_image_enum * penum)
290
243k
{
291
    /* Clean pointers for GC. */
292
243k
    penum->info = 0;
293
243k
    penum->dev = 0;
294
243k
    penum->plane_index = 0;
295
243k
    penum->num_planes = 0;
296
243k
}
297
gs_image_enum *
298
gs_image_enum_alloc(gs_memory_t * mem, client_name_t cname)
299
122k
{
300
122k
    gs_image_enum *penum =
301
122k
        gs_alloc_struct(mem, gs_image_enum, &st_gs_image_enum, cname);
302
303
122k
    if (penum != 0) {
304
122k
        penum->memory = mem;
305
122k
        image_enum_init(penum);
306
122k
    }
307
122k
    return penum;
308
122k
}
309
310
/* Start processing an ImageType 1 image. */
311
int
312
gs_image_init(gs_image_enum * penum, const gs_image_t * pim, bool multi,
313
              bool image_is_text, gs_gstate * pgs)
314
5.31k
{
315
5.31k
    gs_image_t image;
316
5.31k
    gx_image_enum_common_t *pie;
317
5.31k
    int code;
318
319
5.31k
    image = *pim;
320
5.31k
    if (image.ImageMask) {
321
5.31k
        image.ColorSpace = NULL;
322
5.31k
        if (pgs->in_cachedevice <= 1)
323
5.31k
            image.adjust = false;
324
5.31k
    } else {
325
0
        if (pgs->in_cachedevice)
326
0
            return_error(gs_error_undefined);
327
0
        if (image.ColorSpace == NULL) {
328
            /*
329
             * Use of a non-current color space is potentially
330
             * incorrect, but it appears this case doesn't arise.
331
             */
332
0
            image.ColorSpace = gs_cspace_new_DeviceGray(pgs->memory);
333
0
            if (image.ColorSpace == NULL)
334
0
                return_error(gs_error_VMerror);
335
0
        }
336
0
    }
337
5.31k
    code = gs_image_begin_typed((const gs_image_common_t *)&image, pgs,
338
5.31k
                                image.ImageMask | image.CombineWithColor,
339
5.31k
                                image_is_text, &pie);
340
5.31k
    if (code < 0)
341
0
        return code;
342
5.31k
    return gs_image_enum_init(penum, pie, (const gs_data_image_t *)&image,
343
5.31k
                              pgs);
344
5.31k
}
345
346
/*
347
 * Return the number of bytes of data per row for a given plane.
348
 */
349
inline uint
350
gs_image_bytes_per_plane_row(const gs_image_enum * penum, int plane)
351
131k
{
352
131k
    const gx_image_enum_common_t *pie = penum->info;
353
354
131k
    return (pie->plane_widths[plane] * pie->plane_depths[plane] + 7) >> 3;
355
131k
}
356
357
/* Cache information when initializing, or after transferring plane data. */
358
static void
359
cache_planes(gs_image_enum *penum)
360
4.24M
{
361
4.24M
    int i;
362
363
4.24M
    if (penum->wanted_varies) {
364
127k
        penum->wanted_varies =
365
127k
            !gx_image_planes_wanted(penum->info, penum->wanted);
366
259k
        for (i = 0; i < penum->num_planes; ++i)
367
132k
            if (penum->wanted[i])
368
131k
                penum->image_planes[i].raster =
369
131k
                    gs_image_bytes_per_plane_row(penum, i);
370
782
            else
371
782
                penum->image_planes[i].data = 0;
372
127k
    }
373
4.24M
}
374
/* Advance to the next wanted plane. */
375
static void
376
next_plane(gs_image_enum *penum)
377
825k
{
378
825k
    int px = penum->plane_index;
379
380
825k
    do {
381
825k
        if (++px == penum->num_planes)
382
703k
            px = 0;
383
825k
    } while (!penum->wanted[px]);
384
825k
    penum->plane_index = px;
385
825k
}
386
/*
387
 * Initialize plane_index and (if appropriate) wanted and
388
 * wanted_varies at the beginning of a group of planes.
389
 */
390
static void
391
begin_planes(gs_image_enum *penum)
392
121k
{
393
121k
    cache_planes(penum);
394
121k
    penum->plane_index = -1;
395
121k
    next_plane(penum);
396
121k
}
397
398
int
399
gs_image_common_init(gs_image_enum * penum, gx_image_enum_common_t * pie,
400
            const gs_data_image_t * pim, gx_device * dev)
401
122k
{
402
    /*
403
     * HACK : For a compatibility with gs_image_cleanup_and_free_enum,
404
     * penum->memory must be initialized in advance
405
     * with the memory heap that owns *penum.
406
     */
407
122k
    int i;
408
409
122k
    if (pim->Width == 0 || pim->Height == 0) {
410
464
        gx_device *cdev = pie->dev;
411
412
464
        gx_image_end(pie, false);
413
464
        if (dev_proc(cdev, dev_spec_op)(cdev,
414
464
                    gxdso_pattern_is_cpath_accum, NULL, 0))
415
0
            gx_device_retain((gx_device *)cdev, false);
416
464
        return 1;
417
464
    }
418
121k
    image_enum_init(penum);
419
121k
    penum->dev = dev;
420
121k
    penum->info = pie;
421
121k
    penum->num_planes = pie->num_planes;
422
    /*
423
     * Note that for ImageType 3 InterleaveType 2, penum->height (the
424
     * expected number of data rows) differs from pim->Height (the height
425
     * of the source image in scan lines).  This doesn't normally cause
426
     * any problems, because penum->height is not used to determine when
427
     * all the data has been processed: that is up to the plane_data
428
     * procedure for the specific image type.
429
     */
430
121k
    penum->height = pim->Height;
431
243k
    for (i = 0; i < pie->num_planes; ++i) {
432
121k
        penum->planes[i].pos = 0;
433
121k
        penum->planes[i].source.size = 0; /* for gs_image_next_planes */
434
121k
        penum->planes[i].source.data = 0; /* for GC */
435
121k
        penum->planes[i].row.data = 0; /* for GC */
436
121k
        penum->planes[i].row.size = 0; /* ditto */
437
121k
        penum->image_planes[i].data_x = 0; /* just init once, never changes */
438
121k
    }
439
    /* Initialize the dynamic part of the state. */
440
121k
    penum->y = 0;
441
121k
    penum->error = false;
442
121k
    penum->wanted_varies = true;
443
121k
    begin_planes(penum);
444
121k
    return 0;
445
122k
}
446
447
/* Initialize an enumerator for a general image.
448
   penum->memory must be initialized in advance.
449
*/
450
int
451
gs_image_enum_init(gs_image_enum * penum, gx_image_enum_common_t * pie,
452
                   const gs_data_image_t * pim, gs_gstate *pgs)
453
122k
{
454
122k
    pgs->device->sgr.stroke_stored = false;
455
122k
    return gs_image_common_init(penum, pie, pim,
456
122k
                                (pgs->in_charpath ? NULL :
457
122k
                                 gs_currentdevice_inline(pgs)));
458
122k
}
459
460
/* Return the set of planes wanted. */
461
const byte *
462
gs_image_planes_wanted(gs_image_enum *penum)
463
4.80M
{
464
4.80M
    int i;
465
466
    /*
467
     * A plane is wanted at this interface if it is wanted by the
468
     * underlying machinery and has no buffered or retained data.
469
     */
470
9.61M
    for (i = 0; i < penum->num_planes; ++i)
471
4.80M
        penum->client_wanted[i] =
472
4.80M
            (penum->wanted[i] &&
473
4.80M
             penum->planes[i].pos + penum->planes[i].source.size <
474
4.80M
               penum->image_planes[i].raster);
475
4.80M
    return penum->client_wanted;
476
4.80M
}
477
478
/*
479
 * Return the enumerator memory used for allocating the row buffers.
480
 * Because some PostScript files use save/restore within an image data
481
 * reading procedure, this must be a stable allocator.
482
 */
483
static gs_memory_t *
484
gs_image_row_memory(const gs_image_enum *penum)
485
5.02M
{
486
5.02M
    return gs_memory_stable(penum->memory);
487
5.02M
}
488
489
/* Free the row buffers when cleaning up. */
490
static void
491
free_row_buffers(gs_image_enum *penum, int num_planes, client_name_t cname)
492
122k
{
493
122k
    int i;
494
495
244k
    for (i = num_planes - 1; i >= 0; --i) {
496
121k
        if_debug3m('b', penum->memory, "[b]free plane %d row ("PRI_INTPTR",%u)\n",
497
121k
                   i, (intptr_t)penum->planes[i].row.data,
498
121k
                   penum->planes[i].row.size);
499
121k
        gs_free_string(gs_image_row_memory(penum), penum->planes[i].row.data,
500
121k
                       penum->planes[i].row.size, cname);
501
121k
        penum->planes[i].row.data = 0;
502
121k
        penum->planes[i].row.size = 0;
503
121k
    }
504
122k
}
505
506
/* Process the next piece of an image. */
507
int
508
gs_image_next(gs_image_enum * penum, const byte * dbytes, uint dsize,
509
              uint * pused)
510
703k
{
511
703k
    int px = penum->plane_index;
512
703k
    int num_planes = penum->num_planes;
513
703k
    int i, code;
514
703k
    uint used[GS_IMAGE_MAX_COMPONENTS];
515
703k
    gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS];
516
517
703k
    if (penum->planes[px].source.size != 0)
518
0
        return_error(gs_error_rangecheck);
519
1.40M
    for (i = 0; i < num_planes; i++)
520
703k
        plane_data[i].size = 0;
521
703k
    plane_data[px].data = dbytes;
522
703k
    plane_data[px].size = dsize;
523
703k
    penum->error = false;
524
703k
    code = gs_image_next_planes(penum, plane_data, used, false);
525
703k
    *pused = used[px];
526
703k
    if (code >= 0)
527
703k
        next_plane(penum);
528
703k
    return code;
529
703k
}
530
531
int
532
gs_image_next_planes(gs_image_enum * penum,
533
                     gs_const_string *plane_data /*[num_planes]*/,
534
                     uint *used /*[num_planes]*/, bool txfer_control)
535
3.53M
{
536
3.53M
    const int num_planes = penum->num_planes;
537
3.53M
    int i;
538
3.53M
    int code = 0;
539
540
#ifdef DEBUG
541
    if (gs_debug_c('b')) {
542
        int pi;
543
544
        for (pi = 0; pi < num_planes; ++pi)
545
            dmprintf6(penum->memory, "[b]plane %d source="PRI_INTPTR",%u pos=%u data="PRI_INTPTR",%u\n",
546
                     pi, (intptr_t)penum->planes[pi].source.data,
547
                     penum->planes[pi].source.size, penum->planes[pi].pos,
548
                     (intptr_t)plane_data[pi].data, plane_data[pi].size);
549
    }
550
#endif
551
7.06M
    for (i = 0; i < num_planes; ++i) {
552
3.53M
        used[i] = 0;
553
3.53M
        if (penum->wanted[i] && plane_data[i].size != 0) {
554
3.53M
            penum->planes[i].source.size = plane_data[i].size;
555
3.53M
            penum->planes[i].source.data = plane_data[i].data;
556
            /* The gs_string 'orig' in penum->planes is set here if the 'txfer_control' flag is set.
557
             * In this case we now control the lifetime of the string. We need to know the actual
558
             * address of the string, and that gets modified in the peunum->planes->source and size
559
             * members, so we use 'orig' as both a marker for the control and the originalsize and location.
560
             */
561
3.53M
            if (txfer_control) {
562
2.45M
                penum->planes[i].orig.data = plane_data[i].data;
563
2.45M
                penum->planes[i].orig.size = plane_data[i].size;
564
2.45M
            } else {
565
1.08M
                penum->planes[i].orig.data = NULL;
566
1.08M
                penum->planes[i].orig.size = 0;
567
1.08M
            }
568
3.53M
        }
569
3.53M
    }
570
7.53M
    for (;;) {
571
        /* If wanted can vary, only transfer 1 row at a time. */
572
7.53M
        int h = (penum->wanted_varies ? 1 : max_int);
573
574
        /* Move partial rows from source[] to row[]. */
575
15.0M
        for (i = 0; i < num_planes; ++i) {
576
7.54M
            int pos, size;
577
7.54M
            uint raster;
578
579
7.54M
            if (!penum->wanted[i])
580
787
                continue;  /* skip unwanted planes */
581
7.53M
            pos = penum->planes[i].pos;
582
7.53M
            size = penum->planes[i].source.size;
583
7.53M
            raster = penum->image_planes[i].raster;
584
7.53M
            if (size > 0) {
585
5.97M
                if (pos < raster && (pos != 0 || size < raster)) {
586
                    /* Buffer a partial row. */
587
3.17M
                    int copy = min(size, raster - pos);
588
3.17M
                    uint old_size = penum->planes[i].row.size;
589
3.17M
                    gs_memory_t *mem = gs_image_row_memory(penum);
590
591
                    /* Make sure the row buffer is fully allocated. */
592
3.17M
                    if (raster > old_size) {
593
82.4k
                        byte *old_data = penum->planes[i].row.data;
594
82.4k
                        byte *row =
595
82.4k
                            (old_data == 0 ?
596
82.4k
                             gs_alloc_string(mem, raster,
597
82.4k
                                             "gs_image_next(row)") :
598
82.4k
                             gs_resize_string(mem, old_data, old_size, raster,
599
82.4k
                                              "gs_image_next(row)"));
600
601
82.4k
                        if_debug5m('b', mem, "[b]plane %d row ("PRI_INTPTR",%u) => ("PRI_INTPTR",%u)\n",
602
82.4k
                                   i, (intptr_t)old_data, old_size,
603
82.4k
                                   (intptr_t)row, raster);
604
82.4k
                        if (row == 0) {
605
0
                            code = gs_note_error(gs_error_VMerror);
606
0
                            free_row_buffers(penum, i, "gs_image_next(row)");
607
0
                            break;
608
0
                        }
609
82.4k
                        penum->planes[i].row.data = row;
610
82.4k
                        penum->planes[i].row.size = raster;
611
82.4k
                    }
612
3.17M
                    memcpy(penum->planes[i].row.data + pos,
613
3.17M
                           penum->planes[i].source.data, copy);
614
3.17M
                    penum->planes[i].source.data += copy;
615
3.17M
                    penum->planes[i].source.size = size -= copy;
616
                    /* The gs_string 'orig' is only set if the 'txfer_control' flag was set when
617
                     * the 'source' string data was initally passed in. In this case we now control the lifetime
618
                     * of the string. So when we empty the source string, free it. We need to know the actual
619
                     * address of the string, and that gets modified in the peunum->planes->source and size
620
                     * members, so we use 'orig' as both a marker for the control and the originalsize and location.
621
                     */
622
3.17M
                    if (penum->planes[i].source.size == 0 && penum->planes[i].orig.size != 0) {
623
1.57M
                        gs_free_string(mem, (byte *)penum->planes[i].orig.data, penum->planes[i].orig.size, "gs_image_next_planes");
624
1.57M
                        penum->planes[i].orig.size = 0;
625
1.57M
                        penum->planes[i].orig.data = NULL;
626
1.57M
                    }
627
3.17M
                    penum->planes[i].pos = pos += copy;
628
3.17M
                    used[i] += copy;
629
3.17M
                }
630
5.97M
            }
631
7.53M
            if (h == 0)
632
31
                continue;  /* can't transfer any data this cycle */
633
7.53M
            if (pos == raster) {
634
                /*
635
                 * This plane will be transferred from the row buffer,
636
                 * so we can only transfer one row.
637
                 */
638
1.32M
                h = min(h, 1);
639
1.32M
                penum->image_planes[i].data = penum->planes[i].row.data;
640
6.21M
            } else if (pos == 0 && size >= raster) {
641
                /* We can transfer 1 or more planes from the source. */
642
2.80M
                if (raster) {
643
2.80M
                    h = min(h, size / raster);
644
2.80M
                    penum->image_planes[i].data = penum->planes[i].source.data;
645
2.80M
                }
646
0
                else
647
0
                    h = 0;
648
2.80M
            } else
649
3.41M
                h = 0;   /* not enough data in this plane */
650
7.53M
        }
651
7.53M
        if (h == 0 || code != 0)
652
3.41M
            break;
653
        /* Pass rows to the device. */
654
4.12M
        if (penum->dev == 0) {
655
            /*
656
             * ****** NOTE: THE FOLLOWING IS NOT CORRECT FOR ImageType 3
657
             * ****** InterleaveType 2, SINCE MASK HEIGHT AND IMAGE HEIGHT
658
             * ****** MAY DIFFER (BY AN INTEGER FACTOR).  ALSO, plane_depths[0]
659
             * ****** AND plane_widths[0] ARE NOT UPDATED.
660
         */
661
0
            if (penum->y + h < penum->height)
662
0
                code = 0;
663
0
            else
664
0
                h = penum->height - penum->y, code = 1;
665
4.12M
        } else {
666
4.12M
            code = gx_image_plane_data_rows(penum->info, penum->image_planes,
667
4.12M
                                            h, &h);
668
4.12M
            if_debug2m('b', penum->memory, "[b]used %d, code=%d\n", h, code);
669
4.12M
            penum->error = code < 0;
670
4.12M
        }
671
4.12M
        penum->y += h;
672
        /* Update positions and sizes. */
673
4.12M
        if (h == 0)
674
0
            break;
675
8.24M
        for (i = 0; i < num_planes; ++i) {
676
4.12M
            int count;
677
678
4.12M
            if (!penum->wanted[i])
679
779
                continue;
680
4.12M
            count = penum->image_planes[i].raster * h;
681
4.12M
            if (penum->planes[i].pos) {
682
                /* We transferred the row from the row buffer. */
683
1.32M
                penum->planes[i].pos = 0;
684
2.79M
            } else {
685
                /* We transferred the row(s) from the source. */
686
2.79M
                penum->planes[i].source.data += count;
687
2.79M
                penum->planes[i].source.size -= count;
688
                /* The gs_string 'orig' is only set if the 'txfer_control' flag was set when
689
                 * the 'source' string data was initally passed in. In this case we now control the lifetime
690
                 * of the string. So when we empty the source string, free it. We need to know the actual
691
                 * address of the string, and that gets modified in the peunum->planes->source and size
692
                 * members, so we use 'orig' as both a marker for the control and the originalsize and location.
693
                 */
694
2.79M
                if (penum->planes[i].source.size == 0 && penum->planes[i].orig.size != 0) {
695
803k
                    gs_free_string(gs_image_row_memory(penum), (byte *)penum->planes[i].orig.data, penum->planes[i].orig.size, "gs_image_next_planes");
696
803k
                    penum->planes[i].orig.size = 0;
697
803k
                    penum->planes[i].orig.data = NULL;
698
803k
                }
699
2.79M
                used[i] += count;
700
2.79M
            }
701
4.12M
        }
702
4.12M
        cache_planes(penum);
703
4.12M
        if (code != 0)
704
119k
            break;
705
4.12M
    }
706
    /* Return the retained data pointers. */
707
7.06M
    for (i = 0; i < num_planes; ++i)
708
3.53M
        plane_data[i] = penum->planes[i].source;
709
3.53M
    return code;
710
3.53M
}
711
712
/* Clean up after processing an image. */
713
/* Public for ghostpcl. */
714
int
715
gs_image_cleanup(gs_image_enum * penum, gs_gstate *pgs)
716
122k
{
717
122k
    int code = 0, code1;
718
719
122k
    free_row_buffers(penum, penum->num_planes, "gs_image_cleanup(row)");
720
122k
    if (penum->info != 0) {
721
121k
        if (dev_proc(penum->info->dev, dev_spec_op)(penum->info->dev,
722
121k
                    gxdso_pattern_is_cpath_accum, NULL, 0)) {
723
            /* Performing a conversion of imagemask into a clipping path. */
724
11
            gx_device *cdev = penum->info->dev;
725
726
11
            code = gx_image_end(penum->info, !penum->error); /* Releases penum->info . */
727
11
            code1 = gx_image_fill_masked_end(cdev, penum->dev, gs_currentdevicecolor_inline(pgs));
728
11
            if (code == 0)
729
11
                code = code1;
730
11
        } else
731
121k
            code = gx_image_end(penum->info, !penum->error);
732
121k
    }
733
    /* Don't free the local enumerator -- the client does that. */
734
735
122k
    return code;
736
122k
}
737
738
/* Clean up after processing an image and free the enumerator. */
739
int
740
gs_image_cleanup_and_free_enum(gs_image_enum * penum, gs_gstate *pgs)
741
122k
{
742
122k
    int code;
743
744
122k
    if (penum == NULL)
745
0
            return 0;
746
122k
    code = gs_image_cleanup(penum, pgs);
747
748
122k
    gs_free_object(penum->memory, penum, "gs_image_cleanup_and_free_enum");
749
122k
    return code;
750
122k
}