gfx.cpp

Go to the documentation of this file.
00001 /* $Id: gfx.cpp 16247 2009-05-06 22:37:19Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "gfx_func.h"
00008 #include "variables.h"
00009 #include "spritecache.h"
00010 #include "fontcache.h"
00011 #include "genworld.h"
00012 #include "zoom_func.h"
00013 #include "blitter/factory.hpp"
00014 #include "video/video_driver.hpp"
00015 #include "strings_func.h"
00016 #include "settings_type.h"
00017 #include "core/alloc_func.hpp"
00018 #include "core/sort_func.hpp"
00019 #include "landscape_type.h"
00020 #include "network/network_func.h"
00021 #include "thread.h"
00022 
00023 #include "table/palettes.h"
00024 #include "table/sprites.h"
00025 #include "table/control_codes.h"
00026 
00027 byte _dirkeys;        
00028 bool _fullscreen;
00029 CursorVars _cursor;
00030 bool _ctrl_pressed;   
00031 bool _shift_pressed;  
00032 byte _fast_forward;
00033 bool _left_button_down;     
00034 bool _left_button_clicked;  
00035 bool _right_button_down;    
00036 bool _right_button_clicked; 
00037 DrawPixelInfo _screen;
00038 bool _screen_disable_anim = false;   
00039 bool _exit_game;
00040 GameMode _game_mode;
00041 SwitchMode _switch_mode;  
00042 int8 _pause_game;
00043 int _pal_first_dirty;
00044 int _pal_count_dirty;
00045 
00046 Colour _cur_palette[256];
00047 byte _stringwidth_table[FS_END][224]; 
00048 DrawPixelInfo *_cur_dpi;
00049 byte _colour_gradient[COLOUR_END][8];
00050 
00051 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL);
00052 static int ReallyDoDrawString(const char *string, int x, int y, TextColour colour, bool parse_string_also_when_clipped = false);
00053 
00054 FontSize _cur_fontsize;
00055 static FontSize _last_fontsize;
00056 static ReusableBuffer<uint8> _cursor_backup;
00057 
00065 static Rect _invalid_rect;
00066 static const byte *_colour_remap_ptr;
00067 static byte _string_colourremap[3];
00068 
00069 enum {
00070   DIRTY_BLOCK_HEIGHT   = 8,
00071   DIRTY_BLOCK_WIDTH    = 64,
00072 };
00073 static uint _dirty_bytes_per_line = 0;
00074 static byte *_dirty_blocks = NULL;
00075 
00076 void GfxScroll(int left, int top, int width, int height, int xo, int yo)
00077 {
00078   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00079 
00080   if (xo == 0 && yo == 0) return;
00081 
00082   if (_cursor.visible) UndrawMouseCursor();
00083 
00084 #ifdef ENABLE_NETWORK
00085   NetworkUndrawChatMessage();
00086 #endif /* ENABLE_NETWORK */
00087 
00088   blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
00089   /* This part of the screen is now dirty. */
00090   _video_driver->MakeDirty(left, top, width, height);
00091 }
00092 
00093 
00108 void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
00109 {
00110   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00111   const DrawPixelInfo *dpi = _cur_dpi;
00112   void *dst;
00113   const int otop = top;
00114   const int oleft = left;
00115 
00116   if (dpi->zoom != ZOOM_LVL_NORMAL) return;
00117   if (left > right || top > bottom) return;
00118   if (right < dpi->left || left >= dpi->left + dpi->width) return;
00119   if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
00120 
00121   if ( (left -= dpi->left) < 0) left = 0;
00122   right = right - dpi->left + 1;
00123   if (right > dpi->width) right = dpi->width;
00124   right -= left;
00125   assert(right > 0);
00126 
00127   if ( (top -= dpi->top) < 0) top = 0;
00128   bottom = bottom - dpi->top + 1;
00129   if (bottom > dpi->height) bottom = dpi->height;
00130   bottom -= top;
00131   assert(bottom > 0);
00132 
00133   dst = blitter->MoveTo(dpi->dst_ptr, left, top);
00134 
00135   switch (mode) {
00136     default: // FILLRECT_OPAQUE
00137       blitter->DrawRect(dst, right, bottom, (uint8)colour);
00138       break;
00139 
00140     case FILLRECT_RECOLOUR:
00141       blitter->DrawColourMappingRect(dst, right, bottom, GB(colour, 0, PALETTE_WIDTH));
00142       break;
00143 
00144     case FILLRECT_CHECKER: {
00145       byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
00146       do {
00147         for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8)colour);
00148         dst = blitter->MoveTo(dst, 0, 1);
00149       } while (--bottom > 0);
00150       break;
00151     }
00152   }
00153 }
00154 
00155 void GfxDrawLine(int x, int y, int x2, int y2, int colour)
00156 {
00157   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00158   DrawPixelInfo *dpi = _cur_dpi;
00159 
00160   x -= dpi->left;
00161   x2 -= dpi->left;
00162   y -= dpi->top;
00163   y2 -= dpi->top;
00164 
00165   /* Check clipping */
00166   if (x < 0 && x2 < 0) return;
00167   if (y < 0 && y2 < 0) return;
00168   if (x > dpi->width  && x2 > dpi->width)  return;
00169   if (y > dpi->height && y2 > dpi->height) return;
00170 
00171   blitter->DrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour);
00172 }
00173 
00174 void GfxDrawLineUnscaled(int x, int y, int x2, int y2, int colour)
00175 {
00176   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00177   DrawPixelInfo *dpi = _cur_dpi;
00178 
00179   x -= dpi->left;
00180   x2 -= dpi->left;
00181   y -= dpi->top;
00182   y2 -= dpi->top;
00183 
00184   /* Check clipping */
00185   if (x < 0 && x2 < 0) return;
00186   if (y < 0 && y2 < 0) return;
00187   if (x > dpi->width  && x2 > dpi->width)  return;
00188   if (y > dpi->height && y2 > dpi->height) return;
00189 
00190   blitter->DrawLine(dpi->dst_ptr, UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
00191       UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
00192       UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), colour);
00193 }
00194 
00208 void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
00209 {
00210   /*           ....
00211    *         ..    ....
00212    *       ..          ....
00213    *     ..                ^
00214    *   <--__(dx1,dy1)    /(dx2,dy2)
00215    *   :    --__       /   :
00216    *   :        --__ /     :
00217    *   :            *(x,y) :
00218    *   :            |      :
00219    *   :            |     ..
00220    *    ....        |(dx3,dy3)
00221    *        ....    | ..
00222    *            ....V.
00223    */
00224 
00225   static const byte colour = 255;
00226 
00227   GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
00228   GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
00229   GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
00230 
00231   GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
00232   GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
00233   GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
00234   GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
00235   GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
00236   GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
00237 }
00238 
00243 static void SetColourRemap(TextColour colour)
00244 {
00245   if (colour == TC_INVALID) return;
00246 
00247   if (colour & IS_PALETTE_COLOUR) {
00248     _string_colourremap[1] = colour & ~IS_PALETTE_COLOUR;
00249     _string_colourremap[2] = (_use_palette == PAL_DOS) ? 1 : 215;
00250   } else {
00251     _string_colourremap[1] = _string_colourmap[_use_palette][colour].text;
00252     _string_colourremap[2] = _string_colourmap[_use_palette][colour].shadow;
00253   }
00254   _colour_remap_ptr = _string_colourremap;
00255 }
00256 
00257 #if !defined(WITH_ICU)
00258 static void HandleBiDiAndArabicShapes(char *text, const char *lastof) {}
00259 #else
00260 #include <unicode/ubidi.h>
00261 #include <unicode/ushape.h>
00262 
00291 static void HandleBiDiAndArabicShapes(char *buffer, const char *lastof)
00292 {
00293   UChar input_output[DRAW_STRING_BUFFER];
00294   UChar intermediate[DRAW_STRING_BUFFER];
00295 
00296   char *t = buffer;
00297   size_t length = 0;
00298   while (*t != '\0' && length < lengthof(input_output) - 1) {
00299     WChar tmp;
00300     t += Utf8Decode(&tmp, t);
00301     input_output[length++] = tmp;
00302   }
00303   input_output[length] = 0;
00304 
00305   UErrorCode err = U_ZERO_ERROR;
00306   UBiDi *para = ubidi_openSized((int32_t)length, 0, &err);
00307   if (para == NULL) return;
00308 
00309   ubidi_setPara(para, input_output, (int32_t)length, _dynlang.text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, NULL, &err);
00310   ubidi_writeReordered(para, intermediate, (int32_t)length, 0, &err);
00311   length = u_shapeArabic(intermediate, (int32_t)length, input_output, lengthof(input_output), U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE, &err);
00312   ubidi_close(para);
00313 
00314   if (U_FAILURE(err)) return;
00315 
00316   t = buffer;
00317   for (size_t i = 0; i < length && t < (lastof - 4); i++) {
00318     t += Utf8Encode(t, input_output[i]);
00319   }
00320   *t = '\0';
00321 }
00322 #endif /* WITH_ICU */
00323 
00324 
00330 static int TruncateString(char *str, int maxw)
00331 {
00332   int w = 0;
00333   FontSize size = _cur_fontsize;
00334   int ddd, ddd_w;
00335 
00336   WChar c;
00337   char *ddd_pos;
00338 
00339   ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
00340 
00341   for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
00342     if (IsPrintable(c)) {
00343       w += GetCharacterWidth(size, c);
00344 
00345       if (w >= maxw) {
00346         /* string got too big... insert dotdotdot, but make sure we do not
00347          * print anything beyond the string termination character. */
00348         for (int i = 0; *ddd_pos != '\0' && i < 3; i++, ddd_pos++) *ddd_pos = '.';
00349         *ddd_pos = '\0';
00350         return ddd_w;
00351       }
00352     } else {
00353       if (c == SCC_SETX) {
00354         w = *str;
00355         str++;
00356       } else if (c == SCC_SETXY) {
00357         w = *str;
00358         str += 2;
00359       } else if (c == SCC_TINYFONT) {
00360         size = FS_SMALL;
00361         ddd = GetCharacterWidth(size, '.') * 3;
00362       } else if (c == SCC_BIGFONT) {
00363         size = FS_LARGE;
00364         ddd = GetCharacterWidth(size, '.') * 3;
00365       }
00366     }
00367 
00368     /* Remember the last position where three dots fit. */
00369     if (w + ddd < maxw) {
00370       ddd_w = w + ddd;
00371       ddd_pos = str;
00372     }
00373   }
00374 
00375   return w;
00376 }
00377 
00388 static inline int TruncateStringID(StringID src, char *dest, int maxw, const char *last)
00389 {
00390   GetString(dest, src, last);
00391   return TruncateString(dest, maxw);
00392 }
00393 
00404 int DrawString(int x, int y, StringID str, TextColour colour)
00405 {
00406   char buffer[DRAW_STRING_BUFFER];
00407 
00408   GetString(buffer, str, lastof(buffer));
00409   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00410   return ReallyDoDrawString(buffer, x, y, colour);
00411 }
00412 
00424 int DrawStringTruncated(int x, int y, StringID str, TextColour colour, uint maxw)
00425 {
00426   char buffer[DRAW_STRING_BUFFER];
00427   TruncateStringID(str, buffer, maxw, lastof(buffer));
00428   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00429   return ReallyDoDrawString(buffer, x, y, colour);
00430 }
00431 
00442 int DrawStringRightAligned(int x, int y, StringID str, TextColour colour)
00443 {
00444   char buffer[DRAW_STRING_BUFFER];
00445   int w;
00446 
00447   GetString(buffer, str, lastof(buffer));
00448   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00449 
00450   w = GetStringBoundingBox(buffer).width;
00451   ReallyDoDrawString(buffer, x - w, y, colour);
00452 
00453   return w;
00454 }
00455 
00465 void DrawStringRightAlignedTruncated(int x, int y, StringID str, TextColour colour, uint maxw)
00466 {
00467   char buffer[DRAW_STRING_BUFFER];
00468 
00469   TruncateStringID(str, buffer, maxw, lastof(buffer));
00470   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00471   ReallyDoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, colour);
00472 }
00473 
00482 void DrawStringRightAlignedUnderline(int x, int y, StringID str, TextColour colour)
00483 {
00484   int w = DrawStringRightAligned(x, y, str, colour);
00485   GfxFillRect(x - w, y + 10, x, y + 10, _string_colourremap[1]);
00486 }
00487 
00498 int DrawStringCentered(int x, int y, StringID str, TextColour colour)
00499 {
00500   char buffer[DRAW_STRING_BUFFER];
00501   int w;
00502 
00503   GetString(buffer, str, lastof(buffer));
00504   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00505 
00506   w = GetStringBoundingBox(buffer).width;
00507   ReallyDoDrawString(buffer, x - w / 2, y, colour);
00508 
00509   return w;
00510 }
00511 
00523 int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, TextColour colour)
00524 {
00525   char buffer[DRAW_STRING_BUFFER];
00526   TruncateStringID(str, buffer, xr - xl, lastof(buffer));
00527   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00528 
00529   int w = GetStringBoundingBox(buffer).width;
00530   return ReallyDoDrawString(buffer, (xl + xr - w) / 2, y, colour);
00531 }
00532 
00543 int DoDrawStringCentered(int x, int y, const char *str, TextColour colour)
00544 {
00545   char buffer[DRAW_STRING_BUFFER];
00546   strecpy(buffer, str, lastof(buffer));
00547   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00548 
00549   int w = GetStringBoundingBox(buffer).width;
00550   ReallyDoDrawString(buffer, x - w / 2, y, colour);
00551   return w;
00552 }
00553 
00562 void DrawStringCenterUnderline(int x, int y, StringID str, TextColour colour)
00563 {
00564   int w = DrawStringCentered(x, y, str, colour);
00565   GfxFillRect(x - (w >> 1), y + 10, x - (w >> 1) + w, y + 10, _string_colourremap[1]);
00566 }
00567 
00577 void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, TextColour colour)
00578 {
00579   int w = DrawStringCenteredTruncated(xl, xr, y, str, colour);
00580   GfxFillRect((xl + xr - w) / 2, y + 10, (xl + xr + w) / 2, y + 10, _string_colourremap[1]);
00581 }
00582 
00601 uint32 FormatStringLinebreaks(char *str, int maxw)
00602 {
00603   FontSize size = _cur_fontsize;
00604   int num = 0;
00605 
00606   assert(maxw > 0);
00607 
00608   for (;;) {
00609     char *last_space = NULL;
00610     int w = 0;
00611 
00612     for (;;) {
00613       WChar c = Utf8Consume((const char **)&str);
00614       /* whitespace is where we will insert the line-break */
00615       if (IsWhitespace(c)) last_space = str;
00616 
00617       if (IsPrintable(c)) {
00618         w += GetCharacterWidth(size, c);
00619         /* string is longer than maximum width so we need to decide what to
00620          * do. We can do two things:
00621          * 1. If no whitespace was found at all up until now (on this line) then
00622          *    we will truncate the string and bail out.
00623          * 2. In all other cases force a linebreak at the last seen whitespace */
00624         if (w > maxw) {
00625           if (last_space == NULL) {
00626             *Utf8PrevChar(str) = '\0';
00627             return num + (size << 16);
00628           }
00629           str = last_space;
00630           break;
00631         }
00632       } else {
00633         switch (c) {
00634           case '\0': return num + (size << 16); break;
00635           case SCC_SETX:  str++; break;
00636           case SCC_SETXY: str += 2; break;
00637           case SCC_TINYFONT: size = FS_SMALL; break;
00638           case SCC_BIGFONT:  size = FS_LARGE; break;
00639           case '\n': goto end_of_inner_loop;
00640         }
00641       }
00642     }
00643 end_of_inner_loop:
00644     /* String didn't fit on line (or a '\n' was encountered), so 'dummy' terminate
00645      * and increase linecount. We use Utf8PrevChar() as also non 1 char long
00646      * whitespace seperators are supported */
00647     num++;
00648     char *s = Utf8PrevChar(str);
00649     *s++ = '\0';
00650 
00651     /* In which case (see above) we will shift remainder to left and close the gap */
00652     if (str - s >= 1) {
00653       for (; str[-1] != '\0';) *s++ = *str++;
00654     }
00655   }
00656 }
00657 
00658 
00665 static int GetMultilineStringHeight(const char *src, int num)
00666 {
00667   int maxy = 0;
00668   int y = 0;
00669   int fh = GetCharacterHeight(_cur_fontsize);
00670 
00671   for (;;) {
00672     WChar c = Utf8Consume(&src);
00673 
00674     switch (c) {
00675       case 0:            y += fh; if (--num < 0) return maxy; break;
00676       case '\n':         y += fh;                             break;
00677       case SCC_SETX:     src++;                               break;
00678       case SCC_SETXY:    src++; y = (int)*src++;              break;
00679       case SCC_TINYFONT: fh = GetCharacterHeight(FS_SMALL);   break;
00680       case SCC_BIGFONT:  fh = GetCharacterHeight(FS_LARGE);   break;
00681       default:           maxy = max<int>(maxy, y + fh);       break;
00682     }
00683   }
00684 }
00685 
00686 
00692 int GetStringHeight(StringID str, int maxw)
00693 {
00694   char buffer[DRAW_STRING_BUFFER];
00695 
00696   GetString(buffer, str, lastof(buffer));
00697 
00698   uint32 tmp = FormatStringLinebreaks(buffer, maxw);
00699 
00700   return GetMultilineStringHeight(buffer, GB(tmp, 0, 16));
00701 }
00702 
00703 
00709 void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
00710 {
00711   char buffer[DRAW_STRING_BUFFER];
00712   uint32 tmp;
00713   int num, mt;
00714   const char *src;
00715   WChar c;
00716 
00717   GetString(buffer, str, lastof(buffer));
00718 
00719   tmp = FormatStringLinebreaks(buffer, maxw);
00720   num = GB(tmp, 0, 16);
00721 
00722   mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
00723 
00724   y -= (mt >> 1) * num;
00725 
00726   src = buffer;
00727 
00728   for (;;) {
00729     char buf2[DRAW_STRING_BUFFER];
00730     strecpy(buf2, src, lastof(buf2));
00731     HandleBiDiAndArabicShapes(buf2, lastof(buf2));
00732     int w = GetStringBoundingBox(buf2).width;
00733     ReallyDoDrawString(buf2, x - (w >> 1), y, TC_FROMSTRING, true);
00734     _cur_fontsize = _last_fontsize;
00735 
00736     for (;;) {
00737       c = Utf8Consume(&src);
00738       if (c == 0) {
00739         y += mt;
00740         if (--num < 0) {
00741           _cur_fontsize = FS_NORMAL;
00742           return;
00743         }
00744         break;
00745       } else if (c == SCC_SETX) {
00746         src++;
00747       } else if (c == SCC_SETXY) {
00748         src += 2;
00749       }
00750     }
00751   }
00752 }
00753 
00754 
00755 uint DrawStringMultiLine(int x, int y, StringID str, int maxw, int maxh)
00756 {
00757   char buffer[DRAW_STRING_BUFFER];
00758   uint32 tmp;
00759   int num, mt;
00760   uint total_height;
00761   const char *src;
00762   WChar c;
00763 
00764   GetString(buffer, str, lastof(buffer));
00765 
00766   tmp = FormatStringLinebreaks(buffer, maxw);
00767   num = GB(tmp, 0, 16);
00768 
00769   mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
00770   total_height = (num + 1) * mt;
00771 
00772   if (maxh != -1 && (int)total_height > maxh) {
00773     /* Check there's room enough for at least one line. */
00774     if (maxh < mt) return 0;
00775 
00776     num = maxh / mt - 1;
00777     total_height = (num + 1) * mt;
00778   }
00779 
00780   src = buffer;
00781 
00782   for (;;) {
00783     char buf2[DRAW_STRING_BUFFER];
00784     strecpy(buf2, src, lastof(buf2));
00785     HandleBiDiAndArabicShapes(buf2, lastof(buf2));
00786     ReallyDoDrawString(buf2, x, y, TC_FROMSTRING, true);
00787     _cur_fontsize = _last_fontsize;
00788 
00789     for (;;) {
00790       c = Utf8Consume(&src);
00791       if (c == 0) {
00792         y += mt;
00793         if (--num < 0) {
00794           _cur_fontsize = FS_NORMAL;
00795           return total_height;
00796         }
00797         break;
00798       } else if (c == SCC_SETX) {
00799         src++;
00800       } else if (c == SCC_SETXY) {
00801         src += 2;
00802       }
00803     }
00804   }
00805 }
00806 
00814 Dimension GetStringBoundingBox(const char *str)
00815 {
00816   FontSize size = _cur_fontsize;
00817   Dimension br;
00818   int max_width;
00819   WChar c;
00820 
00821   br.width = br.height = max_width = 0;
00822   for (;;) {
00823     c = Utf8Consume(&str);
00824     if (c == 0) break;
00825     if (IsPrintable(c)) {
00826       br.width += GetCharacterWidth(size, c);
00827     } else {
00828       switch (c) {
00829         case SCC_SETX: br.width += (byte)*str++; break;
00830         case SCC_SETXY:
00831           br.width += (byte)*str++;
00832           br.height += (byte)*str++;
00833           break;
00834         case SCC_TINYFONT: size = FS_SMALL; break;
00835         case SCC_BIGFONT:  size = FS_LARGE; break;
00836         case '\n':
00837           br.height += GetCharacterHeight(size);
00838           if (br.width > max_width) max_width = br.width;
00839           br.width = 0;
00840           break;
00841       }
00842     }
00843   }
00844   br.height += GetCharacterHeight(size);
00845 
00846   br.width  = max(br.width, max_width);
00847   return br;
00848 }
00849 
00857 void DrawCharCentered(WChar c, int x, int y, TextColour colour)
00858 {
00859   SetColourRemap(colour);
00860   GfxMainBlitter(GetGlyph(FS_NORMAL, c), x - GetCharacterWidth(FS_NORMAL, c) / 2, y, BM_COLOUR_REMAP);
00861 }
00862 
00880 int DoDrawString(const char *string, int x, int y, TextColour colour, bool parse_string_also_when_clipped)
00881 {
00882   char buffer[DRAW_STRING_BUFFER];
00883   strecpy(buffer, string, lastof(buffer));
00884   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00885 
00886   return ReallyDoDrawString(buffer, x, y, colour, parse_string_also_when_clipped);
00887 }
00888 
00906 static int ReallyDoDrawString(const char *string, int x, int y, TextColour colour, bool parse_string_also_when_clipped)
00907 {
00908   DrawPixelInfo *dpi = _cur_dpi;
00909   FontSize size = _cur_fontsize;
00910   WChar c;
00911   int xo = x, yo = y;
00912 
00913   TextColour previous_colour = colour;
00914 
00915   if (!parse_string_also_when_clipped) {
00916     /* in "mode multiline", the available space have been verified. Not in regular one.
00917      * So if the string cannot be drawn, return the original start to say so.*/
00918     if (x >= dpi->left + dpi->width || y >= dpi->top + dpi->height) return x;
00919 
00920     if (colour != TC_INVALID) { // the invalid colour flag test should not  really occur.  But better be safe
00921 switch_colour:;
00922       SetColourRemap(colour);
00923     }
00924   }
00925 
00926 check_bounds:
00927   if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
00928 skip_char:;
00929     for (;;) {
00930       c = Utf8Consume(&string);
00931       if (!IsPrintable(c)) goto skip_cont;
00932     }
00933   }
00934 
00935   for (;;) {
00936     c = Utf8Consume(&string);
00937 skip_cont:;
00938     if (c == 0) {
00939       _last_fontsize = size;
00940       return x;  // Nothing more to draw, get out. And here is the new x position
00941     }
00942     if (IsPrintable(c)) {
00943       if (x >= dpi->left + dpi->width) goto skip_char;
00944       if (x + 26 >= dpi->left) {
00945         GfxMainBlitter(GetGlyph(size, c), x, y, BM_COLOUR_REMAP);
00946       }
00947       x += GetCharacterWidth(size, c);
00948     } else if (c == '\n') { // newline = {}
00949       x = xo;  // We require a new line, so the x coordinate is reset
00950       y += GetCharacterHeight(size);
00951       goto check_bounds;
00952     } else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change colour?
00953       previous_colour = colour;
00954       colour = (TextColour)(c - SCC_BLUE);
00955       goto switch_colour;
00956     } else if (c == SCC_PREVIOUS_COLOUR) { // revert to the previous colour
00957       Swap(colour, previous_colour);
00958       goto switch_colour;
00959     } else if (c == SCC_SETX) { // {SETX}
00960       x = xo + (byte)*string++;
00961     } else if (c == SCC_SETXY) {// {SETXY}
00962       x = xo + (byte)*string++;
00963       y = yo + (byte)*string++;
00964     } else if (c == SCC_TINYFONT) { // {TINYFONT}
00965       size = FS_SMALL;
00966     } else if (c == SCC_BIGFONT) { // {BIGFONT}
00967       size = FS_LARGE;
00968     } else {
00969       DEBUG(misc, 0, "[utf8] unknown string command character %d", c);
00970     }
00971   }
00972 }
00973 
00986 int DoDrawStringTruncated(const char *str, int x, int y, TextColour colour, uint maxw)
00987 {
00988   char buffer[DRAW_STRING_BUFFER];
00989   strecpy(buffer, str, lastof(buffer));
00990   TruncateString(buffer, maxw);
00991   return DoDrawString(buffer, x, y, colour);
00992 }
00993 
01002 void DrawSprite(SpriteID img, SpriteID pal, int x, int y, const SubSprite *sub)
01003 {
01004   if (HasBit(img, PALETTE_MODIFIER_TRANSPARENT)) {
01005     _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1;
01006     GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH), ST_NORMAL), x, y, BM_TRANSPARENT, sub);
01007   } else if (pal != PAL_NONE) {
01008     _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1;
01009     GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH), ST_NORMAL), x, y, BM_COLOUR_REMAP, sub);
01010   } else {
01011     GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH), ST_NORMAL), x, y, BM_NORMAL, sub);
01012   }
01013 }
01014 
01015 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub)
01016 {
01017   const DrawPixelInfo *dpi = _cur_dpi;
01018   Blitter::BlitterParams bp;
01019 
01020   /* Amount of pixels to clip from the source sprite */
01021   int clip_left   = (sub != NULL ? max(0,                   -sprite->x_offs + sub->left       ) : 0);
01022   int clip_top    = (sub != NULL ? max(0,                   -sprite->y_offs + sub->top        ) : 0);
01023   int clip_right  = (sub != NULL ? max(0, sprite->width  - (-sprite->x_offs + sub->right  + 1)) : 0);
01024   int clip_bottom = (sub != NULL ? max(0, sprite->height - (-sprite->y_offs + sub->bottom + 1)) : 0);
01025 
01026   if (clip_left + clip_right >= sprite->width) return;
01027   if (clip_top + clip_bottom >= sprite->height) return;
01028 
01029   /* Move to the correct offset */
01030   x += sprite->x_offs;
01031   y += sprite->y_offs;
01032 
01033   /* Copy the main data directly from the sprite */
01034   bp.sprite = sprite->data;
01035   bp.sprite_width = sprite->width;
01036   bp.sprite_height = sprite->height;
01037   bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, dpi->zoom);
01038   bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, dpi->zoom);
01039   bp.top = 0;
01040   bp.left = 0;
01041   bp.skip_left = UnScaleByZoomLower(clip_left, dpi->zoom);
01042   bp.skip_top = UnScaleByZoomLower(clip_top, dpi->zoom);
01043 
01044   x += ScaleByZoom(bp.skip_left, dpi->zoom);
01045   y += ScaleByZoom(bp.skip_top, dpi->zoom);
01046 
01047   bp.dst = dpi->dst_ptr;
01048   bp.pitch = dpi->pitch;
01049   bp.remap = _colour_remap_ptr;
01050 
01051   assert(sprite->width > 0);
01052   assert(sprite->height > 0);
01053 
01054   if (bp.width <= 0) return;
01055   if (bp.height <= 0) return;
01056 
01057   y -= dpi->top;
01058   /* Check for top overflow */
01059   if (y < 0) {
01060     bp.height -= -UnScaleByZoom(y, dpi->zoom);
01061     if (bp.height <= 0) return;
01062     bp.skip_top += -UnScaleByZoom(y, dpi->zoom);
01063     y = 0;
01064   } else {
01065     bp.top = UnScaleByZoom(y, dpi->zoom);
01066   }
01067 
01068   /* Check for bottom overflow */
01069   y += ScaleByZoom(bp.height, dpi->zoom) - dpi->height;
01070   if (y > 0) {
01071     bp.height -= UnScaleByZoom(y, dpi->zoom);
01072     if (bp.height <= 0) return;
01073   }
01074 
01075   x -= dpi->left;
01076   /* Check for left overflow */
01077   if (x < 0) {
01078     bp.width -= -UnScaleByZoom(x, dpi->zoom);
01079     if (bp.width <= 0) return;
01080     bp.skip_left += -UnScaleByZoom(x, dpi->zoom);
01081     x = 0;
01082   } else {
01083     bp.left = UnScaleByZoom(x, dpi->zoom);
01084   }
01085 
01086   /* Check for right overflow */
01087   x += ScaleByZoom(bp.width, dpi->zoom) - dpi->width;
01088   if (x > 0) {
01089     bp.width -= UnScaleByZoom(x, dpi->zoom);
01090     if (bp.width <= 0) return;
01091   }
01092 
01093   assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, dpi->zoom));
01094   assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, dpi->zoom));
01095 
01096   BlitterFactoryBase::GetCurrentBlitter()->Draw(&bp, mode, dpi->zoom);
01097 }
01098 
01099 void DoPaletteAnimations();
01100 
01101 void GfxInitPalettes()
01102 {
01103   memcpy(_cur_palette, _palettes[_use_palette], sizeof(_cur_palette));
01104 
01105   DoPaletteAnimations();
01106   _pal_first_dirty = 0;
01107   _pal_count_dirty = 256;
01108 }
01109 
01110 #define EXTR(p, q) (((uint16)(_palette_animation_counter * (p)) * (q)) >> 16)
01111 #define EXTR2(p, q) (((uint16)(~_palette_animation_counter * (p)) * (q)) >> 16)
01112 
01113 void DoPaletteAnimations()
01114 {
01115   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01116   const Colour *s;
01117   const ExtraPaletteValues *ev = &_extra_palette_values;
01118   /* Amount of colours to be rotated.
01119    * A few more for the DOS palette, because the water colours are
01120    * 245-254 for DOS and 217-226 for Windows.  */
01121   const int colour_rotation_amount = (_use_palette == PAL_DOS) ? PALETTE_ANIM_SIZE_DOS : PALETTE_ANIM_SIZE_WIN;
01122   Colour old_val[PALETTE_ANIM_SIZE_DOS];
01123   const int oldval_size = colour_rotation_amount * sizeof(*old_val);
01124   const uint old_tc = _palette_animation_counter;
01125   uint i;
01126   uint j;
01127 
01128   if (blitter != NULL && blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_NONE) {
01129     _palette_animation_counter = 0;
01130   }
01131 
01132   Colour *palette_pos = &_cur_palette[PALETTE_ANIM_SIZE_START];  // Points to where animations are taking place on the palette
01133   /* Makes a copy of the current anmation palette in old_val,
01134    * so the work on the current palette could be compared, see if there has been any changes */
01135   memcpy(old_val, palette_pos, oldval_size);
01136 
01137   /* Dark blue water */
01138   s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->dark_water_TOY : ev->dark_water;
01139   j = EXTR(320, 5);
01140   for (i = 0; i != 5; i++) {
01141     *palette_pos++ = s[j];
01142     j++;
01143     if (j == 5) j = 0;
01144   }
01145 
01146   /* Glittery water */
01147   s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->glitter_water_TOY : ev->glitter_water;
01148   j = EXTR(128, 15);
01149   for (i = 0; i != 5; i++) {
01150     *palette_pos++ = s[j];
01151     j += 3;
01152     if (j >= 15) j -= 15;
01153   }
01154 
01155   /* Fizzy Drink bubbles animation */
01156   s = ev->fizzy_drink;
01157   j = EXTR2(512, 5);
01158   for (i = 0; i != 5; i++) {
01159     *palette_pos++ = s[j];
01160     j++;
01161     if (j == 5) j = 0;
01162   }
01163 
01164   /* Oil refinery fire animation */
01165   s = ev->oil_ref;
01166   j = EXTR2(512, 7);
01167   for (i = 0; i != 7; i++) {
01168     *palette_pos++ = s[j];
01169     j++;
01170     if (j == 7) j = 0;
01171   }
01172 
01173   /* Radio tower blinking */
01174   {
01175     byte i = (_palette_animation_counter >> 1) & 0x7F;
01176     byte v;
01177 
01178     (v = 255, i < 0x3f) ||
01179     (v = 128, i < 0x4A || i >= 0x75) ||
01180     (v = 20);
01181     palette_pos->r = v;
01182     palette_pos->g = 0;
01183     palette_pos->b = 0;
01184     palette_pos++;
01185 
01186     i ^= 0x40;
01187     (v = 255, i < 0x3f) ||
01188     (v = 128, i < 0x4A || i >= 0x75) ||
01189     (v = 20);
01190     palette_pos->r = v;
01191     palette_pos->g = 0;
01192     palette_pos->b = 0;
01193     palette_pos++;
01194   }
01195 
01196   /* Handle lighthouse and stadium animation */
01197   s = ev->lighthouse;
01198   j = EXTR(256, 4);
01199   for (i = 0; i != 4; i++) {
01200     *palette_pos++ = s[j];
01201     j++;
01202     if (j == 4) j = 0;
01203   }
01204 
01205   /* Animate water for old DOS graphics */
01206   if (_use_palette == PAL_DOS) {
01207     /* Dark blue water DOS */
01208     s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->dark_water_TOY : ev->dark_water;
01209     j = EXTR(320, 5);
01210     for (i = 0; i != 5; i++) {
01211       *palette_pos++ = s[j];
01212       j++;
01213       if (j == 5) j = 0;
01214     }
01215 
01216     /* Glittery water DOS */
01217     s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->glitter_water_TOY : ev->glitter_water;
01218     j = EXTR(128, 15);
01219     for (i = 0; i != 5; i++) {
01220       *palette_pos++ = s[j];
01221       j += 3;
01222       if (j >= 15) j -= 15;
01223     }
01224   }
01225 
01226   if (blitter != NULL && blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_NONE) {
01227     _palette_animation_counter = old_tc;
01228   } else {
01229     if (memcmp(old_val, &_cur_palette[PALETTE_ANIM_SIZE_START], oldval_size) != 0) {
01230       /* Did we changed anything on the palette? Seems so.  Mark it as dirty */
01231       _pal_first_dirty = PALETTE_ANIM_SIZE_START;
01232       _pal_count_dirty = colour_rotation_amount;
01233     }
01234   }
01235 }
01236 
01237 
01239 void LoadStringWidthTable()
01240 {
01241   uint i;
01242 
01243   /* Normal font */
01244   for (i = 0; i != 224; i++) {
01245     _stringwidth_table[FS_NORMAL][i] = GetGlyphWidth(FS_NORMAL, i + 32);
01246   }
01247 
01248   /* Small font */
01249   for (i = 0; i != 224; i++) {
01250     _stringwidth_table[FS_SMALL][i] = GetGlyphWidth(FS_SMALL, i + 32);
01251   }
01252 
01253   /* Large font */
01254   for (i = 0; i != 224; i++) {
01255     _stringwidth_table[FS_LARGE][i] = GetGlyphWidth(FS_LARGE, i + 32);
01256   }
01257 }
01258 
01265 byte GetCharacterWidth(FontSize size, WChar key)
01266 {
01267   /* Use _stringwidth_table cache if possible */
01268   if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
01269 
01270   return GetGlyphWidth(size, key);
01271 }
01272 
01273 
01274 void ScreenSizeChanged()
01275 {
01276   _dirty_bytes_per_line = (_screen.width + DIRTY_BLOCK_WIDTH - 1) / DIRTY_BLOCK_WIDTH;
01277   _dirty_blocks = ReallocT<byte>(_dirty_blocks, _dirty_bytes_per_line * ((_screen.height + DIRTY_BLOCK_HEIGHT - 1) / DIRTY_BLOCK_HEIGHT));
01278 
01279   /* check the dirty rect */
01280   if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
01281   if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
01282 
01283   /* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
01284   _cursor.visible = false;
01285 }
01286 
01287 void UndrawMouseCursor()
01288 {
01289   /* Don't undraw the mouse cursor if the screen is not ready */
01290   if (_screen.dst_ptr == NULL) return;
01291 
01292   if (_cursor.visible) {
01293     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01294     _cursor.visible = false;
01295     blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), _cursor_backup.GetBuffer(), _cursor.draw_size.x, _cursor.draw_size.y);
01296     _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
01297   }
01298 }
01299 
01300 void DrawMouseCursor()
01301 {
01302 #if defined(WINCE)
01303   /* Don't ever draw the mouse for WinCE, as we work with a stylus */
01304   return;
01305 #endif
01306 
01307   /* Don't draw the mouse cursor if the screen is not ready */
01308   if (_screen.dst_ptr == NULL) return;
01309 
01310   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01311   int x;
01312   int y;
01313   int w;
01314   int h;
01315 
01316   /* Redraw mouse cursor but only when it's inside the window */
01317   if (!_cursor.in_window) return;
01318 
01319   /* Don't draw the mouse cursor if it's already drawn */
01320   if (_cursor.visible) {
01321     if (!_cursor.dirty) return;
01322     UndrawMouseCursor();
01323   }
01324 
01325   w = _cursor.size.x;
01326   x = _cursor.pos.x + _cursor.offs.x + _cursor.short_vehicle_offset;
01327   if (x < 0) {
01328     w += x;
01329     x = 0;
01330   }
01331   if (w > _screen.width - x) w = _screen.width - x;
01332   if (w <= 0) return;
01333   _cursor.draw_pos.x = x;
01334   _cursor.draw_size.x = w;
01335 
01336   h = _cursor.size.y;
01337   y = _cursor.pos.y + _cursor.offs.y;
01338   if (y < 0) {
01339     h += y;
01340     y = 0;
01341   }
01342   if (h > _screen.height - y) h = _screen.height - y;
01343   if (h <= 0) return;
01344   _cursor.draw_pos.y = y;
01345   _cursor.draw_size.y = h;
01346 
01347   uint8 *buffer = _cursor_backup.Allocate(blitter->BufferSize(w, h));
01348 
01349   /* Make backup of stuff below cursor */
01350   blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), buffer, _cursor.draw_size.x, _cursor.draw_size.y);
01351 
01352   /* Draw cursor on screen */
01353   _cur_dpi = &_screen;
01354   DrawSprite(_cursor.sprite, _cursor.pal, _cursor.pos.x + _cursor.short_vehicle_offset, _cursor.pos.y);
01355 
01356   _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
01357 
01358   _cursor.visible = true;
01359   _cursor.dirty = false;
01360 }
01361 
01362 void RedrawScreenRect(int left, int top, int right, int bottom)
01363 {
01364   assert(right <= _screen.width && bottom <= _screen.height);
01365   if (_cursor.visible) {
01366     if (right > _cursor.draw_pos.x &&
01367         left < _cursor.draw_pos.x + _cursor.draw_size.x &&
01368         bottom > _cursor.draw_pos.y &&
01369         top < _cursor.draw_pos.y + _cursor.draw_size.y) {
01370       UndrawMouseCursor();
01371     }
01372   }
01373 
01374 #ifdef ENABLE_NETWORK
01375   NetworkUndrawChatMessage();
01376 #endif /* ENABLE_NETWORK */
01377 
01378   DrawOverlappedWindowForAll(left, top, right, bottom);
01379 
01380   _video_driver->MakeDirty(left, top, right - left, bottom - top);
01381 }
01382 
01388 void DrawDirtyBlocks()
01389 {
01390   byte *b = _dirty_blocks;
01391   const int w = Align(_screen.width,  DIRTY_BLOCK_WIDTH);
01392   const int h = Align(_screen.height, DIRTY_BLOCK_HEIGHT);
01393   int x;
01394   int y;
01395 
01396   if (IsGeneratingWorld()) {
01397     /* We are generating the world, so release our rights to the map and
01398      * painting while we are waiting a bit. */
01399     _genworld_paint_mutex->EndCritical();
01400     _genworld_mapgen_mutex->EndCritical();
01401 
01402     /* Wait a while and update _realtime_tick so we are given the rights */
01403     CSleep(GENWORLD_REDRAW_TIMEOUT);
01404     _realtime_tick += GENWORLD_REDRAW_TIMEOUT;
01405     _genworld_paint_mutex->BeginCritical();
01406     _genworld_mapgen_mutex->BeginCritical();
01407   }
01408 
01409   y = 0;
01410   do {
01411     x = 0;
01412     do {
01413       if (*b != 0) {
01414         int left;
01415         int top;
01416         int right = x + DIRTY_BLOCK_WIDTH;
01417         int bottom = y;
01418         byte *p = b;
01419         int h2;
01420 
01421         /* First try coalescing downwards */
01422         do {
01423           *p = 0;
01424           p += _dirty_bytes_per_line;
01425           bottom += DIRTY_BLOCK_HEIGHT;
01426         } while (bottom != h && *p != 0);
01427 
01428         /* Try coalescing to the right too. */
01429         h2 = (bottom - y) / DIRTY_BLOCK_HEIGHT;
01430         assert(h2 > 0);
01431         p = b;
01432 
01433         while (right != w) {
01434           byte *p2 = ++p;
01435           int h = h2;
01436           /* Check if a full line of dirty flags is set. */
01437           do {
01438             if (!*p2) goto no_more_coalesc;
01439             p2 += _dirty_bytes_per_line;
01440           } while (--h != 0);
01441 
01442           /* Wohoo, can combine it one step to the right!
01443            * Do that, and clear the bits. */
01444           right += DIRTY_BLOCK_WIDTH;
01445 
01446           h = h2;
01447           p2 = p;
01448           do {
01449             *p2 = 0;
01450             p2 += _dirty_bytes_per_line;
01451           } while (--h != 0);
01452         }
01453         no_more_coalesc:
01454 
01455         left = x;
01456         top = y;
01457 
01458         if (left   < _invalid_rect.left  ) left   = _invalid_rect.left;
01459         if (top    < _invalid_rect.top   ) top    = _invalid_rect.top;
01460         if (right  > _invalid_rect.right ) right  = _invalid_rect.right;
01461         if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
01462 
01463         if (left < right && top < bottom) {
01464           RedrawScreenRect(left, top, right, bottom);
01465         }
01466 
01467       }
01468     } while (b++, (x += DIRTY_BLOCK_WIDTH) != w);
01469   } while (b += -(w / DIRTY_BLOCK_WIDTH) + _dirty_bytes_per_line, (y += DIRTY_BLOCK_HEIGHT) != h);
01470 
01471   _invalid_rect.left = w;
01472   _invalid_rect.top = h;
01473   _invalid_rect.right = 0;
01474   _invalid_rect.bottom = 0;
01475 }
01476 
01492 void SetDirtyBlocks(int left, int top, int right, int bottom)
01493 {
01494   byte *b;
01495   int width;
01496   int height;
01497 
01498   if (left < 0) left = 0;
01499   if (top < 0) top = 0;
01500   if (right > _screen.width) right = _screen.width;
01501   if (bottom > _screen.height) bottom = _screen.height;
01502 
01503   if (left >= right || top >= bottom) return;
01504 
01505   if (left   < _invalid_rect.left  ) _invalid_rect.left   = left;
01506   if (top    < _invalid_rect.top   ) _invalid_rect.top    = top;
01507   if (right  > _invalid_rect.right ) _invalid_rect.right  = right;
01508   if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
01509 
01510   left /= DIRTY_BLOCK_WIDTH;
01511   top  /= DIRTY_BLOCK_HEIGHT;
01512 
01513   b = _dirty_blocks + top * _dirty_bytes_per_line + left;
01514 
01515   width  = ((right  - 1) / DIRTY_BLOCK_WIDTH)  - left + 1;
01516   height = ((bottom - 1) / DIRTY_BLOCK_HEIGHT) - top  + 1;
01517 
01518   assert(width > 0 && height > 0);
01519 
01520   do {
01521     int i = width;
01522 
01523     do b[--i] = 0xFF; while (i);
01524 
01525     b += _dirty_bytes_per_line;
01526   } while (--height != 0);
01527 }
01528 
01534 void MarkWholeScreenDirty()
01535 {
01536   SetDirtyBlocks(0, 0, _screen.width, _screen.height);
01537 }
01538 
01551 bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
01552 {
01553   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01554   const DrawPixelInfo *o = _cur_dpi;
01555 
01556   n->zoom = ZOOM_LVL_NORMAL;
01557 
01558   assert(width > 0);
01559   assert(height > 0);
01560 
01561   if ((left -= o->left) < 0) {
01562     width += left;
01563     if (width <= 0) return false;
01564     n->left = -left;
01565     left = 0;
01566   } else {
01567     n->left = 0;
01568   }
01569 
01570   if (width > o->width - left) {
01571     width = o->width - left;
01572     if (width <= 0) return false;
01573   }
01574   n->width = width;
01575 
01576   if ((top -= o->top) < 0) {
01577     height += top;
01578     if (height <= 0) return false;
01579     n->top = -top;
01580     top = 0;
01581   } else {
01582     n->top = 0;
01583   }
01584 
01585   n->dst_ptr = blitter->MoveTo(o->dst_ptr, left, top);
01586   n->pitch = o->pitch;
01587 
01588   if (height > o->height - top) {
01589     height = o->height - top;
01590     if (height <= 0) return false;
01591   }
01592   n->height = height;
01593 
01594   return true;
01595 }
01596 
01597 static void SetCursorSprite(SpriteID cursor, SpriteID pal)
01598 {
01599   CursorVars *cv = &_cursor;
01600   const Sprite *p;
01601 
01602   if (cv->sprite == cursor) return;
01603 
01604   p = GetSprite(GB(cursor, 0, SPRITE_WIDTH), ST_NORMAL);
01605   cv->sprite = cursor;
01606   cv->pal    = pal;
01607   cv->size.y = p->height;
01608   cv->size.x = p->width;
01609   cv->offs.x = p->x_offs;
01610   cv->offs.y = p->y_offs;
01611 
01612   cv->dirty = true;
01613   cv->short_vehicle_offset = 0;
01614 }
01615 
01616 static void SwitchAnimatedCursor()
01617 {
01618   const AnimCursor *cur = _cursor.animate_cur;
01619 
01620   if (cur == NULL || cur->sprite == AnimCursor::LAST) cur = _cursor.animate_list;
01621 
01622   SetCursorSprite(cur->sprite, _cursor.pal);
01623 
01624   _cursor.animate_timeout = cur->display_time;
01625   _cursor.animate_cur     = cur + 1;
01626 }
01627 
01628 void CursorTick()
01629 {
01630   if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0)
01631     SwitchAnimatedCursor();
01632 }
01633 
01634 void SetMouseCursor(SpriteID sprite, SpriteID pal)
01635 {
01636   /* Turn off animation */
01637   _cursor.animate_timeout = 0;
01638   /* Set cursor */
01639   SetCursorSprite(sprite, pal);
01640 }
01641 
01642 void SetAnimatedMouseCursor(const AnimCursor *table)
01643 {
01644   _cursor.animate_list = table;
01645   _cursor.animate_cur = NULL;
01646   _cursor.pal = PAL_NONE;
01647   SwitchAnimatedCursor();
01648 }
01649 
01650 bool ChangeResInGame(int width, int height)
01651 {
01652   return (_screen.width == width && _screen.height == height) || _video_driver->ChangeResolution(width, height);
01653 }
01654 
01655 bool ToggleFullScreen(bool fs)
01656 {
01657   bool result = _video_driver->ToggleFullscreen(fs);
01658   if (_fullscreen != fs && _num_resolutions == 0) {
01659     DEBUG(driver, 0, "Could not find a suitable fullscreen resolution");
01660   }
01661   return result;
01662 }
01663 
01664 static int CDECL compare_res(const Dimension *pa, const Dimension *pb)
01665 {
01666   int x = pa->width - pb->width;
01667   if (x != 0) return x;
01668   return pa->height - pb->height;
01669 }
01670 
01671 void SortResolutions(int count)
01672 {
01673   QSortT(_resolutions, count, &compare_res);
01674 }

Generated on Fri Jul 31 22:33:14 2009 for OpenTTD by  doxygen 1.5.6