# gfx.cpp

Go to the documentation of this file.

    00001 /* $Id: gfx.cpp 14267 2008-09-07 21:41:47Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "gfx_func.h"
    00008 #include "spritecache.h"
    00009 #include "variables.h"
    00010 #include "fontcache.h"
    00011 #include "genworld.h"
    00012 #include "debug.h"
    00013 #include "zoom_func.h"
    00014 #include "texteff.hpp"
    00015 #include "blitter/factory.hpp"
    00016 #include "video/video_driver.hpp"
    00017 #include "strings_func.h"
    00018 #include "core/math_func.hpp"
    00019 #include "settings_type.h"
    00020
    00021 #include "table/palettes.h"
    00022 #include "table/sprites.h"
    00023 #include "table/control_codes.h"
    00024
    00025 byte _dirkeys;
    00026 bool _fullscreen;
    00027 CursorVars _cursor;
    00028 bool _ctrl_pressed;
    00029 bool _shift_pressed;
    00030 byte _fast_forward;
    00031 bool _left_button_down;
    00032 bool _left_button_clicked;
    00033 bool _right_button_down;
    00034 bool _right_button_clicked;
    00035 DrawPixelInfo _screen;
    00036 bool _screen_disable_anim = false;
    00037 bool _exit_game;
    00038 bool _networking;
    00039 byte _game_mode;
    00040 int8 _pause_game;
    00041 int _pal_first_dirty;
    00042 int _pal_count_dirty;
    00043
    00044 Colour _cur_palette[256];
    00045 byte _stringwidth_table[FS_END][224];
    00046 DrawPixelInfo *_cur_dpi;
    00047 byte _colour_gradient[16][8];
    00048 bool _use_dos_palette;
    00049
    00050 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL);
    00051
    00052 FontSize _cur_fontsize;
    00053 static FontSize _last_fontsize;
    00054 static uint8 _cursor_backup[64 * 64 * 4];
    00055
    00063 static Rect _invalid_rect;
    00064 static const byte *_color_remap_ptr;
    00065 static byte _string_colorremap[3];
    00066
    00067 #define DIRTY_BYTES_PER_LINE (MAX_SCREEN_WIDTH / 64)
    00068 static byte _dirty_blocks[DIRTY_BYTES_PER_LINE * MAX_SCREEN_HEIGHT / 8];
    00069
    00070 void GfxScroll(int left, int top, int width, int height, int xo, int yo)
    00071 {
    00072   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00073
    00074   if (xo == 0 && yo == 0) return;
    00075
    00076   if (_cursor.visible) UndrawMouseCursor();
    00077   UndrawChatMessage();
    00078
    00079   blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
    00080   /* This part of the screen is now dirty. */
    00081   _video_driver->MakeDirty(left, top, width, height);
    00082 }
    00083
    00084
    00085 void GfxFillRect(int left, int top, int right, int bottom, int color)
    00086 {
    00087   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00088   const DrawPixelInfo *dpi = _cur_dpi;
    00089   void *dst;
    00090   const int otop = top;
    00091   const int oleft = left;
    00092
    00093   if (dpi->zoom != ZOOM_LVL_NORMAL) return;
    00094   if (left > right || top > bottom) return;
    00095   if (right < dpi->left || left >= dpi->left + dpi->width) return;
    00096   if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
    00097
    00098   if ( (left -= dpi->left) < 0) left = 0;
    00099   right = right - dpi->left + 1;
    00100   if (right > dpi->width) right = dpi->width;
    00101   right -= left;
    00102   assert(right > 0);
    00103
    00104   if ( (top -= dpi->top) < 0) top = 0;
    00105   bottom = bottom - dpi->top + 1;
    00106   if (bottom > dpi->height) bottom = dpi->height;
    00107   bottom -= top;
    00108   assert(bottom > 0);
    00109
    00110   dst = blitter->MoveTo(dpi->dst_ptr, left, top);
    00111
    00112   if (!HasBit(color, PALETTE_MODIFIER_GREYOUT)) {
    00113     if (!HasBit(color, USE_COLORTABLE)) {
    00114       blitter->DrawRect(dst, right, bottom, (uint8)color);
    00115     } else {
    00116       blitter->DrawColorMappingRect(dst, right, bottom, GB(color, 0, PALETTE_WIDTH));
    00117     }
    00118   } else {
    00119     byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
    00120     do {
    00121       for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8)color);
    00122       dst = blitter->MoveTo(dst, 0, 1);
    00123     } while (--bottom > 0);
    00124   }
    00125 }
    00126
    00127 void GfxDrawLine(int x, int y, int x2, int y2, int color)
    00128 {
    00129   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00130   DrawPixelInfo *dpi = _cur_dpi;
    00131
    00132   x -= dpi->left;
    00133   x2 -= dpi->left;
    00134   y -= dpi->top;
    00135   y2 -= dpi->top;
    00136
    00137   /* Check clipping */
    00138   if (x < 0 && x2 < 0) return;
    00139   if (y < 0 && y2 < 0) return;
    00140   if (x > dpi->width  && x2 > dpi->width)  return;
    00141   if (y > dpi->height && y2 > dpi->height) return;
    00142
    00143   blitter->DrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, color);
    00144 }
    00145
    00146 void GfxDrawLineUnscaled(int x, int y, int x2, int y2, int color)
    00147 {
    00148   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00149   DrawPixelInfo *dpi = _cur_dpi;
    00150
    00151   x -= dpi->left;
    00152   x2 -= dpi->left;
    00153   y -= dpi->top;
    00154   y2 -= dpi->top;
    00155
    00156   /* Check clipping */
    00157   if (x < 0 && x2 < 0) return;
    00158   if (y < 0 && y2 < 0) return;
    00159   if (x > dpi->width  && x2 > dpi->width)  return;
    00160   if (y > dpi->height && y2 > dpi->height) return;
    00161
    00162   blitter->DrawLine(dpi->dst_ptr, UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
    00163       UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
    00164       UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), color);
    00165 }
    00166
    00180 void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
    00181 {
    00182   /*           ....
    00183    *         ..    ....
    00184    *       ..          ....
    00185    *     ..                ^
    00186    *   <--__(dx1,dy1)    /(dx2,dy2)
    00187    *   :    --__       /   :
    00188    *   :        --__ /     :
    00189    *   :            *(x,y) :
    00190    *   :            |      :
    00191    *   :            |     ..
    00192    *    ....        |(dx3,dy3)
    00193    *        ....    | ..
    00194    *            ....V.
    00195    */
    00196
    00197   static const byte color = 255;
    00198
    00199   GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, color);
    00200   GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, color);
    00201   GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, color);
    00202
    00203   GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, color);
    00204   GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, color);
    00205   GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, color);
    00206   GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, color);
    00207   GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, color);
    00208   GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, color);
    00209 }
    00210
    00211
    00217 static int TruncateString(char *str, int maxw)
    00218 {
    00219   int w = 0;
    00220   FontSize size = _cur_fontsize;
    00221   int ddd, ddd_w;
    00222
    00223   WChar c;
    00224   char *ddd_pos;
    00225
    00226   ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
    00227
    00228   for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
    00229     if (IsPrintable(c)) {
    00230       w += GetCharacterWidth(size, c);
    00231
    00232       if (w >= maxw) {
    00233         /* string got too big... insert dotdotdot, but make sure we do not
    00234          * print anything beyond the string termination character. */
    00235         for (int i = 0; *ddd_pos != '\0' && i < 3; i++, ddd_pos++) *ddd_pos = '.';
    00236         *ddd_pos = '\0';
    00237         return ddd_w;
    00238       }
    00239     } else {
    00240       if (c == SCC_SETX) {
    00241         w = *str;
    00242         str++;
    00243       } else if (c == SCC_SETXY) {
    00244         w = *str;
    00245         str += 2;
    00246       } else if (c == SCC_TINYFONT) {
    00247         size = FS_SMALL;
    00248         ddd = GetCharacterWidth(size, '.') * 3;
    00249       } else if (c == SCC_BIGFONT) {
    00250         size = FS_LARGE;
    00251         ddd = GetCharacterWidth(size, '.') * 3;
    00252       }
    00253     }
    00254
    00255     /* Remember the last position where three dots fit. */
    00256     if (w + ddd < maxw) {
    00257       ddd_w = w + ddd;
    00258       ddd_pos = str;
    00259     }
    00260   }
    00261
    00262   return w;
    00263 }
    00264
    00265 static inline int TruncateStringID(StringID src, char *dest, int maxw, const char* last)
    00266 {
    00267   GetString(dest, src, last);
    00268   return TruncateString(dest, maxw);
    00269 }
    00270
    00271 /* returns right coordinate */
    00272 int DrawString(int x, int y, StringID str, uint16 color)
    00273 {
    00274   char buffer[512];
    00275
    00276   GetString(buffer, str, lastof(buffer));
    00277   return DoDrawString(buffer, x, y, color);
    00278 }
    00279
    00280 int DrawStringTruncated(int x, int y, StringID str, uint16 color, uint maxw)
    00281 {
    00282   char buffer[512];
    00283   TruncateStringID(str, buffer, maxw, lastof(buffer));
    00284   return DoDrawString(buffer, x, y, color);
    00285 }
    00286
    00287
    00288 int DrawStringRightAligned(int x, int y, StringID str, uint16 color)
    00289 {
    00290   char buffer[512];
    00291   int w;
    00292
    00293   GetString(buffer, str, lastof(buffer));
    00294   w = GetStringBoundingBox(buffer).width;
    00295   DoDrawString(buffer, x - w, y, color);
    00296
    00297   return w;
    00298 }
    00299
    00300 void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, uint maxw)
    00301 {
    00302   char buffer[512];
    00303
    00304   TruncateStringID(str, buffer, maxw, lastof(buffer));
    00305   DoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, color);
    00306 }
    00307
    00308 void DrawStringRightAlignedUnderline(int x, int y, StringID str, uint16 color)
    00309 {
    00310   int w = DrawStringRightAligned(x, y, str, color);
    00311   GfxFillRect(x - w, y + 10, x, y + 10, _string_colorremap[1]);
    00312 }
    00313
    00314
    00315 int DrawStringCentered(int x, int y, StringID str, uint16 color)
    00316 {
    00317   char buffer[512];
    00318   int w;
    00319
    00320   GetString(buffer, str, lastof(buffer));
    00321
    00322   w = GetStringBoundingBox(buffer).width;
    00323   DoDrawString(buffer, x - w / 2, y, color);
    00324
    00325   return w;
    00326 }
    00327
    00328 int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 color)
    00329 {
    00330   char buffer[512];
    00331   int w = TruncateStringID(str, buffer, xr - xl, lastof(buffer));
    00332   return DoDrawString(buffer, (xl + xr - w) / 2, y, color);
    00333 }
    00334
    00335 int DoDrawStringCentered(int x, int y, const char *str, uint16 color)
    00336 {
    00337   int w = GetStringBoundingBox(str).width;
    00338   DoDrawString(str, x - w / 2, y, color);
    00339   return w;
    00340 }
    00341
    00342 void DrawStringCenterUnderline(int x, int y, StringID str, uint16 color)
    00343 {
    00344   int w = DrawStringCentered(x, y, str, color);
    00345   GfxFillRect(x - (w >> 1), y + 10, x - (w >> 1) + w, y + 10, _string_colorremap[1]);
    00346 }
    00347
    00348 void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, uint16 color)
    00349 {
    00350   int w = DrawStringCenteredTruncated(xl, xr, y, str, color);
    00351   GfxFillRect((xl + xr - w) / 2, y + 10, (xl + xr + w) / 2, y + 10, _string_colorremap[1]);
    00352 }
    00353
    00372 uint32 FormatStringLinebreaks(char *str, int maxw)
    00373 {
    00374   FontSize size = _cur_fontsize;
    00375   int num = 0;
    00376
    00377   assert(maxw > 0);
    00378
    00379   for (;;) {
    00380     char *last_space = NULL;
    00381     int w = 0;
    00382
    00383     for (;;) {
    00384       WChar c = Utf8Consume((const char **)&str);
    00385       /* whitespace is where we will insert the line-break */
    00386       if (IsWhitespace(c)) last_space = str;
    00387
    00388       if (IsPrintable(c)) {
    00389         w += GetCharacterWidth(size, c);
    00390         /* string is longer than maximum width so we need to decide what to
    00391          * do. We can do two things:
    00392          * 1. If no whitespace was found at all up until now (on this line) then
    00393          *    we will truncate the string and bail out.
    00394          * 2. In all other cases force a linebreak at the last seen whitespace */
    00395         if (w > maxw) {
    00396           if (last_space == NULL) {
    00397             *Utf8PrevChar(str) = '\0';
    00398             return num + (size << 16);
    00399           }
    00400           str = last_space;
    00401           break;
    00402         }
    00403       } else {
    00404         switch (c) {
    00405           case '\0': return num + (size << 16); break;
    00406           case SCC_SETX:  str++; break;
    00407           case SCC_SETXY: str += 2; break;
    00408           case SCC_TINYFONT: size = FS_SMALL; break;
    00409           case SCC_BIGFONT:  size = FS_LARGE; break;
    00410           case '\n': goto end_of_inner_loop;
    00411         }
    00412       }
    00413     }
    00414 end_of_inner_loop:
    00415     /* String didn't fit on line (or a '\n' was encountered), so 'dummy' terminate
    00416      * and increase linecount. We use Utf8PrevChar() as also non 1 char long
    00417      * whitespace seperators are supported */
    00418     num++;
    00419     char *s = Utf8PrevChar(str);
    00420     *s++ = '\0';
    00421
    00422     /* In which case (see above) we will shift remainder to left and close the gap */
    00423     if (str - s >= 1) {
    00424       for (; str[-1] != '\0';) *s++ = *str++;
    00425     }
    00426   }
    00427 }
    00428
    00434 void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
    00435 {
    00436   char buffer[512];
    00437   uint32 tmp;
    00438   int num, w, mt;
    00439   const char *src;
    00440   WChar c;
    00441
    00442   GetString(buffer, str, lastof(buffer));
    00443
    00444   tmp = FormatStringLinebreaks(buffer, maxw);
    00445   num = GB(tmp, 0, 16);
    00446
    00447   mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
    00448
    00449   y -= (mt >> 1) * num;
    00450
    00451   src = buffer;
    00452
    00453   for (;;) {
    00454     w = GetStringBoundingBox(src).width;
    00455     DoDrawString(src, x - (w >> 1), y, 0xFE);
    00456     _cur_fontsize = _last_fontsize;
    00457
    00458     for (;;) {
    00459       c = Utf8Consume(&src);
    00460       if (c == 0) {
    00461         y += mt;
    00462         if (--num < 0) {
    00463           _cur_fontsize = FS_NORMAL;
    00464           return;
    00465         }
    00466         break;
    00467       } else if (c == SCC_SETX) {
    00468         src++;
    00469       } else if (c == SCC_SETXY) {
    00470         src += 2;
    00471       }
    00472     }
    00473   }
    00474 }
    00475
    00476
    00477 uint DrawStringMultiLine(int x, int y, StringID str, int maxw, int maxh)
    00478 {
    00479   char buffer[512];
    00480   uint32 tmp;
    00481   int num, mt;
    00482   uint total_height;
    00483   const char *src;
    00484   WChar c;
    00485
    00486   GetString(buffer, str, lastof(buffer));
    00487
    00488   tmp = FormatStringLinebreaks(buffer, maxw);
    00489   num = GB(tmp, 0, 16);
    00490
    00491   mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
    00492   total_height = (num + 1) * mt;
    00493
    00494   if (maxh != -1 && (int)total_height > maxh) {
    00495     /* Check there's room enough for at least one line. */
    00496     if (maxh < mt) return 0;
    00497
    00498     num = maxh / mt - 1;
    00499     total_height = (num + 1) * mt;
    00500   }
    00501
    00502   src = buffer;
    00503
    00504   for (;;) {
    00505     DoDrawString(src, x, y, 0xFE);
    00506     _cur_fontsize = _last_fontsize;
    00507
    00508     for (;;) {
    00509       c = Utf8Consume(&src);
    00510       if (c == 0) {
    00511         y += mt;
    00512         if (--num < 0) {
    00513           _cur_fontsize = FS_NORMAL;
    00514           return total_height;
    00515         }
    00516         break;
    00517       } else if (c == SCC_SETX) {
    00518         src++;
    00519       } else if (c == SCC_SETXY) {
    00520         src += 2;
    00521       }
    00522     }
    00523   }
    00524 }
    00525
    00533 Dimension GetStringBoundingBox(const char *str)
    00534 {
    00535   FontSize size = _cur_fontsize;
    00536   Dimension br;
    00537   int max_width;
    00538   WChar c;
    00539
    00540   br.width = br.height = max_width = 0;
    00541   for (;;) {
    00542     c = Utf8Consume(&str);
    00543     if (c == 0) break;
    00544     if (IsPrintable(c)) {
    00545       br.width += GetCharacterWidth(size, c);
    00546     } else {
    00547       switch (c) {
    00548         case SCC_SETX: br.width += (byte)*str++; break;
    00549         case SCC_SETXY:
    00550           br.width += (byte)*str++;
    00551           br.height += (byte)*str++;
    00552           break;
    00553         case SCC_TINYFONT: size = FS_SMALL; break;
    00554         case SCC_BIGFONT:  size = FS_LARGE; break;
    00555         case '\n':
    00556           br.height += GetCharacterHeight(size);
    00557           if (br.width > max_width) max_width = br.width;
    00558           br.width = 0;
    00559           break;
    00560       }
    00561     }
    00562   }
    00563   br.height += GetCharacterHeight(size);
    00564
    00565   br.width  = max(br.width, max_width);
    00566   return br;
    00567 }
    00568
    00577 int DoDrawString(const char *string, int x, int y, uint16 real_color)
    00578 {
    00579   DrawPixelInfo *dpi = _cur_dpi;
    00580   FontSize size = _cur_fontsize;
    00581   WChar c;
    00582   int xo = x, yo = y;
    00583
    00584   byte color = real_color & 0xFF;
    00585   byte previous_color = color;
    00586
    00587   if (color != 0xFE) {
    00588     if (x >= dpi->left + dpi->width || y >= dpi->top + dpi->height) return x;
    00589
    00590     if (color != 0xFF) {
    00591 switch_color:;
    00592       if (real_color & IS_PALETTE_COLOR) {
    00593         _string_colorremap[1] = color;
    00594         _string_colorremap[2] = _use_dos_palette ? 1 : 215;
    00595       } else {
    00596         uint palette = _use_dos_palette ? 1 : 0;
    00597         _string_colorremap[1] = _string_colormap[palette][color].text;
    00598         _string_colorremap[2] = _string_colormap[palette][color].shadow;
    00599       }
    00600       _color_remap_ptr = _string_colorremap;
    00601     }
    00602   }
    00603
    00604 check_bounds:
    00605   if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
    00606 skip_char:;
    00607     for (;;) {
    00608       c = Utf8Consume(&string);
    00609       if (!IsPrintable(c)) goto skip_cont;
    00610     }
    00611   }
    00612
    00613   for (;;) {
    00614     c = Utf8Consume(&string);
    00615 skip_cont:;
    00616     if (c == 0) {
    00617       _last_fontsize = size;
    00618       return x;
    00619     }
    00620     if (IsPrintable(c)) {
    00621       if (x >= dpi->left + dpi->width) goto skip_char;
    00622       if (x + 26 >= dpi->left) {
    00623         GfxMainBlitter(GetGlyph(size, c), x, y, BM_COLOUR_REMAP);
    00624       }
    00625       x += GetCharacterWidth(size, c);
    00626     } else if (c == '\n') { // newline = {}
    00627       x = xo;
    00628       y += GetCharacterHeight(size);
    00629       goto check_bounds;
    00630     } else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change color?
    00631       previous_color = color;
    00632       color = (byte)(c - SCC_BLUE);
    00633       goto switch_color;
    00634     } else if (c == SCC_PREVIOUS_COLOUR) { // revert to the previous color
    00635       Swap(color, previous_color);
    00636       goto switch_color;
    00637     } else if (c == SCC_SETX) { // {SETX}
    00638       x = xo + (byte)*string++;
    00639     } else if (c == SCC_SETXY) {// {SETXY}
    00640       x = xo + (byte)*string++;
    00641       y = yo + (byte)*string++;
    00642     } else if (c == SCC_TINYFONT) { // {TINYFONT}
    00643       size = FS_SMALL;
    00644     } else if (c == SCC_BIGFONT) { // {BIGFONT}
    00645       size = FS_LARGE;
    00646     } else {
    00647       DEBUG(misc, 0, "[utf8] unknown string command character %d", c);
    00648     }
    00649   }
    00650 }
    00651
    00652 int DoDrawStringTruncated(const char *str, int x, int y, uint16 color, uint maxw)
    00653 {
    00654   char buffer[512];
    00655   ttd_strlcpy(buffer, str, sizeof(buffer));
    00656   TruncateString(buffer, maxw);
    00657   return DoDrawString(buffer, x, y, color);
    00658 }
    00659
    00660 void DrawSprite(SpriteID img, SpriteID pal, int x, int y, const SubSprite *sub)
    00661 {
    00662   if (HasBit(img, PALETTE_MODIFIER_TRANSPARENT)) {
    00663     _color_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH)) + 1;
    00664     GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH)), x, y, BM_TRANSPARENT, sub);
    00665   } else if (pal != PAL_NONE) {
    00666     _color_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH)) + 1;
    00667     GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH)), x, y, BM_COLOUR_REMAP, sub);
    00668   } else {
    00669     GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH)), x, y, BM_NORMAL, sub);
    00670   }
    00671 }
    00672
    00673 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub)
    00674 {
    00675   const DrawPixelInfo *dpi = _cur_dpi;
    00676   Blitter::BlitterParams bp;
    00677
    00678   /* Amount of pixels to clip from the source sprite */
    00679   int clip_left   = (sub != NULL ? max(0,                   -sprite->x_offs + sub->left       ) : 0);
    00680   int clip_top    = (sub != NULL ? max(0,                   -sprite->y_offs + sub->top        ) : 0);
    00681   int clip_right  = (sub != NULL ? max(0, sprite->width  - (-sprite->x_offs + sub->right  + 1)) : 0);
    00682   int clip_bottom = (sub != NULL ? max(0, sprite->height - (-sprite->y_offs + sub->bottom + 1)) : 0);
    00683
    00684   if (clip_left + clip_right >= sprite->width) return;
    00685   if (clip_top + clip_bottom >= sprite->height) return;
    00686
    00687   /* Move to the correct offset */
    00688   x += sprite->x_offs;
    00689   y += sprite->y_offs;
    00690
    00691   /* Copy the main data directly from the sprite */
    00692   bp.sprite = sprite->data;
    00693   bp.sprite_width = sprite->width;
    00694   bp.sprite_height = sprite->height;
    00695   bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, dpi->zoom);
    00696   bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, dpi->zoom);
    00697   bp.top = 0;
    00698   bp.left = 0;
    00699   bp.skip_left = UnScaleByZoomLower(clip_left, dpi->zoom);
    00700   bp.skip_top = UnScaleByZoomLower(clip_top, dpi->zoom);
    00701
    00702   x += ScaleByZoom(bp.skip_left, dpi->zoom);
    00703   y += ScaleByZoom(bp.skip_top, dpi->zoom);
    00704
    00705   bp.dst = dpi->dst_ptr;
    00706   bp.pitch = dpi->pitch;
    00707   bp.remap = _color_remap_ptr;
    00708
    00709   assert(sprite->width > 0);
    00710   assert(sprite->height > 0);
    00711
    00712   if (bp.width <= 0) return;
    00713   if (bp.height <= 0) return;
    00714
    00715   y -= dpi->top;
    00716   /* Check for top overflow */
    00717   if (y < 0) {
    00718     bp.height -= -UnScaleByZoom(y, dpi->zoom);
    00719     if (bp.height <= 0) return;
    00720     bp.skip_top += -UnScaleByZoom(y, dpi->zoom);
    00721     y = 0;
    00722   } else {
    00723     bp.top = UnScaleByZoom(y, dpi->zoom);
    00724   }
    00725
    00726   /* Check for bottom overflow */
    00727   y += ScaleByZoom(bp.height, dpi->zoom) - dpi->height;
    00728   if (y > 0) {
    00729     bp.height -= UnScaleByZoom(y, dpi->zoom);
    00730     if (bp.height <= 0) return;
    00731   }
    00732
    00733   x -= dpi->left;
    00734   /* Check for left overflow */
    00735   if (x < 0) {
    00736     bp.width -= -UnScaleByZoom(x, dpi->zoom);
    00737     if (bp.width <= 0) return;
    00738     bp.skip_left += -UnScaleByZoom(x, dpi->zoom);
    00739     x = 0;
    00740   } else {
    00741     bp.left = UnScaleByZoom(x, dpi->zoom);
    00742   }
    00743
    00744   /* Check for right overflow */
    00745   x += ScaleByZoom(bp.width, dpi->zoom) - dpi->width;
    00746   if (x > 0) {
    00747     bp.width -= UnScaleByZoom(x, dpi->zoom);
    00748     if (bp.width <= 0) return;
    00749   }
    00750
    00751   assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, dpi->zoom));
    00752   assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, dpi->zoom));
    00753
    00754   BlitterFactoryBase::GetCurrentBlitter()->Draw(&bp, mode, dpi->zoom);
    00755 }
    00756
    00757 void DoPaletteAnimations();
    00758
    00759 void GfxInitPalettes()
    00760 {
    00761   memcpy(_cur_palette, _palettes[_use_dos_palette ? 1 : 0], sizeof(_cur_palette));
    00762
    00763   DoPaletteAnimations();
    00764   _pal_first_dirty = 0;
    00765   _pal_count_dirty = 256;
    00766 }
    00767
    00768 #define EXTR(p, q) (((uint16)(_palette_animation_counter * (p)) * (q)) >> 16)
    00769 #define EXTR2(p, q) (((uint16)(~_palette_animation_counter * (p)) * (q)) >> 16)
    00770
    00771 void DoPaletteAnimations()
    00772 {
    00773   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00774   const Colour *s;
    00775   Colour *d;
    00776   /* Amount of colors to be rotated.
    00777    * A few more for the DOS palette, because the water colors are
    00778    * 245-254 for DOS and 217-226 for Windows.  */
    00779   const ExtraPaletteValues *ev = &_extra_palette_values;
    00780   int c = _use_dos_palette ? 38 : 28;
    00781   Colour old_val[38];
    00782   uint i;
    00783   uint j;
    00784   uint old_tc = _palette_animation_counter;
    00785
    00786   if (blitter != NULL && blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_NONE) {
    00787     _palette_animation_counter = 0;
    00788   }
    00789
    00790   d = &_cur_palette[217];
    00791   memcpy(old_val, d, c * sizeof(*old_val));
    00792
    00793   /* Dark blue water */
    00794   s = (_opt.landscape == LT_TOYLAND) ? ev->ac : ev->a;
    00795   j = EXTR(320, 5);
    00796   for (i = 0; i != 5; i++) {
    00797     *d++ = s[j];
    00798     j++;
    00799     if (j == 5) j = 0;
    00800   }
    00801
    00802   /* Glittery water */
    00803   s = (_opt.landscape == LT_TOYLAND) ? ev->bc : ev->b;
    00804   j = EXTR(128, 15);
    00805   for (i = 0; i != 5; i++) {
    00806     *d++ = s[j];
    00807     j += 3;
    00808     if (j >= 15) j -= 15;
    00809   }
    00810
    00811   s = ev->e;
    00812   j = EXTR2(512, 5);
    00813   for (i = 0; i != 5; i++) {
    00814     *d++ = s[j];
    00815     j++;
    00816     if (j == 5) j = 0;
    00817   }
    00818
    00819   /* Oil refinery fire animation */
    00820   s = ev->oil_ref;
    00821   j = EXTR2(512, 7);
    00822   for (i = 0; i != 7; i++) {
    00823     *d++ = s[j];
    00824     j++;
    00825     if (j == 7) j = 0;
    00826   }
    00827
    00828   /* Radio tower blinking */
    00829   {
    00830     byte i = (_palette_animation_counter >> 1) & 0x7F;
    00831     byte v;
    00832
    00833     (v = 255, i < 0x3f) ||
    00834     (v = 128, i < 0x4A || i >= 0x75) ||
    00835     (v = 20);
    00836     d->r = v;
    00837     d->g = 0;
    00838     d->b = 0;
    00839     d++;
    00840
    00841     i ^= 0x40;
    00842     (v = 255, i < 0x3f) ||
    00843     (v = 128, i < 0x4A || i >= 0x75) ||
    00844     (v = 20);
    00845     d->r = v;
    00846     d->g = 0;
    00847     d->b = 0;
    00848     d++;
    00849   }
    00850
    00851   /* Handle lighthouse and stadium animation */
    00852   s = ev->lighthouse;
    00853   j = EXTR(256, 4);
    00854   for (i = 0; i != 4; i++) {
    00855     *d++ = s[j];
    00856     j++;
    00857     if (j == 4) j = 0;
    00858   }
    00859
    00860   /* Animate water for old DOS graphics */
    00861   if (_use_dos_palette) {
    00862     /* Dark blue water DOS */
    00863     s = (_opt.landscape == LT_TOYLAND) ? ev->ac : ev->a;
    00864     j = EXTR(320, 5);
    00865     for (i = 0; i != 5; i++) {
    00866       *d++ = s[j];
    00867       j++;
    00868       if (j == 5) j = 0;
    00869     }
    00870
    00871     /* Glittery water DOS */
    00872     s = (_opt.landscape == LT_TOYLAND) ? ev->bc : ev->b;
    00873     j = EXTR(128, 15);
    00874     for (i = 0; i != 5; i++) {
    00875       *d++ = s[j];
    00876       j += 3;
    00877       if (j >= 15) j -= 15;
    00878     }
    00879   }
    00880
    00881   if (blitter != NULL && blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_NONE) {
    00882     _palette_animation_counter = old_tc;
    00883   } else {
    00884     if (memcmp(old_val, &_cur_palette[217], c * sizeof(*old_val)) != 0) {
    00885       _pal_first_dirty = 217;
    00886       _pal_count_dirty = c;
    00887     }
    00888   }
    00889 }
    00890
    00891
    00892 void LoadStringWidthTable()
    00893 {
    00894   uint i;
    00895
    00896   /* Normal font */
    00897   for (i = 0; i != 224; i++) {
    00898     _stringwidth_table[FS_NORMAL][i] = GetGlyphWidth(FS_NORMAL, i + 32);
    00899   }
    00900
    00901   /* Small font */
    00902   for (i = 0; i != 224; i++) {
    00903     _stringwidth_table[FS_SMALL][i] = GetGlyphWidth(FS_SMALL, i + 32);
    00904   }
    00905
    00906   /* Large font */
    00907   for (i = 0; i != 224; i++) {
    00908     _stringwidth_table[FS_LARGE][i] = GetGlyphWidth(FS_LARGE, i + 32);
    00909   }
    00910 }
    00911
    00912
    00913 byte GetCharacterWidth(FontSize size, WChar key)
    00914 {
    00915   if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
    00916
    00917   return GetGlyphWidth(size, key);
    00918 }
    00919
    00920
    00921 void ScreenSizeChanged()
    00922 {
    00923   /* check the dirty rect */
    00924   if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
    00925   if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
    00926
    00927   /* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
    00928   _cursor.visible = false;
    00929 }
    00930
    00931 void UndrawMouseCursor()
    00932 {
    00933   if (_cursor.visible) {
    00934     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00935     _cursor.visible = false;
    00936     blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), _cursor_backup, _cursor.draw_size.x, _cursor.draw_size.y);
    00937     _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
    00938   }
    00939 }
    00940
    00941 void DrawMouseCursor()
    00942 {
    00943 #if defined(WINCE)
    00944   /* Don't ever draw the mouse for WinCE, as we work with a stylus */
    00945   return;
    00946 #endif
    00947
    00948   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    00949   int x;
    00950   int y;
    00951   int w;
    00952   int h;
    00953
    00954   /* Redraw mouse cursor but only when it's inside the window */
    00955   if (!_cursor.in_window) return;
    00956
    00957   /* Don't draw the mouse cursor if it's already drawn */
    00958   if (_cursor.visible) {
    00959     if (!_cursor.dirty) return;
    00960     UndrawMouseCursor();
    00961   }
    00962
    00963   w = _cursor.size.x;
    00964   x = _cursor.pos.x + _cursor.offs.x + _cursor.short_vehicle_offset;
    00965   if (x < 0) {
    00966     w += x;
    00967     x = 0;
    00968   }
    00969   if (w > _screen.width - x) w = _screen.width - x;
    00970   if (w <= 0) return;
    00971   _cursor.draw_pos.x = x;
    00972   _cursor.draw_size.x = w;
    00973
    00974   h = _cursor.size.y;
    00975   y = _cursor.pos.y + _cursor.offs.y;
    00976   if (y < 0) {
    00977     h += y;
    00978     y = 0;
    00979   }
    00980   if (h > _screen.height - y) h = _screen.height - y;
    00981   if (h <= 0) return;
    00982   _cursor.draw_pos.y = y;
    00983   _cursor.draw_size.y = h;
    00984
    00985   assert(blitter->BufferSize(w, h) < (int)sizeof(_cursor_backup));
    00986
    00987   /* Make backup of stuff below cursor */
    00988   blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), _cursor_backup, _cursor.draw_size.x, _cursor.draw_size.y);
    00989
    00990   /* Draw cursor on screen */
    00991   _cur_dpi = &_screen;
    00992   DrawSprite(_cursor.sprite, _cursor.pal, _cursor.pos.x + _cursor.short_vehicle_offset, _cursor.pos.y);
    00993
    00994   _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
    00995
    00996   _cursor.visible = true;
    00997   _cursor.dirty = false;
    00998 }
    00999
    01000 void RedrawScreenRect(int left, int top, int right, int bottom)
    01001 {
    01002   assert(right <= _screen.width && bottom <= _screen.height);
    01003   if (_cursor.visible) {
    01004     if (right > _cursor.draw_pos.x &&
    01005         left < _cursor.draw_pos.x + _cursor.draw_size.x &&
    01006         bottom > _cursor.draw_pos.y &&
    01007         top < _cursor.draw_pos.y + _cursor.draw_size.y) {
    01008       UndrawMouseCursor();
    01009     }
    01010   }
    01011   UndrawChatMessage();
    01012
    01013   DrawOverlappedWindowForAll(left, top, right, bottom);
    01014
    01015   _video_driver->MakeDirty(left, top, right - left, bottom - top);
    01016 }
    01017
    01023 void DrawDirtyBlocks()
    01024 {
    01025   byte *b = _dirty_blocks;
    01026   const int w = Align(_screen.width, 64);
    01027   const int h = Align(_screen.height, 8);
    01028   int x;
    01029   int y;
    01030
    01031   if (IsGeneratingWorld() && !IsGeneratingWorldReadyForPaint()) return;
    01032
    01033   y = 0;
    01034   do {
    01035     x = 0;
    01036     do {
    01037       if (*b != 0) {
    01038         int left;
    01039         int top;
    01040         int right = x + 64;
    01041         int bottom = y;
    01042         byte *p = b;
    01043         int h2;
    01044
    01045         /* First try coalescing downwards */
    01046         do {
    01047           *p = 0;
    01048           p += DIRTY_BYTES_PER_LINE;
    01049           bottom += 8;
    01050         } while (bottom != h && *p != 0);
    01051
    01052         /* Try coalescing to the right too. */
    01053         h2 = (bottom - y) >> 3;
    01054         assert(h2 > 0);
    01055         p = b;
    01056
    01057         while (right != w) {
    01058           byte *p2 = ++p;
    01059           int h = h2;
    01060           /* Check if a full line of dirty flags is set. */
    01061           do {
    01062             if (!*p2) goto no_more_coalesc;
    01063             p2 += DIRTY_BYTES_PER_LINE;
    01064           } while (--h != 0);
    01065
    01066           /* Wohoo, can combine it one step to the right!
    01067            * Do that, and clear the bits. */
    01068           right += 64;
    01069
    01070           h = h2;
    01071           p2 = p;
    01072           do {
    01073             *p2 = 0;
    01074             p2 += DIRTY_BYTES_PER_LINE;
    01075           } while (--h != 0);
    01076         }
    01077         no_more_coalesc:
    01078
    01079         left = x;
    01080         top = y;
    01081
    01082         if (left   < _invalid_rect.left  ) left   = _invalid_rect.left;
    01083         if (top    < _invalid_rect.top   ) top    = _invalid_rect.top;
    01084         if (right  > _invalid_rect.right ) right  = _invalid_rect.right;
    01085         if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
    01086
    01087         if (left < right && top < bottom) {
    01088           RedrawScreenRect(left, top, right, bottom);
    01089         }
    01090
    01091       }
    01092     } while (b++, (x += 64) != w);
    01093   } while (b += -(w >> 6) + DIRTY_BYTES_PER_LINE, (y += 8) != h);
    01094
    01095   _invalid_rect.left = w;
    01096   _invalid_rect.top = h;
    01097   _invalid_rect.right = 0;
    01098   _invalid_rect.bottom = 0;
    01099
    01100   /* If we are generating a world, and waiting for a paint run, mark it here
    01101    *  as done painting, so we can continue generating. */
    01102   if (IsGeneratingWorld() && IsGeneratingWorldReadyForPaint()) {
    01103     SetGeneratingWorldPaintStatus(false);
    01104   }
    01105 }
    01106
    01122 void SetDirtyBlocks(int left, int top, int right, int bottom)
    01123 {
    01124   byte *b;
    01125   int width;
    01126   int height;
    01127
    01128   if (left < 0) left = 0;
    01129   if (top < 0) top = 0;
    01130   if (right > _screen.width) right = _screen.width;
    01131   if (bottom > _screen.height) bottom = _screen.height;
    01132
    01133   if (left >= right || top >= bottom) return;
    01134
    01135   if (left   < _invalid_rect.left  ) _invalid_rect.left   = left;
    01136   if (top    < _invalid_rect.top   ) _invalid_rect.top    = top;
    01137   if (right  > _invalid_rect.right ) _invalid_rect.right  = right;
    01138   if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
    01139
    01140   left >>= 6;
    01141   top  >>= 3;
    01142
    01143   b = _dirty_blocks + top * DIRTY_BYTES_PER_LINE + left;
    01144
    01145   width  = ((right  - 1) >> 6) - left + 1;
    01146   height = ((bottom - 1) >> 3) - top  + 1;
    01147
    01148   assert(width > 0 && height > 0);
    01149
    01150   do {
    01151     int i = width;
    01152
    01153     do b[--i] = 0xFF; while (i);
    01154
    01155     b += DIRTY_BYTES_PER_LINE;
    01156   } while (--height != 0);
    01157 }
    01158
    01164 void MarkWholeScreenDirty()
    01165 {
    01166   SetDirtyBlocks(0, 0, _screen.width, _screen.height);
    01167 }
    01168
    01181 bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
    01182 {
    01183   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
    01184   const DrawPixelInfo *o = _cur_dpi;
    01185
    01186   n->zoom = ZOOM_LVL_NORMAL;
    01187
    01188   assert(width > 0);
    01189   assert(height > 0);
    01190
    01191   if ((left -= o->left) < 0) {
    01192     width += left;
    01193     if (width <= 0) return false;
    01194     n->left = -left;
    01195     left = 0;
    01196   } else {
    01197     n->left = 0;
    01198   }
    01199
    01200   if (width > o->width - left) {
    01201     width = o->width - left;
    01202     if (width <= 0) return false;
    01203   }
    01204   n->width = width;
    01205
    01206   if ((top -= o->top) < 0) {
    01207     height += top;
    01208     if (height <= 0) return false;
    01209     n->top = -top;
    01210     top = 0;
    01211   } else {
    01212     n->top = 0;
    01213   }
    01214
    01215   n->dst_ptr = blitter->MoveTo(o->dst_ptr, left, top);
    01216   n->pitch = o->pitch;
    01217
    01218   if (height > o->height - top) {
    01219     height = o->height - top;
    01220     if (height <= 0) return false;
    01221   }
    01222   n->height = height;
    01223
    01224   return true;
    01225 }
    01226
    01227 static void SetCursorSprite(SpriteID cursor, SpriteID pal)
    01228 {
    01229   CursorVars *cv = &_cursor;
    01230   const Sprite *p;
    01231
    01232   if (cv->sprite == cursor) return;
    01233
    01234   p = GetSprite(GB(cursor, 0, SPRITE_WIDTH));
    01235   cv->sprite = cursor;
    01236   cv->pal    = pal;
    01237   cv->size.y = p->height;
    01238   cv->size.x = p->width;
    01239   cv->offs.x = p->x_offs;
    01240   cv->offs.y = p->y_offs;
    01241
    01242   cv->dirty = true;
    01243   cv->short_vehicle_offset = 0;
    01244 }
    01245
    01246 static void SwitchAnimatedCursor()
    01247 {
    01248   const AnimCursor *cur = _cursor.animate_cur;
    01249
    01250   if (cur == NULL || cur->sprite == AnimCursor::LAST) cur = _cursor.animate_list;
    01251
    01252   SetCursorSprite(cur->sprite, _cursor.pal);
    01253
    01254   _cursor.animate_timeout = cur->display_time;
    01255   _cursor.animate_cur     = cur + 1;
    01256 }
    01257
    01258 void CursorTick()
    01259 {
    01260   if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0)
    01261     SwitchAnimatedCursor();
    01262 }
    01263
    01264 void SetMouseCursor(SpriteID sprite, SpriteID pal)
    01265 {
    01266   /* Turn off animation */
    01267   _cursor.animate_timeout = 0;
    01268   /* Set cursor */
    01269   SetCursorSprite(sprite, pal);
    01270 }
    01271
    01272 void SetAnimatedMouseCursor(const AnimCursor *table)
    01273 {
    01274   _cursor.animate_list = table;
    01275   _cursor.animate_cur = NULL;
    01276   _cursor.pal = PAL_NONE;
    01277   SwitchAnimatedCursor();
    01278 }
    01279
    01280 bool ChangeResInGame(int width, int height)
    01281 {
    01282   return (_screen.width == width && _screen.height == height) || _video_driver->ChangeResolution(width, height);
    01283 }
    01284
    01285 bool ToggleFullScreen(bool fs)
    01286 {
    01287   bool result = _video_driver->ToggleFullscreen(fs);
    01288   if (_fullscreen != fs && _num_resolutions == 0) {
    01289     DEBUG(driver, 0, "Could not find a suitable fullscreen resolution");
    01290   }
    01291   return result;
    01292 }
    01293
    01294 static int CDECL compare_res(const void *pa, const void *pb)
    01295 {
    01296   int x = ((const uint16*)pa)[0] - ((const uint16*)pb)[0];
    01297   if (x != 0) return x;
    01298   return ((const uint16*)pa)[1] - ((const uint16*)pb)[1];
    01299 }
    01300
    01301 void SortResolutions(int count)
    01302 {
    01303   qsort(_resolutions, count, sizeof(_resolutions[0]), compare_res);
    01304 }