Coverage Report

Created: 2025-06-10 06:59

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