/* Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevm24.c,v 1.2.6.1.2.1 2003/01/17 00:49:00 giles Exp $ */ /* 24-bit-per-pixel "memory" (stored bitmap) device */ #include "memory_.h" #include "gx.h" #include "gxdevice.h" #include "gxdevmem.h" /* semi-public definitions */ #include "gdevmem.h" /* private definitions */ #define mem_true24_strip_copy_rop mem_gray8_rgb24_strip_copy_rop /* * Define whether to use the library's memset. */ /*#define USE_MEMSET*/ /* * Define whether to use memcpy for very wide fills. We thought this * made a big difference, but it turned out to be an artifact of the * profiler. */ /*#define USE_MEMCPY*/ /* Define debugging statistics. */ #ifdef DEBUG struct stats_mem24_s { long fill, fwide, fgray[101], fsetc, fcolor[101], fnarrow[5], fprevc[257]; double ftotal; } stats_mem24; static int prev_count; static uint prev_colors[256]; # define INCR(v) (++(stats_mem24.v)) #else # define INCR(v) DO_NOTHING #endif /* ================ Standard (byte-oriented) device ================ */ #undef chunk #define chunk byte /* Procedures */ declare_mem_procs(mem_true24_copy_mono, mem_true24_copy_color, mem_true24_fill_rectangle); private dev_proc_copy_alpha(mem_true24_copy_alpha); /* The device descriptor. */ const gx_device_memory mem_true24_device = mem_full_alpha_device("image24", 24, 0, mem_open, gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb, mem_true24_copy_mono, mem_true24_copy_color, mem_true24_fill_rectangle, gx_default_map_cmyk_color, mem_true24_copy_alpha, gx_default_strip_tile_rectangle, mem_true24_strip_copy_rop, mem_get_bits_rectangle); /* Convert x coordinate to byte offset in scan line. */ #undef x_to_byte #define x_to_byte(x) ((x) * 3) /* Unpack a color into its bytes. */ #define declare_unpack_color(r, g, b, color)\ byte r = (byte)(color >> 16);\ byte g = (byte)((uint)color >> 8);\ byte b = (byte)color /* Put a 24-bit color into the bitmap. */ #define put3(ptr, r, g, b)\ (ptr)[0] = r, (ptr)[1] = g, (ptr)[2] = b /* Put 4 bytes of color into the bitmap. */ #define putw(ptr, wxyz)\ *(bits32 *)(ptr) = (wxyz) /* Load the 3-word 24-bit-color cache. */ /* Free variables: [m]dev, rgbr, gbrg, brgb. */ #if arch_is_big_endian # define set_color24_cache(crgb, r, g, b)\ mdev->color24.rgbr = rgbr = ((bits32)(crgb) << 8) | (r),\ mdev->color24.gbrg = gbrg = (rgbr << 8) | (g),\ mdev->color24.brgb = brgb = (gbrg << 8) | (b),\ mdev->color24.rgb = (crgb) #else # define set_color24_cache(crgb, r, g, b)\ mdev->color24.rgbr = rgbr =\ ((bits32)(r) << 24) | ((bits32)(b) << 16) |\ ((bits16)(g) << 8) | (r),\ mdev->color24.brgb = brgb = (rgbr << 8) | (b),\ mdev->color24.gbrg = gbrg = (brgb << 8) | (g),\ mdev->color24.rgb = (crgb) #endif /* Fill a rectangle with a color. */ private int mem_true24_fill_rectangle(gx_device * dev, int x, int y, int w, int h, gx_color_index color) { gx_device_memory * const mdev = (gx_device_memory *)dev; declare_unpack_color(r, g, b, color); declare_scan_ptr(dest); /* * In order to avoid testing w > 0 and h > 0 twice, we defer * executing setup_rect, and use fit_fill_xywh instead of * fit_fill. */ fit_fill_xywh(dev, x, y, w, h); INCR(fill); #ifdef DEBUG stats_mem24.ftotal += w; #endif if (w >= 5) { if (h <= 0) return 0; INCR(fwide); setup_rect(dest); if (r == g && r == b) { #ifndef USE_MEMSET /* We think we can do better than the library's memset.... */ int bcntm7 = w * 3 - 7; register bits32 cword = color | (color << 24); INCR(fgray[min(w, 100)]); while (h-- > 0) { register byte *pptr = dest; byte *limit = pptr + bcntm7; /* We want to store full words, but we have to */ /* guarantee that they are word-aligned. */ switch (x & 3) { case 3: *pptr++ = (byte) cword; case 2: *pptr++ = (byte) cword; case 1: *pptr++ = (byte) cword; case 0:; } /* Even with w = 5, we always store at least */ /* 3 full words, regardless of the starting x. */ *(bits32 *) pptr = ((bits32 *) pptr)[1] = ((bits32 *) pptr)[2] = cword; pptr += 12; while (pptr < limit) { *(bits32 *) pptr = ((bits32 *) pptr)[1] = cword; pptr += 8; } switch ((int)(pptr - limit)) { case 0: pptr[6] = (byte) cword; case 1: pptr[5] = (byte) cword; case 2: pptr[4] = (byte) cword; case 3: *(bits32 *) pptr = cword; break; case 4: pptr[2] = (byte) cword; case 5: pptr[1] = (byte) cword; case 6: pptr[0] = (byte) cword; case 7:; } inc_ptr(dest, draster); } #else int bcnt = w * 3; INCR(fgray[min(w, 100)]); while (h-- > 0) { memset(dest, r, bcnt); inc_ptr(dest, draster); } #endif } else { int x3 = -x & 3, ww = w - x3; /* we know ww >= 2 */ bits32 rgbr, gbrg, brgb; if (mdev->color24.rgb == color) { rgbr = mdev->color24.rgbr; gbrg = mdev->color24.gbrg; brgb = mdev->color24.brgb; } else { INCR(fsetc); set_color24_cache(color, r, g, b); } #ifdef DEBUG { int ci; for (ci = 0; ci < prev_count; ++ci) if (prev_colors[ci] == color) break; INCR(fprevc[ci]); if (ci == prev_count) { if (ci < countof(prev_colors)) ++prev_count; else --ci; } if (ci) { memmove(&prev_colors[1], &prev_colors[0], ci * sizeof(prev_colors[0])); prev_colors[0] = color; } } #endif INCR(fcolor[min(w, 100)]); while (h-- > 0) { register byte *pptr = dest; int w1 = ww; switch (x3) { case 1: put3(pptr, r, g, b); pptr += 3; break; case 2: pptr[0] = r; pptr[1] = g; putw(pptr + 2, brgb); pptr += 6; break; case 3: pptr[0] = r; putw(pptr + 1, gbrg); putw(pptr + 5, brgb); pptr += 9; break; case 0: ; } #ifdef USE_MEMCPY /* * For very wide fills, it's most efficient to fill a few * pixels and then use memcpy to fill the rest. */ if (w1 > 16) { #define PUTW4(ptr, w)\ BEGIN\ putw(ptr, w); putw((ptr)+12, w); putw((ptr)+24, w); putw((ptr)+36, w);\ END PUTW4(pptr, rgbr); PUTW4(pptr + 4, gbrg); PUTW4(pptr + 8, brgb); #undef PUTW4 if (w1 > 64) { memcpy(pptr + 48, pptr, 48); memcpy(pptr + 96, pptr, 96); for (pptr += 192; (w1 -= 64) >= 64; pptr += 192) memcpy(pptr, pptr - 192, 192); } else pptr += 48; for (; (w1 -= 16) >= 16; pptr += 48) memcpy(pptr, pptr - 48, 48); } #endif while (w1 >= 4) { putw(pptr, rgbr); putw(pptr + 4, gbrg); putw(pptr + 8, brgb); pptr += 12; w1 -= 4; } switch (w1) { case 1: put3(pptr, r, g, b); break; case 2: putw(pptr, rgbr); pptr[4] = g; pptr[5] = b; break; case 3: putw(pptr, rgbr); putw(pptr + 4, gbrg); pptr[8] = b; break; case 0: ; } inc_ptr(dest, draster); } } } else if (h > 0) { /* w < 5 */ INCR(fnarrow[max(w, 0)]); setup_rect(dest); switch (w) { case 4: do { dest[9] = dest[6] = dest[3] = dest[0] = r; dest[10] = dest[7] = dest[4] = dest[1] = g; dest[11] = dest[8] = dest[5] = dest[2] = b; inc_ptr(dest, draster); } while (--h); break; case 3: do { dest[6] = dest[3] = dest[0] = r; dest[7] = dest[4] = dest[1] = g; dest[8] = dest[5] = dest[2] = b; inc_ptr(dest, draster); } while (--h); break; case 2: do { dest[3] = dest[0] = r; dest[4] = dest[1] = g; dest[5] = dest[2] = b; inc_ptr(dest, draster); } while (--h); break; case 1: do { dest[0] = r, dest[1] = g, dest[2] = b; inc_ptr(dest, draster); } while (--h); break; case 0: default: ; } } return 0; } /* Copy a monochrome bitmap. */ 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) { gx_device_memory * const mdev = (gx_device_memory *)dev; const byte *line; int sbit; int first_bit; declare_scan_ptr(dest); fit_copy(dev, base, sourcex, sraster, id, x, y, w, h); setup_rect(dest); line = base + (sourcex >> 3); sbit = sourcex & 7; first_bit = 0x80 >> sbit; if (zero != gx_no_color_index) { /* Loop for halftones or inverted masks */ /* (never used). */ declare_unpack_color(r0, g0, b0, zero); declare_unpack_color(r1, g1, b1, one); while (h-- > 0) { register byte *pptr = dest; const byte *sptr = line; register int sbyte = *sptr++; register int bit = first_bit; int count = w; do { if (sbyte & bit) { if (one != gx_no_color_index) put3(pptr, r1, g1, b1); } else put3(pptr, r0, g0, b0); pptr += 3; if ((bit >>= 1) == 0) bit = 0x80, sbyte = *sptr++; } while (--count > 0); line += sraster; inc_ptr(dest, draster); } } else if (one != gx_no_color_index) { /* Loop for character and pattern masks. */ /* This is used heavily. */ declare_unpack_color(r1, g1, b1, one); int first_mask = first_bit << 1; int first_count, first_skip; if (sbit + w > 8) first_mask -= 1, first_count = 8 - sbit; else first_mask -= first_mask >> w, first_count = w; first_skip = first_count * 3; while (h-- > 0) { register byte *pptr = dest; const byte *sptr = line; register int sbyte = *sptr++ & first_mask; int count = w - first_count; if (sbyte) { register int bit = first_bit; do { if (sbyte & bit) put3(pptr, r1, g1, b1); pptr += 3; } while ((bit >>= 1) & first_mask); } else pptr += first_skip; while (count >= 8) { sbyte = *sptr++; if (sbyte & 0xf0) { if (sbyte & 0x80) put3(pptr, r1, g1, b1); if (sbyte & 0x40) put3(pptr + 3, r1, g1, b1); if (sbyte & 0x20) put3(pptr + 6, r1, g1, b1); if (sbyte & 0x10) put3(pptr + 9, r1, g1, b1); } if (sbyte & 0xf) { if (sbyte & 8) put3(pptr + 12, r1, g1, b1); if (sbyte & 4) put3(pptr + 15, r1, g1, b1); if (sbyte & 2) put3(pptr + 18, r1, g1, b1); if (sbyte & 1) put3(pptr + 21, r1, g1, b1); } pptr += 24; count -= 8; } if (count > 0) { register int bit = 0x80; sbyte = *sptr++; do { if (sbyte & bit) put3(pptr, r1, g1, b1); pptr += 3; bit >>= 1; } while (--count > 0); } line += sraster; inc_ptr(dest, draster); } } return 0; } /* Copy a color bitmap. */ 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) { gx_device_memory * const mdev = (gx_device_memory *)dev; fit_copy(dev, base, sourcex, sraster, id, x, y, w, h); mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h); return 0; } /* Copy an alpha map. */ 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) { gx_device_memory * const mdev = (gx_device_memory *)dev; const byte *line; declare_scan_ptr(dest); declare_unpack_color(r, g, b, color); fit_copy(dev, base, sourcex, sraster, id, x, y, w, h); setup_rect(dest); line = base; while (h-- > 0) { register byte *pptr = dest; int sx; for (sx = sourcex; sx < sourcex + w; ++sx, pptr += 3) { int alpha2, alpha; if (depth == 2) /* map 0 - 3 to 0 - 15 */ alpha = ((line[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 5; else alpha2 = line[sx >> 1], alpha = (sx & 1 ? alpha2 & 0xf : alpha2 >> 4); if (alpha == 15) { /* Just write the new color. */ put3(pptr, r, g, b); } else if (alpha != 0) { /* Blend RGB values. */ #define make_shade(old, clr, alpha, amax) \ (old) + (((int)(clr) - (int)(old)) * (alpha) / (amax)) pptr[0] = make_shade(pptr[0], r, alpha, 15); pptr[1] = make_shade(pptr[1], g, alpha, 15); pptr[2] = make_shade(pptr[2], b, alpha, 15); #undef make_shade } } line += sraster; inc_ptr(dest, draster); } return 0; } /* ================ "Word"-oriented device ================ */ /* Note that on a big-endian machine, this is the same as the */ /* standard byte-oriented-device. */ #if !arch_is_big_endian /* Procedures */ declare_mem_procs(mem24_word_copy_mono, mem24_word_copy_color, mem24_word_fill_rectangle); /* Here is the device descriptor. */ const gx_device_memory mem_true24_word_device = mem_full_device("image24w", 24, 0, mem_open, gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb, mem24_word_copy_mono, mem24_word_copy_color, mem24_word_fill_rectangle, gx_default_map_cmyk_color, gx_default_strip_tile_rectangle, gx_no_strip_copy_rop, mem_word_get_bits_rectangle); /* Fill a rectangle with a color. */ private int mem24_word_fill_rectangle(gx_device * dev, int x, int y, int w, int h, gx_color_index color) { gx_device_memory * const mdev = (gx_device_memory *)dev; byte *base; uint raster; fit_fill(dev, x, y, w, h); base = scan_line_base(mdev, y); raster = mdev->raster; mem_swap_byte_rect(base, raster, x * 24, w * 24, h, true); mem_true24_fill_rectangle(dev, x, y, w, h, color); mem_swap_byte_rect(base, raster, x * 24, w * 24, h, false); return 0; } /* Copy a bitmap. */ 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) { gx_device_memory * const mdev = (gx_device_memory *)dev; byte *row; uint raster; bool store; fit_copy(dev, base, sourcex, sraster, id, x, y, w, h); row = scan_line_base(mdev, y); raster = mdev->raster; store = (zero != gx_no_color_index && one != gx_no_color_index); mem_swap_byte_rect(row, raster, x * 24, w * 24, h, store); mem_true24_copy_mono(dev, base, sourcex, sraster, id, x, y, w, h, zero, one); mem_swap_byte_rect(row, raster, x * 24, w * 24, h, false); return 0; } /* Copy a color bitmap. */ 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) { gx_device_memory * const mdev = (gx_device_memory *)dev; byte *row; uint raster; fit_copy(dev, base, sourcex, sraster, id, x, y, w, h); row = scan_line_base(mdev, y); raster = mdev->raster; mem_swap_byte_rect(row, raster, x * 24, w * 24, h, true); bytes_copy_rectangle(row + x * 3, raster, base + sourcex * 3, sraster, w * 3, h); mem_swap_byte_rect(row, raster, x * 24, w * 24, h, false); return 0; } #endif /* !arch_is_big_endian */