xref: /plan9/sys/src/cmd/gs/src/gsimage.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1996, 1997, 1998, 1999, 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: gsimage.c,v 1.15 2005/06/21 16:50:50 igor Exp $ */
18 /* Image setup procedures for Ghostscript library */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gsstruct.h"
23 #include "gscspace.h"
24 #include "gsmatrix.h"		/* for gsiparam.h */
25 #include "gsimage.h"
26 #include "gxarith.h"		/* for igcd */
27 #include "gxdevice.h"
28 #include "gxiparam.h"
29 #include "gxpath.h"		/* for gx_effective_clip_path */
30 #include "gzstate.h"
31 
32 
33 /*
34   The main internal invariant for the gs_image machinery is
35   straightforward.  The state consists primarily of N plane buffers
36   (planes[]).
37 */
38 typedef struct image_enum_plane_s {
39 /*
40   The state of each plane consists of:
41 
42   - A row buffer, aligned and (logically) large enough to hold one scan line
43     for that plane.  (It may have to be reallocated if the plane width or
44     depth changes.)  A row buffer is "full" if it holds exactly a full scan
45     line.
46 */
47     gs_string row;
48 /*
49   - A position within the row buffer, indicating how many initial bytes are
50     occupied.
51 */
52     uint pos;
53 /*
54   - A (retained) source string, which may be empty (size = 0).
55 */
56     gs_const_string source;
57 } image_enum_plane_t;
58 /*
59   The possible states for each plane do not depend on the state of any other
60   plane.  Either:
61 
62   - pos = 0, source.size = 0.
63 
64   - If the underlying image processor says the plane is currently wanted,
65     either:
66 
67     - pos = 0, source.size >= one full row of data for this plane.  This
68       case allows us to avoid copying the data from the source string to the
69       row buffer if the client is providing data in blocks of at least one
70       scan line.
71 
72     - pos = full, source.size may have any value.
73 
74     - pos > 0, pos < full, source.size = 0;
75 
76   - If the underlying image processor says the plane is not currently
77     wanted:
78 
79     - pos = 0, source.size may have any value.
80 
81   This invariant holds at the beginning and end of each call on
82   gs_image_next_planes.  Note that for each plane, the "plane wanted" status
83   and size of a full row may change after each call of plane_data.  As
84   documented in gxiparam.h, we assume that a call of plane_data can only
85   change a plane's status from "wanted" to "not wanted", or change the width
86   or depth of a wanted plane, if data for that plane was actually supplied
87   (and used).
88 */
89 
90 /* Define the enumeration state for this interface layer. */
91 /*typedef struct gs_image_enum_s gs_image_enum; *//* in gsimage.h */
92 struct gs_image_enum_s {
93     /* The following are set at initialization time. */
94     gs_memory_t *memory;
95     gx_device *dev;		/* if 0, just skip over the data */
96     gx_image_enum_common_t *info;	/* driver bookkeeping structure */
97     int num_planes;
98     int height;
99     bool wanted_varies;
100     /* The following are updated dynamically. */
101     int plane_index;		/* index of next plane of data, */
102 				/* only needed for gs_image_next */
103     int y;
104     bool error;
105     byte wanted[gs_image_max_planes]; /* cache gx_image_planes_wanted */
106     byte client_wanted[gs_image_max_planes]; /* see gsimage.h */
107     image_enum_plane_t planes[gs_image_max_planes]; /* see above */
108     /*
109      * To reduce setup for transferring complete rows, we maintain a
110      * partially initialized parameter array for gx_image_plane_data_rows.
111      * The data member is always set just before calling
112      * gx_image_plane_data_rows; the data_x and raster members are reset
113      * when needed.
114      */
115     gx_image_plane_t image_planes[gs_image_max_planes];
116 };
117 
118 gs_private_st_composite(st_gs_image_enum, gs_image_enum, "gs_image_enum",
119 			gs_image_enum_enum_ptrs, gs_image_enum_reloc_ptrs);
120 #define gs_image_enum_num_ptrs 2
121 
122 /* GC procedures */
123 private
ENUM_PTRS_WITH(gs_image_enum_enum_ptrs,gs_image_enum * eptr)124 ENUM_PTRS_WITH(gs_image_enum_enum_ptrs, gs_image_enum *eptr)
125 {
126     /* Enumerate the data planes. */
127     index -= gs_image_enum_num_ptrs;
128     if (index < eptr->num_planes)
129 	ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].source);
130     index -= eptr->num_planes;
131     if (index < eptr->num_planes)
132 	ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].row);
133     return 0;
134 }
135 ENUM_PTR(0, gs_image_enum, dev);
136 ENUM_PTR(1, gs_image_enum, info);
137 ENUM_PTRS_END
RELOC_PTRS_WITH(gs_image_enum_reloc_ptrs,gs_image_enum * eptr)138 private RELOC_PTRS_WITH(gs_image_enum_reloc_ptrs, gs_image_enum *eptr)
139 {
140     int i;
141 
142     RELOC_PTR(gs_image_enum, dev);
143     RELOC_PTR(gs_image_enum, info);
144     for (i = 0; i < eptr->num_planes; i++)
145 	RELOC_CONST_STRING_PTR(gs_image_enum, planes[i].source);
146     for (i = 0; i < eptr->num_planes; i++)
147 	RELOC_STRING_PTR(gs_image_enum, planes[i].row);
148 }
149 RELOC_PTRS_END
150 
151 /* Create an image enumerator given image parameters and a graphics state. */
152 int
gs_image_begin_typed(const gs_image_common_t * pic,gs_state * pgs,bool uses_color,gx_image_enum_common_t ** ppie)153 gs_image_begin_typed(const gs_image_common_t * pic, gs_state * pgs,
154 		     bool uses_color, gx_image_enum_common_t ** ppie)
155 {
156     gx_device *dev = gs_currentdevice(pgs);
157     gx_clip_path *pcpath;
158     int code = gx_effective_clip_path(pgs, &pcpath);
159 
160     if (code < 0)
161 	return code;
162     if (uses_color) {
163 	gx_set_dev_color(pgs);
164         code = gs_state_color_load(pgs);
165         if (code < 0)
166 	    return code;
167     }
168     return gx_device_begin_typed_image(dev, (const gs_imager_state *)pgs,
169 		NULL, pic, NULL, pgs->dev_color, pcpath, pgs->memory, ppie);
170 }
171 
172 /* Allocate an image enumerator. */
173 private void
image_enum_init(gs_image_enum * penum)174 image_enum_init(gs_image_enum * penum)
175 {
176     /* Clean pointers for GC. */
177     penum->info = 0;
178     penum->dev = 0;
179     penum->plane_index = 0;
180     penum->num_planes = 0;
181 }
182 gs_image_enum *
gs_image_enum_alloc(gs_memory_t * mem,client_name_t cname)183 gs_image_enum_alloc(gs_memory_t * mem, client_name_t cname)
184 {
185     gs_image_enum *penum =
186 	gs_alloc_struct(mem, gs_image_enum, &st_gs_image_enum, cname);
187 
188     if (penum != 0) {
189 	penum->memory = mem;
190 	image_enum_init(penum);
191     }
192     return penum;
193 }
194 
195 /* Start processing an ImageType 1 image. */
196 int
gs_image_init(gs_image_enum * penum,const gs_image_t * pim,bool multi,gs_state * pgs)197 gs_image_init(gs_image_enum * penum, const gs_image_t * pim, bool multi,
198 	      gs_state * pgs)
199 {
200     gs_image_t image;
201     gx_image_enum_common_t *pie;
202     int code;
203 
204     image = *pim;
205     if (image.ImageMask) {
206 	image.ColorSpace = NULL;
207 	if (pgs->in_cachedevice <= 1)
208 	    image.adjust = false;
209     } else {
210 	if (pgs->in_cachedevice)
211 	    return_error(gs_error_undefined);
212 	if (image.ColorSpace == NULL) {
213             /* parameterless color space - no re-entrancy problems */
214             static gs_color_space cs;
215 
216             /*
217              * Mutiple initialization of a DeviceGray color space is
218              * not harmful, as the space has no parameters. Use of a
219              * non-current color space is potentially incorrect, but
220              * it appears this case doesn't arise.
221              */
222             gs_cspace_init_DeviceGray(pgs->memory, &cs);
223 	    image.ColorSpace = &cs;
224         }
225     }
226     code = gs_image_begin_typed((const gs_image_common_t *)&image, pgs,
227 				image.ImageMask | image.CombineWithColor,
228 				&pie);
229     if (code < 0)
230 	return code;
231     return gs_image_enum_init(penum, pie, (const gs_data_image_t *)&image,
232 			      pgs);
233 }
234 
235 /*
236  * Return the number of bytes of data per row for a given plane.
237  */
238 inline uint
gs_image_bytes_per_plane_row(const gs_image_enum * penum,int plane)239 gs_image_bytes_per_plane_row(const gs_image_enum * penum, int plane)
240 {
241     const gx_image_enum_common_t *pie = penum->info;
242 
243     return (pie->plane_widths[plane] * pie->plane_depths[plane] + 7) >> 3;
244 }
245 
246 /* Cache information when initializing, or after transferring plane data. */
247 private void
cache_planes(gs_image_enum * penum)248 cache_planes(gs_image_enum *penum)
249 {
250     int i;
251 
252     if (penum->wanted_varies) {
253 	penum->wanted_varies =
254 	    !gx_image_planes_wanted(penum->info, penum->wanted);
255 	for (i = 0; i < penum->num_planes; ++i)
256 	    if (penum->wanted[i])
257 		penum->image_planes[i].raster =
258 		    gs_image_bytes_per_plane_row(penum, i);
259 	    else
260 		penum->image_planes[i].data = 0;
261     }
262 }
263 /* Advance to the next wanted plane. */
264 private void
next_plane(gs_image_enum * penum)265 next_plane(gs_image_enum *penum)
266 {
267     int px = penum->plane_index;
268 
269     do {
270 	if (++px == penum->num_planes)
271 	    px = 0;
272     } while (!penum->wanted[px]);
273     penum->plane_index = px;
274 }
275 /*
276  * Initialize plane_index and (if appropriate) wanted and
277  * wanted_varies at the beginning of a group of planes.
278  */
279 private void
begin_planes(gs_image_enum * penum)280 begin_planes(gs_image_enum *penum)
281 {
282     cache_planes(penum);
283     penum->plane_index = -1;
284     next_plane(penum);
285 }
286 
287 static int
gs_image_common_init(gs_image_enum * penum,gx_image_enum_common_t * pie,const gs_data_image_t * pim,gx_device * dev)288 gs_image_common_init(gs_image_enum * penum, gx_image_enum_common_t * pie,
289 	    const gs_data_image_t * pim, gx_device * dev)
290 {
291     /*
292      * HACK : For a compatibility with gs_image_cleanup_and_free_enum,
293      * penum->memory must be initialized in advance
294      * with the memory heap that owns *penum.
295      */
296     int i;
297 
298     if (pim->Width == 0 || pim->Height == 0) {
299 	gx_image_end(pie, false);
300 	return 1;
301     }
302     image_enum_init(penum);
303     penum->dev = dev;
304     penum->info = pie;
305     penum->num_planes = pie->num_planes;
306     /*
307      * Note that for ImageType 3 InterleaveType 2, penum->height (the
308      * expected number of data rows) differs from pim->Height (the height
309      * of the source image in scan lines).  This doesn't normally cause
310      * any problems, because penum->height is not used to determine when
311      * all the data has been processed: that is up to the plane_data
312      * procedure for the specific image type.
313      */
314     penum->height = pim->Height;
315     for (i = 0; i < pie->num_planes; ++i) {
316 	penum->planes[i].pos = 0;
317 	penum->planes[i].source.size = 0;	/* for gs_image_next_planes */
318 	penum->planes[i].row.data = 0; /* for GC */
319 	penum->planes[i].row.size = 0; /* ditto */
320 	penum->image_planes[i].data_x = 0; /* just init once, never changes */
321     }
322     /* Initialize the dynamic part of the state. */
323     penum->y = 0;
324     penum->error = false;
325     penum->wanted_varies = true;
326     begin_planes(penum);
327     return 0;
328 }
329 
330 /* Initialize an enumerator for a general image.
331    penum->memory must be initialized in advance.
332 */
333 int
gs_image_enum_init(gs_image_enum * penum,gx_image_enum_common_t * pie,const gs_data_image_t * pim,gs_state * pgs)334 gs_image_enum_init(gs_image_enum * penum, gx_image_enum_common_t * pie,
335 		   const gs_data_image_t * pim, gs_state *pgs)
336 {
337     return gs_image_common_init(penum, pie, pim,
338 				(pgs->in_charpath ? NULL :
339 				 gs_currentdevice_inline(pgs)));
340 }
341 
342 /* Return the set of planes wanted. */
343 const byte *
gs_image_planes_wanted(gs_image_enum * penum)344 gs_image_planes_wanted(gs_image_enum *penum)
345 {
346     int i;
347 
348     /*
349      * A plane is wanted at this interface if it is wanted by the
350      * underlying machinery and has no buffered or retained data.
351      */
352     for (i = 0; i < penum->num_planes; ++i)
353 	penum->client_wanted[i] =
354 	    (penum->wanted[i] &&
355 	     penum->planes[i].pos + penum->planes[i].source.size <
356 	       penum->image_planes[i].raster);
357     return penum->client_wanted;
358 }
359 
360 /*
361  * Return the enumerator memory used for allocating the row buffers.
362  * Because some PostScript files use save/restore within an image data
363  * reading procedure, this must be a stable allocator.
364  */
365 private gs_memory_t *
gs_image_row_memory(const gs_image_enum * penum)366 gs_image_row_memory(const gs_image_enum *penum)
367 {
368     return gs_memory_stable(penum->memory);
369 }
370 
371 /* Free the row buffers when cleaning up. */
372 private void
free_row_buffers(gs_image_enum * penum,int num_planes,client_name_t cname)373 free_row_buffers(gs_image_enum *penum, int num_planes, client_name_t cname)
374 {
375     int i;
376 
377     for (i = num_planes - 1; i >= 0; --i) {
378 	if_debug3('b', "[b]free plane %d row (0x%lx,%u)\n",
379 		  i, (ulong)penum->planes[i].row.data,
380 		  penum->planes[i].row.size);
381 	gs_free_string(gs_image_row_memory(penum), penum->planes[i].row.data,
382 		       penum->planes[i].row.size, cname);
383 	penum->planes[i].row.data = 0;
384 	penum->planes[i].row.size = 0;
385     }
386 }
387 
388 /* Process the next piece of an image. */
389 int
gs_image_next(gs_image_enum * penum,const byte * dbytes,uint dsize,uint * pused)390 gs_image_next(gs_image_enum * penum, const byte * dbytes, uint dsize,
391 	      uint * pused)
392 {
393     int px = penum->plane_index;
394     int num_planes = penum->num_planes;
395     int i, code;
396     uint used[gs_image_max_planes];
397     gs_const_string plane_data[gs_image_max_planes];
398 
399     if (penum->planes[px].source.size != 0)
400 	return_error(gs_error_rangecheck);
401     for (i = 0; i < num_planes; i++)
402 	plane_data[i].size = 0;
403     plane_data[px].data = dbytes;
404     plane_data[px].size = dsize;
405     penum->error = false;
406     code = gs_image_next_planes(penum, plane_data, used);
407     *pused = used[px];
408     if (code >= 0)
409 	next_plane(penum);
410     return code;
411 }
412 
413 int
gs_image_next_planes(gs_image_enum * penum,gs_const_string * plane_data,uint * used)414 gs_image_next_planes(gs_image_enum * penum,
415 		     gs_const_string *plane_data /*[num_planes]*/,
416 		     uint *used /*[num_planes]*/)
417 {
418     const int num_planes = penum->num_planes;
419     int i;
420     int code = 0;
421 
422 #ifdef DEBUG
423     if (gs_debug_c('b')) {
424 	int pi;
425 
426 	for (pi = 0; pi < num_planes; ++pi)
427 	    dprintf6("[b]plane %d source=0x%lx,%u pos=%u data=0x%lx,%u\n",
428 		     pi, (ulong)penum->planes[pi].source.data,
429 		     penum->planes[pi].source.size, penum->planes[pi].pos,
430 		     (ulong)plane_data[pi].data, plane_data[pi].size);
431     }
432 #endif
433     for (i = 0; i < num_planes; ++i) {
434         used[i] = 0;
435 	if (penum->wanted[i] && plane_data[i].size != 0) {
436 	    penum->planes[i].source.size = plane_data[i].size;
437 	    penum->planes[i].source.data = plane_data[i].data;
438 	}
439     }
440     for (;;) {
441 	/* If wanted can vary, only transfer 1 row at a time. */
442 	int h = (penum->wanted_varies ? 1 : max_int);
443 
444 	/* Move partial rows from source[] to row[]. */
445 	for (i = 0; i < num_planes; ++i) {
446 	    int pos, size;
447 	    uint raster;
448 
449 	    if (!penum->wanted[i])
450 		continue;	/* skip unwanted planes */
451 	    pos = penum->planes[i].pos;
452 	    size = penum->planes[i].source.size;
453 	    raster = penum->image_planes[i].raster;
454 	    if (size > 0) {
455 		if (pos < raster && (pos != 0 || size < raster)) {
456 		    /* Buffer a partial row. */
457 		    int copy = min(size, raster - pos);
458 		    uint old_size = penum->planes[i].row.size;
459 
460 		    /* Make sure the row buffer is fully allocated. */
461 		    if (raster > old_size) {
462 			gs_memory_t *mem = gs_image_row_memory(penum);
463 			byte *old_data = penum->planes[i].row.data;
464 			byte *row =
465 			    (old_data == 0 ?
466 			     gs_alloc_string(mem, raster,
467 					     "gs_image_next(row)") :
468 			     gs_resize_string(mem, old_data, old_size, raster,
469 					      "gs_image_next(row)"));
470 
471 			if_debug5('b', "[b]plane %d row (0x%lx,%u) => (0x%lx,%u)\n",
472 				  i, (ulong)old_data, old_size,
473 				  (ulong)row, raster);
474 			if (row == 0) {
475 			    code = gs_note_error(gs_error_VMerror);
476 			    free_row_buffers(penum, i, "gs_image_next(row)");
477 			    break;
478 			}
479 			penum->planes[i].row.data = row;
480 			penum->planes[i].row.size = raster;
481 		    }
482 		    memcpy(penum->planes[i].row.data + pos,
483 			   penum->planes[i].source.data, copy);
484 		    penum->planes[i].source.data += copy;
485 		    penum->planes[i].source.size = size -= copy;
486 		    penum->planes[i].pos = pos += copy;
487 		    used[i] += copy;
488 		}
489 	    }
490 	    if (h == 0)
491 		continue;	/* can't transfer any data this cycle */
492 	    if (pos == raster) {
493 		/*
494 		 * This plane will be transferred from the row buffer,
495 		 * so we can only transfer one row.
496 		 */
497 		h = min(h, 1);
498 		penum->image_planes[i].data = penum->planes[i].row.data;
499 	    } else if (pos == 0 && size >= raster) {
500 		/* We can transfer 1 or more planes from the source. */
501 		h = min(h, size / raster);
502 		penum->image_planes[i].data = penum->planes[i].source.data;
503 	    } else
504 		h = 0;		/* not enough data in this plane */
505 	}
506 	if (h == 0 || code != 0)
507 	    break;
508 	/* Pass rows to the device. */
509 	if (penum->dev == 0) {
510 	    /*
511 	     * ****** NOTE: THE FOLLOWING IS NOT CORRECT FOR ImageType 3
512 	     * ****** InterleaveType 2, SINCE MASK HEIGHT AND IMAGE HEIGHT
513 	     * ****** MAY DIFFER (BY AN INTEGER FACTOR).  ALSO, plane_depths[0]
514 	     * ****** AND plane_widths[0] ARE NOT UPDATED.
515 	 */
516 	    if (penum->y + h < penum->height)
517 		code = 0;
518 	    else
519 		h = penum->height - penum->y, code = 1;
520 	} else {
521 	    code = gx_image_plane_data_rows(penum->info, penum->image_planes,
522 					    h, &h);
523 	    if_debug2('b', "[b]used %d, code=%d\n", h, code);
524 	    penum->error = code < 0;
525 	}
526 	penum->y += h;
527 	/* Update positions and sizes. */
528 	if (h == 0)
529 	    break;
530 	for (i = 0; i < num_planes; ++i) {
531 	    int count;
532 
533 	    if (!penum->wanted[i])
534 		continue;
535 	    count = penum->image_planes[i].raster * h;
536 	    if (penum->planes[i].pos) {
537 		/* We transferred the row from the row buffer. */
538 		penum->planes[i].pos = 0;
539 	    } else {
540 		/* We transferred the row(s) from the source. */
541 		penum->planes[i].source.data += count;
542 		penum->planes[i].source.size -= count;
543 		used[i] += count;
544 	    }
545 	}
546 	cache_planes(penum);
547 	if (code > 0)
548 	    break;
549     }
550     /* Return the retained data pointers. */
551     for (i = 0; i < num_planes; ++i)
552 	plane_data[i] = penum->planes[i].source;
553     return code;
554 }
555 
556 /* Clean up after processing an image. */
557 int
gs_image_cleanup(gs_image_enum * penum)558 gs_image_cleanup(gs_image_enum * penum)
559 {
560     int code = 0;
561 
562     free_row_buffers(penum, penum->num_planes, "gs_image_cleanup(row)");
563     if (penum->info != 0)
564         code = gx_image_end(penum->info, !penum->error);
565     /* Don't free the local enumerator -- the client does that. */
566     return code;
567 }
568 
569 /* Clean up after processing an image and free the enumerator. */
570 int
gs_image_cleanup_and_free_enum(gs_image_enum * penum)571 gs_image_cleanup_and_free_enum(gs_image_enum * penum)
572 {
573     int code = gs_image_cleanup(penum);
574 
575     gs_free_object(penum->memory, penum, "gs_image_cleanup_and_free_enum");
576     return code;
577 }
578