/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Bitmap stretching functions. * * By Michael Bukin. * * See readme.txt for copyright information. */ #include "allegro.h" /* Information for stretching line (saved state of Bresenham line algorithm). */ static struct { int i1, i2; int dd, dw; int sinc; } _al_stretch; /* * Line stretchers. */ #define DECLARE_STRETCHER(type, size, set, get) \ int dd = _al_stretch.dd; \ type *s = (type*) sptr; \ uintptr_t d = dptr; \ uintptr_t dend = d + _al_stretch.dw; \ ASSERT(s); \ ASSERT(d); \ for (; d < dend; d += (size), s = (type *) \ ((unsigned char*)s + _al_stretch.sinc)) { \ set(d, get(s)); \ if (dd >= 0) { \ s = (type *) ((unsigned char*)s + (size)); \ dd += _al_stretch.i2; \ } \ else \ dd += _al_stretch.i1; \ } #ifdef GFX_HAS_VGA /* * Mode-X line stretcher. */ static void stretch_linex(uintptr_t dptr, unsigned char *sptr) { int plane; int dw = _al_stretch.dw; int first_dd = _al_stretch.dd; ASSERT(dptr); ASSERT(sptr); for (plane = 0; plane < 4; plane++) { int dd = first_dd; unsigned char *s = sptr; uintptr_t d = dptr / 4; uintptr_t dend = (dptr + dw) / 4; outportw(0x3C4, (0x100 << (dptr & 3)) | 2); for (; d < dend; d++, s += 4 * _al_stretch.sinc) { bmp_write8(d, *s); if (dd >= 0) s++, dd += _al_stretch.i2; else dd += _al_stretch.i1; if (dd >= 0) s++, dd += _al_stretch.i2; else dd += _al_stretch.i1; if (dd >= 0) s++, dd += _al_stretch.i2; else dd += _al_stretch.i1; if (dd >= 0) s++, dd += _al_stretch.i2; else dd += _al_stretch.i1; } /* Move to the beginning of next plane. */ if (first_dd >= 0) sptr++, first_dd += _al_stretch.i2; else first_dd += _al_stretch.i1; dptr++; sptr += _al_stretch.sinc; dw--; } } #endif #ifdef ALLEGRO_COLOR8 static void stretch_line8(uintptr_t dptr, unsigned char *sptr) { DECLARE_STRETCHER(unsigned char, 1, bmp_write8, *); } #endif #ifdef ALLEGRO_COLOR16 static void stretch_line15(uintptr_t dptr, unsigned char *sptr) { DECLARE_STRETCHER(unsigned short, sizeof(unsigned short), bmp_write15, *); } static void stretch_line16(uintptr_t dptr, unsigned char *sptr) { DECLARE_STRETCHER(unsigned short, sizeof(unsigned short), bmp_write16, *); } #endif #ifdef ALLEGRO_COLOR24 static void stretch_line24(uintptr_t dptr, unsigned char *sptr) { DECLARE_STRETCHER(unsigned char, 3, bmp_write24, READ3BYTES); } #endif #ifdef ALLEGRO_COLOR32 static void stretch_line32(uintptr_t dptr, unsigned char *sptr) { DECLARE_STRETCHER(uint32_t, sizeof(uint32_t), bmp_write32, *); } #endif /* * Masked line stretchers. */ #define DECLARE_MASKED_STRETCHER(type, size, set, get, mask) \ int dd = _al_stretch.dd; \ type *s = (type*) sptr; \ uintptr_t d = dptr; \ uintptr_t dend = d + _al_stretch.dw; \ ASSERT(s); \ ASSERT(d); \ for (; d < dend; d += (size), s = (type*) \ ((unsigned char*)s + _al_stretch.sinc)) { \ unsigned long color = get(s); \ if (color != (mask)) \ set(d, color); \ if (dd >= 0) { \ s = (type *) ((unsigned char*)s + (size)); \ dd += _al_stretch.i2; \ } \ else \ dd += _al_stretch.i1; \ } #ifdef GFX_HAS_VGA /* * Mode-X masked line stretcher. */ static void stretch_masked_linex(uintptr_t dptr, unsigned char *sptr) { int plane; int dw = _al_stretch.dw; int first_dd = _al_stretch.dd; ASSERT(dptr); ASSERT(sptr); for (plane = 0; plane < 4; plane++) { int dd = first_dd; unsigned char *s = sptr; uintptr_t d = dptr / 4; uintptr_t dend = (dptr + dw) / 4; outportw(0x3C4, (0x100 << (dptr & 3)) | 2); for (; d < dend; d++, s += 4 * _al_stretch.sinc) { unsigned long color = *s; if (color != 0) bmp_write8(d, color); if (dd >= 0) s++, dd += _al_stretch.i2; else dd += _al_stretch.i1; if (dd >= 0) s++, dd += _al_stretch.i2; else dd += _al_stretch.i1; if (dd >= 0) s++, dd += _al_stretch.i2; else dd += _al_stretch.i1; if (dd >= 0) s++, dd += _al_stretch.i2; else dd += _al_stretch.i1; } /* Move to the beginning of next plane. */ if (first_dd >= 0) sptr++, first_dd += _al_stretch.i2; else first_dd += _al_stretch.i1; dptr++; sptr += _al_stretch.sinc; dw--; } } #endif #ifdef ALLEGRO_COLOR8 static void stretch_masked_line8(uintptr_t dptr, unsigned char *sptr) { DECLARE_MASKED_STRETCHER(unsigned char, 1, bmp_write8, *, 0); } #endif #ifdef ALLEGRO_COLOR16 static void stretch_masked_line15(uintptr_t dptr, unsigned char *sptr) { DECLARE_MASKED_STRETCHER(unsigned short, sizeof(unsigned short), bmp_write15, *, MASK_COLOR_15); } static void stretch_masked_line16(uintptr_t dptr, unsigned char *sptr) { DECLARE_MASKED_STRETCHER(unsigned short, sizeof(unsigned short), bmp_write16, *, MASK_COLOR_16); } #endif #ifdef ALLEGRO_COLOR24 static void stretch_masked_line24(uintptr_t dptr, unsigned char *sptr) { DECLARE_MASKED_STRETCHER(unsigned char, 3, bmp_write24, READ3BYTES, MASK_COLOR_24); } #endif #ifdef ALLEGRO_COLOR32 static void stretch_masked_line32(uintptr_t dptr, unsigned char *sptr) { DECLARE_MASKED_STRETCHER(uint32_t, sizeof(uint32_t), bmp_write32, *, MASK_COLOR_32); } #endif /* * Stretch blit work-horse. */ static void _al_stretch_blit(BITMAP *src, BITMAP *dst, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int masked) { int x, y, fixup; int i1, i2, dd; int xinc, yinc; int dxbeg, dxend; int dybeg, dyend; int sxofs, dxofs; void (*stretch_line)(uintptr_t dptr, unsigned char *sptr); ASSERT(src); ASSERT(dst); /* vtable hook */ if (src->vtable->do_stretch_blit) { src->vtable->do_stretch_blit(src, dst, sx, sy, sw, sh, dx, dy, dw, dh, masked); return; } if ((sw <= 0) || (sh <= 0) || (dw <= 0) || (dh <= 0)) return; if (dst->clip) { dybeg = ((dy > dst->ct) ? dy : dst->ct); dyend = (((dy + dh) < dst->cb) ? (dy + dh) : dst->cb); if (dybeg >= dyend) return; dxbeg = ((dx > dst->cl) ? dx : dst->cl); dxend = (((dx + dw) < dst->cr) ? (dx + dw) : dst->cr); if (dxbeg >= dxend) return; } else { dxbeg = dx; dxend = dx + dw; dybeg = dy; dyend = dy + dh; } /* Bresenham algorithm uses difference between points, not number of points. */ sw--; sh--; dw--; dh--; /* How much to add to src-coordinates, when moving to the next dst-coordinate. */ if (dw == 0) xinc = 0; else { xinc = sw / dw; sw %= dw; } if (dh == 0) yinc = 0; else { yinc = sh / dh; sh %= dh; } /* Walk in x direction until dxbeg and save Bresenham state there. */ i2 = (dd = (i1 = 2 * sw) - dw) - dw; for (x = dx, y = sx; x < dxbeg; x++, y += xinc) { if (dd >= 0) y++, dd += i2; else dd += i1; } /* Save Bresenham algorithm state with offset fixups. */ switch (bitmap_color_depth(dst)) { case 15: case 16: fixup = sizeof(uint16_t); break; case 24: fixup = 3; break; case 32: fixup = sizeof(uint32_t); break; default: fixup = 1; break; } sxofs = y * fixup; dxofs = x * fixup; _al_stretch.i1 = i1; _al_stretch.i2 = i2; _al_stretch.dd = dd; _al_stretch.dw = (dxend - dxbeg) * fixup; _al_stretch.sinc = xinc * fixup; /* Find out which stretcher should be used. */ if (masked) { switch (bitmap_color_depth(dst)) { #ifdef ALLEGRO_COLOR8 case 8: if (is_linear_bitmap(dst)) stretch_line = stretch_masked_line8; else { #ifdef GFX_HAS_VGA stretch_line = stretch_masked_linex; #else return; #endif } break; #endif #ifdef ALLEGRO_COLOR16 case 15: stretch_line = stretch_masked_line15; break; case 16: stretch_line = stretch_masked_line16; break; #endif #ifdef ALLEGRO_COLOR24 case 24: stretch_line = stretch_masked_line24; break; #endif #ifdef ALLEGRO_COLOR32 case 32: stretch_line = stretch_masked_line32; break; #endif default: return; } } else { switch (bitmap_color_depth(dst)) { #ifdef ALLEGRO_COLOR8 case 8: if (is_linear_bitmap(dst)) stretch_line = stretch_line8; else { #ifdef GFX_HAS_VGA stretch_line = stretch_linex; #else return; #endif } break; #endif #ifdef ALLEGRO_COLOR16 case 15: stretch_line = stretch_line15; break; case 16: stretch_line = stretch_line16; break; #endif #ifdef ALLEGRO_COLOR24 case 24: stretch_line = stretch_line24; break; #endif #ifdef ALLEGRO_COLOR32 case 32: stretch_line = stretch_line32; break; #endif default: return; } } ASSERT(stretch_line); /* Walk in y direction until we reach first non-clipped line. */ i2 = (dd = (i1 = 2 * sh) - dh) - dh; for (x = dy, y = sy; x < dybeg; x++, y += yinc) { if (dd >= 0) y++, dd += i2; else dd += i1; } /* Stretch all non-clipped lines. */ bmp_select(dst); for (; x < dyend; x++, y += yinc) { (*stretch_line)(bmp_write_line(dst, x) + dxofs, src->line[y] + sxofs); if (dd >= 0) y++, dd += i2; else dd += i1; } bmp_unwrite_line(dst); } /* stretch_blit: * Opaque bitmap scaling function. */ void stretch_blit(BITMAP *src, BITMAP *dst, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh) { ASSERT(src); ASSERT(dst); #ifdef ALLEGRO_MPW if (is_system_bitmap(src) && is_system_bitmap(dst)) system_stretch_blit(src, dst, sx, sy, sw, sh, dx, dy, dw, dh); else #endif _al_stretch_blit(src, dst, sx, sy, sw, sh, dx, dy, dw, dh, 0); } /* masked_stretch_blit: * Masked bitmap scaling function. */ void masked_stretch_blit(BITMAP *src, BITMAP *dst, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh) { ASSERT(src); ASSERT(dst); _al_stretch_blit(src, dst, sx, sy, sw, sh, dx, dy, dw, dh, 1); } /* stretch_sprite: * Masked version of stretch_blit(). */ void stretch_sprite(BITMAP *dst, BITMAP *src, int x, int y, int w, int h) { ASSERT(src); ASSERT(dst); _al_stretch_blit(src, dst, 0, 0, src->w, src->h, x, y, w, h, 1); }