Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gximag3x.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
/* ImageType 3x image implementation */
18
/****** THE REAL WORK IS NYI ******/
19
#include "math_.h"    /* for ceil, floor */
20
#include "memory_.h"
21
#include "gx.h"
22
#include "gserrors.h"
23
#include "gsbitops.h"
24
#include "gscspace.h"
25
#include "gscpixel.h"
26
#include "gsstruct.h"
27
#include "gxdevice.h"
28
#include "gxdevmem.h"
29
#include "gximag3x.h"
30
#include "gxgstate.h"
31
#include "gdevbbox.h"
32
#include <limits.h> /* For INT_MAX etc */
33
34
extern_st(st_color_space);
35
36
/* Forward references */
37
static dev_proc_begin_typed_image(gx_begin_image3x);
38
static image_enum_proc_plane_data(gx_image3x_plane_data);
39
static image_enum_proc_end_image(gx_image3x_end_image);
40
static image_enum_proc_flush(gx_image3x_flush);
41
static image_enum_proc_planes_wanted(gx_image3x_planes_wanted);
42
43
/* GC descriptor */
44
private_st_gs_image3x();
45
46
/* Define the image type for ImageType 3x images. */
47
const gx_image_type_t gs_image_type_3x = {
48
    &st_gs_image3x, gx_begin_image3x,
49
    gx_image_no_sput, gx_image_no_sget, gx_image_default_release,
50
    IMAGE3X_IMAGETYPE
51
};
52
static const gx_image_enum_procs_t image3x_enum_procs = {
53
    gx_image3x_plane_data, gx_image3x_end_image,
54
    gx_image3x_flush, gx_image3x_planes_wanted
55
};
56
57
/* Initialize an ImageType 3x image. */
58
static void
59
gs_image3x_mask_init(gs_image3x_mask_t *pimm)
60
11.9k
{
61
11.9k
    pimm->InterleaveType = 0; /* not a valid type */
62
11.9k
    pimm->has_Matte = false;
63
11.9k
    gs_data_image_t_init(&pimm->MaskDict, 1);
64
11.9k
    pimm->MaskDict.BitsPerComponent = 0;  /* not supplied */
65
11.9k
}
66
void
67
gs_image3x_t_init(gs_image3x_t * pim, gs_color_space * color_space)
68
5.95k
{
69
5.95k
    gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space);
70
5.95k
    pim->type = &gs_image_type_3x;
71
5.95k
    gs_image3x_mask_init(&pim->Opacity);
72
5.95k
    gs_image3x_mask_init(&pim->Shape);
73
5.95k
}
74
75
/*
76
 * We implement ImageType 3 images by interposing a mask clipper in
77
 * front of an ordinary ImageType 1 image.  Note that we build up the
78
 * mask row-by-row as we are processing the image.
79
 *
80
 * We export a generalized form of the begin_image procedure for use by
81
 * the PDF and PostScript writers.
82
 */
83
84
typedef struct image3x_channel_state_s {
85
    gx_image_enum_common_t *info;
86
    gx_device *mdev;    /* gx_device_memory in default impl. */
87
                                /* (only for masks) */
88
    gs_image3_interleave_type_t InterleaveType;
89
    int width, height, full_height, depth;
90
    byte *data;     /* (if chunky) */
91
    /* Only the following change dynamically. */
92
    int y;
93
    int skip;     /* only for masks, # of rows to skip, */
94
                                /* see below */
95
} image3x_channel_state_t;
96
typedef struct gx_image3x_enum_s {
97
    gx_image_enum_common;
98
    gx_device *pcdev;   /* gx_device_mask_clip in default impl. */
99
    int num_components;   /* (not counting masks) */
100
    int bpc;      /* pixel BitsPerComponent */
101
6.33M
#define NUM_MASKS 2    /* opacity, shape */
102
    image3x_channel_state_t mask[NUM_MASKS], pixel;
103
} gx_image3x_enum_t;
104
105
extern_st(st_gx_image_enum_common);
106
gs_private_st_suffix_add9(st_image3x_enum, gx_image3x_enum_t,
107
  "gx_image3x_enum_t", image3x_enum_enum_ptrs, image3x_enum_reloc_ptrs,
108
  st_gx_image_enum_common, pcdev, mask[0].info, mask[0].mdev, mask[0].data,
109
  mask[1].info, mask[1].mdev, mask[1].data, pixel.info, pixel.data);
110
111
/*
112
 * Begin a generic ImageType 3x image, with client handling the creation of
113
 * the mask image and mask clip devices.
114
 */
115
typedef struct image3x_channel_values_s {
116
    gs_matrix matrix;
117
    gs_point corner;
118
    gs_int_rect rect;
119
    gs_image_t image;
120
} image3x_channel_values_t;
121
static int check_image3x_mask(const gs_image3x_t *pim,
122
                               const gs_image3x_mask_t *pimm,
123
                               const image3x_channel_values_t *ppcv,
124
                               image3x_channel_values_t *pmcv,
125
                               image3x_channel_state_t *pmcs,
126
                               gs_memory_t *mem);
127
int
128
gx_begin_image3x_generic(gx_device * dev,
129
                        const gs_gstate *pgs, const gs_matrix *pmat,
130
                        const gs_image_common_t *pic, const gs_int_rect *prect,
131
                        const gx_drawing_color *pdcolor,
132
                        const gx_clip_path *pcpath, gs_memory_t *mem,
133
                        image3x_make_mid_proc_t make_mid,
134
                        image3x_make_mcde_proc_t make_mcde,
135
                        gx_image_enum_common_t **pinfo, char *OC)
