xref: /plan9/sys/src/cmd/gs/src/gdevm24.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1994, 1995, 1996, 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: gdevm24.c,v 1.4 2002/02/21 22:24:51 giles Exp $ */
18 /* 24-bit-per-pixel "memory" (stored bitmap) device */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gxdevice.h"
22 #include "gxdevmem.h"		/* semi-public definitions */
23 #include "gdevmem.h"		/* private definitions */
24 
25 #define mem_true24_strip_copy_rop mem_gray8_rgb24_strip_copy_rop
26 
27 /*
28  * Define whether to use the library's memset.
29  */
30 /*#define USE_MEMSET*/
31 /*
32  * Define whether to use memcpy for very wide fills.  We thought this
33  * made a big difference, but it turned out to be an artifact of the
34  * profiler.
35  */
36 /*#define USE_MEMCPY*/
37 
38 /* Define debugging statistics. */
39 #ifdef DEBUG
40 struct stats_mem24_s {
41     long
42 	fill, fwide, fgray[101], fsetc, fcolor[101], fnarrow[5],
43 	fprevc[257];
44     double ftotal;
45 } stats_mem24;
46 static int prev_count;
47 static uint prev_colors[256];
48 # define INCR(v) (++(stats_mem24.v))
49 #else
50 # define INCR(v) DO_NOTHING
51 #endif
52 
53 
54 /* ================ Standard (byte-oriented) device ================ */
55 
56 #undef chunk
57 #define chunk byte
58 
59 /* Procedures */
60 declare_mem_procs(mem_true24_copy_mono, mem_true24_copy_color, mem_true24_fill_rectangle);
61 private dev_proc_copy_alpha(mem_true24_copy_alpha);
62 
63 /* The device descriptor. */
64 const gx_device_memory mem_true24_device =
65 mem_full_alpha_device("image24", 24, 0, mem_open,
66 		 gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
67      mem_true24_copy_mono, mem_true24_copy_color, mem_true24_fill_rectangle,
68 		      gx_default_map_cmyk_color, mem_true24_copy_alpha,
69 		 gx_default_strip_tile_rectangle, mem_true24_strip_copy_rop,
70 		      mem_get_bits_rectangle);
71 
72 /* Convert x coordinate to byte offset in scan line. */
73 #undef x_to_byte
74 #define x_to_byte(x) ((x) * 3)
75 
76 /* Unpack a color into its bytes. */
77 #define declare_unpack_color(r, g, b, color)\
78 	byte r = (byte)(color >> 16);\
79 	byte g = (byte)((uint)color >> 8);\
80 	byte b = (byte)color
81 /* Put a 24-bit color into the bitmap. */
82 #define put3(ptr, r, g, b)\
83 	(ptr)[0] = r, (ptr)[1] = g, (ptr)[2] = b
84 /* Put 4 bytes of color into the bitmap. */
85 #define putw(ptr, wxyz)\
86 	*(bits32 *)(ptr) = (wxyz)
87 /* Load the 3-word 24-bit-color cache. */
88 /* Free variables: [m]dev, rgbr, gbrg, brgb. */
89 #if arch_is_big_endian
90 #  define set_color24_cache(crgb, r, g, b)\
91 	mdev->color24.rgbr = rgbr = ((bits32)(crgb) << 8) | (r),\
92 	mdev->color24.gbrg = gbrg = (rgbr << 8) | (g),\
93 	mdev->color24.brgb = brgb = (gbrg << 8) | (b),\
94 	mdev->color24.rgb = (crgb)
95 #else
96 #  define set_color24_cache(crgb, r, g, b)\
97 	mdev->color24.rgbr = rgbr =\
98 		((bits32)(r) << 24) | ((bits32)(b) << 16) |\
99 		((bits16)(g) << 8) | (r),\
100 	mdev->color24.brgb = brgb = (rgbr << 8) | (b),\
101 	mdev->color24.gbrg = gbrg = (brgb << 8) | (g),\
102 	mdev->color24.rgb = (crgb)
103 #endif
104 
105 /* Fill a rectangle with a color. */
106 private int
mem_true24_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)107 mem_true24_fill_rectangle(gx_device * dev,
108 			  int x, int y, int w, int h, gx_color_index color)
109 {
110     gx_device_memory * const mdev = (gx_device_memory *)dev;
111     declare_unpack_color(r, g, b, color);
112     declare_scan_ptr(dest);
113 
114     /*
115      * In order to avoid testing w > 0 and h > 0 twice, we defer
116      * executing setup_rect, and use fit_fill_xywh instead of
117      * fit_fill.
118      */
119     fit_fill_xywh(dev, x, y, w, h);
120     INCR(fill);
121 #ifdef DEBUG
122     stats_mem24.ftotal += w;
123 #endif
124     if (w >= 5) {
125 	if (h <= 0)
126 	    return 0;
127 	INCR(fwide);
128 	setup_rect(dest);
129 	if (r == g && r == b) {
130 #ifndef USE_MEMSET
131 	    /* We think we can do better than the library's memset.... */
132 	    int bcntm7 = w * 3 - 7;
133 	    register bits32 cword = color | (color << 24);
134 
135 	    INCR(fgray[min(w, 100)]);
136 	    while (h-- > 0) {
137 		register byte *pptr = dest;
138 		byte *limit = pptr + bcntm7;
139 
140 		/* We want to store full words, but we have to */
141 		/* guarantee that they are word-aligned. */
142 		switch (x & 3) {
143 		    case 3:
144 			*pptr++ = (byte) cword;
145 		    case 2:
146 			*pptr++ = (byte) cword;
147 		    case 1:
148 			*pptr++ = (byte) cword;
149 		    case 0:;
150 		}
151 		/* Even with w = 5, we always store at least */
152 		/* 3 full words, regardless of the starting x. */
153 		*(bits32 *) pptr =
154 		    ((bits32 *) pptr)[1] =
155 		    ((bits32 *) pptr)[2] = cword;
156 		pptr += 12;
157 		while (pptr < limit) {
158 		    *(bits32 *) pptr =
159 			((bits32 *) pptr)[1] = cword;
160 		    pptr += 8;
161 		}
162 		switch ((int)(pptr - limit)) {
163 		    case 0:
164 			pptr[6] = (byte) cword;
165 		    case 1:
166 			pptr[5] = (byte) cword;
167 		    case 2:
168 			pptr[4] = (byte) cword;
169 		    case 3:
170 			*(bits32 *) pptr = cword;
171 			break;
172 		    case 4:
173 			pptr[2] = (byte) cword;
174 		    case 5:
175 			pptr[1] = (byte) cword;
176 		    case 6:
177 			pptr[0] = (byte) cword;
178 		    case 7:;
179 		}
180 		inc_ptr(dest, draster);
181 	    }
182 #else
183 	    int bcnt = w * 3;
184 
185 	    INCR(fgray[min(w, 100)]);
186 	    while (h-- > 0) {
187 		memset(dest, r, bcnt);
188 		inc_ptr(dest, draster);
189 	    }
190 #endif
191 	} else {
192 	    int x3 = -x & 3, ww = w - x3;	/* we know ww >= 2 */
193 	    bits32 rgbr, gbrg, brgb;
194 
195 	    if (mdev->color24.rgb == color) {
196 		rgbr = mdev->color24.rgbr;
197 		gbrg = mdev->color24.gbrg;
198 		brgb = mdev->color24.brgb;
199 	    } else {
200 		INCR(fsetc);
201 		set_color24_cache(color, r, g, b);
202 	    }
203 #ifdef DEBUG
204 	    {
205 		int ci;
206 		for (ci = 0; ci < prev_count; ++ci)
207 		    if (prev_colors[ci] == color)
208 			break;
209 		INCR(fprevc[ci]);
210 		if (ci == prev_count) {
211 		    if (ci < countof(prev_colors))
212 			++prev_count;
213 		    else
214 			--ci;
215 		}
216 		if (ci) {
217 		    memmove(&prev_colors[1], &prev_colors[0],
218 			    ci * sizeof(prev_colors[0]));
219 		    prev_colors[0] = color;
220 		}
221 	    }
222 #endif
223 	    INCR(fcolor[min(w, 100)]);
224 	    while (h-- > 0) {
225 		register byte *pptr = dest;
226 		int w1 = ww;
227 
228 		switch (x3) {
229 		    case 1:
230 			put3(pptr, r, g, b);
231 			pptr += 3;
232 			break;
233 		    case 2:
234 			pptr[0] = r;
235 			pptr[1] = g;
236 			putw(pptr + 2, brgb);
237 			pptr += 6;
238 			break;
239 		    case 3:
240 			pptr[0] = r;
241 			putw(pptr + 1, gbrg);
242 			putw(pptr + 5, brgb);
243 			pptr += 9;
244 			break;
245 		    case 0:
246 			;
247 		}
248 #ifdef USE_MEMCPY
249 		/*
250 		 * For very wide fills, it's most efficient to fill a few
251 		 * pixels and then use memcpy to fill the rest.
252 		 */
253 		if (w1 > 16) {
254 #define PUTW4(ptr, w)\
255   BEGIN\
256     putw(ptr, w); putw((ptr)+12, w); putw((ptr)+24, w); putw((ptr)+36, w);\
257   END
258 		    PUTW4(pptr, rgbr);
259 		    PUTW4(pptr + 4, gbrg);
260 		    PUTW4(pptr + 8, brgb);
261 #undef PUTW4
262 		    if (w1 > 64) {
263 			memcpy(pptr + 48, pptr, 48);
264 			memcpy(pptr + 96, pptr, 96);
265 			for (pptr += 192; (w1 -= 64) >= 64; pptr += 192)
266 			    memcpy(pptr, pptr - 192, 192);
267 		    } else
268 			pptr += 48;
269 		    for (; (w1 -= 16) >= 16; pptr += 48)
270 			memcpy(pptr, pptr - 48, 48);
271 		}
272 #endif
273 		while (w1 >= 4) {
274 		    putw(pptr, rgbr);
275 		    putw(pptr + 4, gbrg);
276 		    putw(pptr + 8, brgb);
277 		    pptr += 12;
278 		    w1 -= 4;
279 		}
280 		switch (w1) {
281 		    case 1:
282 			put3(pptr, r, g, b);
283 			break;
284 		    case 2:
285 			putw(pptr, rgbr);
286 			pptr[4] = g;
287 			pptr[5] = b;
288 			break;
289 		    case 3:
290 			putw(pptr, rgbr);
291 			putw(pptr + 4, gbrg);
292 			pptr[8] = b;
293 			break;
294 		    case 0:
295 			;
296 		}
297 		inc_ptr(dest, draster);
298 	    }
299 	}
300     } else if (h > 0) {		/* w < 5 */
301 	INCR(fnarrow[max(w, 0)]);
302 	setup_rect(dest);
303 	switch (w) {
304 	    case 4:
305 		do {
306 		    dest[9] = dest[6] = dest[3] = dest[0] = r;
307 		    dest[10] = dest[7] = dest[4] = dest[1] = g;
308 		    dest[11] = dest[8] = dest[5] = dest[2] = b;
309 		    inc_ptr(dest, draster);
310 		}
311 		while (--h);
312 		break;
313 	    case 3:
314 		do {
315 		    dest[6] = dest[3] = dest[0] = r;
316 		    dest[7] = dest[4] = dest[1] = g;
317 		    dest[8] = dest[5] = dest[2] = b;
318 		    inc_ptr(dest, draster);
319 		}
320 		while (--h);
321 		break;
322 	    case 2:
323 		do {
324 		    dest[3] = dest[0] = r;
325 		    dest[4] = dest[1] = g;
326 		    dest[5] = dest[2] = b;
327 		    inc_ptr(dest, draster);
328 		}
329 		while (--h);
330 		break;
331 	    case 1:
332 		do {
333 		    dest[0] = r, dest[1] = g, dest[2] = b;
334 		    inc_ptr(dest, draster);
335 		}
336 		while (--h);
337 		break;
338 	    case 0:
339 	    default:
340 		;
341 	}
342     }
343     return 0;
344 }
345 
346 /* Copy a monochrome bitmap. */
347 private int
mem_true24_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)348 mem_true24_copy_mono(gx_device * dev,
349 	       const byte * base, int sourcex, int sraster, gx_bitmap_id id,
350 	int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
351 {
352     gx_device_memory * const mdev = (gx_device_memory *)dev;
353     const byte *line;
354     int sbit;
355     int first_bit;
356 
357     declare_scan_ptr(dest);
358 
359     fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
360     setup_rect(dest);
361     line = base + (sourcex >> 3);
362     sbit = sourcex & 7;
363     first_bit = 0x80 >> sbit;
364     if (zero != gx_no_color_index) {	/* Loop for halftones or inverted masks */
365 	/* (never used). */
366 	declare_unpack_color(r0, g0, b0, zero);
367 	declare_unpack_color(r1, g1, b1, one);
368 	while (h-- > 0) {
369 	    register byte *pptr = dest;
370 	    const byte *sptr = line;
371 	    register int sbyte = *sptr++;
372 	    register int bit = first_bit;
373 	    int count = w;
374 
375 	    do {
376 		if (sbyte & bit) {
377 		    if (one != gx_no_color_index)
378 			put3(pptr, r1, g1, b1);
379 		} else
380 		    put3(pptr, r0, g0, b0);
381 		pptr += 3;
382 		if ((bit >>= 1) == 0)
383 		    bit = 0x80, sbyte = *sptr++;
384 	    }
385 	    while (--count > 0);
386 	    line += sraster;
387 	    inc_ptr(dest, draster);
388 	}
389     } else if (one != gx_no_color_index) {	/* Loop for character and pattern masks. */
390 	/* This is used heavily. */
391 	declare_unpack_color(r1, g1, b1, one);
392 	int first_mask = first_bit << 1;
393 	int first_count, first_skip;
394 
395 	if (sbit + w > 8)
396 	    first_mask -= 1,
397 		first_count = 8 - sbit;
398 	else
399 	    first_mask -= first_mask >> w,
400 		first_count = w;
401 	first_skip = first_count * 3;
402 	while (h-- > 0) {
403 	    register byte *pptr = dest;
404 	    const byte *sptr = line;
405 	    register int sbyte = *sptr++ & first_mask;
406 	    int count = w - first_count;
407 
408 	    if (sbyte) {
409 		register int bit = first_bit;
410 
411 		do {
412 		    if (sbyte & bit)
413 			put3(pptr, r1, g1, b1);
414 		    pptr += 3;
415 		}
416 		while ((bit >>= 1) & first_mask);
417 	    } else
418 		pptr += first_skip;
419 	    while (count >= 8) {
420 		sbyte = *sptr++;
421 		if (sbyte & 0xf0) {
422 		    if (sbyte & 0x80)
423 			put3(pptr, r1, g1, b1);
424 		    if (sbyte & 0x40)
425 			put3(pptr + 3, r1, g1, b1);
426 		    if (sbyte & 0x20)
427 			put3(pptr + 6, r1, g1, b1);
428 		    if (sbyte & 0x10)
429 			put3(pptr + 9, r1, g1, b1);
430 		}
431 		if (sbyte & 0xf) {
432 		    if (sbyte & 8)
433 			put3(pptr + 12, r1, g1, b1);
434 		    if (sbyte & 4)
435 			put3(pptr + 15, r1, g1, b1);
436 		    if (sbyte & 2)
437 			put3(pptr + 18, r1, g1, b1);
438 		    if (sbyte & 1)
439 			put3(pptr + 21, r1, g1, b1);
440 		}
441 		pptr += 24;
442 		count -= 8;
443 	    }
444 	    if (count > 0) {
445 		register int bit = 0x80;
446 
447 		sbyte = *sptr++;
448 		do {
449 		    if (sbyte & bit)
450 			put3(pptr, r1, g1, b1);
451 		    pptr += 3;
452 		    bit >>= 1;
453 		}
454 		while (--count > 0);
455 	    }
456 	    line += sraster;
457 	    inc_ptr(dest, draster);
458 	}
459     }
460     return 0;
461 }
462 
463 /* Copy a color bitmap. */
464 private int
mem_true24_copy_color(gx_device * dev,const byte * base,int sourcex,int sraster,gx_bitmap_id id,int x,int y,int w,int h)465 mem_true24_copy_color(gx_device * dev,
466 	       const byte * base, int sourcex, int sraster, gx_bitmap_id id,
467 		      int x, int y, int w, int h)
468 {
469     gx_device_memory * const mdev = (gx_device_memory *)dev;
470 
471     fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
472     mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h);
473     return 0;
474 }
475 
476 /* Copy an alpha map. */
477 private int
mem_true24_copy_alpha(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 color,int depth)478 mem_true24_copy_alpha(gx_device * dev, const byte * base, int sourcex,
479 		   int sraster, gx_bitmap_id id, int x, int y, int w, int h,
480 		      gx_color_index color, int depth)
481 {
482     gx_device_memory * const mdev = (gx_device_memory *)dev;
483     const byte *line;
484 
485     declare_scan_ptr(dest);
486     declare_unpack_color(r, g, b, color);
487 
488     fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
489     setup_rect(dest);
490     line = base;
491     while (h-- > 0) {
492 	register byte *pptr = dest;
493 	int sx;
494 
495 	for (sx = sourcex; sx < sourcex + w; ++sx, pptr += 3) {
496 	    int alpha2, alpha;
497 
498 	    if (depth == 2)	/* map 0 - 3 to 0 - 15 */
499 		alpha =
500 		    ((line[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 5;
501 	    else
502 		alpha2 = line[sx >> 1],
503 		    alpha = (sx & 1 ? alpha2 & 0xf : alpha2 >> 4);
504 	    if (alpha == 15) {	/* Just write the new color. */
505 		put3(pptr, r, g, b);
506 	    } else if (alpha != 0) {	/* Blend RGB values. */
507 #define make_shade(old, clr, alpha, amax) \
508   (old) + (((int)(clr) - (int)(old)) * (alpha) / (amax))
509 		pptr[0] = make_shade(pptr[0], r, alpha, 15);
510 		pptr[1] = make_shade(pptr[1], g, alpha, 15);
511 		pptr[2] = make_shade(pptr[2], b, alpha, 15);
512 #undef make_shade
513 	    }
514 	}
515 	line += sraster;
516 	inc_ptr(dest, draster);
517     }
518     return 0;
519 }
520 
521 /* ================ "Word"-oriented device ================ */
522 
523 /* Note that on a big-endian machine, this is the same as the */
524 /* standard byte-oriented-device. */
525 
526 #if !arch_is_big_endian
527 
528 /* Procedures */
529 declare_mem_procs(mem24_word_copy_mono, mem24_word_copy_color, mem24_word_fill_rectangle);
530 
531 /* Here is the device descriptor. */
532 const gx_device_memory mem_true24_word_device =
533 mem_full_device("image24w", 24, 0, mem_open,
534 		gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
535      mem24_word_copy_mono, mem24_word_copy_color, mem24_word_fill_rectangle,
536 		gx_default_map_cmyk_color, gx_default_strip_tile_rectangle,
537 		gx_no_strip_copy_rop, mem_word_get_bits_rectangle);
538 
539 /* Fill a rectangle with a color. */
540 private int
mem24_word_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)541 mem24_word_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
542 			  gx_color_index color)
543 {
544     gx_device_memory * const mdev = (gx_device_memory *)dev;
545     byte *base;
546     uint raster;
547 
548     fit_fill(dev, x, y, w, h);
549     base = scan_line_base(mdev, y);
550     raster = mdev->raster;
551     mem_swap_byte_rect(base, raster, x * 24, w * 24, h, true);
552     mem_true24_fill_rectangle(dev, x, y, w, h, color);
553     mem_swap_byte_rect(base, raster, x * 24, w * 24, h, false);
554     return 0;
555 }
556 
557 /* Copy a bitmap. */
558 private int
mem24_word_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)559 mem24_word_copy_mono(gx_device * dev,
560 	       const byte * base, int sourcex, int sraster, gx_bitmap_id id,
561 	int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
562 {
563     gx_device_memory * const mdev = (gx_device_memory *)dev;
564     byte *row;
565     uint raster;
566     bool store;
567 
568     fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
569     row = scan_line_base(mdev, y);
570     raster = mdev->raster;
571     store = (zero != gx_no_color_index && one != gx_no_color_index);
572     mem_swap_byte_rect(row, raster, x * 24, w * 24, h, store);
573     mem_true24_copy_mono(dev, base, sourcex, sraster, id,
574 			 x, y, w, h, zero, one);
575     mem_swap_byte_rect(row, raster, x * 24, w * 24, h, false);
576     return 0;
577 }
578 
579 /* Copy a color bitmap. */
580 private int
mem24_word_copy_color(gx_device * dev,const byte * base,int sourcex,int sraster,gx_bitmap_id id,int x,int y,int w,int h)581 mem24_word_copy_color(gx_device * dev,
582 	       const byte * base, int sourcex, int sraster, gx_bitmap_id id,
583 		      int x, int y, int w, int h)
584 {
585     gx_device_memory * const mdev = (gx_device_memory *)dev;
586     byte *row;
587     uint raster;
588 
589     fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
590     row = scan_line_base(mdev, y);
591     raster = mdev->raster;
592     mem_swap_byte_rect(row, raster, x * 24, w * 24, h, true);
593     bytes_copy_rectangle(row + x * 3, raster, base + sourcex * 3, sraster,
594 			 w * 3, h);
595     mem_swap_byte_rect(row, raster, x * 24, w * 24, h, false);
596     return 0;
597 }
598 
599 #endif /* !arch_is_big_endian */
600