/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Bitmap stretching functions for the i386. * * By Shawn Hargreaves. * * See readme.txt for copyright information. */ #include #include #include #include #include #include "allegro.h" #include "allegro/internal/aintern.h" #include "opcodes.h" #ifdef ALLEGRO_WINDOWS #include "winalleg.h" /* For VirtualProtect */ #endif /* ifdef ALLEGRO_WINDOWS */ /* helper macro for generating stretchers in each color depth */ #define DECLARE_STRETCHER(sz, mask, scale) \ { \ int x, x2; \ int c; \ \ if (sxd == itofix(1)) { /* easy case for 1 -> 1 scaling */ \ if (masked) { \ for (c=0; c itofix(1)) { /* big -> little scaling */ \ for (x=0; x> 16) + 1; \ sx += sxd; \ x2 = (sx >> 16) - x2; \ if (x2*scale > 1) { \ COMPILER_ADD_ESI(x2*scale); \ } \ else if (x2*scale == 1) { \ COMPILER_INC_ESI(); \ } \ } \ } \ else { /* little -> big scaling */ \ x2 = sx >> 16; \ COMPILER_LODS##sz(); \ for (x=0; x> 16) > x2) { \ COMPILER_LODS##sz(); \ x2++; \ } \ } \ } \ } /* make_stretcher_256: * Helper for constructing a 256 color stretcher routine. */ static int make_stretcher_256(int compiler_pos, fixed sx, fixed sxd, int dest_width, int masked) { #ifdef ALLEGRO_COLOR8 DECLARE_STRETCHER(B, mask, 1); #endif return compiler_pos; } /* make_stretcher_15: * Helper for constructing a 15 bit stretcher routine. */ static int make_stretcher_15(int compiler_pos, fixed sx, fixed sxd, int dest_width, int masked) { #ifdef ALLEGRO_COLOR16 DECLARE_STRETCHER(W, MASK_COLOR_15, 2); #endif return compiler_pos; } /* make_stretcher_16: * Helper for constructing a 16 bit stretcher routine. */ static int make_stretcher_16(int compiler_pos, fixed sx, fixed sxd, int dest_width, int masked) { #ifdef ALLEGRO_COLOR16 DECLARE_STRETCHER(W, MASK_COLOR_16, 2); #endif return compiler_pos; } /* make_stretcher_24: * Helper for constructing a 24 bit stretcher routine. */ static int make_stretcher_24(int compiler_pos, fixed sx, fixed sxd, int dest_width, int masked) { #ifdef ALLEGRO_COLOR24 DECLARE_STRETCHER(L2, MASK_COLOR_24, 3); #endif return compiler_pos; } /* make_stretcher_32: * Helper for constructing a 32 bit stretcher routine. */ static int make_stretcher_32(int compiler_pos, fixed sx, fixed sxd, int dest_width, int masked) { #ifdef ALLEGRO_COLOR32 DECLARE_STRETCHER(L, MASK_COLOR_32, 4); #endif return compiler_pos; } /* make_stretcher: * Helper function for stretch_blit(). Builds a machine code stretching * routine in the scratch memory area. */ static int make_stretcher(int compiler_pos, fixed sx, fixed sxd, int dest_width, int masked, int color_depth) { if (dest_width > 0) { switch (color_depth) { case 8: return make_stretcher_256(compiler_pos, sx, sxd, dest_width, masked); case 15: return make_stretcher_15(compiler_pos, sx, sxd, dest_width, masked); case 16: return make_stretcher_16(compiler_pos, sx, sxd, dest_width, masked); case 24: return make_stretcher_24(compiler_pos, sx, sxd, dest_width, masked); case 32: return make_stretcher_32(compiler_pos, sx, sxd, dest_width, masked); } } return compiler_pos; } /* cache of previously constructed stretcher functions */ typedef struct STRETCHER_INFO { fixed sx; fixed sxd; short dest_width; char depth; char flags; int lru; void *data; int size; } STRETCHER_INFO; #define NUM_STRETCHERS 8 static STRETCHER_INFO stretcher_info[NUM_STRETCHERS] = { { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 } }; static int stretcher_count = 0; static int stretcher_virgin = TRUE; /* free_stretchers: * Clean up atexit. */ static void free_stretchers(void) { int i; for (i=0; ivtable->do_stretch_blit) { source->vtable->do_stretch_blit(source, dest, source_x, source_y, source_width, source_height, dest_x, dest_y, dest_width, dest_height, masked); return; } /* trivial reject for zero sizes */ if ((source_width <= 0) || (source_height <= 0) || (dest_width <= 0) || (dest_height <= 0)) return; /* convert to fixed point */ sx = itofix(source_x); sy = itofix(source_y); /* calculate delta values */ sxd = itofix(source_width) / dest_width; syd = itofix(source_height) / dest_height; /* clip? */ if (dest->clip) { while (dest_x < dest->cl) { dest_x++; dest_width--; sx += sxd; } while (dest_y < dest->ct) { dest_y++; dest_height--; sy += syd; } if (dest_x+dest_width > dest->cr) dest_width = dest->cr - dest_x; if (dest_y+dest_height > dest->cb) dest_height = dest->cb - dest_y; if ((dest_width <= 0) || (dest_height <= 0)) return; } /* compensate for a problem where the first column or row can get * an extra pixel than it should do for little -> big scaling, due * to fixed point number representation imprecisions */ if (sxd < itofix(1)) sx += (sxd >> 1); if (syd < itofix(1)) sy += (syd >> 1); /* search the cache */ stretcher_count++; if (stretcher_count <= 0) { stretcher_count = 1; for (i=0; ivtable->color_depth) && (stretcher_info[i].flags == flags)) { /* use a previously generated routine */ if (stretcher_info[i].flags & 2) dest_x >>= 2; _do_stretch(source, dest, stretcher_info[i].data, sx>>16, sy, syd, dest_x, dest_y, dest_height, dest->vtable->color_depth); stretcher_info[i].lru = stretcher_count; return; } else { /* decide which cached routine to discard */ if (stretcher_info[i].lru < best_lru) { best = i; best_lru = stretcher_info[i].lru; } } } /* make sure all allocated memory is freed atexit */ if (stretcher_virgin) { stretcher_virgin = FALSE; _add_exit_func(free_stretchers, "free_stretchers"); } prev_scratch_mem = _scratch_mem; prev_scratch_mem_size = _scratch_mem_size; _scratch_mem = stretcher_info[best].data; _scratch_mem_size = stretcher_info[best].size; if (is_linear_bitmap(dest)) { /* build a simple linear stretcher */ compiler_pos = make_stretcher(0, sx, sxd, dest_width, masked, dest->vtable->color_depth); } #ifdef GFX_HAS_VGA else { int plane, d; /* build four stretchers, one for each mode-X plane */ for (plane=0; plane<4; plane++) { COMPILER_PUSH_ESI(); COMPILER_PUSH_EDI(); COMPILER_MOV_EAX((0x100<<((dest_x+plane)&3))|2); COMPILER_MOV_EDX(0x3C4); COMPILER_OUTW(); compiler_pos = make_stretcher(compiler_pos, sx+sxd*plane, sxd<<2, (dest_width-plane+3)>>2, masked, 8); COMPILER_POP_EDI(); COMPILER_POP_ESI(); if (((dest_x+plane) & 3) == 3) { COMPILER_INC_EDI(); } d = ((sx+sxd*(plane+1))>>16) - ((sx+sxd*plane)>>16); if (d > 0) { COMPILER_ADD_ESI(d); } } dest_x >>= 2; } #endif /* ifdef GFX_HAS_VGA */ COMPILER_RET(); #ifdef ALLEGRO_WINDOWS /* Play nice with Windows executable memory protection */ VirtualProtect(_scratch_mem, _scratch_mem_size, PAGE_EXECUTE_READWRITE, &old_protect); #endif /* ifdef ALLEGRO_WINDOWS */ /* call the stretcher */ _do_stretch(source, dest, _scratch_mem, sx>>16, sy, syd, dest_x, dest_y, dest_height, dest->vtable->color_depth); /* and store it in the cache */ stretcher_info[best].sx = sx; stretcher_info[best].sxd = sxd; stretcher_info[best].dest_width = dest_width; stretcher_info[best].depth = dest->vtable->color_depth; stretcher_info[best].flags = flags; stretcher_info[best].lru = stretcher_count; stretcher_info[best].data = _scratch_mem; stretcher_info[best].size = _scratch_mem_size; _scratch_mem = prev_scratch_mem; _scratch_mem_size = prev_scratch_mem_size; } /* stretch_blit: * Opaque bitmap scaling function. */ void stretch_blit(BITMAP *s, BITMAP *d, int s_x, int s_y, int s_w, int s_h, int d_x, int d_y, int d_w, int d_h) { do_stretch_blit(s, d, s_x, s_y, s_w, s_h, d_x, d_y, d_w, d_h, 0); } /* masked_stretch_blit: * Masked bitmap scaling function. */ void masked_stretch_blit(BITMAP *s, BITMAP *d, int s_x, int s_y, int s_w, int s_h, int d_x, int d_y, int d_w, int d_h) { do_stretch_blit(s, d, s_x, s_y, s_w, s_h, d_x, d_y, d_w, d_h, 1); } /* stretch_sprite: * Masked version of stretch_blit(). */ void stretch_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, int w, int h) { do_stretch_blit(sprite, bmp, 0, 0, sprite->w, sprite->h, x, y, w, h, 1); }