xref: /plan9/sys/src/cmd/gs/src/gdevmem.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 1995, 1997, 1998, 1999 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: gdevmem.c,v 1.9 2005/03/14 18:08:36 dan Exp $ */
18 /* Generic "memory" (stored bitmap) device */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gsrect.h"
23 #include "gsstruct.h"
24 #include "gxarith.h"
25 #include "gxdevice.h"
26 #include "gxgetbit.h"
27 #include "gxdevmem.h"		/* semi-public definitions */
28 #include "gdevmem.h"		/* private definitions */
29 #include "gstrans.h"
30 
31 /* Structure descriptor */
32 public_st_device_memory();
33 
34 /* GC procedures */
35 private
ENUM_PTRS_WITH(device_memory_enum_ptrs,gx_device_memory * mptr)36 ENUM_PTRS_WITH(device_memory_enum_ptrs, gx_device_memory *mptr)
37 {
38     return ENUM_USING(st_device_forward, vptr, sizeof(gx_device_forward), index - 3);
39 }
40 case 0: ENUM_RETURN((mptr->foreign_bits ? NULL : (void *)mptr->base));
41 case 1: ENUM_RETURN((mptr->foreign_line_pointers ? NULL : (void *)mptr->line_ptrs));
42 ENUM_STRING_PTR(2, gx_device_memory, palette);
43 ENUM_PTRS_END
44 private
RELOC_PTRS_WITH(device_memory_reloc_ptrs,gx_device_memory * mptr)45 RELOC_PTRS_WITH(device_memory_reloc_ptrs, gx_device_memory *mptr)
46 {
47     if (!mptr->foreign_bits) {
48 	byte *base_old = mptr->base;
49 	long reloc;
50 	int y;
51 
52 	RELOC_PTR(gx_device_memory, base);
53 	reloc = base_old - mptr->base;
54 	for (y = 0; y < mptr->height; y++)
55 	    mptr->line_ptrs[y] -= reloc;
56 	/* Relocate line_ptrs, which also points into the data area. */
57 	mptr->line_ptrs = (byte **) ((byte *) mptr->line_ptrs - reloc);
58     } else if (!mptr->foreign_line_pointers) {
59 	RELOC_PTR(gx_device_memory, line_ptrs);
60     }
61     RELOC_CONST_STRING_PTR(gx_device_memory, palette);
62     RELOC_USING(st_device_forward, vptr, sizeof(gx_device_forward));
63 }
64 RELOC_PTRS_END
65 
66 /* Define the palettes for monobit devices. */
67 private const byte b_w_palette_string[6] = {
68     0xff, 0xff, 0xff, 0, 0, 0
69 };
70 const gs_const_string mem_mono_b_w_palette = {
71     b_w_palette_string, 6
72 };
73 private const byte w_b_palette_string[6] = {
74     0, 0, 0, 0xff, 0xff, 0xff
75 };
76 const gs_const_string mem_mono_w_b_palette = {
77     w_b_palette_string, 6
78 };
79 
80 /* ------ Generic code ------ */
81 
82 /* Return the appropriate memory device for a given */
83 /* number of bits per pixel (0 if none suitable). */
84 private const gx_device_memory *const mem_devices[65] = {
85     0, &mem_mono_device, &mem_mapped2_device, 0, &mem_mapped4_device,
86     0, 0, 0, &mem_mapped8_device,
87     0, 0, 0, 0, 0, 0, 0, &mem_true16_device,
88     0, 0, 0, 0, 0, 0, 0, &mem_true24_device,
89     0, 0, 0, 0, 0, 0, 0, &mem_true32_device,
90     0, 0, 0, 0, 0, 0, 0, &mem_true40_device,
91     0, 0, 0, 0, 0, 0, 0, &mem_true48_device,
92     0, 0, 0, 0, 0, 0, 0, &mem_true56_device,
93     0, 0, 0, 0, 0, 0, 0, &mem_true64_device
94 };
95 const gx_device_memory *
gdev_mem_device_for_bits(int bits_per_pixel)96 gdev_mem_device_for_bits(int bits_per_pixel)
97 {
98     return ((uint)bits_per_pixel > 64 ? (const gx_device_memory *)0 :
99 	    mem_devices[bits_per_pixel]);
100 }
101 /* Do the same for a word-oriented device. */
102 private const gx_device_memory *const mem_word_devices[65] = {
103     0, &mem_mono_device, &mem_mapped2_word_device, 0, &mem_mapped4_word_device,
104     0, 0, 0, &mem_mapped8_word_device,
105     0, 0, 0, 0, 0, 0, 0, 0 /*no 16-bit word device*/,
106     0, 0, 0, 0, 0, 0, 0, &mem_true24_word_device,
107     0, 0, 0, 0, 0, 0, 0, &mem_true32_word_device,
108     0, 0, 0, 0, 0, 0, 0, &mem_true40_word_device,
109     0, 0, 0, 0, 0, 0, 0, &mem_true48_word_device,
110     0, 0, 0, 0, 0, 0, 0, &mem_true56_word_device,
111     0, 0, 0, 0, 0, 0, 0, &mem_true64_word_device
112 };
113 const gx_device_memory *
gdev_mem_word_device_for_bits(int bits_per_pixel)114 gdev_mem_word_device_for_bits(int bits_per_pixel)
115 {
116     return ((uint)bits_per_pixel > 64 ? (const gx_device_memory *)0 :
117 	    mem_word_devices[bits_per_pixel]);
118 }
119 
120 /* Test whether a device is a memory device */
121 bool
gs_device_is_memory(const gx_device * dev)122 gs_device_is_memory(const gx_device * dev)
123 {
124     /*
125      * We use the draw_thin_line procedure to mark memory devices.
126      * See gdevmem.h.
127      */
128     int bits_per_pixel = dev->color_info.depth;
129     const gx_device_memory *mdproto;
130 
131     if ((uint)bits_per_pixel > 64)
132 	return false;
133     mdproto = mem_devices[bits_per_pixel];
134     if (mdproto != 0 && dev_proc(dev, draw_thin_line) == dev_proc(mdproto, draw_thin_line))
135 	return true;
136     mdproto = mem_word_devices[bits_per_pixel];
137     return (mdproto != 0 && dev_proc(dev, draw_thin_line) == dev_proc(mdproto, draw_thin_line));
138 }
139 
140 /* Make a memory device. */
141 /* Note that the default for monobit devices is white = 0, black = 1. */
142 void
gs_make_mem_device(gx_device_memory * dev,const gx_device_memory * mdproto,gs_memory_t * mem,int page_device,gx_device * target)143 gs_make_mem_device(gx_device_memory * dev, const gx_device_memory * mdproto,
144 		   gs_memory_t * mem, int page_device, gx_device * target)
145 {
146     gx_device_init((gx_device *) dev, (const gx_device *)mdproto,
147 		   mem, true);
148     dev->stype = &st_device_memory;
149     switch (page_device) {
150 	case -1:
151 	    set_dev_proc(dev, get_page_device, gx_default_get_page_device);
152 	    break;
153 	case 1:
154 	    set_dev_proc(dev, get_page_device, gx_page_device_get_page_device);
155 	    break;
156     }
157     /* Preload the black and white cache. */
158     if (target == 0) {
159 	if (dev->color_info.depth == 1) {
160 	    /* The default for black-and-white devices is inverted. */
161 	    dev->cached_colors.black = 1;
162 	    dev->cached_colors.white = 0;
163 	} else {
164 	    dev->cached_colors.black = 0;
165 	    dev->cached_colors.white = (1 << dev->color_info.depth) - 1;
166 	}
167     } else {
168 	gx_device_set_target((gx_device_forward *)dev, target);
169 	/* Forward the color mapping operations to the target. */
170 	gx_device_forward_color_procs((gx_device_forward *) dev);
171 	gx_device_copy_color_procs((gx_device *)dev, target);
172 	dev->cached_colors = target->cached_colors;
173     }
174     if (dev->color_info.depth == 1) {
175 	gdev_mem_mono_set_inverted(dev,
176 				   (target == 0 ||
177                                     dev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE));
178     }
179     check_device_separable((gx_device *)dev);
180     gx_device_fill_in_procs((gx_device *)dev);
181 }
182 /* Make a monobit memory device.  This is never a page device. */
183 /* Note that white=0, black=1. */
184 void
gs_make_mem_mono_device(gx_device_memory * dev,gs_memory_t * mem,gx_device * target)185 gs_make_mem_mono_device(gx_device_memory * dev, gs_memory_t * mem,
186 			gx_device * target)
187 {
188     gx_device_init((gx_device *)dev, (const gx_device *)&mem_mono_device,
189 		   mem, true);
190     set_dev_proc(dev, get_page_device, gx_default_get_page_device);
191     gx_device_set_target((gx_device_forward *)dev, target);
192     gdev_mem_mono_set_inverted(dev, true);
193     check_device_separable((gx_device *)dev);
194     gx_device_fill_in_procs((gx_device *)dev);
195 }
196 
197 /* Define whether a monobit memory device is inverted (black=1). */
198 void
gdev_mem_mono_set_inverted(gx_device_memory * dev,bool black_is_1)199 gdev_mem_mono_set_inverted(gx_device_memory * dev, bool black_is_1)
200 {
201     if (black_is_1)
202 	dev->palette = mem_mono_b_w_palette;
203     else
204 	dev->palette = mem_mono_w_b_palette;
205 }
206 
207 /*
208  * Compute the size of the bitmap storage, including the space for the scan
209  * line pointer table.  Note that scan lines are padded to a multiple of
210  * align_bitmap_mod bytes, and additional padding may be needed if the
211  * pointer table must be aligned to an even larger modulus.
212  *
213  * The computation for planar devices is a little messier.  Each plane
214  * must pad its scan lines, and then we must pad again for the pointer
215  * tables (one table per plane).
216  */
217 ulong
gdev_mem_bits_size(const gx_device_memory * dev,int width,int height)218 gdev_mem_bits_size(const gx_device_memory * dev, int width, int height)
219 {
220     int num_planes = dev->num_planes;
221     gx_render_plane_t plane1;
222     const gx_render_plane_t *planes;
223     ulong size;
224     int pi;
225 
226     if (num_planes)
227 	planes = dev->planes;
228     else
229 	planes = &plane1, plane1.depth = dev->color_info.depth, num_planes = 1;
230     for (size = 0, pi = 0; pi < num_planes; ++pi)
231 	size += bitmap_raster(width * planes[pi].depth);
232     return ROUND_UP(size * height, ARCH_ALIGN_PTR_MOD);
233 }
234 ulong
gdev_mem_line_ptrs_size(const gx_device_memory * dev,int width,int height)235 gdev_mem_line_ptrs_size(const gx_device_memory * dev, int width, int height)
236 {
237     return (ulong)height * sizeof(byte *) * max(dev->num_planes, 1);
238 }
239 ulong
gdev_mem_data_size(const gx_device_memory * dev,int width,int height)240 gdev_mem_data_size(const gx_device_memory * dev, int width, int height)
241 {
242     return gdev_mem_bits_size(dev, width, height) +
243 	gdev_mem_line_ptrs_size(dev, width, height);
244 }
245 /*
246  * Do the inverse computation: given a width (in pixels) and a buffer size,
247  * compute the maximum height.
248  */
249 int
gdev_mem_max_height(const gx_device_memory * dev,int width,ulong size,bool page_uses_transparency)250 gdev_mem_max_height(const gx_device_memory * dev, int width, ulong size,
251 		bool page_uses_transparency)
252 {
253     int height;
254     ulong max_height;
255 
256     if (page_uses_transparency) {
257         /*
258          * If the device is using PDF 1.4 transparency then we will need to
259          * also allocate image buffers for doing the blending operations.
260          * We can only estimate the space requirements.  However since it
261 	 * is only an estimate, we may exceed our desired buffer space while
262 	 * processing the file.
263 	 */
264         max_height = size / (bitmap_raster(width
265 		* dev->color_info.depth + ESTIMATED_PDF14_ROW_SPACE(width))
266 		+ sizeof(byte *) * max(dev->num_planes, 1));
267         height = (int)min(max_height, max_int);
268     } else {
269 	/* For non PDF 1.4 transparency, we can do an exact calculation */
270         max_height = size /
271 	    (bitmap_raster(width * dev->color_info.depth) +
272 	     sizeof(byte *) * max(dev->num_planes, 1));
273         height = (int)min(max_height, max_int);
274         /*
275          * Because of alignment rounding, the just-computed height might
276          * be too large by a small amount.  Adjust it the easy way.
277          */
278         while (gdev_mem_data_size(dev, width, height) > size)
279 	    --height;
280     }
281     return height;
282 }
283 
284 /* Open a memory device, allocating the data area if appropriate, */
285 /* and create the scan line table. */
286 int
mem_open(gx_device * dev)287 mem_open(gx_device * dev)
288 {
289     gx_device_memory *const mdev = (gx_device_memory *)dev;
290 
291     /* Check that we aren't trying to open a planar device as chunky. */
292     if (mdev->num_planes)
293 	return_error(gs_error_rangecheck);
294     return gdev_mem_open_scan_lines(mdev, dev->height);
295 }
296 int
gdev_mem_open_scan_lines(gx_device_memory * mdev,int setup_height)297 gdev_mem_open_scan_lines(gx_device_memory *mdev, int setup_height)
298 {
299     bool line_pointers_adjacent = true;
300 
301     if (setup_height < 0 || setup_height > mdev->height)
302 	return_error(gs_error_rangecheck);
303     if (mdev->bitmap_memory != 0) {
304 	/* Allocate the data now. */
305 	ulong size = gdev_mem_bitmap_size(mdev);
306 
307 	if ((uint) size != size)
308 	    return_error(gs_error_limitcheck);
309 	mdev->base = gs_alloc_bytes(mdev->bitmap_memory, (uint)size,
310 				    "mem_open");
311 	if (mdev->base == 0)
312 	    return_error(gs_error_VMerror);
313 	mdev->foreign_bits = false;
314     } else if (mdev->line_pointer_memory != 0) {
315 	/* Allocate the line pointers now. */
316 
317 	mdev->line_ptrs = (byte **)
318 	    gs_alloc_byte_array(mdev->line_pointer_memory, mdev->height,
319 				sizeof(byte *) * max(mdev->num_planes, 1),
320 				"gdev_mem_open_scan_lines");
321 	if (mdev->line_ptrs == 0)
322 	    return_error(gs_error_VMerror);
323 	mdev->foreign_line_pointers = false;
324 	line_pointers_adjacent = false;
325     }
326     if (line_pointers_adjacent)
327 	mdev->line_ptrs = (byte **)
328 	    (mdev->base + gdev_mem_bits_size(mdev, mdev->width, mdev->height));
329     mdev->raster = gdev_mem_raster(mdev);
330     return gdev_mem_set_line_ptrs(mdev, NULL, 0, NULL, setup_height);
331 }
332 /*
333  * Set up the scan line pointers of a memory device.
334  * See gxdevmem.h for the detailed specification.
335  * Sets or uses line_ptrs, base, raster; uses width, color_info.depth,
336  * num_planes, plane_depths, plane_depth.
337  */
338 int
gdev_mem_set_line_ptrs(gx_device_memory * mdev,byte * base,int raster,byte ** line_ptrs,int setup_height)339 gdev_mem_set_line_ptrs(gx_device_memory * mdev, byte * base, int raster,
340 		       byte **line_ptrs, int setup_height)
341 {
342     int num_planes = mdev->num_planes;
343     gx_render_plane_t plane1;
344     const gx_render_plane_t *planes;
345     byte **pline =
346 	(line_ptrs ? (mdev->line_ptrs = line_ptrs) : mdev->line_ptrs);
347     byte *data =
348 	(base ? (mdev->raster = raster, mdev->base = base) :
349 	 (raster = mdev->raster, mdev->base));
350     int pi;
351 
352     if (num_planes) {
353 	if (base && !mdev->plane_depth)
354 	    return_error(gs_error_rangecheck);
355 	planes = mdev->planes;
356     } else {
357 	planes = &plane1;
358 	plane1.depth = mdev->color_info.depth;
359 	num_planes = 1;
360     }
361 
362     for (pi = 0; pi < num_planes; ++pi) {
363 	int raster = bitmap_raster(mdev->width * planes[pi].depth);
364 	byte **pptr = pline;
365 	byte **pend = pptr + setup_height;
366 	byte *scan_line = data;
367 
368 	while (pptr < pend) {
369 	    *pptr++ = scan_line;
370 	    scan_line += raster;
371 	}
372 	data += raster * mdev->height;
373 	pline += setup_height;	/* not mdev->height, see gxdevmem.h */
374     }
375 
376     return 0;
377 }
378 
379 /* Return the initial transformation matrix */
380 void
mem_get_initial_matrix(gx_device * dev,gs_matrix * pmat)381 mem_get_initial_matrix(gx_device * dev, gs_matrix * pmat)
382 {
383     gx_device_memory * const mdev = (gx_device_memory *)dev;
384 
385     pmat->xx = mdev->initial_matrix.xx;
386     pmat->xy = mdev->initial_matrix.xy;
387     pmat->yx = mdev->initial_matrix.yx;
388     pmat->yy = mdev->initial_matrix.yy;
389     pmat->tx = mdev->initial_matrix.tx;
390     pmat->ty = mdev->initial_matrix.ty;
391 }
392 
393 /* Close a memory device, freeing the data area if appropriate. */
394 int
mem_close(gx_device * dev)395 mem_close(gx_device * dev)
396 {
397     gx_device_memory * const mdev = (gx_device_memory *)dev;
398 
399     if (mdev->bitmap_memory != 0) {
400 	gs_free_object(mdev->bitmap_memory, mdev->base, "mem_close");
401 	/*
402 	 * The following assignment is strictly for the benefit of one
403 	 * client that is sloppy about using is_open properly.
404 	 */
405 	mdev->base = 0;
406     } else if (mdev->line_pointer_memory != 0) {
407 	gs_free_object(mdev->line_pointer_memory, mdev->line_ptrs,
408 		       "mem_close");
409 	mdev->line_ptrs = 0;	/* ibid. */
410     }
411     return 0;
412 }
413 
414 /* Copy bits to a client. */
415 #undef chunk
416 #define chunk byte
417 int
mem_get_bits_rectangle(gx_device * dev,const gs_int_rect * prect,gs_get_bits_params_t * params,gs_int_rect ** unread)418 mem_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
419 		       gs_get_bits_params_t * params, gs_int_rect ** unread)
420 {
421     gx_device_memory * const mdev = (gx_device_memory *)dev;
422     gs_get_bits_options_t options = params->options;
423     int x = prect->p.x, w = prect->q.x - x, y = prect->p.y, h = prect->q.y - y;
424 
425     if (options == 0) {
426 	params->options =
427 	    (GB_ALIGN_STANDARD | GB_ALIGN_ANY) |
428 	    (GB_RETURN_COPY | GB_RETURN_POINTER) |
429 	    (GB_OFFSET_0 | GB_OFFSET_SPECIFIED | GB_OFFSET_ANY) |
430 	    (GB_RASTER_STANDARD | GB_RASTER_SPECIFIED | GB_RASTER_ANY) |
431 	    GB_PACKING_CHUNKY | GB_COLORS_NATIVE | GB_ALPHA_NONE;
432 	return_error(gs_error_rangecheck);
433     }
434     if ((w <= 0) | (h <= 0)) {
435 	if ((w | h) < 0)
436 	    return_error(gs_error_rangecheck);
437 	return 0;
438     }
439     if (x < 0 || w > dev->width - x ||
440 	y < 0 || h > dev->height - y
441 	)
442 	return_error(gs_error_rangecheck);
443     {
444 	gs_get_bits_params_t copy_params;
445 	byte *base = scan_line_base(mdev, y);
446 	int code;
447 
448 	copy_params.options =
449 	    GB_COLORS_NATIVE | GB_PACKING_CHUNKY | GB_ALPHA_NONE |
450 	    (mdev->raster ==
451 	     bitmap_raster(mdev->width * mdev->color_info.depth) ?
452 	     GB_RASTER_STANDARD : GB_RASTER_SPECIFIED);
453 	copy_params.raster = mdev->raster;
454 	code = gx_get_bits_return_pointer(dev, x, h, params,
455 					  &copy_params, base);
456 	if (code >= 0)
457 	    return code;
458 	return gx_get_bits_copy(dev, x, w, h, params, &copy_params, base,
459 				gx_device_raster(dev, true));
460     }
461 }
462 
463 #if !arch_is_big_endian
464 
465 /*
466  * Swap byte order in a rectangular subset of a bitmap.  If store = true,
467  * assume the rectangle will be overwritten, so don't swap any bytes where
468  * it doesn't matter.  The caller has already done a fit_fill or fit_copy.
469  * Note that the coordinates are specified in bits, not in terms of the
470  * actual device depth.
471  */
472 void
mem_swap_byte_rect(byte * base,uint raster,int x,int w,int h,bool store)473 mem_swap_byte_rect(byte * base, uint raster, int x, int w, int h, bool store)
474 {
475     int xbit = x & 31;
476 
477     if (store) {
478 	if (xbit + w > 64) {	/* Operation spans multiple words. */
479 	    /* Just swap the words at the left and right edges. */
480 	    if (xbit != 0)
481 		mem_swap_byte_rect(base, raster, x, 1, h, false);
482 	    x += w - 1;
483 	    xbit = x & 31;
484 	    if (xbit == 31)
485 		return;
486 	    w = 1;
487 	}
488     }
489     /* Swap the entire rectangle (or what's left of it). */
490     {
491 	byte *row = base + ((x >> 5) << 2);
492 	int nw = (xbit + w + 31) >> 5;
493 	int ny;
494 
495 	for (ny = h; ny > 0; row += raster, --ny) {
496 	    int nx = nw;
497 	    bits32 *pw = (bits32 *) row;
498 
499 	    do {
500 		bits32 w = *pw;
501 
502 		*pw++ = (w >> 24) + ((w >> 8) & 0xff00) +
503 		    ((w & 0xff00) << 8) + (w << 24);
504 	    }
505 	    while (--nx);
506 	}
507     }
508 }
509 
510 /* Copy a word-oriented rectangle to the client, swapping bytes as needed. */
511 int
mem_word_get_bits_rectangle(gx_device * dev,const gs_int_rect * prect,gs_get_bits_params_t * params,gs_int_rect ** unread)512 mem_word_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
513 		       gs_get_bits_params_t * params, gs_int_rect ** unread)
514 {
515     gx_device_memory * const mdev = (gx_device_memory *)dev;
516     byte *src;
517     uint dev_raster = gx_device_raster(dev, 1);
518     int x = prect->p.x;
519     int w = prect->q.x - x;
520     int y = prect->p.y;
521     int h = prect->q.y - y;
522     int bit_x, bit_w;
523     int code;
524 
525     fit_fill_xywh(dev, x, y, w, h);
526     if (w <= 0 || h <= 0) {
527 	/*
528 	 * It's easiest to just keep going with an empty rectangle.
529 	 * We pass the original rectangle to mem_get_bits_rectangle,
530 	 * so unread will be filled in correctly.
531 	 */
532 	x = y = w = h = 0;
533     }
534     bit_x = x * dev->color_info.depth;
535     bit_w = w * dev->color_info.depth;
536     src = scan_line_base(mdev, y);
537     mem_swap_byte_rect(src, dev_raster, bit_x, bit_w, h, false);
538     code = mem_get_bits_rectangle(dev, prect, params, unread);
539     mem_swap_byte_rect(src, dev_raster, bit_x, bit_w, h, false);
540     return code;
541 }
542 
543 #endif /* !arch_is_big_endian */
544 
545 /* Map a r-g-b color to a color index for a mapped color memory device */
546 /* (2, 4, or 8 bits per pixel.) */
547 /* This requires searching the palette. */
548 gx_color_index
mem_mapped_map_rgb_color(gx_device * dev,const gx_color_value cv[])549 mem_mapped_map_rgb_color(gx_device * dev, const gx_color_value cv[])
550 {
551     gx_device_memory * const mdev = (gx_device_memory *)dev;
552     byte br = gx_color_value_to_byte(cv[0]);
553 
554     register const byte *pptr = mdev->palette.data;
555     int cnt = mdev->palette.size;
556     const byte *which = 0;	/* initialized only to pacify gcc */
557     int best = 256 * 3;
558 
559     if (mdev->color_info.num_components != 1) {
560 	/* not 1 component, assume three */
561 	/* The comparison is rather simplistic, treating differences in	*/
562 	/* all components as equal. Better choices would be 'distance'	*/
563 	/* in HLS space or other, but these would be much slower.	*/
564 	/* At least exact matches will be found.			*/
565 	byte bg = gx_color_value_to_byte(cv[1]);
566 	byte bb = gx_color_value_to_byte(cv[2]);
567 
568 	while ((cnt -= 3) >= 0) {
569 	    register int diff = *pptr - br;
570 
571 	    if (diff < 0)
572 		diff = -diff;
573 	    if (diff < best) {	/* quick rejection */
574 		    int dg = pptr[1] - bg;
575 
576 		if (dg < 0)
577 		    dg = -dg;
578 		if ((diff += dg) < best) {	/* quick rejection */
579 		    int db = pptr[2] - bb;
580 
581 		    if (db < 0)
582 			db = -db;
583 		    if ((diff += db) < best)
584 			which = pptr, best = diff;
585 		}
586 	    }
587 	    if (diff == 0)	/* can't get any better than 0 diff */
588 		break;
589 	    pptr += 3;
590 	}
591     } else {
592 	/* Gray scale conversion. The palette is made of three equal	*/
593 	/* components, so this case is simpler.				*/
594 	while ((cnt -= 3) >= 0) {
595 	    register int diff = *pptr - br;
596 
597 	    if (diff < 0)
598 		diff = -diff;
599 	    if (diff < best) {	/* quick rejection */
600 		which = pptr, best = diff;
601 	    }
602 	    if (diff == 0)
603 		break;
604 	    pptr += 3;
605 	}
606     }
607     return (gx_color_index) ((which - mdev->palette.data) / 3);
608 }
609 
610 /* Map a color index to a r-g-b color for a mapped color memory device. */
611 int
mem_mapped_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])612 mem_mapped_map_color_rgb(gx_device * dev, gx_color_index color,
613 			 gx_color_value prgb[3])
614 {
615     gx_device_memory * const mdev = (gx_device_memory *)dev;
616     const byte *pptr = mdev->palette.data + (int)color * 3;
617 
618     prgb[0] = gx_color_value_from_byte(pptr[0]);
619     prgb[1] = gx_color_value_from_byte(pptr[1]);
620     prgb[2] = gx_color_value_from_byte(pptr[2]);
621     return 0;
622 }
623 
624 /*
625  * Implement draw_thin_line using a distinguished procedure that serves
626  * as the common marker for all memory devices.
627  */
628 int
mem_draw_thin_line(gx_device * dev,fixed fx0,fixed fy0,fixed fx1,fixed fy1,const gx_drawing_color * pdcolor,gs_logical_operation_t lop)629 mem_draw_thin_line(gx_device *dev, fixed fx0, fixed fy0, fixed fx1, fixed fy1,
630 		   const gx_drawing_color *pdcolor,
631 		   gs_logical_operation_t lop)
632 {
633     return gx_default_draw_thin_line(dev, fx0, fy0, fx1, fy1, pdcolor, lop);
634 }
635