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