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 }

Generated on Wed Oct 1 17:03:20 2008 for openttd by  doxygen 1.5.6