Coverage Report

Created: 2025-08-28 07:06

/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
453
#define gs_image_enum_num_ptrs 2
131
132
/* GC procedures */
133
static
134
755
ENUM_PTRS_WITH(gs_image_enum_enum_ptrs, gs_image_enum *eptr)
135
453
{
136
    /* Enumerate the data planes. */
137
453
    index -= gs_image_enum_num_ptrs;
138
453
    if (index < eptr->num_planes)
139
151
        ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].source);
140
302
    index -= eptr->num_planes;
141
302
    if (index < eptr->num_planes)
142
151
        ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].row);
143
151
    return 0;
144
302
}
145
302
ENUM_PTR(0, gs_image_enum, dev);
146
755
ENUM_PTR(1, gs_image_enum, info);
147
755
ENUM_PTRS_END
148
151
static RELOC_PTRS_WITH(gs_image_enum_reloc_ptrs, gs_image_enum *eptr)
149
151
{
150
151
    int i;
151
152
151
    RELOC_PTR(gs_image_enum, dev);
153
151
    RELOC_PTR(gs_image_enum, info);
154
302
    for (i = 0; i < eptr->num_planes; i++)
155
151
        RELOC_CONST_STRING_PTR(gs_image_enum, planes[i].source);
156
302
    for (i = 0; i < eptr->num_planes; i++)
157
151
        RELOC_STRING_PTR(gs_image_enum, planes[i].row);
158
151
}
159
151
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
1.13M
{
164
1.13M
    gs_rect image_rect = {{0, 0}, {0, 0}};
165
1.13M
    gs_rect device_rect;
166
1.13M
    gs_int_rect device_int_rect;
167
1.13M
    gs_matrix mat;
168
1.13M
    int code;
169
170
1.13M
    image_rect.q.x = pic->Width;
171
1.13M
    image_rect.q.y = pic->Height;
172
1.13M
    if (pic->ImageMatrix.xx == ctm_only(pgs).xx &&
173
1.13M
        pic->ImageMatrix.xy == ctm_only(pgs).xy &&
174
1.13M
        pic->ImageMatrix.yx == ctm_only(pgs).yx &&
175
1.13M
        pic->ImageMatrix.yy == ctm_only(pgs).yy) {
176
        /* Handle common special case separately to accept singular matrix */
177
153k
        mat.xx = mat.yy = 1.;
178
153k
        mat.yx = mat.xy = 0.;
179
153k
        mat.tx = ctm_only(pgs).tx - pic->ImageMatrix.tx;
180
153k
        mat.ty = ctm_only(pgs).ty - pic->ImageMatrix.ty;
181
984k
    } else {
182
984k
        code = gs_matrix_invert(&pic->ImageMatrix, &mat);
183
984k
        if (code < 0)
184
1
            return code;
185
984k
        code = gs_matrix_multiply(&mat, &ctm_only(pgs), &mat);
186
984k
        if (code < 0)
187
0
            return code;
188
984k
    }
189
1.13M
    code = gs_bbox_transform(&image_rect, &mat, &device_rect);
190
1.13M
    if (code < 0)
191
0
        return code;
192
1.13M
    device_int_rect.p.x = (int)floor(device_rect.p.x);
193
1.13M
    device_int_rect.p.y = (int)floor(device_rect.p.y);
194
1.13M
    device_int_rect.q.x = (int)ceil(device_rect.q.x);
195
1.13M
    device_int_rect.q.y = (int)ceil(device_rect.q.y);
196
1.13M
    if (!gx_cpath_rect_visible(pcpath, &device_int_rect))
197
711k
        return 0;
198
426k
    return 1;
199
1.13M
}
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
1.13M
{
206
1.13M
    gx_device *dev = gs_currentdevice(pgs);
207
1.13M
    gx_clip_path *pcpath;
208
1.13M
    int code = gx_effective_clip_path(pgs, &pcpath);
209
1.13M
    gx_device *dev2 = dev;
210
1.13M
    gx_device_color dc_temp, *pdevc = gs_currentdevicecolor_inline(pgs);
211
212
1.13M
    if (code < 0)
213
0
        return code;
214
    /* Processing an image object operation, but this may be for a text object */
215
1.13M
    ensure_tag_is_set(pgs, pgs->device, image_is_text ? GS_TEXT_TAG : GS_IMAGE_TAG);  /* NB: may unset_dev_color */
216
217
1.13M
    if (uses_color) {
218
991k
        code = gx_set_dev_color(pgs);
219
991k
        if (code != 0)
220
15
            return code;
221
991k
        code = gs_gstate_color_load(pgs);
222
991k
        if (code < 0)
223
0
            return code;
224
991k
    }
225
226
1.13M
    if (pgs->overprint || (!pgs->overprint && dev_proc(pgs->device, dev_spec_op)(pgs->device,
227
1.13M
        gxdso_overprint_active, NULL, 0))) {
228
390
        gs_overprint_params_t op_params = { 0 };
229
230
390
        if_debug0m(gs_debug_flag_overprint, pgs->memory,
231
390
            "[overprint] Image Overprint\n");
232
390
        code = gs_do_set_overprint(pgs);
233
390
        if (code < 0)
234
0
            return code;
235
236
390
        op_params.op_state = OP_STATE_FILL;
237
390
        gs_gstate_update_overprint(pgs, &op_params);
238
239
390
        dev = gs_currentdevice(pgs);
240
390
        dev2 = dev;
241
390
    }
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
1.13M
    if (pic->type->begin_typed_image == &gx_begin_image1) {
249
1.13M
        gs_image_t *image = (gs_image_t *)pic;
250
251
1.13M
        if(image->ImageMask) {
252
991k
            bool transpose = false;
253
991k
            gs_matrix_double mat;
254
255
991k
            if((code = gx_image_compute_mat(pgs, NULL, &(image->ImageMatrix), &mat)) < 0)
256
27
                return code;
257
991k
            if ((any_abs(mat.xy) > any_abs(mat.xx)) && (any_abs(mat.yx) > any_abs(mat.yy)))
258
157k
                transpose = true;   /* pure landscape */
259
991k
            code = gx_image_fill_masked_start(dev, gs_currentdevicecolor_inline(pgs), transpose,
260
991k
                                              pcpath, pgs->memory, pgs->log_op, &dev2);
261
991k
            if (code < 0)
262
0
                return code;
263
991k
        }
264
1.13M
        if (dev->interpolate_control < 0) {   /* Force interpolation before begin_typed_image */
265
3.98k
            ((gs_data_image_t *)pic)->Interpolate = true;
266
3.98k
        }
267
1.12M
        else if (dev->interpolate_control == 0) {
268
1.12M
            ((gs_data_image_t *)pic)->Interpolate = false; /* Suppress interpolation */
269
1.12M
        }
270
1.13M
        if (dev2 != dev) {
271
1.17k
            set_nonclient_dev_color(&dc_temp, 1);
272
1.17k
            pdevc = &dc_temp;
273
1.17k
        }
274
1.13M
    }
275
1.13M
    code = gx_device_begin_typed_image(dev2, (const gs_gstate *)pgs,
276
1.13M
                NULL, pic, NULL, pdevc, pcpath, pgs->memory, ppie);
277
1.13M
    if (code < 0)
278
214
        return code;
279
1.13M
    code = is_image_visible(pic, pgs, pcpath);
280
1.13M
    if (code < 0)
281
1
        return code;
282
1.13M
    if (!code)
283
711k
        (*ppie)->skipping = true;
284
1.13M
    return 0;
285
1.13M
}
286
287
/* Allocate an image enumerator. */
288
static void
289
image_enum_init(gs_image_enum * penum)
290
2.26M
{
291
    /* Clean pointers for GC. */
292
2.26M
    penum->info = 0;
293
2.26M
    penum->dev = 0;
294
2.26M
    penum->plane_index = 0;
295
2.26M
    penum->num_planes = 0;
296
2.26M
}
297
gs_image_enum *
298
gs_image_enum_alloc(gs_memory_t * mem, client_name_t cname)
299
1.13M
{
300
1.13M
    gs_image_enum *penum =
301
1.13M
        gs_alloc_struct(mem, gs_image_enum, &st_gs_image_enum, cname);
302
303
1.13M
    if (penum != 0) {
304
1.13M
        penum->memory = mem;
305
1.13M
        image_enum_init(penum);
306
1.13M
    }
307
1.13M
    return penum;
308
1.13M
}
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
152k
{
315
152k
    gs_image_t image;
316
152k
    gx_image_enum_common_t *pie;
317
152k
    int code;
318
319
152k
    image = *pim;
320
152k
    if (image.ImageMask) {
321
152k
        image.ColorSpace = NULL;
322
152k
        if (pgs->in_cachedevice <= 1)
323
152k
            image.adjust = false;
324
152k
    } 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
152k
    code = gs_image_begin_typed((const gs_image_common_t *)&image, pgs,
338
152k
                                image.ImageMask | image.CombineWithColor,
339
152k
                                image_is_text, &pie);
340
152k
    if (code < 0)
341
0
        return code;
342
152k
    return gs_image_enum_init(penum, pie, (const gs_data_image_t *)&image,
343
152k
                              pgs);
344
152k
}
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
2.17M
{
352
2.17M
    const gx_image_enum_common_t *pie = penum->info;
353
354
2.17M
    return (pie->plane_widths[plane] * pie->plane_depths[plane] + 7) >> 3;
355
2.17M
}
356
357
/* Cache information when initializing, or after transferring plane data. */
358
static void
359
cache_planes(gs_image_enum *penum)
360
57.2M
{
361
57.2M
    int i;
362
363
57.2M
    if (penum->wanted_varies) {
364
1.65M
        penum->wanted_varies =
365
1.65M
            !gx_image_planes_wanted(penum->info, penum->wanted);
366
3.83M
        for (i = 0; i < penum->num_planes; ++i)
367
2.18M
            if (penum->wanted[i])
368
2.17M
                penum->image_planes[i].raster =
369
2.17M
                    gs_image_bytes_per_plane_row(penum, i);
370
10.0k
            else
371
10.0k
                penum->image_planes[i].data = 0;
372
1.65M
    }
373
57.2M
}
374
/* Advance to the next wanted plane. */
375
static void
376
next_plane(gs_image_enum *penum)
377
22.5M
{
378
22.5M
    int px = penum->plane_index;
379
380
22.5M
    do {
381
22.5M
        if (++px == penum->num_planes)
382
21.4M
            px = 0;
383
22.5M
    } while (!penum->wanted[px]);
384
22.5M
    penum->plane_index = px;
385
22.5M
}
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
1.13M
{
393
1.13M
    cache_planes(penum);
394
1.13M
    penum->plane_index = -1;
395
1.13M
    next_plane(penum);
396
1.13M
}
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
1.13M
{
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
1.13M
    int i;
408
409
1.13M
    if (pim->Width == 0 || pim->Height == 0) {
410
5.93k
        gx_device *cdev = pie->dev;
411
412
5.93k
        gx_image_end(pie, false);
413
5.93k
        if (dev_proc(cdev, dev_spec_op)(cdev,
414
5.93k
                    gxdso_pattern_is_cpath_accum, NULL, 0))
415
86
            gx_device_retain((gx_device *)cdev, false);
416
5.93k
        return 1;
417
5.93k
    }
418
1.13M
    image_enum_init(penum);
419
1.13M
    penum->dev = dev;
420
1.13M
    penum->info = pie;
421
1.13M
    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
1.13M
    penum->height = pim->Height;
431
2.26M
    for (i = 0; i < pie->num_planes; ++i) {
432
1.13M
        penum->planes[i].pos = 0;
433
1.13M
        penum->planes[i].source.size = 0; /* for gs_image_next_planes */
434
1.13M
        penum->planes[i].source.data = 0; /* for GC */
435
1.13M
        penum->planes[i].row.data = 0; /* for GC */
436
1.13M
        penum->planes[i].row.size = 0; /* ditto */
437
1.13M
        penum->image_planes[i].data_x = 0; /* just init once, never changes */
438
1.13M
    }
439
    /* Initialize the dynamic part of the state. */
440
1.13M
    penum->y = 0;
441
1.13M
    penum->error = false;
442
1.13M
    penum->wanted_varies = true;
443
1.13M
    begin_planes(penum);
444
1.13M
    return 0;
445
1.13M
}
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
1.13M
{
454
1.13M
    pgs->device->sgr.stroke_stored = false;
455
1.13M
    return gs_image_common_init(penum, pie, pim,
456
1.13M
                                (pgs->in_charpath ? NULL :
457
1.13M
                                 gs_currentdevice_inline(pgs)));
458
1.13M
}
459
460
/* Return the set of planes wanted. */
461
const byte *
462
gs_image_planes_wanted(gs_image_enum *penum)
463
36.9M
{
464
36.9M
    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
74.0M
    for (i = 0; i < penum->num_planes; ++i)
471
37.0M
        penum->client_wanted[i] =
472
37.0M
            (penum->wanted[i] &&
473
37.0M
             penum->planes[i].pos + penum->planes[i].source.size <
474
37.0M
               penum->image_planes[i].raster);
475
36.9M
    return penum->client_wanted;
476
36.9M
}
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
53.4M
{
486
53.4M
    return gs_memory_stable(penum->memory);
487
53.4M
}
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
1.13M
{
493
1.13M
    int i;
494
495
2.27M
    for (i = num_planes - 1; i >= 0; --i) {
496
1.13M
        if_debug3m('b', penum->memory, "[b]free plane %d row ("PRI_INTPTR",%u)\n",
497
1.13M
                   i, (intptr_t)penum->planes[i].row.data,
498
1.13M
                   penum->planes[i].row.size);
499
1.13M
        gs_free_string(gs_image_row_memory(penum), penum->planes[i].row.data,
500
1.13M
                       penum->planes[i].row.size, cname);
501
1.13M
        penum->planes[i].row.data = 0;
502
1.13M
        penum->planes[i].row.size = 0;
503
1.13M
    }
504
1.13M
}
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
21.4M
{
511
21.4M
    int px = penum->plane_index;
512
21.4M
    int num_planes = penum->num_planes;
513
21.4M
    int i, code;
514
21.4M
    uint used[GS_IMAGE_MAX_COMPONENTS];
515
21.4M
    gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS];