136
5.80k
{
137
5.80k
    const gs_image3x_t *pim = (const gs_image3x_t *)pic;
138
5.80k
    gx_image3x_enum_t *penum;
139
5.80k
    gx_device *pcdev = 0;
140
5.80k
    image3x_channel_values_t mask[2], pixel;
141
5.80k
    gs_matrix mat;
142
5.80k
    gx_device *midev[2];
143
5.80k
    gx_image_enum_common_t *minfo[2];
144
5.80k
    gs_int_point origin[2];
145
5.80k
    int code;
146
5.80k
    int i;
147
5.80k
    gs_color_space *pmcs = NULL;
148
149
    /* Validate the parameters. */
150
5.80k
    if (pim->Height <= 0)
151
2
        return_error(gs_error_rangecheck);
152
5.80k
    penum = gs_alloc_struct(mem, gx_image3x_enum_t, &st_image3x_enum,
153
5.80k
                            "gx_begin_image3x");
154
5.80k
    if (penum == 0)
155
0
        return_error(gs_error_VMerror);
156
    /* Initialize pointers now in case we bail out. */
157
5.80k
    penum->mask[0].info = 0, penum->mask[0].mdev = 0, penum->mask[0].data = 0;
158
5.80k
    penum->mask[1].info = 0, penum->mask[1].mdev = 0, penum->mask[1].data = 0;
159
5.80k
    penum->pixel.info = 0, penum->pixel.data = 0;
160
5.80k
    if (prect)
161
0
        pixel.rect = *prect;
162
5.80k
    else {
163
5.80k
        pixel.rect.p.x = pixel.rect.p.y = 0;
164
5.80k
        pixel.rect.q.x = pim->Width;
165
5.80k
        pixel.rect.q.y = pim->Height;
166
5.80k
    }
167
5.80k
    if ((code = gs_matrix_invert(&pim->ImageMatrix, &pixel.matrix)) < 0 ||
168
5.80k
        (code = gs_point_transform(pim->Width, pim->Height, &pixel.matrix,
169
5.80k
                                   &pixel.corner)) < 0 ||
170
5.80k
        (code = check_image3x_mask(pim, &pim->Opacity, &pixel, &mask[0],
171
5.80k
                                   &penum->mask[0], mem)) < 0 ||
172
5.79k
        (code = check_image3x_mask(pim, &pim->Shape, &pixel, &mask[1],
173
5.79k
                                   &penum->mask[1], mem)) < 0
174
5.80k
        ) {
175
7
        goto out0;
176
7
    }
177
5.79k
    penum->num_components =
178
5.79k
        gs_color_space_num_components(pim->ColorSpace);
179
5.79k
    code = gx_image_enum_common_init((gx_image_enum_common_t *) penum,
180
5.79k
                              (const gs_data_image_t *)pim,
181
5.79k
                              &image3x_enum_procs, dev,
182
5.79k
                              1 + penum->num_components,
183
5.79k
                              pim->format);
184
5.79k
    if (code < 0)
185
0
        goto out0;
186
187
5.79k
    penum->pixel.width = pixel.rect.q.x - pixel.rect.p.x;
188
5.79k
    penum->pixel.height = pixel.rect.q.y - pixel.rect.p.y;
189
5.79k
    penum->pixel.full_height = pim->Height;
190
5.79k
    penum->pixel.y = 0;
191
5.79k
    if (penum->mask[0].data || penum->mask[1].data) {
192
        /* Also allocate a row buffer for the pixel data. */
193
0
        penum->pixel.data =
194
0
            gs_alloc_bytes(mem,
195
0
                           ((size_t)penum->pixel.width * pim->BitsPerComponent *
196
0
                            penum->num_components + 7) >> 3,
197
0
                           "gx_begin_image3x(pixel.data)");
198
0
        if (penum->pixel.data == 0) {
199
0
            code = gs_note_error(gs_error_VMerror);
200
0
            goto out1;
201
0
        }
202
0
    }
203
5.79k
    penum->bpc = pim->BitsPerComponent;
204
5.79k
    penum->memory = mem;
205
5.79k
    if (pmat == 0)
206
5.79k
        pmat = &ctm_only(pgs);
207
17.3k
    for (i = 0; i < NUM_MASKS; ++i) {
208
11.5k
        gs_rect mrect;
209
11.5k
        gx_device *mdev;
210
        /*
211
         * The mask data has to be defined in a DevicePixel color space
212
         * of the correct depth so that no color mapping will occur.
213
         */
214
11.5k
        if (penum->mask[i].depth == 0) { /* mask not supplied */
215
5.79k
            midev[i] = 0;
216
5.79k
            minfo[i] = 0;
217
5.79k
            continue;
218
5.79k
        }
219
5.79k
        code = gs_cspace_new_DevicePixel(mem, &pmcs, penum->mask[i].depth);
220
5.79k
        if (code < 0)
221
0
            goto out1;
222
5.79k
        mrect.p.x = mrect.p.y = 0;
223
5.79k
        mrect.q.x = penum->mask[i].width;
224
5.79k
        mrect.q.y = penum->mask[i].height;
225
5.79k
        if ((code = gs_matrix_multiply(&mask[i].matrix, pmat, &mat)) < 0 ||
226
5.79k
            (code = gs_bbox_transform(&mrect, &mat, &mrect)) < 0
227
5.79k
            )
228
0
            goto out1;
229
230
        /* Bug 700438: If the rectangle is out of range, bail */
231
5.79k
        if (mrect.p.x >= (double)INT_MAX || mrect.q.x <= (double)INT_MIN ||
232
5.79k
            mrect.p.y >= (double)INT_MAX || mrect.q.y <= (double)INT_MIN) {
233
2
            code = gs_note_error(gs_error_rangecheck);
234
2
            goto out1;
235
2
        }
236
237
        /* This code was changed for bug 686843/687411, but in a way that
238
         * a) looked wrong, and b) doesn't appear to make a difference. Revert
239
         * it to the sane version until we have evidence why not. */
240
5.79k
        origin[i].x = (int)floor(mrect.p.x);
241
5.79k
        origin[i].y = (int)floor(mrect.p.y);
242
5.79k
        code = make_mid(&mdev, dev,
243
5.79k
                        (int)ceil(mrect.q.x) - origin[i].x,
244
5.79k
                        (int)ceil(mrect.q.y) - origin[i].y,
245
5.79k
                        penum->mask[i].depth, mem);
246
5.79k
        if (code < 0)
247
0
            goto out1;
248
5.79k
        code = dev_proc(dev, get_profile)(dev, &mdev->icc_struct);
249
5.79k
        if (code < 0)
250
0
            goto out1;  /* Device not yet open */
251
5.79k
        rc_increment(mdev->icc_struct);
252
5.79k
        penum->mask[i].mdev = mdev;
253
5.79k
        gs_image_t_init(&mask[i].image, pmcs);
254
5.79k
        mask[i].image.ColorSpace = pmcs;
255
5.79k
        mask[i].image.adjust = false;
256
5.79k
        mask[i].image.image_parent_type = gs_image_type3x;
257
5.79k
        {
258
5.79k
            const gx_image_type_t *type1 = mask[i].image.type;
259
5.79k
            const gs_image3x_mask_t *pixm =
260
5.79k
                (i == 0 ? &pim->Opacity : &pim->Shape);
261
262
            /* Use memcpy because direct assignment breaks ANSI aliasing */
263
            /* rules and causes SEGV with gcc 4.5.1 */
264
5.79k
            memcpy(&mask[i].image, &pixm->MaskDict, sizeof(pixm->MaskDict));
265
5.79k
            mask[i].image.type = type1;
266
5.79k
            mask[i].image.BitsPerComponent = pixm->MaskDict.BitsPerComponent;
267
5.79k
        }
268
5.79k
        {
269
5.79k
            gs_matrix m_mat;
270
271
            /*
272
             * Adjust the translation for rendering the mask to include a
273
             * negative translation by origin.{x,y} in device space.
274
             */
275
5.79k
            m_mat = *pmat;
276
5.79k
            m_mat.tx -= origin[i].x;
277
5.79k
            m_mat.ty -= origin[i].y;
278
            /*
279
             * Peter put in a comment that said " Note that pgs = NULL here,
280
             * since we don't want to have to create another gs_gstate with
281
             * default log_op, etc." and passed NULL instead of pgs to this
282
             * routine.  However Image type 1 need the gs_gstate (see
283
             * bug 688348) thus his optimization was removed.
284
             * dcolor = NULL is OK because this is an opaque image with
285
             * CombineWithColor = false.
286
             */
287
5.79k
            code = gx_device_begin_typed_image(mdev, pgs, &m_mat,
288
5.79k
                               (const gs_image_common_t *)&mask[i].image,
289
5.79k
                                               &mask[i].rect, NULL, NULL,
290
5.79k
                                               mem, &penum->mask[i].info);
291
5.79k
            if (code < 0)
292
0
                goto out2;
293
5.79k
        }
294
5.79k
        midev[i] = mdev;
295
5.79k
        minfo[i] = penum->mask[i].info;
296
5.79k
        rc_decrement_only(pmcs, "gx_begin_image3x_generic(pmcs)");
297
5.79k
        pmcs = NULL;
298
5.79k
    }
299
5.79k
    gs_image_t_init(&pixel.image, pim->ColorSpace);
300
5.79k
    {
301
5.79k
        const gx_image_type_t *type1 = pixel.image.type;
302
303
5.79k
        *(gs_pixel_image_t *)&pixel.image = *(const gs_pixel_image_t *)pim;
304
5.79k
        pixel.image.type = type1;
305
5.79k
        pixel.image.image_parent_type = gs_image_type3x;
306
5.79k
    }
307
5.79k
    if (minfo[0] != NULL)
308
5.78k
        minfo[0]->OC = OC;
309
310
5.79k
    code = make_mcde(dev, pgs, pmat, (const gs_image_common_t *)&pixel.image,
311
5.79k
                     prect, pdcolor, pcpath, mem, &penum->pixel.info,
312
5.79k
                     &pcdev, midev, minfo, origin, pim);
313
5.79k
    if (code < 0)
314
11
        goto out3;
315
5.78k
    penum->pcdev = pcdev;
316
    /*
317
     * Set num_planes, plane_widths, and plane_depths from the values in the
318
     * enumerators for the mask(s) and the image data.
319
     */
320
5.78k
    {
321
5.78k
        int added_depth = 0;
322
5.78k
        int pi = 0;
323
324
17.3k
        for (i = 0; i < NUM_MASKS; ++i) {
325
11.5k
            if (penum->mask[i].depth == 0)  /* no mask */
326
5.78k
                continue;
327
5.78k
            switch (penum->mask[i].InterleaveType) {
328
0
            case interleave_chunky:
329
                /* Add the mask data to the depth of the image data. */
330
0
                added_depth += pim->BitsPerComponent;
331
0
                break;
332
5.78k
            case interleave_separate_source:
333
                /* Insert the mask as a separate plane. */
334
5.78k
                penum->plane_widths[pi] = penum->mask[i].width;
335
5.78k
                penum->plane_depths[pi] = penum->mask[i].depth;
336
5.78k
                ++pi;
337
5.78k
                break;
338
0
            default:    /* can't happen */
339
0
                code = gs_note_error(gs_error_Fatal);
340
0
                goto out3;
341
5.78k
            }
342
5.78k
        }
343
5.78k
        memcpy(&penum->plane_widths[pi], &penum->pixel.info->plane_widths[0],
344
5.78k
               penum->pixel.info->num_planes * sizeof(penum->plane_widths[0]));
345
5.78k
        memcpy(&penum->plane_depths[pi], &penum->pixel.info->plane_depths[0],
346
5.78k
               penum->pixel.info->num_planes * sizeof(penum->plane_depths[0]));
347
5.78k
        penum->plane_depths[pi] += added_depth;
348
5.78k
        penum->num_planes = pi + penum->pixel.info->num_planes;
349
5.78k
    }
350
5.78k
    if (midev[0])
351
5.77k
        gx_device_retain(midev[0], true); /* will free explicitly */
352
5.78k
    if (midev[1])
353
10
        gx_device_retain(midev[1], true); /* ditto */
354
5.78k
    gx_device_retain(pcdev, true); /* ditto */
355
5.78k
    *pinfo = (gx_image_enum_common_t *) penum;
356
5.78k
    return 0;
357
11
  out3:
358
11
    if (penum->mask[1].info)
359
0
        gx_image_end(penum->mask[1].info, false);
360
11
    if (penum->mask[0].info)
361
11
        gx_image_end(penum->mask[0].info, false);
362
11
  out2:
363
11
    if (penum->mask[1].mdev) {
364
0
        gs_closedevice(penum->mask[1].mdev);
365
0
        gs_free_object(mem, penum->mask[1].mdev,
366
0
                       "gx_begin_image3x(mask[1].mdev)");
367
0
    }
368
11
    if (penum->mask[0].mdev) {
369
11
        gs_closedevice(penum->mask[0].mdev);
370
11
        gs_free_object(mem, penum->mask[0].mdev,
371
11
                       "gx_begin_image3x(mask[0].mdev)");
372
11
    }
373
13
  out1:
374
13
    rc_decrement(pmcs, "gx_begin_image3x_generic(pmcs)");
375
13
    gs_free_object(mem, penum->mask[0].data, "gx_begin_image3x(mask[0].data)");
376
13
    gs_free_object(mem, penum->mask[1].data, "gx_begin_image3x(mask[1].data)");
377
13
    gs_free_object(mem, penum->pixel.data, "gx_begin_image3x(pixel.data)");
378
20
  out0:
379
20
    gs_free_object(mem, penum, "gx_begin_image3x");
380
20
    return code;
381
13
}
382
static bool
383
check_image3x_extent(double mask_coeff, double data_coeff)
384
23.1k
{
385
23.1k
    if (mask_coeff == 0)
386
11.5k
        return data_coeff == 0;
387
11.5k
    if (data_coeff == 0 || (mask_coeff > 0) != (data_coeff > 0))
388
0
        return false;
389
11.5k
    return true;
390
11.5k
}
391
/*
392
 * Check mask parameters.
393
 * Reads ppcv->{matrix,corner,rect}, sets pmcv->{matrix,corner,rect} and
394
 * pmcs->{InterleaveType,width,height,full_height,depth,data,y,skip}.
395
 * If the mask is omitted, sets pmcs->depth = 0 and returns normally.
396
 */
