summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'base/gximag3x.c')
-rw-r--r--base/gximag3x.c906
1 files changed, 906 insertions, 0 deletions
diff --git a/base/gximag3x.c b/base/gximag3x.c
new file mode 100644
index 00000000..7a5f2c37
--- /dev/null
+++ b/base/gximag3x.c
@@ -0,0 +1,906 @@
+/* Copyright (C) 2001-2019 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
+ CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* ImageType 3x image implementation */
+/****** THE REAL WORK IS NYI ******/
+#include "math_.h" /* for ceil, floor */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsbitops.h"
+#include "gscspace.h"
+#include "gscpixel.h"
+#include "gsstruct.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gximag3x.h"
+#include "gxgstate.h"
+#include "gdevbbox.h"
+#include <limits.h> /* For INT_MAX etc */
+
+extern_st(st_color_space);
+
+/* Forward references */
+static dev_proc_begin_typed_image(gx_begin_image3x);
+static image_enum_proc_plane_data(gx_image3x_plane_data);
+static image_enum_proc_end_image(gx_image3x_end_image);
+static image_enum_proc_flush(gx_image3x_flush);
+static image_enum_proc_planes_wanted(gx_image3x_planes_wanted);
+
+/* GC descriptor */
+private_st_gs_image3x();
+
+/* Define the image type for ImageType 3x images. */
+const gx_image_type_t gs_image_type_3x = {
+ &st_gs_image3x, gx_begin_image3x, gx_data_image_source_size,
+ gx_image_no_sput, gx_image_no_sget, gx_image_default_release,
+ IMAGE3X_IMAGETYPE
+};
+static const gx_image_enum_procs_t image3x_enum_procs = {
+ gx_image3x_plane_data, gx_image3x_end_image,
+ gx_image3x_flush, gx_image3x_planes_wanted
+};
+
+/* Initialize an ImageType 3x image. */
+static void
+gs_image3x_mask_init(gs_image3x_mask_t *pimm)
+{
+ pimm->InterleaveType = 0; /* not a valid type */
+ pimm->has_Matte = false;
+ gs_data_image_t_init(&pimm->MaskDict, 1);
+ pimm->MaskDict.BitsPerComponent = 0; /* not supplied */
+}
+void
+gs_image3x_t_init(gs_image3x_t * pim, gs_color_space * color_space)
+{
+ gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space);
+ pim->type = &gs_image_type_3x;
+ gs_image3x_mask_init(&pim->Opacity);
+ gs_image3x_mask_init(&pim->Shape);
+}
+
+/*
+ * We implement ImageType 3 images by interposing a mask clipper in
+ * front of an ordinary ImageType 1 image. Note that we build up the
+ * mask row-by-row as we are processing the image.
+ *
+ * We export a generalized form of the begin_image procedure for use by
+ * the PDF and PostScript writers.
+ */
+
+typedef struct image3x_channel_state_s {
+ gx_image_enum_common_t *info;
+ gx_device *mdev; /* gx_device_memory in default impl. */
+ /* (only for masks) */
+ gs_image3_interleave_type_t InterleaveType;
+ int width, height, full_height, depth;
+ byte *data; /* (if chunky) */
+ /* Only the following change dynamically. */
+ int y;
+ int skip; /* only for masks, # of rows to skip, */
+ /* see below */
+} image3x_channel_state_t;
+typedef struct gx_image3x_enum_s {
+ gx_image_enum_common;
+ gx_device *pcdev; /* gx_device_mask_clip in default impl. */
+ int num_components; /* (not counting masks) */
+ int bpc; /* pixel BitsPerComponent */
+#define NUM_MASKS 2 /* opacity, shape */
+ image3x_channel_state_t mask[NUM_MASKS], pixel;
+} gx_image3x_enum_t;
+
+extern_st(st_gx_image_enum_common);
+gs_private_st_suffix_add9(st_image3x_enum, gx_image3x_enum_t,
+ "gx_image3x_enum_t", image3x_enum_enum_ptrs, image3x_enum_reloc_ptrs,
+ st_gx_image_enum_common, pcdev, mask[0].info, mask[0].mdev, mask[0].data,
+ mask[1].info, mask[1].mdev, mask[1].data, pixel.info, pixel.data);
+
+/*
+ * Begin a generic ImageType 3x image, with client handling the creation of
+ * the mask image and mask clip devices.
+ */
+typedef struct image3x_channel_values_s {
+ gs_matrix matrix;
+ gs_point corner;
+ gs_int_rect rect;
+ gs_image_t image;
+} image3x_channel_values_t;
+static int check_image3x_mask(const gs_image3x_t *pim,
+ const gs_image3x_mask_t *pimm,
+ const image3x_channel_values_t *ppcv,
+ image3x_channel_values_t *pmcv,
+ image3x_channel_state_t *pmcs,
+ gs_memory_t *mem);
+int
+gx_begin_image3x_generic(gx_device * dev,
+ const gs_gstate *pgs, const gs_matrix *pmat,
+ const gs_image_common_t *pic, const gs_int_rect *prect,
+ const gx_drawing_color *pdcolor,
+ const gx_clip_path *pcpath, gs_memory_t *mem,
+ image3x_make_mid_proc_t make_mid,
+ image3x_make_mcde_proc_t make_mcde,
+ gx_image_enum_common_t **pinfo)
+{
+ const gs_image3x_t *pim = (const gs_image3x_t *)pic;
+ gx_image3x_enum_t *penum;
+ gx_device *pcdev = 0;
+ image3x_channel_values_t mask[2], pixel;
+ gs_matrix mat;
+ gx_device *midev[2];
+ gx_image_enum_common_t *minfo[2];
+ gs_int_point origin[2];
+ int code;
+ int i;
+
+ /* Validate the parameters. */
+ if (pim->Height <= 0)
+ return_error(gs_error_rangecheck);
+ penum = gs_alloc_struct(mem, gx_image3x_enum_t, &st_image3x_enum,
+ "gx_begin_image3x");
+ if (penum == 0)
+ return_error(gs_error_VMerror);
+ /* Initialize pointers now in case we bail out. */
+ penum->mask[0].info = 0, penum->mask[0].mdev = 0, penum->mask[0].data = 0;
+ penum->mask[1].info = 0, penum->mask[1].mdev = 0, penum->mask[1].data = 0;
+ penum->pixel.info = 0, penum->pixel.data = 0;
+ if (prect)
+ pixel.rect = *prect;
+ else {
+ pixel.rect.p.x = pixel.rect.p.y = 0;
+ pixel.rect.q.x = pim->Width;
+ pixel.rect.q.y = pim->Height;
+ }
+ if ((code = gs_matrix_invert(&pim->ImageMatrix, &pixel.matrix)) < 0 ||
+ (code = gs_point_transform(pim->Width, pim->Height, &pixel.matrix,
+ &pixel.corner)) < 0 ||
+ (code = check_image3x_mask(pim, &pim->Opacity, &pixel, &mask[0],
+ &penum->mask[0], mem)) < 0 ||
+ (code = check_image3x_mask(pim, &pim->Shape, &pixel, &mask[1],
+ &penum->mask[1], mem)) < 0
+ ) {
+ goto out0;
+ }
+ penum->num_components =
+ gs_color_space_num_components(pim->ColorSpace);
+ gx_image_enum_common_init((gx_image_enum_common_t *) penum,
+ (const gs_data_image_t *)pim,
+ &image3x_enum_procs, dev,
+ 1 + penum->num_components,
+ pim->format);
+ penum->pixel.width = pixel.rect.q.x - pixel.rect.p.x;
+ penum->pixel.height = pixel.rect.q.y - pixel.rect.p.y;
+ penum->pixel.full_height = pim->Height;
+ penum->pixel.y = 0;
+ if (penum->mask[0].data || penum->mask[1].data) {
+ /* Also allocate a row buffer for the pixel data. */
+ penum->pixel.data =
+ gs_alloc_bytes(mem,
+ (penum->pixel.width * pim->BitsPerComponent *
+ penum->num_components + 7) >> 3,
+ "gx_begin_image3x(pixel.data)");
+ if (penum->pixel.data == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out1;
+ }
+ }
+ penum->bpc = pim->BitsPerComponent;
+ penum->memory = mem;
+ if (pmat == 0)
+ pmat = &ctm_only(pgs);
+ for (i = 0; i < NUM_MASKS; ++i) {
+ gs_rect mrect;
+ gx_device *mdev;
+ /*
+ * The mask data has to be defined in a DevicePixel color space
+ * of the correct depth so that no color mapping will occur.
+ */
+ /****** FREE COLOR SPACE ON ERROR OR AT END ******/
+ gs_color_space *pmcs;
+
+ if (penum->mask[i].depth == 0) { /* mask not supplied */
+ midev[i] = 0;
+ minfo[i] = 0;
+ continue;
+ }
+ code = gs_cspace_new_DevicePixel(mem, &pmcs, penum->mask[i].depth);
+ if (code < 0)
+ return code;
+ mrect.p.x = mrect.p.y = 0;
+ mrect.q.x = penum->mask[i].width;
+ mrect.q.y = penum->mask[i].height;
+ if ((code = gs_matrix_multiply(&mask[i].matrix, pmat, &mat)) < 0 ||
+ (code = gs_bbox_transform(&mrect, &mat, &mrect)) < 0
+ )
+ return code;
+
+ /* Bug 700438: If the rectangle is out of range, bail */
+ if (mrect.p.x >= (double)INT_MAX || mrect.q.x <= (double)INT_MIN ||
+ mrect.p.y >= (double)INT_MAX || mrect.q.y <= (double)INT_MIN) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto out1;
+ }
+
+ /* This code was changed for bug 686843/687411, but in a way that
+ * a) looked wrong, and b) doesn't appear to make a difference. Revert
+ * it to the sane version until we have evidence why not. */
+ origin[i].x = (int)floor(mrect.p.x);
+ origin[i].y = (int)floor(mrect.p.y);
+ code = make_mid(&mdev, dev,
+ (int)ceil(mrect.q.x) - origin[i].x,
+ (int)ceil(mrect.q.y) - origin[i].y,
+ penum->mask[i].depth, mem);
+ if (code < 0)
+ goto out1;
+ code = dev_proc(dev, get_profile)(dev, &mdev->icc_struct);
+ if (code < 0)
+ goto out1; /* Device not yet open */
+ rc_increment(mdev->icc_struct);
+ penum->mask[i].mdev = mdev;
+ gs_image_t_init(&mask[i].image, pmcs);
+ mask[i].image.ColorSpace = pmcs;
+ mask[i].image.adjust = false;
+ mask[i].image.image_parent_type = gs_image_type3x;
+ {
+ const gx_image_type_t *type1 = mask[i].image.type;
+ const gs_image3x_mask_t *pixm =
+ (i == 0 ? &pim->Opacity : &pim->Shape);
+
+ /* Use memcpy because direct assignment breaks ANSI aliasing */
+ /* rules and causes SEGV with gcc 4.5.1 */
+ memcpy(&mask[i].image, &pixm->MaskDict, sizeof(pixm->MaskDict));
+ mask[i].image.type = type1;
+ mask[i].image.BitsPerComponent = pixm->MaskDict.BitsPerComponent;
+ }
+ {
+ gs_matrix m_mat;
+
+ /*
+ * Adjust the translation for rendering the mask to include a
+ * negative translation by origin.{x,y} in device space.
+ */
+ m_mat = *pmat;
+ m_mat.tx -= origin[i].x;
+ m_mat.ty -= origin[i].y;
+ /*
+ * Peter put in a comment that said " Note that pgs = NULL here,
+ * since we don't want to have to create another gs_gstate with
+ * default log_op, etc." and passed NULL instead of pgs to this
+ * routine. However Image type 1 need the gs_gstate (see
+ * bug 688348) thus his optimization was removed.
+ * dcolor = NULL is OK because this is an opaque image with
+ * CombineWithColor = false.
+ */
+ code = gx_device_begin_typed_image(mdev, pgs, &m_mat,
+ (const gs_image_common_t *)&mask[i].image,
+ &mask[i].rect, NULL, NULL,
+ mem, &penum->mask[i].info);
+ if (code < 0)
+ goto out2;
+ }
+ midev[i] = mdev;
+ minfo[i] = penum->mask[i].info;
+ }
+ gs_image_t_init(&pixel.image, pim->ColorSpace);
+ {
+ const gx_image_type_t *type1 = pixel.image.type;
+
+ *(gs_pixel_image_t *)&pixel.image = *(const gs_pixel_image_t *)pim;
+ pixel.image.type = type1;
+ pixel.image.image_parent_type = gs_image_type3x;
+ }
+ code = make_mcde(dev, pgs, pmat, (const gs_image_common_t *)&pixel.image,
+ prect, pdcolor, pcpath, mem, &penum->pixel.info,
+ &pcdev, midev, minfo, origin, pim);
+ if (code < 0)
+ goto out3;
+ penum->pcdev = pcdev;
+ /*
+ * Set num_planes, plane_widths, and plane_depths from the values in the
+ * enumerators for the mask(s) and the image data.
+ */
+ {
+ int added_depth = 0;
+ int pi = 0;
+
+ for (i = 0; i < NUM_MASKS; ++i) {
+ if (penum->mask[i].depth == 0) /* no mask */
+ continue;
+ switch (penum->mask[i].InterleaveType) {
+ case interleave_chunky:
+ /* Add the mask data to the depth of the image data. */
+ added_depth += pim->BitsPerComponent;
+ break;
+ case interleave_separate_source:
+ /* Insert the mask as a separate plane. */
+ penum->plane_widths[pi] = penum->mask[i].width;
+ penum->plane_depths[pi] = penum->mask[i].depth;
+ ++pi;
+ break;
+ default: /* can't happen */
+ code = gs_note_error(gs_error_Fatal);
+ goto out3;
+ }
+ }
+ memcpy(&penum->plane_widths[pi], &penum->pixel.info->plane_widths[0],
+ penum->pixel.info->num_planes * sizeof(penum->plane_widths[0]));
+ memcpy(&penum->plane_depths[pi], &penum->pixel.info->plane_depths[0],
+ penum->pixel.info->num_planes * sizeof(penum->plane_depths[0]));
+ penum->plane_depths[pi] += added_depth;
+ penum->num_planes = pi + penum->pixel.info->num_planes;
+ }
+ if (midev[0])
+ gx_device_retain(midev[0], true); /* will free explicitly */
+ if (midev[1])
+ gx_device_retain(midev[1], true); /* ditto */
+ gx_device_retain(pcdev, true); /* ditto */
+ *pinfo = (gx_image_enum_common_t *) penum;
+ return 0;
+ out3:
+ if (penum->mask[1].info)
+ gx_image_end(penum->mask[1].info, false);
+ if (penum->mask[0].info)
+ gx_image_end(penum->mask[0].info, false);
+ out2:
+ if (penum->mask[1].mdev) {
+ gs_closedevice(penum->mask[1].mdev);
+ gs_free_object(mem, penum->mask[1].mdev,
+ "gx_begin_image3x(mask[1].mdev)");
+ }
+ if (penum->mask[0].mdev) {
+ gs_closedevice(penum->mask[0].mdev);
+ gs_free_object(mem, penum->mask[0].mdev,
+ "gx_begin_image3x(mask[0].mdev)");
+ }
+ out1:
+ gs_free_object(mem, penum->mask[0].data, "gx_begin_image3x(mask[0].data)");
+ gs_free_object(mem, penum->mask[1].data, "gx_begin_image3x(mask[1].data)");
+ gs_free_object(mem, penum->pixel.data, "gx_begin_image3x(pixel.data)");
+ out0:
+ gs_free_object(mem, penum, "gx_begin_image3x");
+ return code;
+}
+static bool
+check_image3x_extent(double mask_coeff, double data_coeff)
+{
+ if (mask_coeff == 0)
+ return data_coeff == 0;
+ if (data_coeff == 0 || (mask_coeff > 0) != (data_coeff > 0))
+ return false;
+ return true;
+}
+/*
+ * Check mask parameters.
+ * Reads ppcv->{matrix,corner,rect}, sets pmcv->{matrix,corner,rect} and
+ * pmcs->{InterleaveType,width,height,full_height,depth,data,y,skip}.
+ * If the mask is omitted, sets pmcs->depth = 0 and returns normally.
+ */
+static bool
+check_image3x_mask(const gs_image3x_t *pim, const gs_image3x_mask_t *pimm,
+ const image3x_channel_values_t *ppcv,
+ image3x_channel_values_t *pmcv,
+ image3x_channel_state_t *pmcs, gs_memory_t *mem)
+{
+ int mask_width = pimm->MaskDict.Width, mask_height = pimm->MaskDict.Height;
+ int code;
+
+ if (pimm->MaskDict.BitsPerComponent == 0) { /* mask missing */
+ pmcs->depth = 0;
+ pmcs->InterleaveType = 0; /* not a valid type */
+ return 0;
+ }
+ if (mask_height <= 0)
+ return_error(gs_error_rangecheck);
+ switch (pimm->InterleaveType) {
+ /*case interleave_scan_lines:*/ /* not supported */
+ default:
+ return_error(gs_error_rangecheck);
+ case interleave_chunky:
+ if (mask_width != pim->Width ||
+ mask_height != pim->Height ||
+ pimm->MaskDict.BitsPerComponent != pim->BitsPerComponent ||
+ pim->format != gs_image_format_chunky
+ )
+ return_error(gs_error_rangecheck);
+ break;
+ case interleave_separate_source:
+ switch (pimm->MaskDict.BitsPerComponent) {
+ case 1: case 2: case 4: case 8: case 12: case 16:
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ }
+ if (!check_image3x_extent(pim->ImageMatrix.xx,
+ pimm->MaskDict.ImageMatrix.xx) ||
+ !check_image3x_extent(pim->ImageMatrix.xy,
+ pimm->MaskDict.ImageMatrix.xy) ||
+ !check_image3x_extent(pim->ImageMatrix.yx,
+ pimm->MaskDict.ImageMatrix.yx) ||
+ !check_image3x_extent(pim->ImageMatrix.yy,
+ pimm->MaskDict.ImageMatrix.yy)
+ )
+ return_error(gs_error_rangecheck);
+ if ((code = gs_matrix_invert(&pimm->MaskDict.ImageMatrix, &pmcv->matrix)) < 0 ||
+ (code = gs_point_transform(mask_width, mask_height,
+ &pmcv->matrix, &pmcv->corner)) < 0
+ )
+ return code;
+ if (fabs(ppcv->matrix.tx - pmcv->matrix.tx) >= 0.5 ||
+ fabs(ppcv->matrix.ty - pmcv->matrix.ty) >= 0.5 ||
+ fabs(ppcv->corner.x - pmcv->corner.x) >= 0.5 ||
+ fabs(ppcv->corner.y - pmcv->corner.y) >= 0.5
+ )
+ return_error(gs_error_rangecheck);
+ pmcv->rect.p.x = ppcv->rect.p.x * mask_width / pim->Width;
+ pmcv->rect.p.y = ppcv->rect.p.y * mask_height / pim->Height;
+ pmcv->rect.q.x = (ppcv->rect.q.x * mask_width + pim->Width - 1) /
+ pim->Width;
+ pmcv->rect.q.y = (ppcv->rect.q.y * mask_height + pim->Height - 1) /
+ pim->Height;
+ /* Initialize the channel state in the enumerator. */
+ pmcs->InterleaveType = pimm->InterleaveType;
+ pmcs->width = pmcv->rect.q.x - pmcv->rect.p.x;
+ pmcs->height = pmcv->rect.q.y - pmcv->rect.p.y;
+ pmcs->full_height = pimm->MaskDict.Height;
+ pmcs->depth = pimm->MaskDict.BitsPerComponent;
+ if (pmcs->InterleaveType == interleave_chunky) {
+ /* Allocate a buffer for the data. */
+ pmcs->data =
+ gs_alloc_bytes(mem,
+ (pmcs->width * pimm->MaskDict.BitsPerComponent + 7) >> 3,
+ "gx_begin_image3x(mask data)");
+ if (pmcs->data == 0)
+ return_error(gs_error_VMerror);
+ }
+ pmcs->y = pmcs->skip = 0;
+ return 0;
+}
+
+/*
+ * Return > 0 if we want more data from channel 1 now, < 0 if we want more
+ * from channel 2 now, 0 if we want both.
+ */
+static int
+channel_next(const image3x_channel_state_t *pics1,
+ const image3x_channel_state_t *pics2)
+{
+ /*
+ * The invariant we need to maintain is that we always have at least as
+ * much channel N as channel N+1 data, where N = 0 = opacity, 1 = shape,
+ * and 2 = pixel. I.e., for any two consecutive channels c1 and c2, we
+ * require c1.y / c1.full_height >= c2.y / c2.full_height, or, to avoid
+ * floating point, c1.y * c2.full_height >= c2.y * c1.full_height. We
+ * know this condition is true now; return a value that indicates how to
+ * maintain it.
+ */
+ int h1 = pics1->full_height;
+ int h2 = pics2->full_height;
+ long current = pics1->y * (long)h2 - pics2->y * (long)h1;
+
+#ifdef DEBUG
+ if (current < 0)
+ lprintf4("channel_next invariant fails: %d/%d < %d/%d\n",
+ pics1->y, pics1->full_height,
+ pics2->y, pics2->full_height);
+#endif
+ return ((current -= h1) >= 0 ? -1 :
+ current + h2 >= 0 ? 0 : 1);
+}
+
+/* Define the default implementation of ImageType 3 processing. */
+static IMAGE3X_MAKE_MID_PROC(make_midx_default); /* check prototype */
+static int
+make_midx_default(gx_device **pmidev, gx_device *dev, int width, int height,
+ int depth, gs_memory_t *mem)
+{
+ const gx_device_memory *mdproto = gdev_mem_device_for_bits(depth);
+ gx_device_memory *midev;
+ int code;
+
+ if (width != 0)
+ if (height > max_ulong/width) /* protect against overflow in bitmap size */
+ return_error(gs_error_VMerror);
+ if (mdproto == 0)
+ return_error(gs_error_rangecheck);
+ midev = gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
+ "make_mid_default");
+ if (midev == 0)
+ return_error(gs_error_VMerror);
+ gs_make_mem_device(midev, mdproto, mem, 0, NULL);
+ midev->bitmap_memory = mem;
+ midev->width = width;
+ midev->height = height;
+ check_device_separable((gx_device *)midev);
+ gx_device_fill_in_procs((gx_device *)midev);
+ code = dev_proc(midev, open_device)((gx_device *)midev);
+ if (code < 0) {
+ gs_free_object(mem, midev, "make_midx_default");
+ return code;
+ }
+ midev->is_open = true;
+ dev_proc(midev, fill_rectangle)
+ ((gx_device *)midev, 0, 0, width, height, (gx_color_index)0);
+ *pmidev = (gx_device *)midev;
+ return 0;
+}
+static IMAGE3X_MAKE_MCDE_PROC(make_mcdex_default); /* check prototype */
+static int
+make_mcdex_default(gx_device *dev, const gs_gstate *pgs,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect *prect, const gx_drawing_color *pdcolor,
+ const gx_clip_path *pcpath, gs_memory_t *mem,
+ gx_image_enum_common_t **pinfo,
+ gx_device **pmcdev, gx_device *midev[2],
+ gx_image_enum_common_t *pminfo[2],
+ const gs_int_point origin[2],
+ const gs_image3x_t *pim)
+{
+ /**************** NYI ****************/
+ /*
+ * There is no soft-mask analogue of make_mcde_default, because
+ * soft-mask clipping is a more complicated operation, implemented
+ * by the general transparency code. As a default, we simply ignore
+ * the soft mask. However, we have to create an intermediate device
+ * that can be freed at the end and that simply forwards all calls.
+ * The most convenient device for this purpose is the bbox device.
+ */
+ gx_device_bbox *bbdev;
+ int code;
+ cmm_dev_profile_t *icc_struct;
+
+ code = dev_proc(dev, get_profile)(dev, &icc_struct);
+ if (code < 0) {
+ return(code);
+ }
+
+ bbdev = gs_alloc_struct_immovable(mem, gx_device_bbox, &st_device_bbox,
+ "make_mcdex_default");
+
+ if (bbdev == 0)
+ return_error(gs_error_VMerror);
+
+ gx_device_bbox_init(bbdev, dev, mem);
+
+ bbdev->icc_struct = icc_struct;
+ rc_increment(bbdev->icc_struct);
+
+ gx_device_bbox_fwd_open_close(bbdev, false);
+ code = dev_proc(bbdev, begin_typed_image)
+ ((gx_device *)bbdev, pgs, pmat, pic, prect, pdcolor, pcpath, mem,
+ pinfo);
+ if (code < 0) {
+ gs_free_object(mem, bbdev, "make_mcdex_default");
+ return code;
+ }
+ *pmcdev = (gx_device *)bbdev;
+ return 0;
+}
+static int
+gx_begin_image3x(gx_device * dev,
+ const gs_gstate * pgs, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
+{
+ return gx_begin_image3x_generic(dev, pgs, pmat, pic, prect, pdcolor,
+ pcpath, mem, make_midx_default,
+ make_mcdex_default, pinfo);
+}
+
+/* Process the next piece of an ImageType 3 image. */
+static int
+gx_image3x_plane_data(gx_image_enum_common_t * info,
+ const gx_image_plane_t * planes, int height,
+ int *rows_used)
+{
+ gx_image3x_enum_t *penum = (gx_image3x_enum_t *) info;
+ int pixel_height = penum->pixel.height;
+ int pixel_used = 0;
+ int mask_height[2];
+ int mask_used[2];
+ int h1 = pixel_height - penum->pixel.y;
+ int h;
+ const gx_image_plane_t *pixel_planes;
+ gx_image_plane_t pixel_plane, mask_plane[2];
+ int code = 0;
+ int i, pi = 0;
+ int num_chunky = 0;
+
+ for (i = 0; i < NUM_MASKS; ++i) {
+ int mh = mask_height[i] = penum->mask[i].height;
+
+ mask_plane[i].data = 0;
+ mask_plane[i].raster = 0;
+ mask_used[i] = 0;
+ if (!penum->mask[i].depth)
+ continue;
+ h1 = min(h1, ((mh > penum->mask[i].y) ? (mh - penum->mask[i].y) : mh));
+ if (penum->mask[i].InterleaveType == interleave_chunky)
+ ++num_chunky;
+ }
+ h = min(height, h1);
+ /* Initialized rows_used in case we get an error. */
+ *rows_used = 0;
+
+ if (h <= 0)
+ return 0;
+
+ /* Handle masks from separate sources. */
+ for (i = 0; i < NUM_MASKS; ++i)
+ if (penum->mask[i].InterleaveType == interleave_separate_source) {
+ /*
+ * In order to be able to recover from interruptions, we must
+ * limit separate-source processing to 1 scan line at a time.
+ */
+ if (h > 1)
+ h = 1;
+ mask_plane[i] = planes[pi++];
+ }
+ pixel_planes = &planes[pi];
+
+ /* Handle chunky masks. */
+ if (num_chunky) {
+ int bpc = penum->bpc;
+ int num_components = penum->num_components;
+ int width = penum->pixel.width;
+ /* Pull apart the source data and the mask data. */
+ /* We do this in the simplest (not fastest) way for now. */
+ uint bit_x = bpc * (num_components + num_chunky) * planes[pi].data_x;
+ const byte *sptr = planes[0].data + (bit_x >> 3);
+ int sbit = bit_x & 7;
+ byte *pptr = penum->pixel.data;
+ int pbit = 0;
+ byte pbbyte = (pbit ? (byte)(*pptr & (0xff00 >> pbit)) : 0);
+ byte *dptr[NUM_MASKS];
+ int dbit[NUM_MASKS];
+ byte dbbyte[NUM_MASKS];
+ int depth[NUM_MASKS];
+ int x;
+
+ if (h > 1) {
+ /* Do the operation one row at a time. */
+ h = 1;
+ }
+ for (i = 0; i < NUM_MASKS; ++i)
+ if (penum->mask[i].data) {
+ depth[i] = penum->mask[i].depth;
+ mask_plane[i].data = dptr[i] = penum->mask[i].data;
+ mask_plane[i].data_x = 0;
+ /* raster doesn't matter */
+ dbit[i] = 0;
+ dbbyte[i] = 0;
+ } else
+ depth[i] = 0;
+ pixel_plane.data = pptr;
+ pixel_plane.data_x = 0;
+ /* raster doesn't matter */
+ pixel_planes = &pixel_plane;
+ for (x = 0; x < width; ++x) {
+ uint value;
+
+ for (i = 0; i < NUM_MASKS; ++i)
+ if (depth[i]) {
+ if (sample_load_next12(&value, &sptr, &sbit, bpc) < 0)
+ return_error(gs_error_rangecheck);
+ if (sample_store_next12(value, &dptr[i], &dbit[i], depth[i],
+ &dbbyte[i]) < 0)
+ return_error(gs_error_rangecheck);
+ }
+ for (i = 0; i < num_components; ++i) {
+ if (sample_load_next12(&value, &sptr, &sbit, bpc) < 0)
+ return_error(gs_error_rangecheck);
+ if (sample_store_next12(value, &pptr, &pbit, bpc, &pbbyte) < 0)
+ return_error(gs_error_rangecheck);
+ }
+ }
+ for (i = 0; i < NUM_MASKS; ++i)
+ if (penum->mask[i].data) {
+ sample_store_flush(dptr[i], dbit[i], dbbyte[i]);
+ }
+ sample_store_flush(pptr, pbit, pbbyte);
+ }
+ /*
+ * Process the mask data first, so it will set up the mask
+ * device for clipping the pixel data.
+ */
+ for (i = 0; i < NUM_MASKS; ++i)
+ if (mask_plane[i].data) {
+ /*
+ * If, on the last call, we processed some mask rows
+ * successfully but processing the pixel rows was interrupted,
+ * we set rows_used to indicate the number of pixel rows
+ * processed (since there is no way to return two rows_used
+ * values). If this happened, some mask rows may get presented
+ * again. We must skip over them rather than processing them
+ * again.
+ */
+ int skip = penum->mask[i].skip;
+
+ if (skip >= h) {
+ penum->mask[i].skip = skip - (mask_used[i] = h);
+ } else {
+ int mask_h = h - skip;
+
+ mask_plane[i].data += skip * mask_plane[i].raster;
+ penum->mask[i].skip = 0;
+ code = gx_image_plane_data_rows(penum->mask[i].info,
+ &mask_plane[i],
+ mask_h, &mask_used[i]);
+ mask_used[i] += skip;
+ }
+ *rows_used = mask_used[i];
+ penum->mask[i].y += mask_used[i];
+ if (code < 0)
+ return code;
+ }
+ if (pixel_planes[0].data) {
+ /*
+ * If necessary, flush any buffered mask data to the mask clipping
+ * device.
+ */
+ for (i = 0; i < NUM_MASKS; ++i)
+ if (penum->mask[i].info)
+ gx_image_flush(penum->mask[i].info);
+ code = gx_image_plane_data_rows(penum->pixel.info, pixel_planes, h,
+ &pixel_used);
+ /*
+ * There isn't any way to set rows_used if different amounts of
+ * the mask and pixel data were used. Fake it.
+ */
+ *rows_used = pixel_used;
+ /*
+ * Don't return code yet: we must account for the fact that
+ * some mask data may have been processed.
+ */
+ penum->pixel.y += pixel_used;
+ if (code < 0) {
+ /*
+ * We must prevent the mask data from being processed again.
+ * We rely on the fact that h > 1 is only possible if the
+ * mask and pixel data have the same Y scaling.
+ */
+ for (i = 0; i < NUM_MASKS; ++i)
+ if (mask_used[i] > pixel_used) {
+ int skip = mask_used[i] - pixel_used;
+
+ penum->mask[i].skip = skip;
+ penum->mask[i].y -= skip;
+ mask_used[i] = pixel_used;
+ }
+ }
+ }
+ if_debug7m('b', penum->memory,
+ "[b]image3x h=%d %sopacity.y=%d %sopacity.y=%d %spixel.y=%d\n",
+ h, (mask_plane[0].data ? "+" : ""), penum->mask[0].y,
+ (mask_plane[1].data ? "+" : ""), penum->mask[1].y,
+ (pixel_planes[0].data ? "+" : ""), penum->pixel.y);
+ if (penum->mask[0].depth == 0 || penum->mask[0].y >= penum->mask[0].height) {
+ if (penum->mask[1].depth == 0 || penum->mask[1].y >= penum->mask[1].height) {
+ if (penum->pixel.y >= penum->pixel.height) {
+ return 1;
+ }
+ }
+ }
+ /*
+ * The mask may be complete (gx_image_plane_data_rows returned 1),
+ * but there may still be pixel rows to go, so don't return 1 here.
+ */
+ return (code < 0 ? code : 0);
+}
+
+/* Flush buffered data. */
+static int
+gx_image3x_flush(gx_image_enum_common_t * info)
+{
+ gx_image3x_enum_t * const penum = (gx_image3x_enum_t *) info;
+ int code = gx_image_flush(penum->mask[0].info);
+
+ if (code >= 0)
+ code = gx_image_flush(penum->mask[1].info);
+ if (code >= 0)
+ code = gx_image_flush(penum->pixel.info);
+ return code;
+}
+
+/* Determine which data planes are wanted. */
+static bool
+gx_image3x_planes_wanted(const gx_image_enum_common_t * info, byte *wanted)
+{
+ const gx_image3x_enum_t * const penum = (const gx_image3x_enum_t *) info;
+ /*
+ * We always want at least as much of the mask(s) to be filled as the
+ * pixel data.
+ */
+ bool
+ sso = penum->mask[0].InterleaveType == interleave_separate_source,
+ sss = penum->mask[1].InterleaveType == interleave_separate_source;
+
+ if (sso & sss) {
+ /* Both masks have separate sources. */
+ int mask_next = channel_next(&penum->mask[1], &penum->pixel);
+
+ memset(wanted + 2, (mask_next <= 0 ? 0xff : 0), info->num_planes - 2);
+ wanted[1] = (mask_next >= 0 ? 0xff : 0);
+ if (wanted[1]) {
+ mask_next = channel_next(&penum->mask[0], &penum->mask[1]);
+ wanted[0] = mask_next >= 0;
+ } else
+ wanted[0] = 0;
+ return false; /* see below */
+ } else if (sso | sss) {
+ /* Only one separate source. */
+ const image3x_channel_state_t *pics =
+ (sso ? &penum->mask[0] : &penum->mask[1]);
+ int mask_next = channel_next(pics, &penum->pixel);
+
+ wanted[0] = (mask_next >= 0 ? 0xff : 0);
+ memset(wanted + 1, (mask_next <= 0 ? 0xff : 0), info->num_planes - 1);
+ /*
+ * In principle, wanted will always be true for both mask and pixel
+ * data if the full_heights are equal. Unfortunately, even in this
+ * case, processing may be interrupted after a mask row has been
+ * passed to the underlying image processor but before the data row
+ * has been passed, in which case pixel data will be 'wanted', but
+ * not mask data, for the next call. Therefore, we must return
+ * false.
+ */
+ return false
+ /*(next == 0 &&
+ pics->full_height == penum->pixel.full_height)*/;
+ } else {
+ /* Everything is chunky, only 1 plane. */
+ wanted[0] = 0xff;
+ return true;
+ }
+}
+
+/* Clean up after processing an ImageType 3x image. */
+static int
+gx_image3x_end_image(gx_image_enum_common_t * info, bool draw_last)
+{
+ gx_image3x_enum_t *penum = (gx_image3x_enum_t *) info;
+ gs_memory_t *mem = penum->memory;
+ gx_device *mdev0 = penum->mask[0].mdev;
+ int ocode =
+ (penum->mask[0].info ? gx_image_end(penum->mask[0].info, draw_last) :
+ 0);
+ gx_device *mdev1 = penum->mask[1].mdev;
+ int scode =
+ (penum->mask[1].info ? gx_image_end(penum->mask[1].info, draw_last) :
+ 0);
+ gx_device *pcdev = penum->pcdev;
+ int pcode = gx_image_end(penum->pixel.info, draw_last);
+
+ rc_decrement(pcdev->icc_struct, "gx_image3x_end_image(pcdev->icc_struct)");
+ pcdev->icc_struct = NULL;
+
+ gs_closedevice(pcdev);
+ if (mdev0)
+ gs_closedevice(mdev0);
+ if (mdev1)
+ gs_closedevice(mdev1);
+ gs_free_object(mem, penum->mask[0].data,
+ "gx_image3x_end_image(mask[0].data)");
+ gs_free_object(mem, penum->mask[1].data,
+ "gx_image3x_end_image(mask[1].data)");
+ gs_free_object(mem, penum->pixel.data,
+ "gx_image3x_end_image(pixel.data)");
+ gs_free_object(mem, pcdev, "gx_image3x_end_image(pcdev)");
+ gs_free_object(mem, mdev0, "gx_image3x_end_image(mask[0].mdev)");
+ gs_free_object(mem, mdev1, "gx_image3x_end_image(mask[1].mdev)");
+ gx_image_free_enum(&info);
+ return (pcode < 0 ? pcode : scode < 0 ? scode : ocode);
+}