516
517
21.4M
    if (penum->planes[px].source.size != 0)
518
0
        return_error(gs_error_rangecheck);
519
42.9M
    for (i = 0; i < num_planes; i++)
520
21.4M
        plane_data[i].size = 0;
521
21.4M
    plane_data[px].data = dbytes;
522
21.4M
    plane_data[px].size = dsize;
523
21.4M
    penum->error = false;
524
21.4M
    code = gs_image_next_planes(penum, plane_data, used, false);
525
21.4M
    *pused = used[px];
526
21.4M
    if (code >= 0)
527
21.4M
        next_plane(penum);
528
21.4M
    return code;
529
21.4M
}
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
53.1M
{
536
53.1M
    const int num_planes = penum->num_planes;
537
53.1M
    int i;
538
53.1M
    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
106M
    for (i = 0; i < num_planes; ++i) {
552
53.3M
        used[i] = 0;
553
53.3M
        if (penum->wanted[i] && plane_data[i].size != 0) {
554
53.3M
            penum->planes[i].source.size = plane_data[i].size;
555
53.3M
            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
53.3M
            if (txfer_control) {
562
18.7M
                penum->planes[i].orig.data = plane_data[i].data;
563
18.7M
                penum->planes[i].orig.size = plane_data[i].size;
564
34.5M
            } else {
565
34.5M
                penum->planes[i].orig.data = NULL;
566
34.5M
                penum->planes[i].orig.size = 0;
567
34.5M
            }
568
53.3M
        }
569
53.3M
    }
570
108M
    for (;;) {
571
        /* If wanted can vary, only transfer 1 row at a time. */
572
108M
        int h = (penum->wanted_varies ? 1 : max_int);
573
574
        /* Move partial rows from source[] to row[]. */
575
217M
        for (i = 0; i < num_planes; ++i) {
576
108M
            int pos, size;
577
108M
            uint raster;
578
579
108M
            if (!penum->wanted[i])
580
10.0k
                continue;  /* skip unwanted planes */
581
108M
            pos = penum->planes[i].pos;
582
108M
            size = penum->planes[i].source.size;
583
108M
            raster = penum->image_planes[i].raster;
584
108M
            if (size > 0) {
585
79.6M
                if (pos < raster && (pos != 0 || size < raster)) {
586
                    /* Buffer a partial row. */
587
38.7M
                    int copy = min(size, raster - pos);
588
38.7M
                    uint old_size = penum->planes[i].row.size;
589
38.7M
                    gs_memory_t *mem = gs_image_row_memory(penum);
590
591
                    /* Make sure the row buffer is fully allocated. */
592
38.7M
                    if (raster > old_size) {
593
486k
                        byte *old_data = penum->planes[i].row.data;
594
486k
                        byte *row =
595
486k
                            (old_data == 0 ?
596
486k
                             gs_alloc_string(mem, raster,
597
486k
                                             "gs_image_next(row)") :
598
486k
                             gs_resize_string(mem, old_data, old_size, raster,
599
486k
                                              "gs_image_next(row)"));
600
601
486k
                        if_debug5m('b', mem, "[b]plane %d row ("PRI_INTPTR",%u) => ("PRI_INTPTR",%u)\n",
602
486k
                                   i, (intptr_t)old_data, old_size,
603
486k
                                   (intptr_t)row, raster);
604
486k
                        if (row == 0) {
605
1
                            code = gs_note_error(gs_error_VMerror);
606
1
                            free_row_buffers(penum, i, "gs_image_next(row)");
607
1
                            break;
608
1
                        }
609
486k
                        penum->planes[i].row.data = row;
610
486k
                        penum->planes[i].row.size = raster;
611
486k
                    }
612
38.7M
                    memcpy(penum->planes[i].row.data + pos,
613
38.7M
                           penum->planes[i].source.data, copy);
614
38.7M
                    penum->planes[i].source.data += copy;
615
38.7M
                    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
38.7M
                    if (penum->planes[i].source.size == 0 && penum->planes[i].orig.size != 0) {
623
12.1M
                        gs_free_string(mem, (byte *)penum->planes[i].orig.data, penum->planes[i].orig.size, "gs_image_next_planes");
624
12.1M
                        penum->planes[i].orig.size = 0;
625
12.1M
                        penum->planes[i].orig.data = NULL;
626
12.1M
                    }
627
38.7M
                    penum->planes[i].pos = pos += copy;
628
38.7M
                    used[i] += copy;
629
38.7M
                }
630
79.6M
            }
631
108M
            if (h == 0)
632
8.37k
                continue;  /* can't transfer any data this cycle */
633
108M
            if (pos == raster) {
634
                /*
635
                 * This plane will be transferred from the row buffer,
636
                 * so we can only transfer one row.
637
                 */
638
15.8M
                h = min(h, 1);
639
15.8M
                penum->image_planes[i].data = penum->planes[i].row.data;
640
93.0M
            } else if (pos == 0 && size >= raster) {
641
                /* We can transfer 1 or more planes from the source. */
642
40.9M
                if (raster) {
643
40.9M
                    h = min(h, size / raster);
644
40.9M
                    penum->image_planes[i].data = penum->planes[i].source.data;
645
40.9M
                }
646
0
                else
647
0
                    h = 0;
648
40.9M
            } else
649
52.0M
                h = 0;   /* not enough data in this plane */
650
108M
        }
651
108M
        if (h == 0 || code != 0)
652
52.0M
            break;
653
        /* Pass rows to the device. */
654
56.1M
        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
56.1M
        } else {
666
56.1M
            code = gx_image_plane_data_rows(penum->info, penum->image_planes,
667
56.1M
                                            h, &h);
668
56.1M
            if_debug2m('b', penum->memory, "[b]used %d, code=%d\n", h, code);
669
56.1M
            penum->error = code < 0;
670
56.1M
        }
671
56.1M
        penum->y += h;
672
        /* Update positions and sizes. */
673
56.1M
        if (h == 0)
674
1
            break;
675
112M
        for (i = 0; i < num_planes; ++i) {
676
56.6M
            int count;
677
678
56.6M
            if (!penum->wanted[i])
679
9.94k
                continue;
680
56.6M
            count = penum->image_planes[i].raster * h;
681
56.6M
            if (penum->planes[i].pos) {
682
                /* We transferred the row from the row buffer. */
683
15.8M
                penum->planes[i].pos = 0;
684
40.8M
            } else {
685
                /* We transferred the row(s) from the source. */
686
40.8M
                penum->planes[i].source.data += count;
687
40.8M
                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
40.8M
                if (penum->planes[i].source.size == 0 && penum->planes[i].orig.size != 0) {
695
6.21M
                    gs_free_string(gs_image_row_memory(penum), (byte *)penum->planes[i].orig.data, penum->planes[i].orig.size, "gs_image_next_planes");
696
6.21M
                    penum->planes[i].orig.size = 0;
697
6.21M
                    penum->planes[i].orig.data = NULL;
698
6.21M
                }
699
40.8M
                used[i] += count;
700
40.8M
            }
701
56.6M
        }
702
56.1M
        cache_planes(penum);
703
56.1M
        if (code != 0)
704
1.09M
            break;
705
56.1M
    }
