Coverage Report

Created: 2026-08-08 08:00

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
8.85k
{
61
8.85k
    pimm->InterleaveType = 0; /* not a valid type */
62
8.85k
    pimm->has_Matte = false;
63
8.85k
    gs_data_image_t_init(&pimm->MaskDict, 1);
64
8.85k
    pimm->MaskDict.BitsPerComponent = 0;  /* not supplied */
65
8.85k
}
66
void
67
gs_image3x_t_init(gs_image3x_t * pim, gs_color_space * color_space)
68
4.42k
{
69
4.42k
    gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space);
70
4.42k
    pim->type = &gs_image_type_3x;
71
4.42k
    gs_image3x_mask_init(&pim->Opacity);
72
4.42k
    gs_image3x_mask_init(&pim->Shape);
73
4.42k
}
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
4.81M
#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
4.36k
{
137
4.36k
    const gs_image3x_t *pim = (const gs_image3x_t *)pic;
138
4.36k
    gx_image3x_enum_t *penum;
139
4.36k
    gx_device *pcdev = 0;
140
4.36k
    image3x_channel_values_t mask[2], pixel;
141
4.36k
    gs_matrix mat;
142
4.36k
    gx_device *midev[2];
143
4.36k
    gx_image_enum_common_t *minfo[2];
144
4.36k
    gs_int_point origin[2];
145
4.36k
    int code;
146
4.36k
    int i;
147
4.36k
    gs_color_space *pmcs = NULL;
148
149
    /* Validate the parameters. */
150
4.36k
    if (pim->Height <= 0)
151
0
        return_error(gs_error_rangecheck);
152
4.36k
    penum = gs_alloc_struct(mem, gx_image3x_enum_t, &st_image3x_enum,
153
4.36k
                            "gx_begin_image3x");
154
4.36k
    if (penum == 0)
155
0
        return_error(gs_error_VMerror);
156
    /* Initialize pointers now in case we bail out. */
157
4.36k
    penum->mask[0].info = 0, penum->mask[0].mdev = 0, penum->mask[0].data = 0;
158
4.36k
    penum->mask[1].info = 0, penum->mask[1].mdev = 0, penum->mask[1].data = 0;
159
4.36k
    penum->pixel.info = 0, penum->pixel.data = 0;
160
4.36k
    if (prect)
161
0
        pixel.rect = *prect;
162
4.36k
    else {
163
4.36k
        pixel.rect.p.x = pixel.rect.p.y = 0;
164
4.36k
        pixel.rect.q.x = pim->Width;
165
4.36k
        pixel.rect.q.y = pim->Height;
166
4.36k
    }
167
4.36k
    if ((code = gs_matrix_invert(&pim->ImageMatrix, &pixel.matrix)) < 0 ||
168
4.36k
        (code = gs_point_transform(pim->Width, pim->Height, &pixel.matrix,
169
4.36k
                                   &pixel.corner)) < 0 ||
170
4.36k
        (code = check_image3x_mask(pim, &pim->Opacity, &pixel, &mask[0],
171
4.36k
                                   &penum->mask[0], mem)) < 0 ||
172
4.35k
        (code = check_image3x_mask(pim, &pim->Shape, &pixel, &mask[1],
173
4.35k
                                   &penum->mask[1], mem)) < 0
174
4.36k
        ) {
175
7
        goto out0;
176
7
    }
177
4.35k
    penum->num_components =
178
4.35k
        gs_color_space_num_components(pim->ColorSpace);
179
4.35k
    code = gx_image_enum_common_init((gx_image_enum_common_t *) penum,
180
4.35k
                              (const gs_data_image_t *)pim,
181
4.35k
                              &image3x_enum_procs, dev,
182
4.35k
                              1 + penum->num_components,
183
4.35k
                              pim->format);
184
4.35k
    if (code < 0)
185
0
        goto out0;
186
187
4.35k
    penum->pixel.width = pixel.rect.q.x - pixel.rect.p.x;
188
4.35k
    penum->pixel.height = pixel.rect.q.y - pixel.rect.p.y;
189
4.35k
    penum->pixel.full_height = pim->Height;
190
4.35k
    penum->pixel.y = 0;
191
4.35k
    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
4.35k
    penum->bpc = pim->BitsPerComponent;
204
4.35k
    penum->memory = mem;
205
4.35k
    if (pmat == 0)
206
4.35k
        pmat = &ctm_only(pgs);
207
13.0k
    for (i = 0; i < NUM_MASKS; ++i) {
208
8.71k
        gs_rect mrect;
209
8.71k
        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
8.71k
        if (penum->mask[i].depth == 0) { /* mask not supplied */
215
4.35k
            midev[i] = 0;
216
4.35k
            minfo[i] = 0;
217
4.35k
            continue;
218
4.35k
        }
219
4.35k
        code = gs_cspace_new_DevicePixel(mem, &pmcs, penum->mask[i].depth);
220
4.35k
        if (code < 0)
221
0
            goto out1;
222
4.35k
        mrect.p.x = mrect.p.y = 0;
223
4.35k
        mrect.q.x = penum->mask[i].width;
224
4.35k
        mrect.q.y = penum->mask[i].height;
225
4.35k
        if ((code = gs_matrix_multiply(&mask[i].matrix, pmat, &mat)) < 0 ||
226
4.35k
            (code = gs_bbox_transform(&mrect, &mat, &mrect)) < 0
227
4.35k
            )
228
0
            goto out1;
229
230
        /* Bug 700438: If the rectangle is out of range, bail */
231
4.35k
        if (mrect.p.x >= (double)INT_MAX || mrect.q.x <= (double)INT_MIN ||
232
4.35k
            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
4.35k
        origin[i].x = (int)floor(mrect.p.x);
241
4.35k
        origin[i].y = (int)floor(mrect.p.y);
242
4.35k
        code = make_mid(&mdev, dev,
243
4.35k
                        (int)ceil(mrect.q.x) - origin[i].x,
244
4.35k
                        (int)ceil(mrect.q.y) - origin[i].y,
245
4.35k
                        penum->mask[i].depth, mem);
246
4.35k
        if (code < 0)
247
0
            goto out1;
248
4.35k
        code = dev_proc(dev, get_profile)(dev, &mdev->icc_struct);
249
4.35k
        if (code < 0)
250
0
            goto out1;  /* Device not yet open */
251
4.35k
        rc_increment(mdev->icc_struct);
252
4.35k
        penum->mask[i].mdev = mdev;
253
4.35k
        gs_image_t_init(&mask[i].image, pmcs);
254
4.35k
        mask[i].image.ColorSpace = pmcs;
255
4.35k
        mask[i].image.adjust = false;
256
4.35k
        mask[i].image.image_parent_type = gs_image_type3x;
257
4.35k
        {
258
4.35k
            const gx_image_type_t *type1 = mask[i].image.type;
259
4.35k
            const gs_image3x_mask_t *pixm =
260
4.35k
                (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
4.35k
            memcpy(&mask[i].image, &pixm->MaskDict, sizeof(pixm->MaskDict));
265
4.35k
            mask[i].image.type = type1;
266
4.35k
            mask[i].image.BitsPerComponent = pixm->MaskDict.BitsPerComponent;
267
4.35k
        }
268
4.35k
        {
269
4.35k
            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
4.35k
            m_mat = *pmat;
276
4.35k
            m_mat.tx -= origin[i].x;
277
4.35k
            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
4.35k
            code = gx_device_begin_typed_image(mdev, pgs, &m_mat,
288
4.35k
                               (const gs_image_common_t *)&mask[i].image,
289
4.35k
                                               &mask[i].rect, NULL, NULL,
290
4.35k
                                               mem, &penum->mask[i].info);
291
4.35k
            if (code < 0)
292
0
                goto out2;
293
4.35k
        }
294
4.35k
        midev[i] = mdev;
295
4.35k
        minfo[i] = penum->mask[i].info;
296
4.35k
        rc_decrement_only(pmcs, "gx_begin_image3x_generic(pmcs)");
297
4.35k
        pmcs = NULL;
298
4.35k
    }
299
4.35k
    gs_image_t_init(&pixel.image, pim->ColorSpace);
300
4.35k
    {
301
4.35k
        const gx_image_type_t *type1 = pixel.image.type;
302
303
4.35k
        *(gs_pixel_image_t *)&pixel.image = *(const gs_pixel_image_t *)pim;
304
4.35k
        pixel.image.type = type1;
305
4.35k
        pixel.image.image_parent_type = gs_image_type3x;
306
4.35k
    }
307
4.35k
    if (minfo[0] != NULL)
308
4.34k
        minfo[0]->OC = OC;
309
310
4.35k
    code = make_mcde(dev, pgs, pmat, (const gs_image_common_t *)&pixel.image,
311
4.35k
                     prect, pdcolor, pcpath, mem, &penum->pixel.info,
312
4.35k
                     &pcdev, midev, minfo, origin, pim);
313
4.35k
    if (code < 0)
314
2
        goto out3;
315
4.35k
    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
4.35k
    {
321
4.35k
        int added_depth = 0;
322
4.35k
        int pi = 0;
323
324
13.0k
        for (i = 0; i < NUM_MASKS; ++i) {
325
8.70k
            if (penum->mask[i].depth == 0)  /* no mask */
326
4.35k
                continue;
327
4.35k
            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
4.35k
            case interleave_separate_source:
333
                /* Insert the mask as a separate plane. */
334
4.35k
                penum->plane_widths[pi] = penum->mask[i].width;
335
4.35k
                penum->plane_depths[pi] = penum->mask[i].depth;
336
4.35k
                ++pi;
337
4.35k
                break;
338
0
            default:    /* can't happen */
339
0
                code = gs_note_error(gs_error_Fatal);
340
0
                goto out3;
341
4.35k
            }
342
4.35k
        }
343
4.35k
        memcpy(&penum->plane_widths[pi], &penum->pixel.info->plane_widths[0],
344
4.35k
               penum->pixel.info->num_planes * sizeof(penum->plane_widths[0]));
345
4.35k
        memcpy(&penum->plane_depths[pi], &penum->pixel.info->plane_depths[0],
346
4.35k
               penum->pixel.info->num_planes * sizeof(penum->plane_depths[0]));
347
4.35k
        penum->plane_depths[pi] += added_depth;
348
4.35k
        penum->num_planes = pi + penum->pixel.info->num_planes;
349
4.35k
    }
350
4.35k
    if (midev[0])
351
4.34k
        gx_device_retain(midev[0], true); /* will free explicitly */
352
4.35k
    if (midev[1])
353
9
        gx_device_retain(midev[1], true); /* ditto */
354
4.35k
    gx_device_retain(pcdev, true); /* ditto */
355
4.35k
    *pinfo = (gx_image_enum_common_t *) penum;
356
4.35k
    return 0;
357
2
  out3:
358
2
    if (penum->mask[1].info)
359
0
        gx_image_end(penum->mask[1].info, false);
360
2
    if (penum->mask[0].info)
361
2
        gx_image_end(penum->mask[0].info, false);
362
2
  out2:
363
2
    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
2
    if (penum->mask[0].mdev) {
369
2
        gs_closedevice(penum->mask[0].mdev);
370
2
        gs_free_object(mem, penum->mask[0].mdev,
371
2
                       "gx_begin_image3x(mask[0].mdev)");
372
2
    }
373
4
  out1:
374
4
    rc_decrement(pmcs, "gx_begin_image3x_generic(pmcs)");
375
4
    gs_free_object(mem, penum->mask[0].data, "gx_begin_image3x(mask[0].data)");
376
4
    gs_free_object(mem, penum->mask[1].data, "gx_begin_image3x(mask[1].data)");
377
4
    gs_free_object(mem, penum->pixel.data, "gx_begin_image3x(pixel.data)");
378
11
  out0:
379
11
    gs_free_object(mem, penum, "gx_begin_image3x");
380
11
    return code;
381
4
}
382
static bool
383
check_image3x_extent(double mask_coeff, double data_coeff)
384
17.4k
{
385
17.4k
    if (mask_coeff == 0)
386
8.71k
        return data_coeff == 0;
387
8.71k
    if (data_coeff == 0 || (mask_coeff > 0) != (data_coeff > 0))
388
0
        return false;
389
8.71k
    return true;
390
8.71k
}
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
8.72k
{
403
8.72k
    int mask_width = pimm->MaskDict.Width, mask_height = pimm->MaskDict.Height;
404
8.72k
    int code;
405
406
8.72k
    if (pimm->MaskDict.BitsPerComponent == 0) { /* mask missing */
407
4.35k
        pmcs->depth = 0;
408
4.35k
        pmcs->InterleaveType = 0; /* not a valid type */
409
4.35k
        return 0;
410
4.35k
    }
411
4.36k
    if (mask_height <= 0)
412
7
        return_error(gs_error_rangecheck);
413
4.35k
    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
4.35k
        case interleave_separate_source:
426
4.35k
            switch (pimm->MaskDict.BitsPerComponent) {
427
4.35k
                    case 1: case 2: case 4: case 8: case 12: case 16:
428
4.35k
                break;
429
0
            default:
430
0
                return_error(gs_error_rangecheck);
431
4.35k
            }
432
4.35k
    }
433
4.35k
    if (!check_image3x_extent(pim->ImageMatrix.xx,
434
4.35k
                              pimm->MaskDict.ImageMatrix.xx) ||
435
4.35k
        !check_image3x_extent(pim->ImageMatrix.xy,
436
4.35k
                              pimm->MaskDict.ImageMatrix.xy) ||
437
4.35k
        !check_image3x_extent(pim->ImageMatrix.yx,
438
4.35k
                              pimm->MaskDict.ImageMatrix.yx) ||
439
4.35k
        !check_image3x_extent(pim->ImageMatrix.yy,
440
4.35k
                              pimm->MaskDict.ImageMatrix.yy)
441
4.35k
        )
442
0
        return_error(gs_error_rangecheck);
443
4.35k
    if ((code = gs_matrix_invert(&pimm->MaskDict.ImageMatrix, &pmcv->matrix)) < 0 ||
444
4.35k
        (code = gs_point_transform(mask_width, mask_height,
445
4.35k
                                   &pmcv->matrix, &pmcv->corner)) < 0
446
4.35k
        )
447
0
        return code;
448
4.35k
    if (fabs(ppcv->matrix.tx - pmcv->matrix.tx) >= 0.5 ||
449
4.35k
        fabs(ppcv->matrix.ty - pmcv->matrix.ty) >= 0.5 ||
450
4.35k
        fabs(ppcv->corner.x - pmcv->corner.x) >= 0.5 ||
451
4.35k
        fabs(ppcv->corner.y - pmcv->corner.y) >= 0.5
452
4.35k
        )
453
0
        return_error(gs_error_rangecheck);
454
4.35k
    pmcv->rect.p.x = ppcv->rect.p.x * mask_width / pim->Width;
455
4.35k
    pmcv->rect.p.y = ppcv->rect.p.y * mask_height / pim->Height;
456
4.35k
    pmcv->rect.q.x = (ppcv->rect.q.x * mask_width + pim->Width - 1) /
457
4.35k
        pim->Width;
458
4.35k
    pmcv->rect.q.y = (ppcv->rect.q.y * mask_height + pim->Height - 1) /
459
4.35k
        pim->Height;
460
    /* Initialize the channel state in the enumerator. */
461
4.35k
    pmcs->InterleaveType = pimm->InterleaveType;
462
4.35k
    pmcs->width = pmcv->rect.q.x - pmcv->rect.p.x;
463
4.35k
    pmcs->height = pmcv->rect.q.y - pmcv->rect.p.y;
464
4.35k
    pmcs->full_height = pimm->MaskDict.Height;
465
4.35k
    pmcs->depth = pimm->MaskDict.BitsPerComponent;
466
4.35k
    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
4.35k
    pmcs->y = pmcs->skip = 0;
476
4.35k
    return 0;
477
4.35k
}
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
403k
{
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
403k
    int h1 = pics1->full_height;
497
403k
    int h2 = pics2->full_height;
498
403k
    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
403k
    return ((current -= h1) >= 0 ? -1 :
507
403k
            current + h2 >= 0 ? 0 : 1);
508
403k
}
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
399k
{
616
399k
    gx_image3x_enum_t *penum = (gx_image3x_enum_t *) info;
617
399k
    int pixel_height = penum->pixel.height;
618
399k
    int pixel_used = 0;
619
399k
    int mask_height[2];
620
399k
    int mask_used[2];
621
399k
    int h1 = pixel_height - penum->pixel.y;
622
399k
    int h;
623
399k
    const gx_image_plane_t *pixel_planes;
624
399k
    gx_image_plane_t pixel_plane, mask_plane[2];
625
399k
    int code = 0;
626
399k
    int i, pi = 0;
627
399k
    int num_chunky = 0;
628
629
1.19M
    for (i = 0; i < NUM_MASKS; ++i) {
630
798k
        int mh = mask_height[i] = penum->mask[i].height;
631
632
798k
        mask_plane[i].data = 0;
633
798k
        mask_plane[i].raster = 0;
634
798k
        mask_used[i] = 0;
635
798k
        if (!penum->mask[i].depth)
636
399k
            continue;
637
399k
        h1 = min(h1, ((mh > penum->mask[i].y) ? (mh - penum->mask[i].y) : mh));
638
399k
        if (penum->mask[i].InterleaveType == interleave_chunky)
639
0
            ++num_chunky;
640
399k
    }
641
399k
    h = min(height, h1);
642
    /* Initialized rows_used in case we get an error. */
643
399k
    *rows_used = 0;
644
645
399k
    if (h <= 0)
646
0
        return 0;
647
648
    /* Handle masks from separate sources. */
649
1.19M
    for (i = 0; i < NUM_MASKS; ++i)
650
798k
        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
399k
            if (h > 1)
656
0
                h = 1;
657
399k
            mask_plane[i] = planes[pi++];
658
399k
        }