397
static int
398
check_image3x_mask(const gs_image3x_t *pim, const gs_image3x_mask_t *pimm,
399
                   const image3x_channel_values_t *ppcv,
400
                   image3x_channel_values_t *pmcv,
401
                   image3x_channel_state_t *pmcs, gs_memory_t *mem)
402
11.5k
{
403
11.5k
    int mask_width = pimm->MaskDict.Width, mask_height = pimm->MaskDict.Height;
404
11.5k
    int code;
405
406
11.5k
    if (pimm->MaskDict.BitsPerComponent == 0) { /* mask missing */
407
5.79k
        pmcs->depth = 0;
408
5.79k
        pmcs->InterleaveType = 0; /* not a valid type */
409
5.79k
        return 0;
410
5.79k
    }
411
5.80k
    if (mask_height <= 0)
412
7
        return_error(gs_error_rangecheck);
413
5.79k
    switch (pimm->InterleaveType) {
414
        /*case interleave_scan_lines:*/ /* not supported */
415
0
        default:
416
0
            return_error(gs_error_rangecheck);
417
0
        case interleave_chunky:
418
0
            if (mask_width != pim->Width ||
419
0
                mask_height != pim->Height ||
420
0
                pimm->MaskDict.BitsPerComponent != pim->BitsPerComponent ||
421
0
                pim->format != gs_image_format_chunky
422
0
                )
423
0
                return_error(gs_error_rangecheck);
424
0
            break;
425
5.79k
        case interleave_separate_source:
426
5.79k
            switch (pimm->MaskDict.BitsPerComponent) {
427
5.79k
                    case 1: case 2: case 4: case 8: case 12: case 16:
428
5.79k
                break;
429
0
            default:
430
0
                return_error(gs_error_rangecheck);
431
5.79k
            }
432
5.79k
    }
433
5.79k
    if (!check_image3x_extent(pim->ImageMatrix.xx,
434
5.79k
                              pimm->MaskDict.ImageMatrix.xx) ||
435
5.79k
        !check_image3x_extent(pim->ImageMatrix.xy,
436
5.79k
                              pimm->MaskDict.ImageMatrix.xy) ||
437
5.79k
        !check_image3x_extent(pim->ImageMatrix.yx,
438
5.79k
                              pimm->MaskDict.ImageMatrix.yx) ||
439
5.79k
        !check_image3x_extent(pim->ImageMatrix.yy,
440
5.79k
                              pimm->MaskDict.ImageMatrix.yy)
441
5.79k
        )
442
0
        return_error(gs_error_rangecheck);
443
5.79k
    if ((code = gs_matrix_invert(&pimm->MaskDict.ImageMatrix, &pmcv->matrix)) < 0 ||
444
5.79k
        (code = gs_point_transform(mask_width, mask_height,
445
5.79k
                                   &pmcv->matrix, &pmcv->corner)) < 0
446
5.79k
        )
447
0
        return code;
448
5.79k
    if (fabs(ppcv->matrix.tx - pmcv->matrix.tx) >= 0.5 ||
449
5.79k
        fabs(ppcv->matrix.ty - pmcv->matrix.ty) >= 0.5 ||
450
5.79k
        fabs(ppcv->corner.x - pmcv->corner.x) >= 0.5 ||
451
5.79k
        fabs(ppcv->corner.y - pmcv->corner.y) >= 0.5
452
5.79k
        )
453
0
        return_error(gs_error_rangecheck);
454
5.79k
    pmcv->rect.p.x = ppcv->rect.p.x * mask_width / pim->Width;
455
5.79k
    pmcv->rect.p.y = ppcv->rect.p.y * mask_height / pim->Height;
456
5.79k
    pmcv->rect.q.x = (ppcv->rect.q.x * mask_width + pim->Width - 1) /
457
5.79k
        pim->Width;
458
5.79k
    pmcv->rect.q.y = (ppcv->rect.q.y * mask_height + pim->Height - 1) /
459
5.79k
        pim->Height;
460
    /* Initialize the channel state in the enumerator. */
461
5.79k
    pmcs->InterleaveType = pimm->InterleaveType;
462
5.79k
    pmcs->width = pmcv->rect.q.x - pmcv->rect.p.x;
463
5.79k
    pmcs->height = pmcv->rect.q.y - pmcv->rect.p.y;
464
5.79k
    pmcs->full_height = pimm->MaskDict.Height;
465
5.79k
    pmcs->depth = pimm->MaskDict.BitsPerComponent;
466
5.79k
    if (pmcs->InterleaveType == interleave_chunky) {
467
        /* Allocate a buffer for the data. */
468
0
        pmcs->data =
469
0
            gs_alloc_bytes(mem,
470
0
                           ((size_t)pmcs->width * pimm->MaskDict.BitsPerComponent + 7) >> 3,
471
0
                           "gx_begin_image3x(mask data)");
472
0
        if (pmcs->data == 0)
473
0
            return_error(gs_error_VMerror);
474
0
    }
475
5.79k
    pmcs->y = pmcs->skip = 0;
476
5.79k
    return 0;
477
5.79k
}
478
479
/*
480
 * Return > 0 if we want more data from channel 1 now, < 0 if we want more
481
 * from channel 2 now, 0 if we want both.
482
 */
