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