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