483
static int
484
channel_next(const image3x_channel_state_t *pics1,
485
             const image3x_channel_state_t *pics2)
486
531k
{
487
    /*
488
     * The invariant we need to maintain is that we always have at least as
489
     * much channel N as channel N+1 data, where N = 0 = opacity, 1 = shape,
490
     * and 2 = pixel.  I.e., for any two consecutive channels c1 and c2, we
491
     * require c1.y / c1.full_height >= c2.y / c2.full_height, or, to avoid
492
     * floating point, c1.y * c2.full_height >= c2.y * c1.full_height.  We
493
     * know this condition is true now; return a value that indicates how to
494
     * maintain it.
495
     */
496
531k
    int h1 = pics1->full_height;
497
531k
    int h2 = pics2->full_height;
498
531k
    long current = pics1->y * (long)h2 - pics2->y * (long)h1;
499
500
#ifdef DEBUG
501
    if (current < 0)
502
        lprintf4("channel_next invariant fails: %d/%d < %d/%d\n",
503
                 pics1->y, pics1->full_height,
504
                 pics2->y, pics2->full_height);
505
#endif
506
531k
    return ((current -= h1) >= 0 ? -1 :
507
531k
            current + h2 >= 0 ? 0 : 1);
508
531k
}
509
510
/* Define the default implementation of ImageType 3 processing. */
511
static IMAGE3X_MAKE_MID_PROC(make_midx_default); /* check prototype */
512
static int
513
make_midx_default(gx_device **pmidev, gx_device *dev, int width, int height,
514
                 int depth, gs_memory_t *mem)
