1 /* Copyright (C) 1994, 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: gdevabuf.c,v 1.7 2002/09/28 18:29:40 giles Exp $ */
18 /* Alpha-buffering memory devices */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gxdevice.h"
23 #include "gxdevmem.h" /* semi-public definitions */
24 #include "gdevmem.h" /* private definitions */
25
26 /* ================ Alpha devices ================ */
27
28 /*
29 * These devices store 2 or 4 bits of alpha. They are a hybrid of a
30 * monobit device (for color mapping) and a 2- or 4-bit device (for painting).
31 * Currently, we only use them for character rasterizing, but they might be
32 * useful for other things someday.
33 */
34
35 /* We can't initialize the device descriptor statically very well, */
36 /* so we patch up the image2 or image4 descriptor. */
37 private dev_proc_map_rgb_color(mem_alpha_map_rgb_color);
38 private dev_proc_map_color_rgb(mem_alpha_map_color_rgb);
39 private dev_proc_map_rgb_alpha_color(mem_alpha_map_rgb_alpha_color);
40 private dev_proc_copy_alpha(mem_alpha_copy_alpha);
41
42 void
gs_make_mem_alpha_device(gx_device_memory * adev,gs_memory_t * mem,gx_device * target,int alpha_bits)43 gs_make_mem_alpha_device(gx_device_memory * adev, gs_memory_t * mem,
44 gx_device * target, int alpha_bits)
45 {
46 gs_make_mem_device(adev, gdev_mem_device_for_bits(alpha_bits),
47 mem, 0, target);
48 /* This is a black-and-white device ... */
49 adev->color_info = gdev_mem_device_for_bits(1)->color_info;
50 /* ... but it has multiple bits per pixel ... */
51 adev->color_info.depth = alpha_bits;
52 /* ... and different color mapping. */
53 set_dev_proc(adev, map_rgb_color, mem_alpha_map_rgb_color);
54 set_dev_proc(adev, map_color_rgb, mem_alpha_map_color_rgb);
55 set_dev_proc(adev, map_rgb_alpha_color, mem_alpha_map_rgb_alpha_color);
56 set_dev_proc(adev, copy_alpha, mem_alpha_copy_alpha);
57 }
58
59 /* Reimplement color mapping. */
60 private gx_color_index
mem_alpha_map_rgb_color(gx_device * dev,const gx_color_value cv[])61 mem_alpha_map_rgb_color(gx_device * dev, const gx_color_value cv[])
62 {
63 gx_device_memory * const mdev = (gx_device_memory *)dev;
64 gx_color_index color = gx_forward_map_rgb_color(dev, cv);
65
66 return (color == 0 || color == gx_no_color_index ? color :
67 (gx_color_index) ((1 << mdev->log2_alpha_bits) - 1));
68 }
69 private int
mem_alpha_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])70 mem_alpha_map_color_rgb(gx_device * dev, gx_color_index color,
71 gx_color_value prgb[3])
72 {
73 return
74 gx_forward_map_color_rgb(dev,
75 (color == 0 ? color : (gx_color_index) 1),
76 prgb);
77 }
78 private gx_color_index
mem_alpha_map_rgb_alpha_color(gx_device * dev,gx_color_value r,gx_color_value g,gx_color_value b,gx_color_value alpha)79 mem_alpha_map_rgb_alpha_color(gx_device * dev, gx_color_value r,
80 gx_color_value g, gx_color_value b, gx_color_value alpha)
81 {
82 gx_device_memory * const mdev = (gx_device_memory *)dev;
83 gx_color_index color;
84 gx_color_value cv[3];
85
86 cv[0] = r; cv[1] = g; cv[2] = b;
87 color = gx_forward_map_rgb_color(dev, cv);
88
89 return (color == 0 || color == gx_no_color_index ? color :
90 (gx_color_index) (alpha >> (gx_color_value_bits -
91 mdev->log2_alpha_bits)));
92 }
93 /* Implement alpha copying. */
94 private int
mem_alpha_copy_alpha(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int width,int height,gx_color_index color,int depth)95 mem_alpha_copy_alpha(gx_device * dev, const byte * data, int data_x,
96 int raster, gx_bitmap_id id, int x, int y, int width, int height,
97 gx_color_index color, int depth)
98 { /* Just use copy_color. */
99 return (color == 0 ?
100 (*dev_proc(dev, fill_rectangle)) (dev, x, y, width, height,
101 color) :
102 (*dev_proc(dev, copy_color)) (dev, data, data_x, raster, id,
103 x, y, width, height));
104 }
105
106 /* ================ Alpha-buffer device ================ */
107
108 /*
109 * This device converts graphics sampled at a higher resolution to
110 * alpha values at a lower resolution. It does this by accumulating
111 * the bits of a band and then converting the band to alphas.
112 * In order to make this work, the client of the device must promise
113 * only to visit each band at most once, except possibly for a single
114 * scan line overlapping the adjacent band, and must promise only to write
115 * a single color into the output. In particular, this works
116 * within a single call on gx_fill_path (if the fill loop is constrained
117 * to process bands of limited height on each pass) or a single masked image
118 * scanned in Y order, but not across such calls and not for other
119 * kinds of painting operations.
120 *
121 * We implement this device as a subclass of a monobit memory device.
122 * (We put its state in the definition of gx_device_memory just because
123 * actual subclassing introduces a lot of needless boilerplate.)
124 * We only allocate enough bits for one band. The height of the band
125 * must be a multiple of the Y scale factor; the minimum height
126 * of the band is twice the Y scale factor.
127 *
128 * The bits in storage are actually a sliding window on the true
129 * oversampled image. To avoid having to copy the bits around when we
130 * move the window, we adjust the mapping between the client's Y values
131 * and our own, as follows:
132 * Client Stored
133 * ------ ------
134 * y0..y0+m-1 n-m..n-1
135 * y0+m..y0+n-1 0..n-m-1
136 * where n and m are multiples of the Y scale factor and 0 <= m <= n <=
137 * the height of the band. (In the device structure, m is called
138 * mapped_start and n is called mapped_height.) This allows us to slide
139 * the window incrementally in either direction without copying any bits.
140 */
141
142 /* Procedures */
143 private dev_proc_close_device(mem_abuf_close);
144 private dev_proc_copy_mono(mem_abuf_copy_mono);
145 private dev_proc_fill_rectangle(mem_abuf_fill_rectangle);
146 private dev_proc_get_clipping_box(mem_abuf_get_clipping_box);
147
148 /* The device descriptor. */
149 private const gx_device_memory mem_alpha_buffer_device =
150 mem_device("image(alpha buffer)", 0, 1,
151 gx_forward_map_rgb_color, gx_forward_map_color_rgb,
152 mem_abuf_copy_mono, gx_default_copy_color, mem_abuf_fill_rectangle,
153 gx_no_strip_copy_rop);
154
155 /* Make an alpha-buffer memory device. */
156 /* We use abuf instead of alpha_buffer because */
157 /* gcc under VMS only retains 23 characters of procedure names. */
158 void
gs_make_mem_abuf_device(gx_device_memory * adev,gs_memory_t * mem,gx_device * target,const gs_log2_scale_point * pscale,int alpha_bits,int mapped_x)159 gs_make_mem_abuf_device(gx_device_memory * adev, gs_memory_t * mem,
160 gx_device * target, const gs_log2_scale_point * pscale,
161 int alpha_bits, int mapped_x)
162 {
163 gs_make_mem_device(adev, &mem_alpha_buffer_device, mem, 0, target);
164 adev->max_fill_band = 1 << pscale->y;
165 adev->log2_scale = *pscale;
166 adev->log2_alpha_bits = alpha_bits >> 1; /* works for 1,2,4 */
167 adev->mapped_x = mapped_x;
168 set_dev_proc(adev, close_device, mem_abuf_close);
169 set_dev_proc(adev, get_clipping_box, mem_abuf_get_clipping_box);
170 adev->color_info.anti_alias.text_bits =
171 adev->color_info.anti_alias.graphics_bits =
172 alpha_bits;
173 }
174
175 /* Test whether a device is an alpha-buffering device. */
176 bool
gs_device_is_abuf(const gx_device * dev)177 gs_device_is_abuf(const gx_device * dev)
178 { /* We can't just compare the procs, or even an individual proc, */
179 /* because we might be tracing. Instead, check the identity of */
180 /* the device name. */
181 return dev->dname == mem_alpha_buffer_device.dname;
182 }
183
184 /* Internal routine to flush a block of the buffer. */
185 /* A block is a group of scan lines whose initial Y is a multiple */
186 /* of the Y scale and whose height is equal to the Y scale. */
187 private int
abuf_flush_block(gx_device_memory * adev,int y)188 abuf_flush_block(gx_device_memory * adev, int y)
189 {
190 gx_device *target = adev->target;
191 int block_height = 1 << adev->log2_scale.y;
192 int alpha_bits = 1 << adev->log2_alpha_bits;
193 int ddepth =
194 (adev->width >> adev->log2_scale.x) << adev->log2_alpha_bits;
195 uint draster = bitmap_raster(ddepth);
196 int buffer_y = y - adev->mapped_y + adev->mapped_start;
197 byte *bits;
198
199 if (buffer_y >= adev->height)
200 buffer_y -= adev->height;
201 bits = scan_line_base(adev, buffer_y);
202 { /*
203 * Many bits are typically zero. Save time by computing
204 * an accurate X bounding box before compressing.
205 * Unfortunately, in order to deal with alpha nibble swapping
206 * (see gsbitops.c), we can't expand the box only to pixel
207 * boundaries:
208 int alpha_mask = -1 << adev->log2_alpha_bits;
209 * Instead, we must expand it to byte boundaries,
210 */
211 int alpha_mask = ~7;
212 gs_int_rect bbox;
213 int width;
214
215 bits_bounding_box(bits, block_height, adev->raster, &bbox);
216 bbox.p.x &= alpha_mask;
217 bbox.q.x = (bbox.q.x + ~alpha_mask) & alpha_mask;
218 width = bbox.q.x - bbox.p.x;
219 bits_compress_scaled(bits, bbox.p.x, width, block_height,
220 adev->raster, bits, draster, &adev->log2_scale,
221 adev->log2_alpha_bits);
222 return (*dev_proc(target, copy_alpha)) (target,
223 bits, 0, draster, gx_no_bitmap_id,
224 (adev->mapped_x + bbox.p.x) >>
225 adev->log2_scale.x,
226 y >> adev->log2_scale.y,
227 width >> adev->log2_scale.x, 1,
228 adev->save_color, alpha_bits);
229 }
230 }
231 /* Flush the entire buffer. */
232 private int
abuf_flush(gx_device_memory * adev)233 abuf_flush(gx_device_memory * adev)
234 {
235 int y, code = 0;
236 int block_height = 1 << adev->log2_scale.y;
237
238 for (y = 0; y < adev->mapped_height; y += block_height)
239 if ((code = abuf_flush_block(adev, adev->mapped_y + y)) < 0)
240 return code;
241 adev->mapped_height = adev->mapped_start = 0;
242 return 0;
243 }
244
245 /* Close the device, flushing the buffer. */
246 private int
mem_abuf_close(gx_device * dev)247 mem_abuf_close(gx_device * dev)
248 {
249 gx_device_memory * const mdev = (gx_device_memory *)dev;
250 int code = abuf_flush(mdev);
251
252 if (code < 0)
253 return code;
254 return mem_close(dev);
255 }
256
257 /*
258 * Framework for mapping a requested imaging operation to the buffer.
259 * For now, we assume top-to-bottom transfers and use a very simple algorithm.
260 */
261 typedef struct y_transfer_s {
262 int y_next;
263 int height_left;
264 int transfer_y;
265 int transfer_height;
266 } y_transfer;
267 private void
y_transfer_init(y_transfer * pyt,gx_device * dev,int ty,int th)268 y_transfer_init(y_transfer * pyt, gx_device * dev, int ty, int th)
269 {
270 gx_device_memory * const mdev = (gx_device_memory *)dev;
271 int bh = 1 << mdev->log2_scale.y;
272
273 if (ty < mdev->mapped_y || ty > mdev->mapped_y + mdev->mapped_height) {
274 abuf_flush(mdev);
275 mdev->mapped_y = ty & -bh;
276 mdev->mapped_height = bh;
277 memset(scan_line_base(mdev, 0), 0, bh * mdev->raster);
278 }
279 pyt->y_next = ty;
280 pyt->height_left = th;
281 pyt->transfer_height = 0;
282 }
283 /* while ( yt.height_left > 0 ) { y_transfer_next(&yt, mdev); ... } */
284 private void
y_transfer_next(y_transfer * pyt,gx_device * dev)285 y_transfer_next(y_transfer * pyt, gx_device * dev)
286 {
287 gx_device_memory * const mdev = (gx_device_memory *)dev;
288 int my = mdev->mapped_y, mh = mdev->mapped_height;
289 int ms = mdev->mapped_start;
290 int ty = pyt->y_next += pyt->transfer_height;
291 int th = pyt->height_left;
292 int bh = 1 << mdev->log2_scale.y;
293
294 /* From here on, we know that my <= ty <= my + mh. */
295 int tby, tbh;
296
297 if (ty == my + mh) { /* Add a new block at my1. */
298 if (mh == mdev->height) {
299 abuf_flush_block(mdev, my);
300 mdev->mapped_y = my += bh;
301 if ((mdev->mapped_start = ms += bh) == mh)
302 mdev->mapped_start = ms = 0;
303 } else { /* Because we currently never extend backwards, */
304 /* we know we can't wrap around in this case. */
305 mdev->mapped_height = mh += bh;
306 }
307 memset(scan_line_base(mdev, (ms == 0 ? mh : ms) - bh),
308 0, bh * mdev->raster);
309 }
310 /* Now we know that my <= ty < my + mh. */
311 tby = ty - my + ms;
312 if (tby < mdev->height) {
313 tbh = mdev->height - ms;
314 if (tbh > mh)
315 tbh = mh;
316 tbh -= tby - ms;
317 } else { /* wrap around */
318 tby -= mdev->height;
319 tbh = ms + mh - dev->height - tby;
320 }
321 if_debug7('V',
322 "[V]abuf: my=%d, mh=%d, ms=%d, ty=%d, th=%d, tby=%d, tbh=%d\n",
323 my, mh, ms, ty, th, tby, tbh);
324 if (tbh > th)
325 tbh = th;
326 pyt->height_left = th - tbh;
327 pyt->transfer_y = tby;
328 pyt->transfer_height = tbh;
329 }
330
331 /* Copy a monobit image. */
332 private int
mem_abuf_copy_mono(gx_device * dev,const byte * base,int sourcex,int sraster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index zero,gx_color_index one)333 mem_abuf_copy_mono(gx_device * dev,
334 const byte * base, int sourcex, int sraster, gx_bitmap_id id,
335 int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
336 {
337 gx_device_memory * const mdev = (gx_device_memory *)dev;
338 y_transfer yt;
339
340 if (zero != gx_no_color_index || one == gx_no_color_index)
341 return_error(gs_error_undefinedresult);
342 x -= mdev->mapped_x;
343 fit_copy_xyw(dev, base, sourcex, sraster, id, x, y, w, h); /* don't limit h */
344 if (w <= 0 || h <= 0)
345 return 0;
346 mdev->save_color = one;
347 y_transfer_init(&yt, dev, y, h);
348 while (yt.height_left > 0) {
349 y_transfer_next(&yt, dev);
350 (*dev_proc(&mem_mono_device, copy_mono)) (dev,
351 base + (yt.y_next - y) * sraster,
352 sourcex, sraster, gx_no_bitmap_id,
353 x, yt.transfer_y, w, yt.transfer_height,
354 gx_no_color_index, (gx_color_index) 1);
355 }
356 return 0;
357 }
358
359 /* Fill a rectangle. */
360 private int
mem_abuf_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)361 mem_abuf_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
362 gx_color_index color)
363 {
364 gx_device_memory * const mdev = (gx_device_memory *)dev;
365 y_transfer yt;
366
367 x -= mdev->mapped_x;
368 fit_fill_xy(dev, x, y, w, h);
369 fit_fill_w(dev, x, w); /* don't limit h */
370 /* or check w <= 0, h <= 0 */
371 mdev->save_color = color;
372 y_transfer_init(&yt, dev, y, h);
373 while (yt.height_left > 0) {
374 y_transfer_next(&yt, dev);
375 (*dev_proc(&mem_mono_device, fill_rectangle)) (dev,
376 x, yt.transfer_y, w, yt.transfer_height,
377 (gx_color_index) 1);
378 }
379 return 0;
380 }
381
382 /* Get the clipping box. We must scale this up by the number of alpha bits. */
383 private void
mem_abuf_get_clipping_box(gx_device * dev,gs_fixed_rect * pbox)384 mem_abuf_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
385 {
386 gx_device_memory * const mdev = (gx_device_memory *)dev;
387 gx_device *tdev = mdev->target;
388
389 (*dev_proc(tdev, get_clipping_box)) (tdev, pbox);
390 pbox->p.x <<= mdev->log2_scale.x;
391 pbox->p.y <<= mdev->log2_scale.y;
392 pbox->q.x <<= mdev->log2_scale.x;
393 pbox->q.y <<= mdev->log2_scale.y;
394 }
395