xref: /plan9-contrib/sys/src/cmd/gs/src/gsdevmem.c (revision 6a9fc400c33447ef5e1cda7185cb4de2c8e8010e)
1 /* Copyright (C) 1995, 1999 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gsdevmem.c,v 1.2 2000/09/19 19:00:27 lpd Exp $ */
20 /* Memory device creation for Ghostscript library */
21 #include "math_.h"		/* for fabs */
22 #include "memory_.h"
23 #include "gx.h"
24 #include "gserrors.h"
25 #include "gsdevice.h"		/* for prototypes */
26 #include "gxarith.h"
27 #include "gxdevice.h"
28 #include "gxdevmem.h"
29 
30 /* Make a memory (image) device. */
31 /* If colors_size = -16, -24, or -32, this is a true-color device; */
32 /* otherwise, colors_size is the size of the palette in bytes */
33 /* (2^N for gray scale, 3*2^N for RGB color). */
34 /* We separate device allocation and initialization at customer request. */
35 int
36 gs_initialize_wordimagedevice(gx_device_memory * new_dev, const gs_matrix * pmat,
37 	      uint width, uint height, const byte * colors, int colors_size,
38 		    bool word_oriented, bool page_device, gs_memory_t * mem)
39 {
40     const gx_device_memory *proto_dev;
41     int palette_count = colors_size;
42     int num_components = 1;
43     int pcount;
44     int bits_per_pixel;
45     float x_pixels_per_unit, y_pixels_per_unit;
46     byte palette[256 * 3];
47     bool has_color;
48 
49     switch (colors_size) {
50 	case 3 * 2:
51 	    palette_count = 2;
52 	    num_components = 3;
53 	case 2:
54 	    bits_per_pixel = 1;
55 	    break;
56 	case 3 * 4:
57 	    palette_count = 4;
58 	    num_components = 3;
59 	case 4:
60 	    bits_per_pixel = 2;
61 	    break;
62 	case 3 * 16:
63 	    palette_count = 16;
64 	    num_components = 3;
65 	case 16:
66 	    bits_per_pixel = 4;
67 	    break;
68 	case 3 * 256:
69 	    palette_count = 256;
70 	    num_components = 3;
71 	case 256:
72 	    bits_per_pixel = 8;
73 	    break;
74 	case -16:
75 	    bits_per_pixel = 16;
76 	    palette_count = 0;
77 	    break;
78 	case -24:
79 	    bits_per_pixel = 24;
80 	    palette_count = 0;
81 	    break;
82 	case -32:
83 	    bits_per_pixel = 32;
84 	    palette_count = 0;
85 	    break;
86 	default:
87 	    return_error(gs_error_rangecheck);
88     }
89     proto_dev = (word_oriented ?
90 		 gdev_mem_word_device_for_bits(bits_per_pixel) :
91 		 gdev_mem_device_for_bits(bits_per_pixel));
92     if (proto_dev == 0)		/* no suitable device */
93 	return_error(gs_error_rangecheck);
94     pcount = palette_count * 3;
95     /* Check to make sure the palette contains white and black, */
96     /* and, if it has any colors, the six primaries. */
97     if (bits_per_pixel <= 8) {
98 	const byte *p;
99 	byte *q;
100 	int primary_mask = 0;
101 	int i;
102 
103 	has_color = false;
104 	for (i = 0, p = colors, q = palette;
105 	     i < palette_count; i++, q += 3
106 	    ) {
107 	    int mask = 1;
108 
109 	    switch (num_components) {
110 		case 1:	/* gray */
111 		    q[0] = q[1] = q[2] = *p++;
112 		    break;
113 		default /* case 3 */ :		/* RGB */
114 		    q[0] = p[0], q[1] = p[1], q[2] = p[2];
115 		    p += 3;
116 	    }
117 #define shift_mask(b,n)\
118   switch ( b ) { case 0xff: mask <<= n; case 0: break; default: mask = 0; }
119 	    shift_mask(q[0], 4);
120 	    shift_mask(q[1], 2);
121 	    shift_mask(q[2], 1);
122 #undef shift_mask
123 	    primary_mask |= mask;
124 	    if (q[0] != q[1] || q[0] != q[2])
125 		has_color = true;
126 	}
127 	switch (primary_mask) {
128 	    case 129:		/* just black and white */
129 		if (has_color)	/* color but no primaries */
130 		    return_error(gs_error_rangecheck);
131 	    case 255:		/* full color */
132 		break;
133 	    default:
134 		return_error(gs_error_rangecheck);
135 	}
136     } else
137 	has_color = true;
138     /*
139      * The initial transformation matrix must map 1 user unit to
140      * 1/72".  Let W and H be the width and height in pixels, and
141      * assume the initial matrix is of the form [A 0 0 B X Y].
142      * Then the size of the image in user units is (W/|A|,H/|B|),
143      * hence the size in inches is ((W/|A|)/72,(H/|B|)/72), so
144      * the number of pixels per inch is
145      * (W/((W/|A|)/72),H/((H/|B|)/72)), or (|A|*72,|B|*72).
146      * Similarly, if the initial matrix is [0 A B 0 X Y] for a 90
147      * or 270 degree rotation, the size of the image in user
148      * units is (W/|B|,H/|A|), so the pixels per inch are
149      * (|B|*72,|A|*72).  We forbid non-orthogonal transformation
150      * matrices.
151      */
152     if (is_fzero2(pmat->xy, pmat->yx))
153 	x_pixels_per_unit = pmat->xx, y_pixels_per_unit = pmat->yy;
154     else if (is_fzero2(pmat->xx, pmat->yy))
155 	x_pixels_per_unit = pmat->yx, y_pixels_per_unit = pmat->xy;
156     else
157 	return_error(gs_error_undefinedresult);
158     /* All checks done, initialize the device. */
159     if (bits_per_pixel == 1) {
160 	/* Determine the polarity from the palette. */
161 	gs_make_mem_device(new_dev, proto_dev, mem,
162 			   (page_device ? 1 : -1), 0);
163 	/* This is somewhat bogus, but does the right thing */
164 	/* in the only cases we care about. */
165 	gdev_mem_mono_set_inverted(new_dev,
166 			       (palette[0] | palette[1] | palette[2]) != 0);
167     } else {
168 	byte *dev_palette = gs_alloc_string(mem, pcount,
169 					    "gs_makeimagedevice(palette)");
170 
171 	if (dev_palette == 0)
172 	    return_error(gs_error_VMerror);
173 	gs_make_mem_device(new_dev, proto_dev, mem,
174 			   (page_device ? 1 : -1), 0);
175 	new_dev->palette.size = pcount;
176 	new_dev->palette.data = dev_palette;
177 	memcpy(dev_palette, palette, pcount);
178 	if (!has_color) {
179 	    new_dev->color_info.num_components = 1;
180 	    new_dev->color_info.max_color = 0;
181 	    new_dev->color_info.dither_colors = 0;
182 	}
183     }
184     new_dev->initial_matrix = *pmat;
185     new_dev->MarginsHWResolution[0] = new_dev->HWResolution[0] =
186 	fabs(x_pixels_per_unit) * 72;
187     new_dev->MarginsHWResolution[1] = new_dev->HWResolution[1] =
188 	fabs(y_pixels_per_unit) * 72;
189     gx_device_set_width_height((gx_device *) new_dev, width, height);
190     /* Set the ImagingBBox so we get a correct clipping region. */
191     {
192 	gs_rect bbox;
193 
194 	bbox.p.x = 0;
195 	bbox.p.y = 0;
196 	bbox.q.x = width;
197 	bbox.q.y = height;
198 	gs_bbox_transform_inverse(&bbox, pmat, &bbox);
199 	new_dev->ImagingBBox[0] = bbox.p.x;
200 	new_dev->ImagingBBox[1] = bbox.p.y;
201 	new_dev->ImagingBBox[2] = bbox.q.x;
202 	new_dev->ImagingBBox[3] = bbox.q.y;
203 	new_dev->ImagingBBox_set = true;
204     }
205     /* The bitmap will be allocated when the device is opened. */
206     new_dev->is_open = false;
207     new_dev->bitmap_memory = mem;
208     return 0;
209 }
210 
211 int
212 gs_makewordimagedevice(gx_device ** pnew_dev, const gs_matrix * pmat,
213 	       uint width, uint height, const byte * colors, int num_colors,
214 		    bool word_oriented, bool page_device, gs_memory_t * mem)
215 {
216     int code;
217     gx_device_memory *pnew =
218     gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
219 		    "gs_makeimagedevice(device)");
220 
221     if (pnew == 0)
222 	return_error(gs_error_VMerror);
223     code = gs_initialize_wordimagedevice(pnew, pmat, width, height,
224 					 colors, num_colors, word_oriented,
225 					 page_device, mem);
226     if (code < 0) {
227 	gs_free_object(mem, pnew, "gs_makeimagedevice(device)");
228 	return code;
229     }
230     *pnew_dev = (gx_device *) pnew;
231     return 0;
232 }
233