Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_fixcpp/sys/src/cmd/gs/src/gximag3x.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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