Coverage Report

Created: 2025-06-10 06:58

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