tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@2163: #include "functions.h" tron@4428: #include "macros.h" tron@1349: #include "spritecache.h" tron@1309: #include "strings.h" Darkvater@2097: #include "string.h" truelight@0: #include "gfx.h" truelight@0: #include "table/palettes.h" celestar@2187: #include "table/sprites.h" truelight@0: #include "hal.h" tron@2153: #include "variables.h" peter1138@5108: #include "table/control_codes.h" peter1138@5108: #include "fontcache.h" truelight@4300: #include "genworld.h" rubidium@5217: #include "debug.h" truelight@0: tron@2649: #ifdef _DEBUG tron@2649: bool _dbg_screen_rect; tron@2649: #endif tron@2649: tron@1991: Colour _cur_palette[256]; peter1138@3798: byte _stringwidth_table[FS_END][224]; tron@1991: tron@1357: static void GfxMainBlitter(const Sprite *sprite, int x, int y, int mode); truelight@0: peter1138@3798: FontSize _cur_fontsize; peter1138@3798: static FontSize _last_fontsize; tron@2062: static Pixel _cursor_backup[64 * 64]; truelight@0: static Rect _invalid_rect; tron@1357: static const byte *_color_remap_ptr; truelight@0: static byte _string_colorremap[3]; truelight@0: tron@2010: #define DIRTY_BYTES_PER_LINE (MAX_SCREEN_WIDTH / 64) truelight@0: static byte _dirty_blocks[DIRTY_BYTES_PER_LINE * MAX_SCREEN_HEIGHT / 8]; truelight@0: Darkvater@4949: void memcpy_pitch(void *dst, void *src, int w, int h, int srcpitch, int dstpitch) truelight@0: { Darkvater@4949: byte *dstp = (byte*)dst; Darkvater@4949: byte *srcp = (byte*)src; truelight@0: truelight@0: assert(h >= 0); tron@1056: for (; h != 0; --h) { Darkvater@4949: memcpy(dstp, srcp, w); Darkvater@4949: dstp += dstpitch; Darkvater@4949: srcp += srcpitch; tron@1056: } truelight@0: } truelight@0: tron@2010: void GfxScroll(int left, int top, int width, int height, int xo, int yo) tron@2010: { tron@2062: const Pixel *src; tron@2062: Pixel *dst; truelight@0: int p; truelight@0: int ht; truelight@0: tron@2010: if (xo == 0 && yo == 0) return; truelight@0: tron@2010: if (_cursor.visible) UndrawMouseCursor(); truelight@543: UndrawTextMessage(); truelight@0: truelight@0: p = _screen.pitch; truelight@0: tron@375: if (yo > 0) { truelight@0: // Calculate pointers tron@375: dst = _screen.dst_ptr + (top + height - 1) * p + left; truelight@0: src = dst - yo * p; truelight@0: truelight@0: // Decrease height and increase top truelight@0: top += yo; truelight@0: height -= yo; truelight@0: assert(height > 0); truelight@0: truelight@0: // Adjust left & width truelight@0: if (xo >= 0) { tron@375: dst += xo; truelight@0: left += xo; truelight@0: width -= xo; truelight@0: } else { tron@375: src -= xo; truelight@0: width += xo; truelight@0: } truelight@0: tron@375: for (ht = height; ht > 0; --ht) { tron@375: memcpy(dst, src, width); truelight@0: src -= p; truelight@0: dst -= p; tron@375: } truelight@0: } else { tron@375: // Calculate pointers truelight@0: dst = _screen.dst_ptr + top * p + left; truelight@0: src = dst - yo * p; truelight@0: truelight@0: // Decrese height. (yo is <=0). truelight@0: height += yo; truelight@0: assert(height > 0); truelight@0: truelight@0: // Adjust left & width truelight@0: if (xo >= 0) { truelight@0: dst += xo; truelight@0: left += xo; truelight@0: width -= xo; truelight@0: } else { truelight@0: src -= xo; truelight@0: width += xo; truelight@0: } truelight@0: tron@375: // the y-displacement may be 0 therefore we have to use memmove, tron@375: // because source and destination may overlap tron@375: for (ht = height; ht > 0; --ht) { tron@375: memmove(dst, src, width); truelight@0: src += p; truelight@0: dst += p; tron@375: } truelight@0: } tron@375: // This part of the screen is now dirty. tron@375: _video_driver->make_dirty(left, top, width, height); truelight@0: } truelight@0: truelight@0: tron@2010: void GfxFillRect(int left, int top, int right, int bottom, int color) tron@2010: { tron@2548: const DrawPixelInfo* dpi = _cur_dpi; tron@2062: Pixel *dst; tron@332: const int otop = top; tron@332: const int oleft = left; truelight@0: tron@2010: if (dpi->zoom != 0) return; tron@2010: if (left > right || top > bottom) return; tron@2010: if (right < dpi->left || left >= dpi->left + dpi->width) return; tron@2010: if (bottom < dpi->top || top >= dpi->top + dpi->height) return; truelight@0: truelight@0: if ( (left -= dpi->left) < 0) left = 0; truelight@0: right = right - dpi->left + 1; tron@2010: if (right > dpi->width) right = dpi->width; truelight@0: right -= left; truelight@0: assert(right > 0); truelight@0: truelight@0: if ( (top -= dpi->top) < 0) top = 0; truelight@0: bottom = bottom - dpi->top + 1; tron@2010: if (bottom > dpi->height) bottom = dpi->height; truelight@0: bottom -= top; truelight@0: assert(bottom > 0); truelight@0: truelight@0: dst = dpi->dst_ptr + top * dpi->pitch + left; truelight@0: celestar@2218: if (!(color & PALETTE_MODIFIER_GREYOUT)) { celestar@2218: if (!(color & USE_COLORTABLE)) { truelight@0: do { truelight@0: memset(dst, color, right); truelight@0: dst += dpi->pitch; truelight@0: } while (--bottom); truelight@0: } else { truelight@0: /* use colortable mode */ celestar@2218: const byte* ctab = GetNonSprite(color & COLORTABLE_MASK) + 1; tron@1357: truelight@0: do { truelight@0: int i; tron@2005: for (i = 0; i != right; i++) dst[i] = ctab[dst[i]]; truelight@0: dst += dpi->pitch; truelight@0: } while (--bottom); truelight@0: } truelight@0: } else { tron@332: byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1; truelight@0: do { truelight@0: int i; tron@2005: for (i = (bo ^= 1); i < right; i += 2) dst[i] = (byte)color; truelight@0: dst += dpi->pitch; tron@332: } while (--bottom > 0); truelight@0: } truelight@0: } truelight@0: tron@410: static void GfxSetPixel(int x, int y, int color) truelight@0: { tron@2548: const DrawPixelInfo* dpi = _cur_dpi; truelight@0: if ((x-=dpi->left) < 0 || x>=dpi->width || (y-=dpi->top)<0 || y>=dpi->height) truelight@0: return; tron@2010: dpi->dst_ptr[y * dpi->pitch + x] = color; truelight@0: } truelight@0: truelight@0: void GfxDrawLine(int x, int y, int x2, int y2, int color) truelight@0: { truelight@0: int dy; truelight@0: int dx; tron@2010: int stepx; tron@2010: int stepy; truelight@0: int frac; truelight@0: truelight@0: // Check clipping first truelight@0: { tron@4522: DrawPixelInfo *dpi = _cur_dpi; truelight@0: int t; truelight@0: tron@2010: if (x < dpi->left && x2 < dpi->left) return; truelight@0: tron@2010: if (y < dpi->top && y2 < dpi->top) return; truelight@0: truelight@0: t = dpi->left + dpi->width; tron@2010: if (x > t && x2 > t) return; truelight@0: truelight@0: t = dpi->top + dpi->height; tron@2010: if (y > t && y2 > t) return; truelight@0: } truelight@0: truelight@0: dy = (y2 - y) * 2; tron@2010: if (dy < 0) { tron@2010: dy = -dy; tron@2010: stepy = -1; tron@2010: } else { tron@2010: stepy = 1; tron@2010: } truelight@0: truelight@0: dx = (x2 - x) * 2; tron@2010: if (dx < 0) { tron@2010: dx = -dx; tron@2010: stepx = -1; tron@2010: } else { tron@2010: stepx = 1; tron@2010: } truelight@0: truelight@0: GfxSetPixel(x, y, color); truelight@0: if (dx > dy) { truelight@0: frac = dy - (dx >> 1); truelight@0: while (x != x2) { truelight@0: if (frac >= 0) { truelight@0: y += stepy; truelight@0: frac -= dx; truelight@0: } truelight@0: x += stepx; truelight@0: frac += dy; truelight@0: GfxSetPixel(x, y, color); truelight@0: } truelight@0: } else { truelight@0: frac = dx - (dy >> 1); truelight@0: while (y != y2) { truelight@0: if (frac >= 0) { truelight@0: x += stepx; truelight@0: frac -= dy; truelight@0: } truelight@0: y += stepy; truelight@0: frac += dx; truelight@0: GfxSetPixel(x, y, color); truelight@0: } truelight@0: } truelight@0: } truelight@0: peter1138@3798: Darkvater@2097: /** Truncate a given string to a maximum width if neccessary. Darkvater@2097: * If the string is truncated, add three dots ('...') to show this. Darkvater@2097: * @param *dest string that is checked and possibly truncated Darkvater@2097: * @param maxw maximum width in pixels of the string Darkvater@2097: * @return new width of (truncated) string */ Darkvater@2113: static int TruncateString(char *str, int maxw) Darkvater@2097: { Darkvater@2113: int w = 0; peter1138@3798: FontSize size = _cur_fontsize; Darkvater@2097: int ddd, ddd_w; Darkvater@2097: peter1138@5108: WChar c; Darkvater@2097: char *ddd_pos; Darkvater@2097: peter1138@3798: ddd_w = ddd = GetCharacterWidth(size, '.') * 3; Darkvater@2097: peter1138@5108: for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) { peter1138@5108: if (IsPrintable(c)) { peter1138@3798: w += GetCharacterWidth(size, c); Darkvater@2097: Darkvater@2097: if (w >= maxw) { Darkvater@2097: // string got too big... insert dotdotdot Darkvater@2097: ddd_pos[0] = ddd_pos[1] = ddd_pos[2] = '.'; Darkvater@2097: ddd_pos[3] = 0; Darkvater@2097: return ddd_w; Darkvater@2097: } Darkvater@2097: } else { peter1138@5108: if (c == SCC_SETX) str++; peter1138@5108: else if (c == SCC_SETXY) str += 2; peter1138@5108: else if (c == SCC_TINYFONT) { peter1138@3798: size = FS_SMALL; peter1138@3798: ddd = GetCharacterWidth(size, '.') * 3; peter1138@5108: } else if (c == SCC_BIGFONT) { peter1138@3798: size = FS_LARGE; peter1138@3798: ddd = GetCharacterWidth(size, '.') * 3; Darkvater@2097: } Darkvater@2097: } Darkvater@2097: Darkvater@2097: // Remember the last position where three dots fit. Darkvater@2097: if (w + ddd < maxw) { Darkvater@2097: ddd_w = w + ddd; Darkvater@2113: ddd_pos = str; Darkvater@2097: } Darkvater@2097: } Darkvater@2097: Darkvater@2097: return w; Darkvater@2097: } Darkvater@2097: Darkvater@4912: static inline int TruncateStringID(StringID src, char *dest, int maxw, const char* last) Darkvater@2097: { Darkvater@4912: GetString(dest, src, last); Darkvater@2097: return TruncateString(dest, maxw); Darkvater@2097: } truelight@0: truelight@0: /* returns right coordinate */ Darkvater@2097: int DrawString(int x, int y, StringID str, uint16 color) truelight@0: { tron@1336: char buffer[512]; tron@1336: Darkvater@4912: GetString(buffer, str, lastof(buffer)); tron@1336: return DoDrawString(buffer, x, y, color); truelight@0: } truelight@0: Darkvater@2097: int DrawStringTruncated(int x, int y, StringID str, uint16 color, uint maxw) Darkvater@2097: { Darkvater@2097: char buffer[512]; Darkvater@4912: TruncateStringID(str, buffer, maxw, lastof(buffer)); Darkvater@2097: return DoDrawString(buffer, x, y, color); Darkvater@2097: } truelight@0: Darkvater@2097: rubidium@4314: int DrawStringRightAligned(int x, int y, StringID str, uint16 color) truelight@0: { tron@1336: char buffer[512]; rubidium@4314: int w; tron@1336: Darkvater@4912: GetString(buffer, str, lastof(buffer)); Darkvater@4609: w = GetStringBoundingBox(buffer).width; rubidium@4314: DoDrawString(buffer, x - w, y, color); rubidium@4314: rubidium@4314: return w; truelight@0: } truelight@0: Darkvater@2097: void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, uint maxw) Darkvater@2097: { Darkvater@2097: char buffer[512]; truelight@0: Darkvater@4912: TruncateStringID(str, buffer, maxw, lastof(buffer)); Darkvater@4609: DoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, color); Darkvater@2097: } Darkvater@2097: rubidium@4314: void DrawStringRightAlignedUnderline(int x, int y, StringID str, uint16 color) rubidium@4314: { rubidium@4314: int w = DrawStringRightAligned(x, y, str, color); rubidium@4314: GfxFillRect(x - w, y + 10, x, y + 10, _string_colorremap[1]); rubidium@4314: } rubidium@4314: Darkvater@2097: Darkvater@2097: int DrawStringCentered(int x, int y, StringID str, uint16 color) truelight@0: { tron@1336: char buffer[512]; truelight@0: int w; truelight@0: Darkvater@4912: GetString(buffer, str, lastof(buffer)); truelight@0: Darkvater@4609: w = GetStringBoundingBox(buffer).width; tron@1336: DoDrawString(buffer, x - w / 2, y, color); truelight@0: truelight@0: return w; truelight@0: } truelight@0: Darkvater@2113: int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 color) Darkvater@2097: { Darkvater@2097: char buffer[512]; Darkvater@4912: int w = TruncateStringID(str, buffer, xr - xl, lastof(buffer)); Darkvater@2113: return DoDrawString(buffer, (xl + xr - w) / 2, y, color); Darkvater@2097: } Darkvater@2097: Darkvater@2134: int DoDrawStringCentered(int x, int y, const char *str, uint16 color) Darkvater@2134: { Darkvater@4609: int w = GetStringBoundingBox(str).width; Darkvater@2134: DoDrawString(str, x - w / 2, y, color); Darkvater@2134: return w; Darkvater@2134: } Darkvater@2134: Darkvater@2097: void DrawStringCenterUnderline(int x, int y, StringID str, uint16 color) truelight@0: { truelight@0: int w = DrawStringCentered(x, y, str, color); tron@2010: GfxFillRect(x - (w >> 1), y + 10, x - (w >> 1) + w, y + 10, _string_colorremap[1]); truelight@0: } truelight@0: Darkvater@2113: void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, uint16 color) Darkvater@2097: { Darkvater@2113: int w = DrawStringCenteredTruncated(xl, xr, y, str, color); Darkvater@2113: GfxFillRect((xl + xr - w) / 2, y + 10, (xl + xr + w) / 2, y + 10, _string_colorremap[1]); Darkvater@2097: } Darkvater@2097: Darkvater@4954: /** 'Correct' a string to a maximum length. Longer strings will be cut into Darkvater@4954: * additional lines at whitespace characters if possible. The string parameter Darkvater@4954: * is modified with terminating characters mid-string which are the Darkvater@4954: * placeholders for the newlines.
Darkvater@4954: * The string WILL be truncated if there was no whitespace for the current Darkvater@4954: * line's maximum width. Darkvater@4954: * Darkvater@4954: * @note To know if the the terminating '\0' is the string end or just a Darkvater@4954: * newline, the returned 'num' value should be consulted. The num'th '\0', Darkvater@4954: * starting with index 0 is the real string end. Darkvater@4954: * Darkvater@4954: * @param str string to check and correct for length restrictions Darkvater@4954: * @param maxw the maximum width the string can have on one line Darkvater@4954: * @return return a 32bit wide number consisting of 2 packed values: Darkvater@4954: * 0 - 15 the number of lines ADDED to the string Darkvater@4954: * 16 - 31 the fontsize in which the length calculation was done at */ Darkvater@4954: uint32 FormatStringLinebreaks(char *str, int maxw) tron@1095: { Darkvater@4954: FontSize size = _cur_fontsize; truelight@0: int num = 0; Darkvater@4954: Darkvater@4954: assert(maxw > 0); truelight@0: tron@2952: for (;;) { Darkvater@4954: char *last_space = NULL; Darkvater@4954: int w = 0; truelight@0: tron@2952: for (;;) { peter1138@5108: WChar c = Utf8Consume((const char **)&str); Darkvater@4954: /* whitespace is where we will insert the line-break */ peter1138@5108: if (c == ' ') last_space = str; truelight@0: peter1138@5108: if (IsPrintable(c)) { peter1138@3798: w += GetCharacterWidth(size, c); Darkvater@4954: /* string is longer than maximum width so we need to decide what to Darkvater@4954: * do. We can do two things: Darkvater@4954: * 1. If no whitespace was found at all up until now (on this line) then Darkvater@4954: * we will truncate the string and bail out. Darkvater@4954: * 2. In all other cases force a linebreak at the last seen whitespace */ truelight@0: if (w > maxw) { Darkvater@4954: if (last_space == NULL) { Darkvater@4954: str[-1] = '\0'; Darkvater@4954: return num + (size << 16); Darkvater@4954: } truelight@0: str = last_space; truelight@0: break; truelight@0: } truelight@0: } else { Darkvater@4954: switch (c) { Darkvater@4954: case '\0': return num + (size << 16); break; peter1138@5108: case SCC_SETX: str++; break; peter1138@5108: case SCC_SETXY: str +=2; break; peter1138@5108: case SCC_TINYFONT: size = FS_SMALL; break; peter1138@5108: case SCC_BIGFONT: size = FS_LARGE; break; peter1138@5108: case '\n': goto end_of_inner_loop; Darkvater@4954: } truelight@0: } truelight@0: } Darkvater@4954: end_of_inner_loop: Darkvater@4954: /* string didn't fit on line, so 'dummy' terminate and increase linecount */ truelight@0: num++; tron@2548: str[-1] = '\0'; truelight@0: } truelight@0: } truelight@0: Darkvater@5579: /** Draw a given string with the centre around the given x coordinates Darkvater@5579: * @param x Centre the string around this pixel width Darkvater@5579: * @param y Draw the string at this pixel height (first line's bottom) Darkvater@5579: * @param str String to draw Darkvater@5579: * @param max Maximum width the string can have before it is wrapped */ tron@2634: void DrawStringMultiCenter(int x, int y, StringID str, int maxw) truelight@0: { tron@1336: char buffer[512]; truelight@0: uint32 tmp; tron@2631: int num, w, mt; tron@1323: const char *src; peter1138@5108: WChar c; truelight@0: Darkvater@4912: GetString(buffer, str, lastof(buffer)); truelight@0: tron@1336: tmp = FormatStringLinebreaks(buffer, maxw); tron@2635: num = GB(tmp, 0, 16); truelight@0: peter1138@3798: mt = GetCharacterHeight(GB(tmp, 16, 16)); truelight@0: truelight@0: y -= (mt >> 1) * num; truelight@0: tron@1336: src = buffer; truelight@0: tron@2952: for (;;) { Darkvater@4609: w = GetStringBoundingBox(src).width; truelight@0: DoDrawString(src, x - (w>>1), y, 0xFE); peter1138@3798: _cur_fontsize = _last_fontsize; truelight@0: tron@2952: for (;;) { peter1138@5108: c = Utf8Consume(&src); truelight@0: if (c == 0) { truelight@0: y += mt; truelight@0: if (--num < 0) { peter1138@3798: _cur_fontsize = FS_NORMAL; truelight@0: return; truelight@0: } truelight@0: break; peter1138@5108: } else if (c == SCC_SETX) { truelight@0: src++; peter1138@5108: } else if (c == SCC_SETXY) { truelight@0: src+=2; truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@0: peter1138@4928: peter1138@4928: uint DrawStringMultiLine(int x, int y, StringID str, int maxw) tron@2010: { tron@1336: char buffer[512]; truelight@0: uint32 tmp; tron@2753: int num, mt; peter1138@4928: uint total_height; tron@1323: const char *src; peter1138@5108: WChar c; truelight@0: Darkvater@4912: GetString(buffer, str, lastof(buffer)); truelight@0: tron@1336: tmp = FormatStringLinebreaks(buffer, maxw); tron@2635: num = GB(tmp, 0, 16); tron@2631: peter1138@3798: mt = GetCharacterHeight(GB(tmp, 16, 16)); peter1138@4928: total_height = (num + 1) * mt; truelight@0: tron@1336: src = buffer; truelight@0: tron@2952: for (;;) { truelight@0: DoDrawString(src, x, y, 0xFE); peter1138@3798: _cur_fontsize = _last_fontsize; truelight@0: tron@2952: for (;;) { peter1138@5108: c = Utf8Consume(&src); truelight@0: if (c == 0) { truelight@0: y += mt; truelight@0: if (--num < 0) { peter1138@3798: _cur_fontsize = FS_NORMAL; peter1138@4928: return total_height; truelight@0: } truelight@0: break; peter1138@5108: } else if (c == SCC_SETX) { truelight@0: src++; peter1138@5108: } else if (c == SCC_SETXY) { truelight@0: src+=2; truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@0: Darkvater@4609: /** Return the string dimension in pixels. The height and width are returned Darkvater@4609: * in a single BoundingRect value. TINYFONT, BIGFONT modifiers are only Darkvater@4609: * supported as the first character of the string. The returned dimensions Darkvater@4609: * are therefore a rough estimation correct for all the current strings Darkvater@4609: * but not every possible combination Darkvater@4609: * @param str string to calculate pixel-width Darkvater@4609: * @return string width and height in pixels */ Darkvater@4609: BoundingRect GetStringBoundingBox(const char *str) tron@1323: { peter1138@3798: FontSize size = _cur_fontsize; Darkvater@4609: BoundingRect br; Darkvater@4609: int max_width; peter1138@5108: WChar c; Darkvater@4557: Darkvater@4609: br.width = br.height = max_width = 0; peter1138@5108: for (;;) { peter1138@5108: c = Utf8Consume(&str); peter1138@5108: if (c == 0) break; peter1138@5108: if (IsPrintable(c)) { Darkvater@4609: br.width += GetCharacterWidth(size, c); truelight@0: } else { Darkvater@4609: switch (c) { Darkvater@5579: case SCC_SETX: br.width += (byte)*str++; break; peter1138@5108: case SCC_SETXY: Darkvater@5579: br.width += (byte)*str++; Darkvater@5579: br.height += (byte)*str++; Darkvater@4609: break; peter1138@5108: case SCC_TINYFONT: size = FS_SMALL; break; peter1138@5108: case SCC_BIGFONT: size = FS_LARGE; break; peter1138@5108: case '\n': Darkvater@4609: br.height += GetCharacterHeight(size); Darkvater@4610: if (br.width > max_width) max_width = br.width; Darkvater@4610: br.width = 0; Darkvater@4609: break; Darkvater@4609: } truelight@0: } truelight@0: } Darkvater@4609: br.height += GetCharacterHeight(size); Darkvater@4557: Darkvater@4609: br.width = max(br.width, max_width); Darkvater@4609: return br; truelight@0: } truelight@0: Darkvater@4949: /** Draw a string at the given coordinates with the given colour Darkvater@4949: * @param string the string to draw Darkvater@4949: * @param x offset from left side of the screen, if negative offset from the right side Darkvater@4949: * @param x offset from top side of the screen, if negative offset from the bottom Darkvater@4949: * @param real_color colour of the string, see _string_colormap in Darkvater@4949: * table/palettes.h or docs/ottd-colourtext-palette.png Darkvater@4949: * @return the x-coordinates where the drawing has finished. If nothing is drawn Darkvater@4949: * the originally passed x-coordinate is returned */ tron@1323: int DoDrawString(const char *string, int x, int y, uint16 real_color) tron@1323: { tron@4522: DrawPixelInfo *dpi = _cur_dpi; peter1138@3798: FontSize size = _cur_fontsize; peter1138@5108: WChar c; truelight@543: byte color; truelight@0: int xo = x, yo = y; truelight@0: truelight@543: color = real_color & 0xFF; truelight@543: truelight@0: if (color != 0xFE) { truelight@0: if (x >= dpi->left + dpi->width || truelight@0: x + _screen.width*2 <= dpi->left || truelight@0: y >= dpi->top + dpi->height || truelight@0: y + _screen.height <= dpi->top) truelight@0: return x; truelight@0: truelight@0: if (color != 0xFF) { truelight@0: switch_color:; dominik@657: if (real_color & IS_PALETTE_COLOR) { truelight@543: _string_colorremap[1] = color; truelight@543: _string_colorremap[2] = 215; truelight@543: } else { truelight@543: _string_colorremap[1] = _string_colormap[color].text; truelight@543: _string_colorremap[2] = _string_colormap[color].shadow; truelight@543: } truelight@0: _color_remap_ptr = _string_colorremap; truelight@0: } truelight@0: } truelight@0: truelight@0: check_bounds: truelight@0: if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) { truelight@0: skip_char:; tron@2952: for (;;) { peter1138@5108: c = Utf8Consume(&string); peter1138@5108: if (!IsPrintable(c)) goto skip_cont; truelight@0: } truelight@0: } truelight@0: tron@2952: for (;;) { peter1138@5108: c = Utf8Consume(&string); truelight@0: skip_cont:; truelight@0: if (c == 0) { peter1138@3798: _last_fontsize = size; truelight@0: return x; truelight@0: } peter1138@5108: if (IsPrintable(c)) { truelight@0: if (x >= dpi->left + dpi->width) goto skip_char; truelight@0: if (x + 26 >= dpi->left) { peter1138@5108: GfxMainBlitter(GetGlyph(size, c), x, y, 1); truelight@0: } peter1138@3798: x += GetCharacterWidth(size, c); peter1138@5108: } else if (c == '\n') { // newline = {} truelight@0: x = xo; peter1138@3798: y += GetCharacterHeight(size); truelight@0: goto check_bounds; peter1138@5108: } else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change color? peter1138@5108: color = (byte)(c - SCC_BLUE); truelight@0: goto switch_color; peter1138@5108: } else if (c == SCC_SETX) { // {SETX} tron@1323: x = xo + (byte)*string++; peter1138@5108: } else if (c == SCC_SETXY) {// {SETXY} tron@1323: x = xo + (byte)*string++; tron@1323: y = yo + (byte)*string++; peter1138@5108: } else if (c == SCC_TINYFONT) { // {TINYFONT} peter1138@3798: size = FS_SMALL; peter1138@5108: } else if (c == SCC_BIGFONT) { // {BIGFONT} peter1138@3798: size = FS_LARGE; truelight@0: } else { Darkvater@5579: DEBUG(misc, 0, "[utf8] unknown string command character %d", c); truelight@0: } truelight@0: } truelight@0: } truelight@0: Darkvater@2097: int DoDrawStringTruncated(const char *str, int x, int y, uint16 color, uint maxw) Darkvater@2097: { Darkvater@2097: char buffer[512]; Darkvater@2097: ttd_strlcpy(buffer, str, sizeof(buffer)); Darkvater@2097: TruncateString(buffer, maxw); Darkvater@2097: return DoDrawString(buffer, x, y, color); Darkvater@2097: } Darkvater@2097: tron@2010: void DrawSprite(uint32 img, int x, int y) tron@2010: { celestar@2187: if (img & PALETTE_MODIFIER_COLOR) { celestar@2187: _color_remap_ptr = GetNonSprite(GB(img, PALETTE_SPRITE_START, PALETTE_SPRITE_WIDTH)) + 1; celestar@2187: GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, 1); celestar@2187: } else if (img & PALETTE_MODIFIER_TRANSPARENT) { celestar@2187: _color_remap_ptr = GetNonSprite(GB(img, PALETTE_SPRITE_START, PALETTE_SPRITE_WIDTH)) + 1; celestar@2187: GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, 2); truelight@0: } else { celestar@2187: GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, 0); truelight@0: } truelight@0: } truelight@0: truelight@0: typedef struct BlitterParams { truelight@0: int start_x, start_y; belugas@4171: const byte *sprite; tron@2062: Pixel *dst; truelight@0: int mode; truelight@0: int width, height; tron@2010: int width_org; truelight@0: int pitch; truelight@0: } BlitterParams; truelight@0: truelight@0: static void GfxBlitTileZoomIn(BlitterParams *bp) truelight@0: { belugas@4171: const byte *src_o = bp->sprite; belugas@4171: const byte *src; truelight@0: int num, skip; truelight@0: byte done; tron@2062: Pixel *dst; belugas@4171: const byte *ctab; truelight@0: tron@4516: src_o += ReadLE16Aligned(src_o + bp->start_y * 2); tron@4516: switch (bp->mode) { tron@4516: case 1: truelight@0: do { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src = src_o + 2; tron@4516: src_o += num + 2; truelight@0: tron@4516: dst = bp->dst; truelight@0: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip; tron@4516: } else { tron@4516: src -= skip; tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } truelight@0: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } truelight@0: tron@4516: ctab = _color_remap_ptr; tron@4516: tron@4516: for (; num >= 4; num -=4) { tron@4516: dst[3] = ctab[src[3]]; tron@4516: dst[2] = ctab[src[2]]; tron@4516: dst[1] = ctab[src[1]]; tron@4516: dst[0] = ctab[src[0]]; truelight@0: dst += 4; truelight@0: src += 4; tron@4516: } tron@4516: for (; num != 0; num--) *dst++ = ctab[*src++]; tron@4516: } while (!(done & 0x80)); tron@4516: tron@4516: bp->dst += bp->pitch; tron@4516: } while (--bp->height != 0); tron@4516: break; tron@4516: tron@4516: case 2: tron@4516: do { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src_o += num + 2; tron@4516: tron@4516: dst = bp->dst; tron@4516: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip; tron@4516: } else { tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } tron@4516: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } tron@4516: tron@4516: ctab = _color_remap_ptr; tron@4516: for (; num != 0; num--) { tron@4516: *dst = ctab[*dst]; tron@4516: dst++; tron@4516: } tron@4516: } while (!(done & 0x80)); tron@4516: tron@4516: bp->dst += bp->pitch; tron@4516: } while (--bp->height != 0); tron@4516: break; tron@4516: tron@4516: default: tron@4516: do { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src = src_o + 2; tron@4516: src_o += num + 2; tron@4516: tron@4516: dst = bp->dst; tron@4516: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip; tron@4516: } else { tron@4516: src -= skip; tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } tron@4516: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } tron@4516: #if defined(_WIN32) tron@4516: if (num & 1) *dst++ = *src++; tron@4516: if (num & 2) { *(uint16*)dst = *(uint16*)src; dst += 2; src += 2; } tron@4516: if (num >>= 2) { tron@4516: do { tron@4516: *(uint32*)dst = *(uint32*)src; tron@4516: dst += 4; tron@4516: src += 4; tron@4516: } while (--num != 0); tron@4516: } truelight@0: #else tron@4516: memcpy(dst, src, num); truelight@0: #endif tron@4516: } while (!(done & 0x80)); truelight@0: tron@4516: bp->dst += bp->pitch; tron@4516: } while (--bp->height != 0); tron@4516: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static void GfxBlitZoomInUncomp(BlitterParams *bp) truelight@0: { tron@1357: const byte *src = bp->sprite; tron@2062: Pixel *dst = bp->dst; truelight@0: int height = bp->height; truelight@0: int width = bp->width; truelight@0: int i; truelight@0: truelight@0: assert(height > 0); truelight@0: assert(width > 0); truelight@0: tron@4516: switch (bp->mode) { tron@4517: case 1: { tron@4517: const byte *ctab = _color_remap_ptr; tron@2004: tron@4517: do { tron@4517: for (i = 0; i != width; i++) { tron@4517: byte b = ctab[src[i]]; truelight@0: tron@4517: if (b != 0) dst[i] = b; tron@4517: } tron@4517: src += bp->width_org; tron@4517: dst += bp->pitch; tron@4517: } while (--height != 0); tron@4517: break; tron@4517: } truelight@0: tron@4517: case 2: { tron@4517: const byte *ctab = _color_remap_ptr; tron@4517: tron@4517: do { tron@4517: for (i = 0; i != width; i++) tron@4517: if (src[i] != 0) dst[i] = ctab[dst[i]]; tron@4517: src += bp->width_org; tron@4517: dst += bp->pitch; tron@4517: } while (--height != 0); tron@4516: break; tron@4517: } tron@4516: tron@4516: default: tron@4517: do { tron@4517: int n = width; tron@4516: tron@4517: for (; n >= 4; n -= 4) { tron@4517: if (src[0] != 0) dst[0] = src[0]; tron@4517: if (src[1] != 0) dst[1] = src[1]; tron@4517: if (src[2] != 0) dst[2] = src[2]; tron@4517: if (src[3] != 0) dst[3] = src[3]; tron@4516: tron@4517: dst += 4; tron@4517: src += 4; tron@4517: } tron@4516: tron@4517: for (; n != 0; n--) { tron@4517: if (src[0] != 0) dst[0] = src[0]; tron@4517: src++; tron@4517: dst++; tron@4517: } tron@4517: tron@4517: src += bp->width_org - width; tron@4517: dst += bp->pitch - width; tron@4517: } while (--height != 0); tron@4516: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static void GfxBlitTileZoomMedium(BlitterParams *bp) truelight@0: { belugas@4171: const byte *src_o = bp->sprite; belugas@4171: const byte *src; truelight@0: int num, skip; truelight@0: byte done; tron@2062: Pixel *dst; belugas@4171: const byte *ctab; truelight@0: tron@4516: src_o += ReadLE16Aligned(src_o + bp->start_y * 2); tron@4516: switch (bp->mode) { tron@4516: case 1: truelight@0: do { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src = src_o + 2; tron@4516: src_o += num + 2; truelight@0: tron@4516: dst = bp->dst; truelight@0: tron@4516: if (skip & 1) { tron@4516: skip++; tron@4516: src++; tron@4516: if (--num == 0) continue; tron@4516: } truelight@0: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip >> 1; tron@4516: } else { tron@4516: src -= skip; tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } truelight@0: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } truelight@0: tron@4516: ctab = _color_remap_ptr; tron@4516: num = (num + 1) >> 1; tron@4516: for (; num != 0; num--) { tron@4516: *dst = ctab[*src]; tron@4516: dst++; tron@4516: src += 2; tron@4516: } tron@4516: } while (!(done & 0x80)); tron@4516: bp->dst += bp->pitch; tron@4516: if (--bp->height == 0) return; truelight@0: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: } while (--bp->height != 0); tron@4516: break; tron@4516: tron@4516: case 2: truelight@0: do { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src_o += num + 2; tron@4516: tron@4516: dst = bp->dst; tron@4516: tron@4516: if (skip & 1) { tron@4516: skip++; tron@4516: if (--num == 0) continue; tron@4516: } tron@4516: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip >> 1; tron@4516: } else { tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } tron@4516: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } tron@4516: tron@4516: ctab = _color_remap_ptr; tron@4516: num = (num + 1) >> 1; tron@4516: for (; num != 0; num--) { tron@4516: *dst = ctab[*dst]; tron@4516: dst++; tron@4516: } tron@4516: } while (!(done & 0x80)); tron@4516: bp->dst += bp->pitch; tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: } while (--bp->height != 0); tron@4516: break; tron@4516: tron@4516: default: tron@4516: do { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src = src_o + 2; tron@4516: src_o += num + 2; tron@4516: tron@4516: dst = bp->dst; tron@4516: tron@4516: if (skip & 1) { tron@4516: skip++; tron@4516: src++; tron@4516: if (--num == 0) continue; tron@4516: } tron@4516: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip >> 1; tron@4516: } else { tron@4516: src -= skip; tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } tron@4516: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } tron@4516: tron@4516: num = (num + 1) >> 1; tron@4516: tron@4516: for (; num != 0; num--) { tron@4516: *dst = *src; tron@4516: dst++; tron@4516: src += 2; tron@4516: } tron@4516: tron@4516: } while (!(done & 0x80)); tron@4516: tron@4516: bp->dst += bp->pitch; tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: } while (--bp->height != 0); tron@4516: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static void GfxBlitZoomMediumUncomp(BlitterParams *bp) truelight@0: { tron@1357: const byte *src = bp->sprite; tron@2062: Pixel *dst = bp->dst; truelight@0: int height = bp->height; truelight@0: int width = bp->width; truelight@0: int i; truelight@0: truelight@0: assert(height > 0); truelight@0: assert(width > 0); truelight@0: tron@4516: switch (bp->mode) { tron@4517: case 1: { tron@4517: const byte *ctab = _color_remap_ptr; tron@1357: tron@4517: for (height >>= 1; height != 0; height--) { tron@4517: for (i = 0; i != width >> 1; i++) { tron@4517: byte b = ctab[src[i * 2]]; tron@1996: tron@4517: if (b != 0) dst[i] = b; tron@1996: } tron@4517: src += bp->width_org * 2; tron@4517: dst += bp->pitch; tron@1996: } tron@4516: break; tron@4517: } tron@4516: tron@4517: case 2: { tron@4517: const byte *ctab = _color_remap_ptr; tron@4517: tron@4517: for (height >>= 1; height != 0; height--) { tron@4517: for (i = 0; i != width >> 1; i++) tron@4517: if (src[i * 2] != 0) dst[i] = ctab[dst[i]]; tron@4517: src += bp->width_org * 2; tron@4517: dst += bp->pitch; tron@1996: } tron@4516: break; tron@4517: } tron@4516: tron@4516: default: tron@4517: for (height >>= 1; height != 0; height--) { tron@4517: for (i = 0; i != width >> 1; i++) tron@4517: if (src[i * 2] != 0) dst[i] = src[i * 2]; tron@4517: src += bp->width_org * 2; tron@4517: dst += bp->pitch; tron@1996: } tron@4516: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static void GfxBlitTileZoomOut(BlitterParams *bp) truelight@0: { belugas@4171: const byte *src_o = bp->sprite; belugas@4171: const byte *src; truelight@0: int num, skip; truelight@0: byte done; tron@2062: Pixel *dst; belugas@4171: const byte *ctab; truelight@0: tron@4516: src_o += ReadLE16Aligned(src_o + bp->start_y * 2); tron@4516: switch (bp->mode) { tron@4516: case 1: tron@4516: for (;;) { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src = src_o + 2; tron@4516: src_o += num + 2; truelight@0: tron@4516: dst = bp->dst; truelight@0: tron@4516: if (skip & 1) { tron@4516: skip++; tron@4516: src++; tron@4516: if (--num == 0) continue; tron@4516: } truelight@0: tron@4516: if (skip & 2) { tron@4516: skip += 2; tron@4516: src += 2; tron@4516: num -= 2; tron@4516: if (num <= 0) continue; tron@4516: } truelight@0: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip >> 2; tron@4516: } else { tron@4516: src -= skip; tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } truelight@0: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } truelight@0: tron@4516: ctab = _color_remap_ptr; tron@4516: num = (num + 3) >> 2; tron@4516: for (; num != 0; num--) { tron@4516: *dst = ctab[*src]; tron@4516: dst++; tron@4516: src += 4; tron@4516: } tron@4516: } while (!(done & 0x80)); tron@4516: bp->dst += bp->pitch; tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: } tron@4516: break; tron@4516: tron@4516: case 2: tron@4516: for (;;) { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src_o += num + 2; tron@4516: tron@4516: dst = bp->dst; tron@4516: tron@4516: if (skip & 1) { tron@4516: skip++; tron@4516: if (--num == 0) continue; tron@4516: } tron@4516: tron@4516: if (skip & 2) { tron@4516: skip += 2; tron@4516: num -= 2; tron@4516: if (num <= 0) continue; tron@4516: } tron@4516: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip >> 2; tron@4516: } else { tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } tron@4516: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } tron@4516: tron@4516: ctab = _color_remap_ptr; tron@4516: num = (num + 3) >> 2; tron@4516: for (; num != 0; num--) { tron@4516: *dst = ctab[*dst]; tron@4516: dst++; tron@4516: } tron@4516: tron@4516: } while (!(done & 0x80)); tron@4516: bp->dst += bp->pitch; tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: } tron@4516: break; tron@4516: tron@4516: default: tron@4516: for (;;) { tron@4516: do { tron@4516: done = src_o[0]; tron@4516: num = done & 0x7F; tron@4516: skip = src_o[1]; tron@4516: src = src_o + 2; tron@4516: src_o += num + 2; tron@4516: tron@4516: dst = bp->dst; tron@4516: tron@4516: if (skip & 1) { tron@4516: skip++; tron@4516: src++; tron@4516: if (--num == 0) continue; tron@4516: } tron@4516: tron@4516: if (skip & 2) { tron@4516: skip += 2; tron@4516: src += 2; tron@4516: num -= 2; tron@4516: if (num <= 0) continue; tron@4516: } tron@4516: tron@4516: if ( (skip -= bp->start_x) > 0) { tron@4516: dst += skip >> 2; tron@4516: } else { tron@4516: src -= skip; tron@4516: num += skip; tron@4516: if (num <= 0) continue; tron@4516: skip = 0; tron@4516: } tron@4516: tron@4516: skip = skip + num - bp->width; tron@4516: if (skip > 0) { tron@4516: num -= skip; tron@4516: if (num <= 0) continue; tron@4516: } tron@4516: tron@4516: num = (num + 3) >> 2; tron@4516: tron@4516: for (; num != 0; num--) { tron@4516: *dst = *src; tron@4516: dst++; tron@4516: src += 4; tron@4516: } tron@4516: } while (!(done & 0x80)); tron@4516: tron@4516: bp->dst += bp->pitch; tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: tron@4516: do { tron@4516: done = src_o[0]; tron@4516: src_o += (done & 0x7F) + 2; tron@4516: } while (!(done & 0x80)); tron@4516: if (--bp->height == 0) return; tron@4516: } tron@4516: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static void GfxBlitZoomOutUncomp(BlitterParams *bp) truelight@0: { belugas@4171: const byte *src = bp->sprite; tron@2062: Pixel *dst = bp->dst; truelight@0: int height = bp->height; truelight@0: int width = bp->width; truelight@0: int i; truelight@0: truelight@0: assert(height > 0); truelight@0: assert(width > 0); truelight@0: tron@4516: switch (bp->mode) { tron@4517: case 1: { tron@4517: const byte *ctab = _color_remap_ptr; tron@1357: tron@4517: for (height >>= 2; height != 0; height--) { tron@4517: for (i = 0; i != width >> 2; i++) { tron@4517: byte b = ctab[src[i * 4]]; tron@1996: tron@4517: if (b != 0) dst[i] = b; tron@1996: } tron@4517: src += bp->width_org * 4; tron@4517: dst += bp->pitch; tron@1996: } tron@4516: break; tron@4517: } tron@4516: tron@4517: case 2: { tron@4517: const byte *ctab = _color_remap_ptr; tron@4517: tron@4517: for (height >>= 2; height != 0; height--) { tron@4517: for (i = 0; i != width >> 2; i++) tron@4517: if (src[i * 4] != 0) dst[i] = ctab[dst[i]]; tron@4517: src += bp->width_org * 4; tron@4517: dst += bp->pitch; tron@1996: } tron@4516: break; tron@4517: } tron@4516: tron@4516: default: tron@4517: for (height >>= 2; height != 0; height--) { tron@4517: for (i = 0; i != width >> 2; i++) tron@4517: if (src[i * 4] != 0) dst[i] = src[i * 4]; tron@4517: src += bp->width_org * 4; tron@4517: dst += bp->pitch; tron@1996: } tron@4516: break; truelight@0: } truelight@0: } truelight@0: truelight@0: belugas@4171: static void GfxMainBlitter(const Sprite *sprite, int x, int y, int mode) truelight@0: { belugas@4171: const DrawPixelInfo *dpi = _cur_dpi; truelight@0: int start_x, start_y; truelight@0: BlitterParams bp; tron@2005: int zoom_mask = ~((1 << dpi->zoom) - 1); truelight@0: truelight@0: /* decode sprite header */ tron@1351: x += sprite->x_offs; tron@1351: y += sprite->y_offs; tron@1351: bp.width_org = bp.width = sprite->width; tron@4518: bp.height = sprite->height; tron@4518: bp.sprite = sprite->data; truelight@0: bp.dst = dpi->dst_ptr; truelight@0: bp.mode = mode; truelight@0: bp.pitch = dpi->pitch; truelight@0: truelight@0: assert(bp.height > 0); truelight@0: assert(bp.width > 0); truelight@0: tron@4517: if (sprite->info & 8) { truelight@0: /* tile blit */ truelight@0: start_y = 0; truelight@0: truelight@0: if (dpi->zoom > 0) { tron@2005: start_y += bp.height & ~zoom_mask; truelight@193: bp.height &= zoom_mask; truelight@0: if (bp.height == 0) return; tron@2005: y &= zoom_mask; truelight@0: } truelight@0: truelight@0: if ( (y -= dpi->top) < 0) { tron@2005: bp.height += y; tron@2005: if (bp.height <= 0) return; truelight@0: start_y -= y; truelight@0: y = 0; truelight@0: } else { tron@2005: bp.dst += bp.pitch * (y >> dpi->zoom); truelight@0: } truelight@0: bp.start_y = start_y; truelight@0: truelight@0: if ( (y = y + bp.height - dpi->height) > 0) { tron@2005: bp.height -= y; tron@2005: if (bp.height <= 0) return; truelight@0: } truelight@0: truelight@0: start_x = 0; truelight@0: x &= zoom_mask; truelight@0: if ( (x -= dpi->left) < 0) { tron@2005: bp.width += x; tron@2005: if (bp.width <= 0) return; truelight@0: start_x -= x; truelight@0: x = 0; truelight@0: } truelight@0: bp.start_x = start_x; tron@2005: bp.dst += x >> dpi->zoom; truelight@0: truelight@0: if ( (x = x + bp.width - dpi->width) > 0) { tron@2005: bp.width -= x; tron@2005: if (bp.width <= 0) return; truelight@0: } truelight@0: tron@4425: switch (dpi->zoom) { tron@4425: default: NOT_REACHED(); tron@4425: case 0: GfxBlitTileZoomIn(&bp); break; tron@4425: case 1: GfxBlitTileZoomMedium(&bp); break; tron@4425: case 2: GfxBlitTileZoomOut(&bp); break; tron@4425: } truelight@0: } else { truelight@0: bp.sprite += bp.width * (bp.height & ~zoom_mask); truelight@0: bp.height &= zoom_mask; tron@2005: if (bp.height == 0) return; truelight@0: truelight@0: y &= zoom_mask; truelight@0: truelight@0: if ( (y -= dpi->top) < 0) { tron@2005: bp.height += y; tron@2005: if (bp.height <= 0) return; truelight@0: bp.sprite -= bp.width * y; truelight@0: y = 0; truelight@0: } else { tron@2005: bp.dst += bp.pitch * (y >> dpi->zoom); truelight@0: } truelight@0: tron@2037: if (bp.height > dpi->height - y) { tron@2037: bp.height = dpi->height - y; tron@2005: if (bp.height <= 0) return; truelight@0: } truelight@0: truelight@0: x &= zoom_mask; truelight@0: truelight@0: if ( (x -= dpi->left) < 0) { tron@2005: bp.width += x; tron@2005: if (bp.width <= 0) return; truelight@0: bp.sprite -= x; truelight@0: x = 0; truelight@0: } tron@2005: bp.dst += x >> dpi->zoom; truelight@0: tron@2037: if (bp.width > dpi->width - x) { tron@2037: bp.width = dpi->width - x; tron@2005: if (bp.width <= 0) return; truelight@0: } truelight@0: tron@4425: switch (dpi->zoom) { tron@4425: default: NOT_REACHED(); tron@4425: case 0: GfxBlitZoomInUncomp(&bp); break; tron@4425: case 1: GfxBlitZoomMediumUncomp(&bp); break; tron@4425: case 2: GfxBlitZoomOutUncomp(&bp); break; tron@4425: } truelight@0: } truelight@0: } truelight@0: tron@1093: void DoPaletteAnimations(void); dominik@614: tron@1093: void GfxInitPalettes(void) truelight@0: { tron@1991: memcpy(_cur_palette, _palettes[_use_dos_palette ? 1 : 0], sizeof(_cur_palette)); dominik@614: truelight@0: _pal_first_dirty = 0; truelight@0: _pal_last_dirty = 255; dominik@614: DoPaletteAnimations(); truelight@0: } truelight@0: tron@2010: #define EXTR(p, q) (((uint16)(_timer_counter * (p)) * (q)) >> 16) tron@2010: #define EXTR2(p, q) (((uint16)(~_timer_counter * (p)) * (q)) >> 16) truelight@0: tron@1093: void DoPaletteAnimations(void) truelight@0: { belugas@4171: const Colour *s; belugas@4171: Colour *d; dominik@614: /* Amount of colors to be rotated. dominik@614: * A few more for the DOS palette, because the water colors are dominik@614: * 245-254 for DOS and 217-226 for Windows. */ truelight@0: const ExtraPaletteValues *ev = &_extra_palette_values; tron@2005: int c = _use_dos_palette ? 38 : 28; tron@1991: Colour old_val[38]; // max(38, 28) tron@2005: uint i; tron@2005: uint j; truelight@0: tron@1991: d = &_cur_palette[217]; tron@1991: memcpy(old_val, d, c * sizeof(*old_val)); truelight@0: truelight@0: // Dark blue water tron@2005: s = (_opt.landscape == LT_CANDY) ? ev->ac : ev->a; tron@2005: j = EXTR(320, 5); tron@2005: for (i = 0; i != 5; i++) { tron@1991: *d++ = s[j]; tron@1991: j++; tron@1991: if (j == 5) j = 0; truelight@0: } truelight@0: truelight@0: // Glittery water tron@2005: s = (_opt.landscape == LT_CANDY) ? ev->bc : ev->b; tron@1991: j = EXTR(128, 15); tron@2005: for (i = 0; i != 5; i++) { tron@1991: *d++ = s[j]; tron@1991: j += 3; tron@1991: if (j >= 15) j -= 15; truelight@0: } truelight@0: truelight@0: s = ev->e; tron@1991: j = EXTR2(512, 5); tron@2005: for (i = 0; i != 5; i++) { tron@1991: *d++ = s[j]; tron@1991: j++; tron@1991: if (j == 5) j = 0; truelight@0: } truelight@0: truelight@0: // Oil refinery fire animation truelight@0: s = ev->oil_ref; tron@1991: j = EXTR2(512, 7); tron@2005: for (i = 0; i != 7; i++) { tron@1991: *d++ = s[j]; tron@1991: j++; tron@1991: if (j == 7) j = 0; truelight@0: } truelight@0: truelight@0: // Radio tower blinking truelight@0: { tron@2005: byte i = (_timer_counter >> 1) & 0x7F; tron@2005: byte v; tron@2005: truelight@0: (v = 255, i < 0x3f) || truelight@0: (v = 128, i < 0x4A || i >= 0x75) || truelight@0: (v = 20); tron@1991: d->r = v; tron@1991: d->g = 0; tron@1991: d->b = 0; tron@1991: d++; truelight@0: truelight@0: i ^= 0x40; truelight@0: (v = 255, i < 0x3f) || truelight@0: (v = 128, i < 0x4A || i >= 0x75) || truelight@0: (v = 20); tron@1991: d->r = v; tron@1991: d->g = 0; tron@1991: d->b = 0; tron@1991: d++; truelight@0: } truelight@0: truelight@0: // Handle lighthouse and stadium animation truelight@0: s = ev->lighthouse; tron@1991: j = EXTR(256, 4); tron@2005: for (i = 0; i != 4; i++) { tron@1991: *d++ = s[j]; tron@1991: j++; tron@1991: if (j == 4) j = 0; truelight@0: } truelight@0: dominik@614: // Animate water for old DOS graphics tron@2005: if (_use_dos_palette) { dominik@614: // Dark blue water DOS tron@2005: s = (_opt.landscape == LT_CANDY) ? ev->ac : ev->a; tron@2005: j = EXTR(320, 5); tron@2005: for (i = 0; i != 5; i++) { tron@1991: *d++ = s[j]; tron@1991: j++; tron@1991: if (j == 5) j = 0; dominik@614: } tron@915: dominik@614: // Glittery water DOS tron@2005: s = (_opt.landscape == LT_CANDY) ? ev->bc : ev->b; tron@1991: j = EXTR(128, 15); tron@2005: for (i = 0; i != 5; i++) { tron@1991: *d++ = s[j]; tron@1991: j += 3; tron@1991: if (j >= 15) j -= 15; dominik@614: } dominik@614: } dominik@614: tron@1991: if (memcmp(old_val, &_cur_palette[217], c * sizeof(*old_val)) != 0) { truelight@0: if (_pal_first_dirty > 217) _pal_first_dirty = 217; tron@1991: if (_pal_last_dirty < 217 + c) _pal_last_dirty = 217 + c; truelight@0: } truelight@0: } truelight@0: truelight@0: tron@1093: void LoadStringWidthTable(void) truelight@0: { tron@2005: uint i; truelight@0: peter1138@3797: /* Normal font */ peter1138@3798: for (i = 0; i != 224; i++) { peter1138@5108: _stringwidth_table[FS_NORMAL][i] = GetGlyphWidth(FS_NORMAL, i + 32); truelight@0: } truelight@0: peter1138@3797: /* Small font */ peter1138@3798: for (i = 0; i != 224; i++) { peter1138@5108: _stringwidth_table[FS_SMALL][i] = GetGlyphWidth(FS_SMALL, i + 32); truelight@0: } truelight@0: peter1138@3797: /* Large font */ peter1138@3798: for (i = 0; i != 224; i++) { peter1138@5108: _stringwidth_table[FS_LARGE][i] = GetGlyphWidth(FS_LARGE, i + 32); truelight@0: } truelight@0: } truelight@0: peter1138@5108: peter1138@5108: byte GetCharacterWidth(FontSize size, WChar key) peter1138@5108: { peter1138@5108: if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32]; peter1138@5108: peter1138@5108: return GetGlyphWidth(size, key); peter1138@5108: } peter1138@5108: peter1138@5108: tron@1093: void ScreenSizeChanged(void) truelight@0: { truelight@0: // check the dirty rect truelight@0: if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width; truelight@0: if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height; truelight@0: truelight@0: // screen size changed and the old bitmap is invalid now, so we don't want to undraw it truelight@0: _cursor.visible = false; truelight@0: } truelight@0: tron@1093: void UndrawMouseCursor(void) truelight@0: { truelight@0: if (_cursor.visible) { truelight@0: _cursor.visible = false; truelight@0: memcpy_pitch( truelight@0: _screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch, truelight@0: _cursor_backup, truelight@0: _cursor.draw_size.x, _cursor.draw_size.y, _cursor.draw_size.x, _screen.pitch); truelight@193: truelight@0: _video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y); truelight@0: } truelight@0: } truelight@0: tron@1093: void DrawMouseCursor(void) truelight@0: { tron@2010: int x; tron@2010: int y; tron@2010: int w; tron@2010: int h; truelight@0: Darkvater@3312: /* Redraw mouse cursor but only when it's inside the window */ Darkvater@3312: if (!_cursor.in_window) return; Darkvater@3312: truelight@0: // Don't draw the mouse cursor if it's already drawn truelight@0: if (_cursor.visible) { tron@2010: if (!_cursor.dirty) return; truelight@0: UndrawMouseCursor(); truelight@0: } truelight@0: truelight@0: w = _cursor.size.x; truelight@0: x = _cursor.pos.x + _cursor.offs.x; tron@2010: if (x < 0) { tron@2010: w += x; tron@2010: x = 0; tron@2010: } tron@2010: if (w > _screen.width - x) w = _screen.width - x; truelight@0: if (w <= 0) return; truelight@0: _cursor.draw_pos.x = x; truelight@0: _cursor.draw_size.x = w; truelight@0: truelight@0: h = _cursor.size.y; truelight@0: y = _cursor.pos.y + _cursor.offs.y; tron@2010: if (y < 0) { tron@2010: h += y; tron@2010: y = 0; tron@2010: } tron@2010: if (h > _screen.height - y) h = _screen.height - y; truelight@0: if (h <= 0) return; truelight@0: _cursor.draw_pos.y = y; truelight@0: _cursor.draw_size.y = h; truelight@0: tron@2010: assert(w * h < (int)sizeof(_cursor_backup)); truelight@0: truelight@0: // Make backup of stuff below cursor truelight@0: memcpy_pitch( truelight@0: _cursor_backup, truelight@0: _screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch, darkvater@306: _cursor.draw_size.x, _cursor.draw_size.y, _screen.pitch, _cursor.draw_size.x); truelight@0: truelight@0: // Draw cursor on screen truelight@0: _cur_dpi = &_screen; truelight@0: DrawSprite(_cursor.sprite, _cursor.pos.x, _cursor.pos.y); truelight@0: truelight@0: _video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y); truelight@0: truelight@0: _cursor.visible = true; truelight@0: _cursor.dirty = false; truelight@0: } truelight@0: darkvater@798: #if defined(_DEBUG) tron@410: static void DbgScreenRect(int left, int top, int right, int bottom) truelight@0: { tron@2010: DrawPixelInfo dp; belugas@4171: DrawPixelInfo *old; truelight@0: truelight@0: old = _cur_dpi; truelight@0: _cur_dpi = &dp; truelight@0: dp = _screen; tron@2010: GfxFillRect(left, top, right - 1, bottom - 1, rand() & 255); truelight@0: _cur_dpi = old; truelight@0: } darkvater@798: #endif truelight@0: truelight@0: void RedrawScreenRect(int left, int top, int right, int bottom) truelight@0: { truelight@0: assert(right <= _screen.width && bottom <= _screen.height); truelight@0: if (_cursor.visible) { truelight@0: if (right > _cursor.draw_pos.x && truelight@0: left < _cursor.draw_pos.x + _cursor.draw_size.x && truelight@0: bottom > _cursor.draw_pos.y && truelight@0: top < _cursor.draw_pos.y + _cursor.draw_size.y) { truelight@0: UndrawMouseCursor(); truelight@0: } truelight@0: } truelight@543: UndrawTextMessage(); truelight@0: truelight@0: #if defined(_DEBUG) truelight@0: if (_dbg_screen_rect) truelight@0: DbgScreenRect(left, top, right, bottom); truelight@0: else truelight@0: #endif truelight@0: DrawOverlappedWindowForAll(left, top, right, bottom); truelight@0: tron@2010: _video_driver->make_dirty(left, top, right - left, bottom - top); truelight@0: } truelight@0: tron@1093: void DrawDirtyBlocks(void) truelight@0: { truelight@0: byte *b = _dirty_blocks; tron@2398: const int w = ALIGN(_screen.width, 64); tron@2398: const int h = ALIGN(_screen.height, 8); tron@2025: int x; tron@2025: int y; truelight@0: truelight@4300: if (IsGeneratingWorld() && !IsGeneratingWorldReadyForPaint()) return; truelight@4300: tron@2025: y = 0; truelight@0: do { tron@2025: x = 0; tron@2025: do { tron@2025: if (*b != 0) { tron@2025: int left; tron@2025: int top; tron@2025: int right = x + 64; tron@2025: int bottom = y; tron@2025: byte *p = b; tron@2025: int h2; truelight@0: tron@2025: // First try coalescing downwards truelight@0: do { tron@2025: *p = 0; tron@2025: p += DIRTY_BYTES_PER_LINE; tron@2025: bottom += 8; tron@2025: } while (bottom != h && *p != 0); truelight@0: tron@2025: // Try coalescing to the right too. tron@2025: h2 = (bottom - y) >> 3; tron@2025: assert(h2 > 0); tron@2025: p = b; truelight@0: tron@2025: while (right != w) { tron@2025: byte *p2 = ++p; tron@2025: int h = h2; tron@2025: // Check if a full line of dirty flags is set. tron@2025: do { tron@2025: if (!*p2) goto no_more_coalesc; tron@2025: p2 += DIRTY_BYTES_PER_LINE; tron@2025: } while (--h != 0); tron@2025: tron@2025: // Wohoo, can combine it one step to the right! tron@2025: // Do that, and clear the bits. tron@2025: right += 64; tron@2025: tron@2025: h = h2; tron@2025: p2 = p; tron@2025: do { tron@2025: *p2 = 0; tron@2025: p2 += DIRTY_BYTES_PER_LINE; tron@2025: } while (--h != 0); tron@2025: } tron@2025: no_more_coalesc: tron@2025: tron@2025: left = x; tron@2025: top = y; tron@2025: tron@2025: if (left < _invalid_rect.left ) left = _invalid_rect.left; tron@2025: if (top < _invalid_rect.top ) top = _invalid_rect.top; tron@2025: if (right > _invalid_rect.right ) right = _invalid_rect.right; tron@2025: if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom; tron@2025: tron@2025: if (left < right && top < bottom) { tron@2025: RedrawScreenRect(left, top, right, bottom); tron@2025: } tron@2025: truelight@0: } tron@2025: } while (b++, (x += 64) != w); tron@2025: } while (b += -(w >> 6) + DIRTY_BYTES_PER_LINE, (y += 8) != h); truelight@0: truelight@0: _invalid_rect.left = w; truelight@0: _invalid_rect.top = h; truelight@0: _invalid_rect.right = 0; truelight@0: _invalid_rect.bottom = 0; truelight@4300: truelight@4300: /* If we are generating a world, and waiting for a paint run, mark it here truelight@4300: * as done painting, so we can continue generating. */ truelight@4300: if (IsGeneratingWorld() && IsGeneratingWorldReadyForPaint()) { truelight@4300: SetGeneratingWorldPaintStatus(false); truelight@4300: } truelight@0: } truelight@0: truelight@0: truelight@0: void SetDirtyBlocks(int left, int top, int right, int bottom) truelight@0: { truelight@0: byte *b; tron@2010: int width; tron@2010: int height; truelight@0: truelight@0: if (left < 0) left = 0; truelight@0: if (top < 0) top = 0; truelight@0: if (right > _screen.width) right = _screen.width; truelight@0: if (bottom > _screen.height) bottom = _screen.height; truelight@0: tron@2010: if (left >= right || top >= bottom) return; truelight@0: tron@2010: if (left < _invalid_rect.left ) _invalid_rect.left = left; tron@2010: if (top < _invalid_rect.top ) _invalid_rect.top = top; tron@2010: if (right > _invalid_rect.right ) _invalid_rect.right = right; tron@2010: if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom; truelight@0: truelight@0: left >>= 6; tron@2010: top >>= 3; truelight@0: truelight@0: b = _dirty_blocks + top * DIRTY_BYTES_PER_LINE + left; truelight@0: tron@2010: width = ((right - 1) >> 6) - left + 1; tron@2010: height = ((bottom - 1) >> 3) - top + 1; truelight@0: truelight@0: assert(width > 0 && height > 0); truelight@0: truelight@0: do { tron@2010: int i = width; tron@2010: truelight@0: do b[--i] = 0xFF; while (i); truelight@0: truelight@0: b += DIRTY_BYTES_PER_LINE; tron@2010: } while (--height != 0); truelight@0: } truelight@0: tron@1093: void MarkWholeScreenDirty(void) truelight@0: { truelight@0: SetDirtyBlocks(0, 0, _screen.width, _screen.height); truelight@0: } truelight@0: Darkvater@4958: /** Set up a clipping area for only drawing into a certain area. To do this, Darkvater@4958: * Fill a DrawPixelInfo object with the supplied relative rectangle, backup Darkvater@4958: * the original (calling) _cur_dpi and assign the just returned DrawPixelInfo Darkvater@4958: * _cur_dpi. When you are done, give restore _cur_dpi's original value Darkvater@4958: * @param *n the DrawPixelInfo that will be the clipping rectangle box allowed Darkvater@4958: * for drawing Darkvater@4958: * @param left,top,width,height the relative coordinates of the clipping Darkvater@4958: * rectangle relative to the current _cur_dpi. This will most likely be the Darkvater@4958: * offset from the calling window coordinates Darkvater@4958: * @return return false if the requested rectangle is not possible with the Darkvater@4958: * current dpi pointer. Only continue of the return value is true, or you'll Darkvater@4958: * get some nasty results */ tron@4429: bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height) truelight@0: { tron@4429: const DrawPixelInfo *o = _cur_dpi; truelight@0: truelight@0: n->zoom = 0; truelight@0: truelight@0: assert(width > 0); truelight@0: assert(height > 0); truelight@0: truelight@0: if ((left -= o->left) < 0) { tron@2010: width += left; tron@4525: if (width <= 0) return false; truelight@0: n->left = -left; truelight@0: left = 0; tron@4429: } else { tron@4429: n->left = 0; truelight@0: } truelight@0: tron@4429: if (width > o->width - left) { tron@4429: width = o->width - left; tron@4525: if (width <= 0) return false; truelight@0: } truelight@0: n->width = width; truelight@0: truelight@0: if ((top -= o->top) < 0) { tron@2010: height += top; tron@4525: if (height <= 0) return false; truelight@0: n->top = -top; truelight@0: top = 0; tron@4429: } else { tron@4429: n->top = 0; truelight@0: } truelight@0: truelight@0: n->dst_ptr = o->dst_ptr + left + top * (n->pitch = o->pitch); truelight@0: tron@4429: if (height > o->height - top) { tron@4429: height = o->height - top; tron@4525: if (height <= 0) return false; truelight@0: } truelight@0: n->height = height; truelight@0: truelight@0: return true; truelight@0: } truelight@0: Darkvater@1914: static void SetCursorSprite(CursorID cursor) truelight@0: { truelight@0: CursorVars *cv = &_cursor; tron@1348: const Sprite *p; truelight@0: tron@2010: if (cv->sprite == cursor) return; truelight@0: celestar@2187: p = GetSprite(cursor & SPRITE_MASK); truelight@0: cv->sprite = cursor; tron@1348: cv->size.y = p->height; tron@1351: cv->size.x = p->width; tron@1351: cv->offs.x = p->x_offs; tron@1351: cv->offs.y = p->y_offs; truelight@0: truelight@0: cv->dirty = true; truelight@0: } truelight@0: tron@1093: static void SwitchAnimatedCursor(void) truelight@0: { truelight@0: CursorVars *cv = &_cursor; Darkvater@1914: const CursorID *cur = cv->animate_cur; Darkvater@1914: CursorID sprite; truelight@0: Darkvater@1914: // ANIM_CURSOR_END is 0xFFFF in table/animcursors.h Darkvater@1914: if (cur == NULL || *cur == 0xFFFF) cur = cv->animate_list; Darkvater@1914: truelight@0: sprite = cur[0]; truelight@0: cv->animate_timeout = cur[1]; truelight@0: cv->animate_cur = cur + 2; truelight@0: truelight@0: SetCursorSprite(sprite); truelight@0: } truelight@0: tron@1093: void CursorTick(void) tron@1093: { tron@2010: if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0) truelight@0: SwitchAnimatedCursor(); truelight@0: } truelight@0: Darkvater@1914: void SetMouseCursor(CursorID cursor) truelight@0: { truelight@0: // Turn off animation truelight@0: _cursor.animate_timeout = 0; truelight@0: // Set cursor truelight@0: SetCursorSprite(cursor); truelight@0: } truelight@0: Darkvater@1914: void SetAnimatedMouseCursor(const CursorID *table) truelight@0: { truelight@0: _cursor.animate_list = table; truelight@0: _cursor.animate_cur = NULL; truelight@0: SwitchAnimatedCursor(); truelight@0: } truelight@0: truelight@0: bool ChangeResInGame(int w, int h) truelight@193: { tron@2645: return tron@2645: (_screen.width == w && _screen.height == h) || tron@2645: _video_driver->change_resolution(w, h); truelight@0: } darkvater@298: rubidium@5217: void ToggleFullScreen(bool fs) rubidium@5217: { rubidium@5217: _video_driver->toggle_fullscreen(fs); rubidium@5217: if (_fullscreen != fs && _num_resolutions == 0) { Darkvater@5568: DEBUG(driver, 0, "Could not find a suitable fullscreen resolution"); rubidium@5217: } rubidium@5217: } Darkvater@1829: Darkvater@1806: static int CDECL compare_res(const void *pa, const void *pb) darkvater@298: { Darkvater@1806: int x = ((const uint16*)pa)[0] - ((const uint16*)pb)[0]; Darkvater@1806: if (x != 0) return x; Darkvater@1806: return ((const uint16*)pa)[1] - ((const uint16*)pb)[1]; Darkvater@1806: } Darkvater@1806: Darkvater@1806: void SortResolutions(int count) Darkvater@1806: { Darkvater@1806: qsort(_resolutions, count, sizeof(_resolutions[0]), compare_res); darkvater@298: }