706
    /* Return the retained data pointers. */
707
106M
    for (i = 0; i < num_planes; ++i)
708
53.3M
        plane_data[i] = penum->planes[i].source;
709
53.1M
    return code;
710
53.1M
}
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
1.13M
{
717
1.13M
    int code = 0, code1;
718
719
1.13M
    free_row_buffers(penum, penum->num_planes, "gs_image_cleanup(row)");
720
1.13M
    if (penum->info != 0) {
721
1.13M
        if (dev_proc(penum->info->dev, dev_spec_op)(penum->info->dev,
722
1.13M
                    gxdso_pattern_is_cpath_accum, NULL, 0)) {
723
            /* Performing a conversion of imagemask into a clipping path. */
724
1.09k
            gx_device *cdev = penum->info->dev;
725
726
1.09k
            code = gx_image_end(penum->info, !penum->error); /* Releases penum->info . */
727
1.09k
            code1 = gx_image_fill_masked_end(cdev, penum->dev, gs_currentdevicecolor_inline(pgs));
728
1.09k
            if (code == 0)
729
1.09k
                code = code1;
730
1.09k
        } else
731
1.13M
            code = gx_image_end(penum->info, !penum->error);
732
1.13M
    }
733
    /* Don't free the local enumerator -- the client does that. */
734
735
1.13M
    return code;
736
1.13M
}
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
1.13M
{
742
1.13M
    int code;
743
744
1.13M
    if (penum == NULL)
745
0
            return 0;
746
1.13M
    code = gs_image_cleanup(penum, pgs);
747
748
1.13M
    gs_free_object(penum->memory, penum, "gs_image_cleanup_and_free_enum");
749
1.13M
    return code;
750
1.13M
}