659
399k
    pixel_planes = &planes[pi];
660
661
    /* Handle chunky masks. */
662
399k
    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.19M
    for (i = 0; i < NUM_MASKS; ++i)
727
798k
        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
399k
            int skip = penum->mask[i].skip;
738
739
399k
            if (skip >= h) {
740
0
                penum->mask[i].skip = skip - (mask_used[i] = h);
741
399k
            } else {
742
399k
                int mask_h = h - skip;
743
744
399k
                mask_plane[i].data += skip * mask_plane[i].raster;
745
399k
                penum->mask[i].skip = 0;
746
399k
                code = gx_image_plane_data_rows(penum->mask[i].info,
747
399k
                                                &mask_plane[i],
748
399k
                                                mask_h, &mask_used[i]);
749
399k
                mask_used[i] += skip;
750
399k
            }
751
399k
            *rows_used = mask_used[i];
752
399k
            penum->mask[i].y += mask_used[i];
753
399k
            if (code < 0)
754
0
                return code;
755
399k
        }
756
399k
    if (pixel_planes[0].data) {
757
        /*
758
         * If necessary, flush any buffered mask data to the mask clipping
759
         * device.
760
         */
761
1.19M
        for (i = 0; i < NUM_MASKS; ++i)
762
797k
            if (penum->mask[i].info)
763
398k
                gx_image_flush(penum->mask[i].info);
764
398k
        code = gx_image_plane_data_rows(penum->pixel.info, pixel_planes, h,
765
398k
                                        &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
398k
        *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
398k
        penum->pixel.y += pixel_used;
776
398k
        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
398k
    }
792
399k
    if_debug7m('b', penum->memory,
793
399k
               "[b]image3x h=%d %sopacity.y=%d %sopacity.y=%d %spixel.y=%d\n",
794
399k
               h, (mask_plane[0].data ? "+" : ""), penum->mask[0].y,
795
399k
               (mask_plane[1].data ? "+" : ""), penum->mask[1].y,
796
399k
               (pixel_planes[0].data ? "+" : ""), penum->pixel.y);
797
399k
    if (penum->mask[0].depth == 0 || penum->mask[0].y >= penum->mask[0].height) {
798
6.62k
        if (penum->mask[1].depth == 0 || penum->mask[1].y >= penum->mask[1].height) {
799
3.97k
            if (penum->pixel.y >= penum->pixel.height) {
800
3.97k
                return 1;
801
3.97k
            }
802
3.97k
        }
803
6.62k
    }
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
395k
    return (code < 0 ? code : 0);
809
399k
}
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
403k
{
829
403k
    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
403k
    bool
835
403k
        sso = penum->mask[0].InterleaveType == interleave_separate_source,
836
403k
        sss = penum->mask[1].InterleaveType == interleave_separate_source;
837
838
403k
    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
403k
    } else if (sso | sss) {
851
        /* Only one separate source. */
852
403k
        const image3x_channel_state_t *pics =
853
403k
            (sso ? &penum->mask[0] : &penum->mask[1]);
854
403k
        int mask_next = channel_next(pics, &penum->pixel);
855
856
403k
        wanted[0] = (mask_next >= 0 ? 0xff : 0);
857
403k
        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
403k
        return false
868
            /*(next == 0 &&
869
403k
              pics->full_height == penum->pixel.full_height)*/;
870
403k
    } else {
871
        /* Everything is chunky, only 1 plane. */
872
0
        wanted[0] = 0xff;
873
0
        return true;
874
0
    }