515
0
{
516
0
    const gx_device_memory *mdproto = gdev_mem_device_for_bits(depth);
517
0
    gx_device_memory *midev;
518
0
    int code;
519
520
0
    if (width != 0)
521
0
        if (height > max_ulong/width) /* protect against overflow in bitmap size */
522
0
            return_error(gs_error_VMerror);
523
0
    if (mdproto == 0)
524
0
        return_error(gs_error_rangecheck);
525
0
    midev = gs_alloc_struct_immovable(mem, gx_device_memory, &st_device_memory,
526
0
                            "make_mid_default");
527
0
    if (midev == 0)
528
0
        return_error(gs_error_VMerror);
529
0
    gs_make_mem_device(midev, mdproto, mem, 0, NULL);
530
0
    midev->bitmap_memory = mem;
531
0
    midev->width = width;
532
0
    midev->height = height;
533
0
    check_device_separable((gx_device *)midev);
534
0
    gx_device_fill_in_procs((gx_device *)midev);
535
0
    code = dev_proc(midev, open_device)((gx_device *)midev);
536
0
    if (code < 0) {
537
0
        gs_free_object(mem, midev, "make_midx_default");
538
0
        return code;
539
0
    }
540
0
    midev->is_open = true;
541
0
    dev_proc(midev, fill_rectangle)
542
0
        ((gx_device *)midev, 0, 0, width, height, (gx_color_index)0);
543
0
    *pmidev = (gx_device *)midev;
544
0
    return 0;
545
0
}
546
static IMAGE3X_MAKE_MCDE_PROC(make_mcdex_default);  /* check prototype */
547
static int
548
make_mcdex_default(gx_device *dev, const gs_gstate *pgs,
549
                   const gs_matrix *pmat, const gs_image_common_t *pic,
550
                   const gs_int_rect *prect, const gx_drawing_color *pdcolor,
551
                   const gx_clip_path *pcpath, gs_memory_t *mem,
552
                   gx_image_enum_common_t **pinfo,
553
                   gx_device **pmcdev, gx_device *midev[2],
554
                   gx_image_enum_common_t *pminfo[2],
555
                   const gs_int_point origin[2],
556
                   const gs_image3x_t *pim)
