#include "stdafx.h" #include "ttd.h" #include "spritecache.h" #include "strings.h" #include "gfx.h" #include "table/palettes.h" #include "hal.h" static void GfxMainBlitter(const Sprite *sprite, int x, int y, int mode); static int _stringwidth_out; static byte _cursor_backup[64*64]; static Rect _invalid_rect; static const byte *_color_remap_ptr; static byte _string_colorremap[3]; #define DIRTY_BYTES_PER_LINE (MAX_SCREEN_WIDTH/64) static byte _dirty_blocks[DIRTY_BYTES_PER_LINE * MAX_SCREEN_HEIGHT / 8]; void memcpy_pitch(void *d, void *s, int w, int h, int spitch, int dpitch) { byte *dp = (byte*)d; byte *sp = (byte*)s; assert(h >= 0); for (; h != 0; --h) { memcpy(dp, sp, w); dp += dpitch; sp += spitch; } } void GfxScroll(int left, int top, int width, int height, int xo, int yo) { byte *src, *dst; int p; int ht; if (xo == 0 && yo == 0) return; if (_cursor.visible) UndrawMouseCursor(); UndrawTextMessage(); p = _screen.pitch; if (yo > 0) { // Calculate pointers dst = _screen.dst_ptr + (top + height - 1) * p + left; src = dst - yo * p; // Decrease height and increase top top += yo; height -= yo; assert(height > 0); // Adjust left & width if (xo >= 0) { dst += xo; left += xo; width -= xo; } else { src -= xo; width += xo; } for (ht = height; ht > 0; --ht) { memcpy(dst, src, width); src -= p; dst -= p; } } else { // Calculate pointers dst = _screen.dst_ptr + top * p + left; src = dst - yo * p; // Decrese height. (yo is <=0). height += yo; assert(height > 0); // Adjust left & width if (xo >= 0) { dst += xo; left += xo; width -= xo; } else { src -= xo; width += xo; } // the y-displacement may be 0 therefore we have to use memmove, // because source and destination may overlap for (ht = height; ht > 0; --ht) { memmove(dst, src, width); src += p; dst += p; } } // This part of the screen is now dirty. _video_driver->make_dirty(left, top, width, height); } void GfxFillRect(int left, int top, int right, int bottom, int color) { DrawPixelInfo *dpi = _cur_dpi; byte *dst; const int otop = top; const int oleft = left; if (dpi->zoom != 0) return; if (left > right || top > bottom) return; if (right < dpi->left || left >= dpi->left + dpi->width) return; if (bottom < dpi->top || top >= dpi->top + dpi->height) return; if ( (left -= dpi->left) < 0) left = 0; right = right - dpi->left + 1; if (right > dpi->width) right=dpi->width; right -= left; assert(right > 0); if ( (top -= dpi->top) < 0) top = 0; bottom = bottom - dpi->top + 1; if (bottom > dpi->height) bottom=dpi->height; bottom -= top; assert(bottom > 0); dst = dpi->dst_ptr + top * dpi->pitch + left; if (!(color & 0x8000)) { if (!(color & 0x4000)) { do { memset(dst, color, right); dst += dpi->pitch; } while (--bottom); } else { /* use colortable mode */ const byte* ctab = GetNonSprite(color & 0x3FFF) + 1; do { int i; for(i=0; i!=right;i++) dst[i] = ctab[dst[i]]; dst += dpi->pitch; } while (--bottom); } } else { byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1; do { int i; for (i = (bo ^= 1); i < right; i += 2) dst[i] = (byte)color; dst += dpi->pitch; } while (--bottom > 0); } } static void GfxSetPixel(int x, int y, int color) { DrawPixelInfo *dpi = _cur_dpi; if ((x-=dpi->left) < 0 || x>=dpi->width || (y-=dpi->top)<0 || y>=dpi->height) return; dpi->dst_ptr[y*dpi->pitch+x] = color; } void GfxDrawLine(int x, int y, int x2, int y2, int color) { int dy; int dx; int stepx, stepy; int frac; // Check clipping first { DrawPixelInfo *dpi = _cur_dpi; int t; if (x < dpi->left && x2 < dpi->left) return; if (y < dpi->top && y2 < dpi->top) return; t = dpi->left + dpi->width; if (x > t && x2 > t) return; t = dpi->top + dpi->height; if (y > t && y2 > t) return; } dy = (y2 - y) * 2; if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } dx = (x2 - x) * 2; if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } GfxSetPixel(x, y, color); if (dx > dy) { frac = dy - (dx >> 1); while (x != x2) { if (frac >= 0) { y += stepy; frac -= dx; } x += stepx; frac += dy; GfxSetPixel(x, y, color); } } else { frac = dx - (dy >> 1); while (y != y2) { if (frac >= 0) { x += stepx; frac -= dy; } y += stepy; frac += dx; GfxSetPixel(x, y, color); } } } // ASSIGNMENT OF ASCII LETTERS < 32 // 0 - end of string // 1 - SETX <BYTE> // 2 - SETXY <BYTE> <BYTE> // 3-7 - // 8 - TINYFONT // 9 - BIGFONT // 10 - newline // 11-14 - // 15-31 - 17 colors enum { ASCII_SETX = 1, ASCII_SETXY = 2, ASCII_TINYFONT = 8, ASCII_BIGFONT = 9, ASCII_NL = 10, ASCII_COLORSTART = 15, }; /* returns right coordinate */ int DrawString(int x, int y, uint16 str, uint16 color) { char buffer[512]; GetString(buffer, str); return DoDrawString(buffer, x, y, color); } void DrawStringRightAligned(int x, int y, uint16 str, uint16 color) { char buffer[512]; GetString(buffer, str); DoDrawString(buffer, x - GetStringWidth(buffer), y, color); } int DrawStringCentered(int x, int y, uint16 str, uint16 color) { char buffer[512]; int w; GetString(buffer, str); w = GetStringWidth(buffer); DoDrawString(buffer, x - w / 2, y, color); return w; } void DrawStringCenterUnderline(int x, int y, uint16 str, uint16 color) { int w = DrawStringCentered(x, y, str, color); GfxFillRect(x-(w>>1), y+10, x-(w>>1)+w, y+10, _string_colorremap[1]); } static uint32 FormatStringLinebreaks(char *str, int maxw) { int num = 0; int base = _stringwidth_base; int w; char *last_space; byte c; for(;;) { w = 0; last_space = NULL; for(;;) { c = *str++; if (c == ASCII_LETTERSTART) last_space = str; if (c >= ASCII_LETTERSTART) { w += GetCharacterWidth(base + (byte)c); if (w > maxw) { str = last_space; if (str == NULL) return num + (base << 16); break; } } else { if (c == 0) return num + (base << 16); if (c == ASCII_NL) break; if (c == ASCII_SETX) str++; else if (c == ASCII_SETXY) str += 2; else if (c == ASCII_TINYFONT) base = 224; else if (c == ASCII_BIGFONT) base = 448; } } num++; str[-1] = 0; } } void DrawStringMultiCenter(int x, int y, uint16 str, int maxw) { char buffer[512]; uint32 tmp; int num, w, mt, t; const char *src; byte c; GetString(buffer, str); tmp = FormatStringLinebreaks(buffer, maxw); num = (uint16)tmp; t = tmp >> 16; mt = 10; if (t != 0) { mt = 6; if (t != 244) mt = 18; } y -= (mt >> 1) * num; src = buffer; for(;;) { w = GetStringWidth(src); DoDrawString(src, x - (w>>1), y, 0xFE); _stringwidth_base = _stringwidth_out; for(;;) { c = *src++; if (c == 0) { y += mt; if (--num < 0) { _stringwidth_base = 0; return; } break; } else if (c == ASCII_SETX) { src++; } else if (c == ASCII_SETXY) { src+=2; } } } } void DrawStringMultiLine(int x, int y, uint16 str, int maxw) { char buffer[512]; uint32 tmp; int num, w, mt, t; const char *src; byte c; GetString(buffer, str); tmp = FormatStringLinebreaks(buffer, maxw); num = (uint16)tmp; t = tmp >> 16; mt = 10; if (t != 0) { mt = 6; if (t != 244) mt = 18; } src = buffer; for(;;) { w = GetStringWidth(src); DoDrawString(src, x, y, 0xFE); _stringwidth_base = _stringwidth_out; for(;;) { c = *src++; if (c == 0) { y += mt; if (--num < 0) { _stringwidth_base = 0; return; } break; } else if (c == ASCII_SETX) { src++; } else if (c == ASCII_SETXY) { src+=2; } } } } int GetStringWidth(const char *str) { int w = 0; byte c; int base = _stringwidth_base; for (c = *str; c != '\0'; c = *(++str)) { if (c >= ASCII_LETTERSTART) { w += GetCharacterWidth(base + c); } else { if (c == ASCII_SETX) str++; else if (c == ASCII_SETXY) str += 2; else if (c == ASCII_TINYFONT) base = 224; else if (c == ASCII_BIGFONT) base = 448; } } return w; } void DrawFrameRect(int left, int top, int right, int bottom, int ctab, int flags) { byte color_2 = _color_list[ctab].window_color_1a; byte color_interior = _color_list[ctab].window_color_bga; byte color_3 = _color_list[ctab].window_color_bgb; byte color = _color_list[ctab].window_color_2; if (!(flags & 0x8)) { if (!(flags & 0x20)) { GfxFillRect(left, top, left, bottom-1, color); GfxFillRect(left+1, top, right-1, top, color); GfxFillRect(right, top, right, bottom-1, color_2); GfxFillRect(left, bottom, right, bottom, color_2); if (!(flags & 0x10)) { GfxFillRect(left+1, top+1, right-1, bottom-1, color_interior); } } else { GfxFillRect(left, top, left, bottom, color_2); GfxFillRect(left+1, top, right, top, color_2); GfxFillRect(right, top+1, right, bottom-1, color); GfxFillRect(left+1, bottom, right, bottom, color); if (!(flags & 0x10)) { GfxFillRect(left+1, top+1, right-1, bottom-1, flags&0x40 ? color_interior : color_3); } } } else if (flags & 0x1) { // transparency GfxFillRect(left, top, right, bottom, 0x4322); } else { GfxFillRect(left, top, right, bottom, color_interior); } } int DoDrawString(const char *string, int x, int y, uint16 real_color) { DrawPixelInfo *dpi = _cur_dpi; int base = _stringwidth_base; byte c; byte color; int xo = x, yo = y; color = real_color & 0xFF; if (color != 0xFE) { if (x >= dpi->left + dpi->width || x + _screen.width*2 <= dpi->left || y >= dpi->top + dpi->height || y + _screen.height <= dpi->top) return x; if (color != 0xFF) { switch_color:; if (real_color & IS_PALETTE_COLOR) { _string_colorremap[1] = color; _string_colorremap[2] = 215; } else { _string_colorremap[1] = _string_colormap[color].text; _string_colorremap[2] = _string_colormap[color].shadow; } _color_remap_ptr = _string_colorremap; } } check_bounds: if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) { skip_char:; for(;;) { c = *string++; if (c < ASCII_LETTERSTART) goto skip_cont; } } for(;;) { c = *string++; skip_cont:; if (c == 0) { _stringwidth_out = base; return x; } if (c >= ASCII_LETTERSTART) { if (x >= dpi->left + dpi->width) goto skip_char; if (x + 26 >= dpi->left) { GfxMainBlitter(GetSprite(base + 2 + c - ASCII_LETTERSTART), x, y, 1); } x += GetCharacterWidth(base + c); } else if (c == ASCII_NL) { // newline = {} x = xo; y += 10; if (base != 0) { y -= 4; if (base != 0xE0) y += 12; } goto check_bounds; } else if (c >= ASCII_COLORSTART) { // change color? color = (byte)(c - ASCII_COLORSTART); goto switch_color; } else if (c == ASCII_SETX) { // {SETX} x = xo + (byte)*string++; } else if (c == ASCII_SETXY) {// {SETXY} x = xo + (byte)*string++; y = yo + (byte)*string++; } else if (c == ASCII_TINYFONT) { // {TINYFONT} base = 0xE0; } else if (c == ASCII_BIGFONT) { // {BIGFONT} base = 0x1C0; } else { printf("Unknown string command character %d\n", c); } } } void DrawSprite(uint32 img, int x, int y) { if (img & 0x8000) { _color_remap_ptr = GetNonSprite(img >> 16) + 1; GfxMainBlitter(GetSprite(img & 0x3FFF), x, y, 1); } else if (img & 0x4000) { _color_remap_ptr = GetNonSprite(img >> 16) + 1; GfxMainBlitter(GetSprite(img & 0x3FFF), x, y, 2); } else { GfxMainBlitter(GetSprite(img & 0x3FFF), x, y, 0); } } typedef struct BlitterParams { int start_x, start_y; const byte* sprite; const byte* sprite_org; byte *dst; int mode; int width, height; int width_org, height_org; int pitch; byte info; } BlitterParams; static void GfxBlitTileZoomIn(BlitterParams *bp) { const byte* src_o = bp->sprite; const byte* src; int num, skip; byte done; byte *dst; const byte* ctab; if (bp->mode & 1) { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); do { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src = src_o + 2; src_o += num + 2; dst = bp->dst; if ( (skip -= bp->start_x) > 0) { dst += skip; } else { src -= skip; num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } ctab = _color_remap_ptr; while (num >= 4) { dst[3] = ctab[src[3]]; dst[2] = ctab[src[2]]; dst[1] = ctab[src[1]]; dst[0] = ctab[src[0]]; dst += 4; src += 4; num -= 4; } while (num) { *dst++ = ctab[*src++]; num--; } } while (!(done & 0x80)); bp->dst += bp->pitch; } while (--bp->height); } else if (bp->mode & 2) { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); do { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src_o += num + 2; dst = bp->dst; if ( (skip -= bp->start_x) > 0) { dst += skip; } else { num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } ctab = _color_remap_ptr; while (num) { *dst = ctab[*dst]; dst++; num--; } } while (!(done & 0x80)); bp->dst += bp->pitch; } while (--bp->height); } else { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); do { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src = src_o + 2; src_o += num + 2; dst = bp->dst; if ( (skip -= bp->start_x) > 0) { dst += skip; } else { src -= skip; num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } #if defined(_WIN32) if (num & 1) *dst++ = *src++; if (num & 2) { *(uint16*)dst = *(uint16*)src; dst += 2; src += 2; } if (num >>= 2) { do { *(uint32*)dst = *(uint32*)src; dst += 4; src += 4; } while (--num); } #else memcpy(dst, src, num); #endif } while (!(done & 0x80)); bp->dst += bp->pitch; } while (--bp->height); } } static void GfxBlitZoomInUncomp(BlitterParams *bp) { const byte *src = bp->sprite; byte *dst = bp->dst; int height = bp->height; int width = bp->width; int i; assert(height > 0); assert(width > 0); if (bp->mode & 1) { if (bp->info & 1) { const byte *ctab = _color_remap_ptr; byte b; do { for(i=0; i!=width; i++) { if ((b=ctab[src[i]]) != 0) dst[i] = b; } src += bp->width_org; dst += bp->pitch; } while (--height); } } else if (bp->mode & 2) { if (bp->info & 1) { const byte *ctab = _color_remap_ptr; do { for(i=0; i!=width; i++) if (src[i]) dst[i] = ctab[dst[i]]; src += bp->width_org; dst += bp->pitch; } while (--height); } } else { if (!(bp->info & 1)) { do { memcpy(dst, src, width); src += bp->width_org; dst += bp->pitch; } while (--height); } else { do { int n = width; while (n >= 4) { if (src[0]) dst[0] = src[0]; if (src[1]) dst[1] = src[1]; if (src[2]) dst[2] = src[2]; if (src[3]) dst[3] = src[3]; dst += 4; src += 4; n -= 4; } while (n) { if (src[0]) dst[0] = src[0]; src++; dst++; n--; } src += bp->width_org - width; dst += bp->pitch - width; } while (--height); } } } static void GfxBlitTileZoomMedium(BlitterParams *bp) { const byte* src_o = bp->sprite; const byte* src; int num, skip; byte done; byte *dst; const byte* ctab; if (bp->mode & 1) { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); do { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src = src_o + 2; src_o += num + 2; dst = bp->dst; if (skip & 1) { skip++; src++; if (!--num) continue; } if ( (skip -= bp->start_x) > 0) { dst += skip >> 1; } else { src -= skip; num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } ctab = _color_remap_ptr; num = (num + 1) >> 1; if (num) { do { *dst = ctab[*src]; dst++; src+=2; } while (--num); } } while (!(done & 0x80)); bp->dst += bp->pitch; if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); } while (--bp->height); } else if (bp->mode & 2) { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); do { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src_o += num + 2; dst = bp->dst; if (skip & 1) { skip++; if (!--num) continue; } if ( (skip -= bp->start_x) > 0) { dst += skip >> 1; } else { num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } ctab = _color_remap_ptr; num = (num + 1) >> 1; if (num) { do { *dst = ctab[*dst]; dst++; } while (--num); } } while (!(done & 0x80)); bp->dst += bp->pitch; if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); } while (--bp->height); } else { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); do { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src = src_o + 2; src_o += num + 2; dst = bp->dst; if (skip & 1) { skip++; src++; if (!--num) continue; } if ( (skip -= bp->start_x) > 0) { dst += skip >> 1; } else { src -= skip; num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } num = (num + 1) >> 1; if (num) { do { *dst = *src; dst++; src+=2; } while (--num); } } while (!(done & 0x80)); bp->dst += bp->pitch; if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); } while (--bp->height); } } static void GfxBlitZoomMediumUncomp(BlitterParams *bp) { const byte *src = bp->sprite; byte *dst = bp->dst; int height = bp->height; int width = bp->width; int i; assert(height > 0); assert(width > 0); if (bp->mode & 1) { if (bp->info & 1) { const byte *ctab = _color_remap_ptr; byte b; height >>= 1; if (height) do { for(i=0; i!=width>>1; i++) if ((b=ctab[src[i*2]]) != 0) dst[i] = b; src += bp->width_org * 2; dst += bp->pitch; } while (--height); } } else if (bp->mode & 2) { if (bp->info & 1) { const byte *ctab = _color_remap_ptr; height >>= 1; if (height) do { for(i=0; i!=width>>1; i++) if (src[i*2]) dst[i] = ctab[dst[i]]; src += bp->width_org * 2; dst += bp->pitch; } while (--height); } } else { if (bp->info & 1) { height >>= 1; if (height) do { for(i=0; i!=width>>1; i++) if (src[i*2]) dst[i] = src[i*2]; src += bp->width_org * 2; dst += bp->pitch; } while (--height); } } } static void GfxBlitTileZoomOut(BlitterParams *bp) { const byte* src_o = bp->sprite; const byte* src; int num, skip; byte done; byte *dst; const byte* ctab; if (bp->mode & 1) { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); for(;;) { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src = src_o + 2; src_o += num + 2; dst = bp->dst; if (skip & 1) { skip++; src++; if (!--num) continue; } if (skip & 2) { skip+=2; src+=2; if ((num-=2) <= 0) continue; } if ( (skip -= bp->start_x) > 0) { dst += skip >> 2; } else { src -= skip; num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } ctab = _color_remap_ptr; num = (num + 3) >> 2; if (num) { do { *dst = ctab[*src]; dst++; src+=4; } while (--num); } } while (!(done & 0x80)); bp->dst += bp->pitch; if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; } } else if (bp->mode & 2) { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); for(;;) { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src_o += num + 2; dst = bp->dst; if (skip & 1) { skip++; if (!--num) continue; } if (skip & 2) { skip+=2; if ((num-=2) <= 0) continue; } if ( (skip -= bp->start_x) > 0) { dst += skip >> 2; } else { num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } ctab = _color_remap_ptr; num = (num + 3) >> 2; if (num) { do { *dst = ctab[*dst]; dst++; } while (--num); } } while (!(done & 0x80)); bp->dst += bp->pitch; if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; } } else { src_o += READ_LE_UINT16(src_o + bp->start_y * 2); for(;;) { do { done = src_o[0]; num = done & 0x7F; skip = src_o[1]; src = src_o + 2; src_o += num + 2; dst = bp->dst; if (skip & 1) { skip++; src++; if (!--num) continue; } if (skip & 2) { skip+=2; src+=2; if ((num-=2) <= 0) continue; } if ( (skip -= bp->start_x) > 0) { dst += skip >> 2; } else { src -= skip; num += skip; if (num <= 0) continue; skip = 0; } skip = skip + num - bp->width; if (skip > 0) { num -= skip; if (num <= 0) continue; } num = (num + 3) >> 2; if (num) { do { *dst = *src; dst++; src+=4; } while (--num); } } while (!(done & 0x80)); bp->dst += bp->pitch; if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; do { done = src_o[0]; src_o += (done & 0x7F) + 2; } while (!(done & 0x80)); if (!--bp->height) return; } } } static void GfxBlitZoomOutUncomp(BlitterParams *bp) { const byte* src = bp->sprite; byte *dst = bp->dst; int height = bp->height; int width = bp->width; int i; assert(height > 0); assert(width > 0); if (bp->mode & 1) { if (bp->info & 1) { const byte *ctab = _color_remap_ptr; byte b; height >>= 2; if (height) do { for(i=0; i!=width>>2; i++) if ((b=ctab[src[i*4]]) != 0) dst[i] = b; src += bp->width_org * 4; dst += bp->pitch; } while (--height); } } else if (bp->mode & 2) { if (bp->info & 1) { const byte *ctab = _color_remap_ptr; height >>= 2; if (height) do { for(i=0; i!=width>>2; i++) if (src[i*4]) dst[i] = ctab[dst[i]]; src += bp->width_org * 4; dst += bp->pitch; } while (--height); } } else { if (bp->info & 1) { height >>= 2; if (height) do { for(i=0; i!=width>>2; i++) if (src[i*4]) dst[i] = src[i*4]; src += bp->width_org * 4; dst += bp->pitch; } while (--height); } } } typedef void (*BlitZoomFunc)(BlitterParams *bp); static void GfxMainBlitter(const Sprite* sprite, int x, int y, int mode) { DrawPixelInfo *dpi = _cur_dpi; int start_x, start_y; byte info; BlitterParams bp; int zoom_mask = ~((1 << (dpi->zoom))-1); static const BlitZoomFunc zf_tile[3] = { GfxBlitTileZoomIn, GfxBlitTileZoomMedium, GfxBlitTileZoomOut }; static const BlitZoomFunc zf_uncomp[3] = { GfxBlitZoomInUncomp, GfxBlitZoomMediumUncomp, GfxBlitZoomOutUncomp }; /* decode sprite header */ x += sprite->x_offs; y += sprite->y_offs; bp.width_org = bp.width = sprite->width; bp.height_org = bp.height = sprite->height; info = sprite->info; bp.info = info; bp.sprite_org = bp.sprite = sprite->data; bp.dst = dpi->dst_ptr; bp.mode = mode; bp.pitch = dpi->pitch; assert(bp.height > 0); assert(bp.width > 0); if (info & 8) { /* tile blit */ start_y = 0; if (dpi->zoom > 0) { start_y += bp.height &~ zoom_mask; bp.height &= zoom_mask; if (bp.height == 0) return; y&=zoom_mask; } if ( (y -= dpi->top) < 0) { if ((bp.height += y) <= 0) return; start_y -= y; y = 0; } else { bp.dst += bp.pitch * (y>>(dpi->zoom)); } bp.start_y = start_y; if ( (y = y + bp.height - dpi->height) > 0) { if ( (bp.height -= y) <= 0) return; } start_x = 0; x &= zoom_mask; if ( (x -= dpi->left) < 0) { if ((bp.width += x) <= 0) return; start_x -= x; x = 0; } bp.start_x = start_x; bp.dst += x>>(dpi->zoom); if ( (x = x + bp.width - dpi->width) > 0) { if ( (bp.width -= x) <= 0) return; } zf_tile[dpi->zoom](&bp); } else { bp.sprite += bp.width * (bp.height & ~zoom_mask); bp.height &= zoom_mask; if (bp.height == 0) return; y &= zoom_mask; if ( (y -= dpi->top) < 0) { if ((bp.height += y) <= 0) return; bp.sprite -= bp.width * y; y = 0; } else { bp.dst += bp.pitch * (y>>(dpi->zoom)); } if ( (y = y + bp.height - dpi->height) > 0) { if ( (bp.height -= y) <= 0) return; } start_x = 0; x &= zoom_mask; if ( (x -= dpi->left) < 0) { if ((bp.width += x) <= 0) return; start_x -= x; bp.sprite -= x; x = 0; } bp.dst += x>>(dpi->zoom); if ( (x = x + bp.width - dpi->width) > 0) { if ( (bp.width -= x) <= 0) return; start_x += x; } bp.start_x = start_x; if (info&2) { int totpix = bp.height_org * bp.width_org; byte *dst = (byte*)alloca(totpix); const byte *src = bp.sprite_org; signed char b; bp.sprite = dst + (bp.sprite - bp.sprite_org); while (totpix != 0) { assert(totpix > 0); b = *src++; if (b >= 0) { int i, count=b; for(i=0; i!=count; i++) dst[i] = src[i]; dst += count; src += count; totpix -= count; } else { byte *tmp = dst- (((b&7)<<8)|(*src++)); int i; int count = -(b >> 3); for(i=0; i!=count; i++) dst[i] = tmp[i]; dst += count; totpix -= count; } } } zf_uncomp[dpi->zoom](&bp); } } #if 0 static void GfxScalePalette(int pal, byte scaling) { byte *dst, *src; size_t count; GfxInitPalettes(); dst = _cur_palette; src = GET_PALETTE(pal); count = 256; do { dst[0] = (byte)(src[0] * scaling >> 8); dst[1] = (byte)(src[1] * scaling >> 8); dst[2] = (byte)(src[2] * scaling >> 8); dst += 3; src += 3; } while (--count); } #endif void DoPaletteAnimations(void); void GfxInitPalettes(void) { int pal = _use_dos_palette?1:0; memcpy(_cur_palette, _palettes[pal], 256*3); _pal_first_dirty = 0; _pal_last_dirty = 255; DoPaletteAnimations(); } #define EXTR(p,q) (((uint16)(_timer_counter * (p)) * (q)) >> 16) #define EXTR2(p,q) (((uint16)(~_timer_counter * (p)) * (q)) >> 16) #define COPY_TRIPLET do {d[0]=s[0+j]; d[1]=s[1+j]; d[2]=s[2+j];d+=3;}while(0) void DoPaletteAnimations(void) { const byte *s; byte *d; /* Amount of colors to be rotated. * A few more for the DOS palette, because the water colors are * 245-254 for DOS and 217-226 for Windows. */ int c = _use_dos_palette?38:28; int j; int i; const ExtraPaletteValues *ev = &_extra_palette_values; byte old_val[114]; // max(c*(38:28)) = 114 d = _cur_palette + 217*3; memcpy(old_val, d, c*3); // Dark blue water s = ev->a; if (_opt.landscape == LT_CANDY) s = ev->ac; j = EXTR(320,5) * 3; for(i=0; i!=5; i++) { COPY_TRIPLET; j+=3; if (j == 15) j = 0; } // Glittery water s = ev->b; if (_opt.landscape == LT_CANDY) s = ev->bc; j = EXTR(128, 15) * 3; for(i=0; i!=5; i++) { COPY_TRIPLET; j += 9; if (j >= 45) j -= 45; } s = ev->e; j = EXTR2(512, 5) * 3; for(i=0; i!=5; i++) { COPY_TRIPLET; j += 3; if (j == 3*5) j = 0; } // Oil refinery fire animation s = ev->oil_ref; j = EXTR2(512, 7) * 3; for(i=0; i!=7; i++) { COPY_TRIPLET; j += 3; if (j == 3*7) j = 0; } // Radio tower blinking { byte i,v; i = (_timer_counter >> 1) & 0x7F; (v = 255, i < 0x3f) || (v = 128, i < 0x4A || i >= 0x75) || (v = 20); d[0] = v; d[1] = d[2] = 0; d += 3; i ^= 0x40; (v = 255, i < 0x3f) || (v = 128, i < 0x4A || i >= 0x75) || (v = 20); d[0] = v; d[1] = d[2] = 0; d += 3; } // Handle lighthouse and stadium animation s = ev->lighthouse; j = EXTR(256, 4) * 3; for(i=0; i!=4; i++) { COPY_TRIPLET; j += 3; if (j == 3*4) j = 0; } // Animate water for old DOS graphics if(_use_dos_palette) { // Dark blue water DOS s = ev->a; if (_opt.landscape == LT_CANDY) s = ev->ac; j = EXTR(320,5) * 3; for(i=0; i!=5; i++) { COPY_TRIPLET; j+=3; if (j == 15) j = 0; } // Glittery water DOS s = ev->b; if (_opt.landscape == LT_CANDY) s = ev->bc; j = EXTR(128, 15) * 3; for(i=0; i!=5; i++) { COPY_TRIPLET; j += 9; if (j >= 45) j -= 45; } } if (memcmp(old_val, _cur_palette + 217*3, c*3)) { if (_pal_first_dirty > 217) _pal_first_dirty = 217; if (_pal_last_dirty < 217+c) _pal_last_dirty = 217+c; } } void LoadStringWidthTable(void) { int i; byte *b; b = _stringwidth_table; // 2 equals space. for(i=2; i != 0xE2; i++) { *b++ = (byte)((i < 93 || i >= 129 || i == 98) ? GetSprite(i)->width : 0); } for(i=0xE2; i != 0x1C2; i++) { *b++ = (byte)((i < 317 || i >= 353) ? GetSprite(i)->width + 1 : 0); } for(i=0x1C2; i != 0x2A2; i++) { *b++ = (byte)((i < 541 || i >= 577) ? GetSprite(i)->width + 1 : 0); } } void ScreenSizeChanged(void) { // check the dirty rect if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width; if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height; // screen size changed and the old bitmap is invalid now, so we don't want to undraw it _cursor.visible = false; } void UndrawMouseCursor(void) { if (_cursor.visible) { _cursor.visible = false; memcpy_pitch( _screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch, _cursor_backup, _cursor.draw_size.x, _cursor.draw_size.y, _cursor.draw_size.x, _screen.pitch); _video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y); } } void DrawMouseCursor(void) { int x,y,w,h; // Don't draw the mouse cursor if it's already drawn if (_cursor.visible) { if (!_cursor.dirty) return; UndrawMouseCursor(); } w = _cursor.size.x; x = _cursor.pos.x + _cursor.offs.x; if (x < 0) { w += x; x=0; } if (w>_screen.width-x) { w = _screen.width-x; } if (w <= 0) return; _cursor.draw_pos.x = x; _cursor.draw_size.x = w; h = _cursor.size.y; y = _cursor.pos.y + _cursor.offs.y; if (y < 0) { h += y; y=0; } if (h>_screen.height-y) { h = _screen.height-y; } if (h <= 0) return; _cursor.draw_pos.y = y; _cursor.draw_size.y = h; assert(w*h < (int) sizeof(_cursor_backup)); // Make backup of stuff below cursor memcpy_pitch( _cursor_backup, _screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch, _cursor.draw_size.x, _cursor.draw_size.y, _screen.pitch, _cursor.draw_size.x); // Draw cursor on screen _cur_dpi = &_screen; DrawSprite(_cursor.sprite, _cursor.pos.x, _cursor.pos.y); _video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y); _cursor.visible = true; _cursor.dirty = false; } #if defined(_DEBUG) static void DbgScreenRect(int left, int top, int right, int bottom) { DrawPixelInfo dp,*old; old = _cur_dpi; _cur_dpi = &dp; dp = _screen; GfxFillRect(left, top, right-1, bottom-1, rand() & 255); _cur_dpi = old; } #endif extern bool _dbg_screen_rect; void RedrawScreenRect(int left, int top, int right, int bottom) { assert(right <= _screen.width && bottom <= _screen.height); if (_cursor.visible) { if (right > _cursor.draw_pos.x && left < _cursor.draw_pos.x + _cursor.draw_size.x && bottom > _cursor.draw_pos.y && top < _cursor.draw_pos.y + _cursor.draw_size.y) { UndrawMouseCursor(); } } UndrawTextMessage(); #if defined(_DEBUG) if (_dbg_screen_rect) DbgScreenRect(left, top, right, bottom); else #endif DrawOverlappedWindowForAll(left, top, right, bottom); _video_driver->make_dirty(left, top, right-left, bottom-top); } void DrawDirtyBlocks(void) { byte *b = _dirty_blocks; int x=0,y=0; const int w = (_screen.width + 63) & ~63; const int h = (_screen.height + 7) & ~7; do { if (*b != 0) { int left,top; int right = x + 64; int bottom = y; byte *p = b; int h2; // First try coalescing downwards do { *p = 0; p += DIRTY_BYTES_PER_LINE; bottom += 8; } while (bottom != h && *p); // Try coalescing to the right too. h2 = (bottom - y) >> 3; assert(h2>0); p = b; while (right != w) { byte *p2 = ++p; int h = h2; // Check if a full line of dirty flags is set. do { if (!*p2) goto no_more_coalesc; p2 += DIRTY_BYTES_PER_LINE; } while (--h); // Wohoo, can combine it one step to the right! // Do that, and clear the bits. right += 64; h = h2; p2 = p; do { *p2 = 0; p2 += DIRTY_BYTES_PER_LINE; } while (--h); } no_more_coalesc:; left = x; top = y; if (left < _invalid_rect.left)left = _invalid_rect.left; if (top < _invalid_rect.top) top = _invalid_rect.top; if (right > _invalid_rect.right)right = _invalid_rect.right; if (bottom > _invalid_rect.bottom)bottom = _invalid_rect.bottom; if (left < right && top < bottom) { RedrawScreenRect(left, top, right, bottom); } } } while (b++, (x+=64) != w || (x=0,b+=-(w>>6)+DIRTY_BYTES_PER_LINE,(y+=8) != h)); _invalid_rect.left = w; _invalid_rect.top = h; _invalid_rect.right = 0; _invalid_rect.bottom = 0; } void SetDirtyBlocks(int left, int top, int right, int bottom) { byte *b; int width,height,i; if (left < 0) left = 0; if (top < 0) top = 0; if (right > _screen.width) right = _screen.width; if (bottom > _screen.height) bottom = _screen.height; if (left >= right || top >= bottom) return; if (left < _invalid_rect.left) _invalid_rect.left = left; if (top < _invalid_rect.top) _invalid_rect.top = top; if (right > _invalid_rect.right)_invalid_rect.right = right; if (bottom > _invalid_rect.bottom)_invalid_rect.bottom = bottom; left >>= 6; top >>= 3; b = _dirty_blocks + top * DIRTY_BYTES_PER_LINE + left; width = ((right-1) >> 6) - left + 1; height = ((bottom-1) >> 3) - top + 1; assert(width > 0 && height > 0); do { i=width; do b[--i] = 0xFF; while (i); b += DIRTY_BYTES_PER_LINE; } while (--height); } void MarkWholeScreenDirty(void) { SetDirtyBlocks(0, 0, _screen.width, _screen.height); } bool FillDrawPixelInfo(DrawPixelInfo *n, DrawPixelInfo *o, int left, int top, int width, int height) { int t; if (o == NULL) o = _cur_dpi; n->zoom = 0; assert(width > 0); assert(height > 0); n->left = 0; if ((left -= o->left) < 0) { if ((width += left) < 0) return false; n->left = -left; left = 0; } if ((t=width + left - o->width) > 0) { if ((width -= t) < 0) return false; } n->width = width; n->top = 0; if ((top -= o->top) < 0) { if ((height += top) < 0) return false; n->top = -top; top = 0; } n->dst_ptr = o->dst_ptr + left + top * (n->pitch = o->pitch); if ((t=height + top - o->height) > 0) { if ((height-=t) < 0) return false; } n->height = height; return true; } static void SetCursorSprite(uint cursor) { CursorVars *cv = &_cursor; const Sprite *p; if (cv->sprite == cursor) return; p = GetSprite(cursor & 0x3FFF); cv->sprite = cursor; cv->size.y = p->height; cv->size.x = p->width; cv->offs.x = p->x_offs; cv->offs.y = p->y_offs; cv->dirty = true; } static void SwitchAnimatedCursor(void) { CursorVars *cv = &_cursor; const uint16 *cur; uint sprite; cur = cv->animate_cur; if (cur == NULL || *cur == 0xFFFF) cur = cv->animate_list; sprite = cur[0]; cv->animate_timeout = cur[1]; cv->animate_cur = cur + 2; SetCursorSprite(sprite); } void CursorTick(void) { CursorVars *cv = &_cursor; if (cv->animate_timeout && !--cv->animate_timeout) SwitchAnimatedCursor(); } void SetMouseCursor(uint cursor) { // Turn off animation _cursor.animate_timeout = 0; // Set cursor SetCursorSprite(cursor); } void SetAnimatedMouseCursor(const uint16 *table) { _cursor.animate_list = table; _cursor.animate_cur = NULL; SwitchAnimatedCursor(); } bool ChangeResInGame(int w, int h) { if ((_screen.width != w || _screen.height != h) && !_video_driver->change_resolution(w, h)) return false; _cur_resolution[0] = w; _cur_resolution[1] = h; return true; } void ToggleFullScreen(const bool full_screen) { _fullscreen = full_screen; /* use preset resolutions, not _screen.height and _screen.width. On windows for example if Desktop-size is 1280x1024, and gamesize is also 1280x1024, _screen.height will be only 1000 because of possible start-bar. For this reason you cannot switch to fullscreen mode from this resolution. Use of preset resolution will fix this */ if (!_video_driver->change_resolution(_cur_resolution[0], _cur_resolution[1])) _fullscreen ^= true; // switching resolution failed, put back full_screen to original status } uint16 GetDrawStringPlayerColor(byte player) { // Get the color for DrawString-subroutines which matches the color // of the player if (player == OWNER_SPECTATOR || player == OWNER_SPECTATOR - 1) return 1; return (_color_list[_player_colors[player]].window_color_1b) | IS_PALETTE_COLOR; }