Coverage Report

Created: 2026-10-28 08:05

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