557
0
{
558
    /**************** NYI ****************/
559
    /*
560
     * There is no soft-mask analogue of make_mcde_default, because
561
     * soft-mask clipping is a more complicated operation, implemented
562
     * by the general transparency code.  As a default, we simply ignore
563
     * the soft mask.  However, we have to create an intermediate device
564
     * that can be freed at the end and that simply forwards all calls.
565
     * The most convenient device for this purpose is the bbox device.
566
     */
567
0
    gx_device_bbox *bbdev;
568
0
    int code;
569
0
    cmm_dev_profile_t *icc_struct;
570
571
0
    code = dev_proc(dev, get_profile)(dev, &icc_struct);
572
0
    if (code < 0) {
573
0
        return(code);
574
0
    }
575
576
0
    bbdev = gs_alloc_struct_immovable(mem, gx_device_bbox, &st_device_bbox,
577
0
                                  "make_mcdex_default");
578
579
0
    if (bbdev == 0)
580
0
        return_error(gs_error_VMerror);
581
582
0
    gx_device_bbox_init(bbdev, dev, mem);
583
584
0
    bbdev->icc_struct = icc_struct;
585
0
    rc_increment(bbdev->icc_struct);
586
587
0
    gx_device_bbox_fwd_open_close(bbdev, false);
588
0
    code = dev_proc(bbdev, begin_typed_image)
589
0
        ((gx_device *)bbdev, pgs, pmat, pic, prect, pdcolor, pcpath, mem,
590
0
         pinfo);
591
0
    if (code < 0) {
592
0
        gs_free_object(mem, bbdev, "make_mcdex_default");
593
0
        return code;
594
0
    }
595
0
    *pmcdev = (gx_device *)bbdev;
596
0
    return 0;
597
0
}
598
static int
599
gx_begin_image3x(gx_device * dev,
600
                const gs_gstate * pgs, const gs_matrix * pmat,
601
                const gs_image_common_t * pic, const gs_int_rect * prect,
602
                const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
603
                gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
604
0
{
605
0
    return gx_begin_image3x_generic(dev, pgs, pmat, pic, prect, pdcolor,
606
0
                                    pcpath, mem, make_midx_default,
607
0
                                    make_mcdex_default, pinfo, 0);
608
0
}
609
610
/* Process the next piece of an ImageType 3 image. */
611
static int
612
gx_image3x_plane_data(gx_image_enum_common_t * info,
613
                     const gx_image_plane_t * planes, int height,
614
                     int *rows_used)
615
526k
{
616
526k
    gx_image3x_enum_t *penum = (gx_image3x_enum_t *) info;
617
526k
    int pixel_height = penum->pixel.height;
618
526k
    int pixel_used = 0;
619
526k
    int mask_height[2];
620
526k
    int mask_used[2];
621
526k
    int h1 = pixel_height - penum->pixel.y;
622
526k
    int h;
623
526k
    const gx_image_plane_t *pixel_planes;
624
526k
    gx_image_plane_t pixel_plane, mask_plane[2];
625
526k
    int code = 0;
626
526k
    int i, pi = 0;
627
526k
    int num_chunky = 0;
628
629
1.57M
    for (i = 0; i < NUM_MASKS; ++i) {
630
1.05M
        int mh = mask_height[i] = penum->mask[i].height;
631
632
1.05M
        mask_plane[i].data = 0;
633
1.05M
        mask_plane[i].raster = 0;
634
1.05M
        mask_used[i] = 0;
635
1.05M
        if (!penum->mask[i].depth)
636
526k
            continue;
637
526k
        h1 = min(h1, ((mh > penum->mask[i].y) ? (mh - penum->mask[i].y) : mh));
638
526k
        if (penum->mask[i].InterleaveType == interleave_chunky)
639
0
            ++num_chunky;
640
526k
    }
641
526k
    h = min(height, h1);
642
    /* Initialized rows_used in case we get an error. */
643
526k
    *rows_used = 0;
644
645
526k
    if (h <= 0)
646
0
        return 0;
647
648
    /* Handle masks from separate sources. */
649
1.57M
    for (i = 0; i < NUM_MASKS; ++i)
650
1.05M
        if (penum->mask[i].InterleaveType == interleave_separate_source) {
651
            /*
652
             * In order to be able to recover from interruptions, we must
653
             * limit separate-source processing to 1 scan line at a time.
654
             */
655
526k
            if (h > 1)
656
0
                h = 1;
657
526k
            mask_plane[i] = planes[pi++];
658
526k
        }
659
526k
    pixel_planes = &planes[pi];
660
661
    /* Handle chunky masks. */
662
526k
    if (num_chunky) {
663
0
        int bpc = penum->bpc;
664
0
        int num_components = penum->num_components;
665
0
        int width = penum->pixel.width;
666
        /* Pull apart the source data and the mask data. */
667
        /* We do this in the simplest (not fastest) way for now. */
668
0
        uint bit_x = bpc * (num_components + num_chunky) * planes[pi].data_x;
669
0
        const byte *sptr = planes[0].data + (bit_x >> 3);
670
0
        int sbit = bit_x & 7;
671
0
        byte *pptr = penum->pixel.data;
672
0
        int pbit = 0;
673
0
        byte pbbyte = (pbit ? (byte)(*pptr & (0xff00 >> pbit)) : 0);
674
0
        byte *dptr[NUM_MASKS];
675
0
        int dbit[NUM_MASKS];
676
0
        byte dbbyte[NUM_MASKS];
677
0
        int depth[NUM_MASKS];
678
0
        int x;
679
680
0
        if (h > 1) {
681
            /* Do the operation one row at a time. */
682
0
            h = 1;
683
0
        }
684
0
        for (i = 0; i < NUM_MASKS; ++i)
685
0
            if (penum->mask[i].data) {
686
0
                depth[i] = penum->mask[i].depth;
687
0
                mask_plane[i].data = dptr[i] = penum->mask[i].data;
688
0
                mask_plane[i].data_x = 0;
689
                /* raster doesn't matter */
690
0
                dbit[i] = 0;
691
0
                dbbyte[i] = 0;
692
0
            } else
693
0
                depth[i] = 0;
694
0
        pixel_plane.data = pptr;
695
0
        pixel_plane.data_x = 0;
696
        /* raster doesn't matter */
697
0
        pixel_planes = &pixel_plane;
698
0
        for (x = 0; x < width; ++x) {
699
0
            uint value;
700
701
0
            for (i = 0; i < NUM_MASKS; ++i)
702
0
                if (depth[i]) {
703
0
                    if (sample_load_next12(&value, &sptr, &sbit, bpc) < 0)
704
0
                        return_error(gs_error_rangecheck);
705
0
                    if (sample_store_next12(value, &dptr[i], &dbit[i], depth[i],
706
0
                                        &dbbyte[i]) < 0)
707
0
                        return_error(gs_error_rangecheck);
708
0
                }
709
0
            for (i = 0; i < num_components; ++i) {
710
0
                if (sample_load_next12(&value, &sptr, &sbit, bpc) < 0)
711
0
                    return_error(gs_error_rangecheck);
712
0
                if (sample_store_next12(value, &pptr, &pbit, bpc, &pbbyte) < 0)
713
0
                    return_error(gs_error_rangecheck);
714
0
            }
715
0
        }
716
0
        for (i = 0; i < NUM_MASKS; ++i)
717
0
            if (penum->mask[i].data) {
718
0
                sample_store_flush(dptr[i], dbit[i], dbbyte[i]);
719
0
            }
720
0
        sample_store_flush(pptr, pbit, pbbyte);
721
0
        }
722
    /*
723
     * Process the mask data first, so it will set up the mask
724
     * device for clipping the pixel data.
725
     */
726
1.57M
    for (i = 0; i < NUM_MASKS; ++i)
727
1.05M
        if (mask_plane[i].data) {
728
            /*
729
             * If, on the last call, we processed some mask rows
730
             * successfully but processing the pixel rows was interrupted,
731
             * we set rows_used to indicate the number of pixel rows
732
             * processed (since there is no way to return two rows_used
733
             * values).  If this happened, some mask rows may get presented
734
             * again.  We must skip over them rather than processing them
735
             * again.
736
             */
737
526k
            int skip = penum->mask[i].skip;
738
739
526k
            if (skip >= h) {
740
0
                penum->mask[i].skip = skip - (mask_used[i] = h);
741
526k
            } else {
742
526k
                int mask_h = h - skip;
743
744
526k
                mask_plane[i].data += skip * mask_plane[i].raster;
745
526k
                penum->mask[i].skip = 0;
746
526k
                code = gx_image_plane_data_rows(penum->mask[i].info,
747
526k
                                                &mask_plane[i],
748
526k
                                                mask_h, &mask_used[i]);
749
526k
                mask_used[i] += skip;
750
526k
            }
751
526k
            *rows_used = mask_used[i];
752
526k
            penum->mask[i].y += mask_used[i];
753
526k
            if (code < 0)
754
0
                return code;
755
526k
        }
756
526k
    if (pixel_planes[0].data) {
757
        /*
758
         * If necessary, flush any buffered mask data to the mask clipping
759
         * device.
760
         */
761
1.56M
        for (i = 0; i < NUM_MASKS; ++i)
762
1.04M
            if (penum->mask[i].info)
763
522k
                gx_image_flush(penum->mask[i].info);
764
522k
        code = gx_image_plane_data_rows(penum->pixel.info, pixel_planes, h,
765
522k
                                        &pixel_used);
766
        /*
767
         * There isn't any way to set rows_used if different amounts of
768
         * the mask and pixel data were used.  Fake it.
769
         */
770
522k
        *rows_used = pixel_used;
771
        /*
772
         * Don't return code yet: we must account for the fact that
773
         * some mask data may have been processed.
774
         */
775
522k
        penum->pixel.y += pixel_used;
776
522k
        if (code < 0) {
777
            /*
778
             * We must prevent the mask data from being processed again.
779
             * We rely on the fact that h > 1 is only possible if the
780
             * mask and pixel data have the same Y scaling.
781
             */
782
0
            for (i = 0; i < NUM_MASKS; ++i)
783
0
                if (mask_used[i] > pixel_used) {
784
0
                    int skip = mask_used[i] - pixel_used;
785
786
0
                    penum->mask[i].skip = skip;
787
0
                    penum->mask[i].y -= skip;
788
0
                    mask_used[i] = pixel_used;
789
0
                }
790
0
        }
791
522k
    }
792
526k
    if_debug7m('b', penum->memory,
793
526k
               "[b]image3x h=%d %sopacity.y=%d %sopacity.y=%d %spixel.y=%d\n",
794
526k
               h, (mask_plane[0].data ? "+" : ""), penum->mask[0].y,
795
526k
               (mask_plane[1].data ? "+" : ""), penum->mask[1].y,
796
526k
               (pixel_planes[0].data ? "+" : ""), penum->pixel.y);
797
526k
    if (penum->mask[0].depth == 0 || penum->mask[0].y >= penum->mask[0].height) {
798
7.69k
        if (penum->mask[1].depth == 0 || penum->mask[1].y >= penum->mask[1].height) {
799
4.70k
            if (penum->pixel.y >= penum->pixel.height) {
800
4.70k
                return 1;
801
4.70k
            }
802
4.70k
        }
803
7.69k
    }
804
    /*
805
     * The mask may be complete (gx_image_plane_data_rows returned 1),
806
     * but there may still be pixel rows to go, so don't return 1 here.
807
     */
808
521k
    return (code < 0 ? code : 0);
809
526k
}
810
811
/* Flush buffered data. */
812
static int
813
gx_image3x_flush(gx_image_enum_common_t * info)
814
0
{
815
0
    gx_image3x_enum_t * const penum = (gx_image3x_enum_t *) info;
816
0
    int code = gx_image_flush(penum->mask[0].info);
817
818
0
    if (code >= 0)
819
0
        code = gx_image_flush(penum->mask[1].info);
820
0
    if (code >= 0)
821
0
        code = gx_image_flush(penum->pixel.info);
822
0
    return code;
823
0
}
824
825
/* Determine which data planes are wanted. */
826
static bool
827
gx_image3x_planes_wanted(const gx_image_enum_common_t * info, byte *wanted)
828
531k
{
829
531k
    const gx_image3x_enum_t * const penum = (const gx_image3x_enum_t *) info;
830
    /*
831
     * We always want at least as much of the mask(s) to be filled as the
832
     * pixel data.
833
     */
834
531k
    bool
835
531k
        sso = penum->mask[0].InterleaveType == interleave_separate_source,
836
531k
        sss = penum->mask[1].InterleaveType == interleave_separate_source;
837
838
531k
    if (sso & sss) {
839
        /* Both masks have separate sources. */
840
0
        int mask_next = channel_next(&penum->mask[1], &penum->pixel);
841
842
0
        memset(wanted + 2, (mask_next <= 0 ? 0xff : 0), info->num_planes - 2);
843
0
        wanted[1] = (mask_next >= 0 ? 0xff : 0);
844
0
        if (wanted[1]) {
845
0
            mask_next = channel_next(&penum->mask[0], &penum->mask[1]);
846
0
            wanted[0] = mask_next >= 0;
847
0
        } else
848
0
            wanted[0] = 0;
849
0
        return false;   /* see below */
850
531k
    } else if (sso | sss) {
851
        /* Only one separate source. */
852
531k
        const image3x_channel_state_t *pics =
853
531k
            (sso ? &penum->mask[0] : &penum->mask[1]);
854
531k
        int mask_next = channel_next(pics, &penum->pixel);
855
856
531k
        wanted[0] = (mask_next >= 0 ? 0xff : 0);
857
531k
        memset(wanted + 1, (mask_next <= 0 ? 0xff : 0), info->num_planes - 1);
858
        /*
859
         * In principle, wanted will always be true for both mask and pixel
860
         * data if the full_heights are equal.  Unfortunately, even in this
861
         * case, processing may be interrupted after a mask row has been
862
         * passed to the underlying image processor but before the data row
863
         * has been passed, in which case pixel data will be 'wanted', but
864
         * not mask data, for the next call.  Therefore, we must return
865
         * false.
866
         */
867
531k
        return false
868
            /*(next == 0 &&
869
531k
              pics->full_height == penum->pixel.full_height)*/;
870
531k
    } else {
871
        /* Everything is chunky, only 1 plane. */
872
0
        wanted[0] = 0xff;
873
0
        return true;
874
0
    }
875
531k
}
876
877
/* Clean up after processing an ImageType 3x image. */
878
static int
879
gx_image3x_end_image(gx_image_enum_common_t * info, bool draw_last)
880
5.78k
{
881
5.78k
    gx_image3x_enum_t *penum = (gx_image3x_enum_t *) info;
882
5.78k
    gs_memory_t *mem = penum->memory;
883
5.78k
    gx_device *mdev0 = penum->mask[0].mdev;
884
5.78k
    int ocode =
885
5.78k
        (penum->mask[0].info ? gx_image_end(penum->mask[0].info, draw_last) :
886
5.78k
         0);
887
5.78k
    gx_device *mdev1 = penum->mask[1].mdev;
888
5.78k
    int scode =
889
5.78k
        (penum->mask[1].info ? gx_image_end(penum->mask[1].info, draw_last) :
890
5.78k
         0);
891
5.78k
    gx_device *pcdev = penum->pcdev;
892
5.78k
    int pcode = gx_image_end(penum->pixel.info, draw_last);
893
894
5.78k
    rc_decrement(pcdev->icc_struct, "gx_image3x_end_image(pcdev->icc_struct)");
895
5.78k
    pcdev->icc_struct = NULL;
896
897
5.78k
    gs_closedevice(pcdev);
898
5.78k
    if (mdev0)
899
5.77k
        gs_closedevice(mdev0);
900
5.78k
    if (mdev1)
901
10
        gs_closedevice(mdev1);
902
5.78k
    gs_free_object(mem, penum->mask[0].data,
903
5.78k
                   "gx_image3x_end_image(mask[0].data)");
904
5.78k
    gs_free_object(mem, penum->mask[1].data,
905
5.78k
                   "gx_image3x_end_image(mask[1].data)");
906
5.78k
    gs_free_object(mem, penum->pixel.data,
907
5.78k
                   "gx_image3x_end_image(pixel.data)");
908
5.78k
    gs_free_object(mem, pcdev, "gx_image3x_end_image(pcdev)");
909
5.78k
    gs_free_object(mem, mdev0, "gx_image3x_end_image(mask[0].mdev)");
910
5.78k
    gs_free_object(mem, mdev1, "gx_image3x_end_image(mask[1].mdev)");
911
5.78k
    gx_image_free_enum(&info);
912
5.78k
    return (pcode < 0 ? pcode : scode < 0 ? scode : ocode);
913
5.78k
}