875
403k
}
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
4.35k
{
881
4.35k
    gx_image3x_enum_t *penum = (gx_image3x_enum_t *) info;
882
4.35k
    gs_memory_t *mem = penum->memory;
883
4.35k
    gx_device *mdev0 = penum->mask[0].mdev;
884
4.35k
    int ocode =
885
4.35k
        (penum->mask[0].info ? gx_image_end(penum->mask[0].info, draw_last) :
886
4.35k
         0);
887
4.35k
    gx_device *mdev1 = penum->mask[1].mdev;
888
4.35k
    int scode =
889
4.35k
        (penum->mask[1].info ? gx_image_end(penum->mask[1].info, draw_last) :
890
4.35k
         0);
891
4.35k
    gx_device *pcdev = penum->pcdev;
892
4.35k
    int pcode = gx_image_end(penum->pixel.info, draw_last);
893
894
4.35k
    rc_decrement(pcdev->icc_struct, "gx_image3x_end_image(pcdev->icc_struct)");
895
4.35k
    pcdev->icc_struct = NULL;
896
897
4.35k
    gs_closedevice(pcdev);
898
4.35k
    if (mdev0)
899
4.34k
        gs_closedevice(mdev0);
900
4.35k
    if (mdev1)
901
9
        gs_closedevice(mdev1);
902
4.35k
    gs_free_object(mem, penum->mask[0].data,
903
4.35k
                   "gx_image3x_end_image(mask[0].data)");
904
4.35k
    gs_free_object(mem, penum->mask[1].data,
905
4.35k
                   "gx_image3x_end_image(mask[1].data)");
906
4.35k
    gs_free_object(mem, penum->pixel.data,
907
4.35k
                   "gx_image3x_end_image(pixel.data)");
908
4.35k
    gs_free_object(mem, pcdev, "gx_image3x_end_image(pcdev)");
909
4.35k
    gs_free_object(mem, mdev0, "gx_image3x_end_image(mask[0].mdev)");
910
4.35k
    gs_free_object(mem, mdev1, "gx_image3x_end_image(mask[1].mdev)");
911
4.35k
    gx_image_free_enum(&info);
912
4.35k
    return (pcode < 0 ? pcode : scode < 0 ? scode : ocode);
913
4.35k
}