gfx.cpp

Go to the documentation of this file.
00001 /* $Id: gfx.cpp 14555 2008-11-02 11:20:15Z skidd13 $ */
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 #include "core/alloc_func.hpp"
00021 #include "core/sort_func.hpp"
00022 #include "landscape_type.h"
00023 #include "network/network_func.h"
00024 
00025 #include "table/palettes.h"
00026 #include "table/sprites.h"
00027 #include "table/control_codes.h"
00028 
00029 byte _dirkeys;        
00030 bool _fullscreen;
00031 CursorVars _cursor;
00032 bool _ctrl_pressed;   
00033 bool _shift_pressed;  
00034 byte _fast_forward;
00035 bool _left_button_down;     
00036 bool _left_button_clicked;  
00037 bool _right_button_down;    
00038 bool _right_button_clicked; 
00039 DrawPixelInfo _screen;
00040 bool _screen_disable_anim = false;   
00041 bool _exit_game;
00042 bool _networking;         
00043 byte _game_mode;
00044 int8 _pause_game;
00045 int _pal_first_dirty;
00046 int _pal_count_dirty;
00047 
00048 Colour _cur_palette[256];
00049 byte _stringwidth_table[FS_END][224]; 
00050 DrawPixelInfo *_cur_dpi;
00051 byte _colour_gradient[COLOUR_END][8];
00052 
00053 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL);
00054 static int ReallyDoDrawString(const char *string, int x, int y, uint16 real_colour, bool parse_string_also_when_clipped = false);
00055 
00056 FontSize _cur_fontsize;
00057 static FontSize _last_fontsize;
00058 static uint8 _cursor_backup[64 * 64 * 4];
00059 
00067 static Rect _invalid_rect;
00068 static const byte *_color_remap_ptr;
00069 static byte _string_colorremap[3];
00070 
00071 enum {
00072   DIRTY_BLOCK_HEIGHT   = 8,
00073   DIRTY_BLOCK_WIDTH    = 64,
00074 };
00075 static uint _dirty_bytes_per_line = 0;
00076 static byte *_dirty_blocks = NULL;
00077 
00078 void GfxScroll(int left, int top, int width, int height, int xo, int yo)
00079 {
00080   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00081 
00082   if (xo == 0 && yo == 0) return;
00083 
00084   if (_cursor.visible) UndrawMouseCursor();
00085 
00086 #ifdef ENABLE_NETWORK
00087   NetworkUndrawChatMessage();
00088 #endif /* ENABLE_NETWORK */
00089 
00090   blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
00091   /* This part of the screen is now dirty. */
00092   _video_driver->MakeDirty(left, top, width, height);
00093 }
00094 
00095 
00110 void GfxFillRect(int left, int top, int right, int bottom, int color, FillRectMode mode)
00111 {
00112   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00113   const DrawPixelInfo *dpi = _cur_dpi;
00114   void *dst;
00115   const int otop = top;
00116   const int oleft = left;
00117 
00118   if (dpi->zoom != ZOOM_LVL_NORMAL) return;
00119   if (left > right || top > bottom) return;
00120   if (right < dpi->left || left >= dpi->left + dpi->width) return;
00121   if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
00122 
00123   if ( (left -= dpi->left) < 0) left = 0;
00124   right = right - dpi->left + 1;
00125   if (right > dpi->width) right = dpi->width;
00126   right -= left;
00127   assert(right > 0);
00128 
00129   if ( (top -= dpi->top) < 0) top = 0;
00130   bottom = bottom - dpi->top + 1;
00131   if (bottom > dpi->height) bottom = dpi->height;
00132   bottom -= top;
00133   assert(bottom > 0);
00134 
00135   dst = blitter->MoveTo(dpi->dst_ptr, left, top);
00136 
00137   switch (mode) {
00138     default: // FILLRECT_OPAQUE
00139       blitter->DrawRect(dst, right, bottom, (uint8)color);
00140       break;
00141 
00142     case FILLRECT_RECOLOR:
00143       blitter->DrawColorMappingRect(dst, right, bottom, GB(color, 0, PALETTE_WIDTH));
00144       break;
00145 
00146     case FILLRECT_CHECKER: {
00147       byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
00148       do {
00149         for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8)color);
00150         dst = blitter->MoveTo(dst, 0, 1);
00151       } while (--bottom > 0);
00152       break;
00153     }
00154   }
00155 }
00156 
00157 void GfxDrawLine(int x, int y, int x2, int y2, int color)
00158 {
00159   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00160   DrawPixelInfo *dpi = _cur_dpi;
00161 
00162   x -= dpi->left;
00163   x2 -= dpi->left;
00164   y -= dpi->top;
00165   y2 -= dpi->top;
00166 
00167   /* Check clipping */
00168   if (x < 0 && x2 < 0) return;
00169   if (y < 0 && y2 < 0) return;
00170   if (x > dpi->width  && x2 > dpi->width)  return;
00171   if (y > dpi->height && y2 > dpi->height) return;
00172 
00173   blitter->DrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, color);
00174 }
00175 
00176 void GfxDrawLineUnscaled(int x, int y, int x2, int y2, int color)
00177 {
00178   Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00179   DrawPixelInfo *dpi = _cur_dpi;
00180 
00181   x -= dpi->left;
00182   x2 -= dpi->left;
00183   y -= dpi->top;
00184   y2 -= dpi->top;
00185 
00186   /* Check clipping */
00187   if (x < 0 && x2 < 0) return;
00188   if (y < 0 && y2 < 0) return;
00189   if (x > dpi->width  && x2 > dpi->width)  return;
00190   if (y > dpi->height && y2 > dpi->height) return;
00191 
00192   blitter->DrawLine(dpi->dst_ptr, UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
00193       UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
00194       UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), color);
00195 }
00196 
00210 void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
00211 {
00212   /*           ....
00213    *         ..    ....
00214    *       ..          ....
00215    *     ..                ^
00216    *   <--__(dx1,dy1)    /(dx2,dy2)
00217    *   :    --__       /   :
00218    *   :        --__ /     :
00219    *   :            *(x,y) :
00220    *   :            |      :
00221    *   :            |     ..
00222    *    ....        |(dx3,dy3)
00223    *        ....    | ..
00224    *            ....V.
00225    */
00226 
00227   static const byte color = 255;
00228 
00229   GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, color);
00230   GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, color);
00231   GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, color);
00232 
00233   GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, color);
00234   GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, color);
00235   GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, color);
00236   GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, color);
00237   GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, color);
00238   GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, color);
00239 }
00240 
00241 
00242 #if !defined(WITH_ICU)
00243 static void HandleBiDiAndArabicShapes(char *text, const char *lastof) {}
00244 #else
00245 #include "unicode/ubidi.h"
00246 #include "unicode/ushape.h"
00247 
00276 static void HandleBiDiAndArabicShapes(char *buffer, const char *lastof)
00277 {
00278   UChar input_output[DRAW_STRING_BUFFER];
00279   UChar intermediate[DRAW_STRING_BUFFER];
00280 
00281   char *t = buffer;
00282   size_t length = 0;
00283   while (*t != '\0' && length < lengthof(input_output)) {
00284     WChar tmp;
00285     t += Utf8Decode(&tmp, t);
00286     input_output[length++] = tmp;
00287   }
00288   input_output[length] = 0;
00289 
00290   UErrorCode err = U_ZERO_ERROR;
00291   UBiDi *para = ubidi_openSized((int32_t)length, 0, &err);
00292   if (para == NULL) return;
00293 
00294   ubidi_setPara(para, input_output, (int32_t)length, _dynlang.text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, NULL, &err);
00295   ubidi_writeReordered(para, intermediate, (int32_t)length, 0, &err);
00296   length = u_shapeArabic(intermediate, (int32_t)length, input_output, lengthof(input_output), U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE, &err);
00297   ubidi_close(para);
00298 
00299   if (U_FAILURE(err)) return;
00300 
00301   t = buffer;
00302   for (size_t i = 0; i < length && t < (lastof - 4); i++) {
00303     t += Utf8Encode(t, input_output[i]);
00304   }
00305   *t = '\0';
00306 }
00307 #endif /* WITH_ICU */
00308 
00309 
00315 static int TruncateString(char *str, int maxw)
00316 {
00317   int w = 0;
00318   FontSize size = _cur_fontsize;
00319   int ddd, ddd_w;
00320 
00321   WChar c;
00322   char *ddd_pos;
00323 
00324   ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
00325 
00326   for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
00327     if (IsPrintable(c)) {
00328       w += GetCharacterWidth(size, c);
00329 
00330       if (w >= maxw) {
00331         /* string got too big... insert dotdotdot, but make sure we do not
00332          * print anything beyond the string termination character. */
00333         for (int i = 0; *ddd_pos != '\0' && i < 3; i++, ddd_pos++) *ddd_pos = '.';
00334         *ddd_pos = '\0';
00335         return ddd_w;
00336       }
00337     } else {
00338       if (c == SCC_SETX) {
00339         w = *str;
00340         str++;
00341       } else if (c == SCC_SETXY) {
00342         w = *str;
00343         str += 2;
00344       } else if (c == SCC_TINYFONT) {
00345         size = FS_SMALL;
00346         ddd = GetCharacterWidth(size, '.') * 3;
00347       } else if (c == SCC_BIGFONT) {
00348         size = FS_LARGE;
00349         ddd = GetCharacterWidth(size, '.') * 3;
00350       }
00351     }
00352 
00353     /* Remember the last position where three dots fit. */
00354     if (w + ddd < maxw) {
00355       ddd_w = w + ddd;
00356       ddd_pos = str;
00357     }
00358   }
00359 
00360   return w;
00361 }
00362 
00373 static inline int TruncateStringID(StringID src, char *dest, int maxw, const char* last)
00374 {
00375   GetString(dest, src, last);
00376   return TruncateString(dest, maxw);
00377 }
00378 
00389 int DrawString(int x, int y, StringID str, uint16 color)
00390 {
00391   char buffer[DRAW_STRING_BUFFER];
00392 
00393   GetString(buffer, str, lastof(buffer));
00394   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00395   return ReallyDoDrawString(buffer, x, y, color);
00396 }
00397 
00409 int DrawStringTruncated(int x, int y, StringID str, uint16 color, uint maxw)
00410 {
00411   char buffer[DRAW_STRING_BUFFER];
00412   TruncateStringID(str, buffer, maxw, lastof(buffer));
00413   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00414   return ReallyDoDrawString(buffer, x, y, color);
00415 }
00416 
00427 int DrawStringRightAligned(int x, int y, StringID str, uint16 color)
00428 {
00429   char buffer[DRAW_STRING_BUFFER];
00430   int w;
00431 
00432   GetString(buffer, str, lastof(buffer));
00433   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00434 
00435   w = GetStringBoundingBox(buffer).width;
00436   ReallyDoDrawString(buffer, x - w, y, color);
00437 
00438   return w;
00439 }
00440 
00450 void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, uint maxw)
00451 {
00452   char buffer[DRAW_STRING_BUFFER];
00453 
00454   TruncateStringID(str, buffer, maxw, lastof(buffer));
00455   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00456   ReallyDoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, color);
00457 }
00458 
00467 void DrawStringRightAlignedUnderline(int x, int y, StringID str, uint16 color)
00468 {
00469   int w = DrawStringRightAligned(x, y, str, color);
00470   GfxFillRect(x - w, y + 10, x, y + 10, _string_colorremap[1]);
00471 }
00472 
00483 int DrawStringCentered(int x, int y, StringID str, uint16 color)
00484 {
00485   char buffer[DRAW_STRING_BUFFER];
00486   int w;
00487 
00488   GetString(buffer, str, lastof(buffer));
00489   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00490 
00491   w = GetStringBoundingBox(buffer).width;
00492   ReallyDoDrawString(buffer, x - w / 2, y, color);
00493 
00494   return w;
00495 }
00496 
00508 int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 color)
00509 {
00510   char buffer[DRAW_STRING_BUFFER];
00511   TruncateStringID(str, buffer, xr - xl, lastof(buffer));
00512   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00513 
00514   int w = GetStringBoundingBox(buffer).width;
00515   return ReallyDoDrawString(buffer, (xl + xr - w) / 2, y, color);
00516 }
00517 
00528 int DoDrawStringCentered(int x, int y, const char *str, uint16 color)
00529 {
00530   char buffer[DRAW_STRING_BUFFER];
00531   strecpy(buffer, str, lastof(buffer));
00532   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00533 
00534   int w = GetStringBoundingBox(buffer).width;
00535   ReallyDoDrawString(buffer, x - w / 2, y, color);
00536   return w;
00537 }
00538 
00547 void DrawStringCenterUnderline(int x, int y, StringID str, uint16 color)
00548 {
00549   int w = DrawStringCentered(x, y, str, color);
00550   GfxFillRect(x - (w >> 1), y + 10, x - (w >> 1) + w, y + 10, _string_colorremap[1]);
00551 }
00552 
00562 void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, uint16 color)
00563 {
00564   int w = DrawStringCenteredTruncated(xl, xr, y, str, color);
00565   GfxFillRect((xl + xr - w) / 2, y + 10, (xl + xr + w) / 2, y + 10, _string_colorremap[1]);
00566 }
00567 
00586 uint32 FormatStringLinebreaks(char *str, int maxw)
00587 {
00588   FontSize size = _cur_fontsize;
00589   int num = 0;
00590 
00591   assert(maxw > 0);
00592 
00593   for (;;) {
00594     char *last_space = NULL;
00595     int w = 0;
00596 
00597     for (;;) {
00598       WChar c = Utf8Consume((const char **)&str);
00599       /* whitespace is where we will insert the line-break */
00600       if (IsWhitespace(c)) last_space = str;
00601 
00602       if (IsPrintable(c)) {
00603         w += GetCharacterWidth(size, c);
00604         /* string is longer than maximum width so we need to decide what to
00605          * do. We can do two things:
00606          * 1. If no whitespace was found at all up until now (on this line) then
00607          *    we will truncate the string and bail out.
00608          * 2. In all other cases force a linebreak at the last seen whitespace */
00609         if (w > maxw) {
00610           if (last_space == NULL) {
00611             *Utf8PrevChar(str) = '\0';
00612             return num + (size << 16);
00613           }
00614           str = last_space;
00615           break;
00616         }
00617       } else {
00618         switch (c) {
00619           case '\0': return num + (size << 16); break;
00620           case SCC_SETX:  str++; break;
00621           case SCC_SETXY: str += 2; break;
00622           case SCC_TINYFONT: size = FS_SMALL; break;
00623           case SCC_BIGFONT:  size = FS_LARGE; break;
00624           case '\n': goto end_of_inner_loop;
00625         }
00626       }
00627     }
00628 end_of_inner_loop:
00629     /* String didn't fit on line (or a '\n' was encountered), so 'dummy' terminate
00630      * and increase linecount. We use Utf8PrevChar() as also non 1 char long
00631      * whitespace seperators are supported */
00632     num++;
00633     char *s = Utf8PrevChar(str);
00634     *s++ = '\0';
00635 
00636     /* In which case (see above) we will shift remainder to left and close the gap */
00637     if (str - s >= 1) {
00638       for (; str[-1] != '\0';) *s++ = *str++;
00639     }
00640   }
00641 }
00642 
00643 
00650 static int GetMultilineStringHeight(const char *src, int num)
00651 {
00652   int maxy = 0;
00653   int y = 0;
00654   int fh = GetCharacterHeight(_cur_fontsize);
00655 
00656   for (;;) {
00657     WChar c = Utf8Consume(&src);
00658 
00659     switch (c) {
00660       case 0:            y += fh; if (--num < 0) return maxy; break;
00661       case '\n':         y += fh;                             break;
00662       case SCC_SETX:     src++;                               break;
00663       case SCC_SETXY:    src++; y = (int)*src++;              break;
00664       case SCC_TINYFONT: fh = GetCharacterHeight(FS_SMALL);   break;
00665       case SCC_BIGFONT:  fh = GetCharacterHeight(FS_LARGE);   break;
00666       default:           maxy = max<int>(maxy, y + fh);       break;
00667     }
00668   }
00669 }
00670 
00671 
00677 int GetStringHeight(StringID str, int maxw)
00678 {
00679   char buffer[DRAW_STRING_BUFFER];
00680 
00681   GetString(buffer, str, lastof(buffer));
00682 
00683   uint32 tmp = FormatStringLinebreaks(buffer, maxw);
00684 
00685   return GetMultilineStringHeight(buffer, GB(tmp, 0, 16));
00686 }
00687 
00688 
00694 void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
00695 {
00696   char buffer[DRAW_STRING_BUFFER];
00697   uint32 tmp;
00698   int num, mt;
00699   const char *src;
00700   WChar c;
00701 
00702   GetString(buffer, str, lastof(buffer));
00703 
00704   tmp = FormatStringLinebreaks(buffer, maxw);
00705   num = GB(tmp, 0, 16);
00706 
00707   mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
00708 
00709   y -= (mt >> 1) * num;
00710 
00711   src = buffer;
00712 
00713   for (;;) {
00714     char buf2[DRAW_STRING_BUFFER];
00715     strecpy(buf2, src, lastof(buf2));
00716     HandleBiDiAndArabicShapes(buf2, lastof(buf2));
00717     int w = GetStringBoundingBox(buf2).width;
00718     ReallyDoDrawString(buf2, x - (w >> 1), y, 0xFE, true);
00719     _cur_fontsize = _last_fontsize;
00720 
00721     for (;;) {
00722       c = Utf8Consume(&src);
00723       if (c == 0) {
00724         y += mt;
00725         if (--num < 0) {
00726           _cur_fontsize = FS_NORMAL;
00727           return;
00728         }
00729         break;
00730       } else if (c == SCC_SETX) {
00731         src++;
00732       } else if (c == SCC_SETXY) {
00733         src += 2;
00734       }
00735     }
00736   }
00737 }
00738 
00739 
00740 uint DrawStringMultiLine(int x, int y, StringID str, int maxw, int maxh)
00741 {
00742   char buffer[DRAW_STRING_BUFFER];
00743   uint32 tmp;
00744   int num, mt;
00745   uint total_height;
00746   const char *src;
00747   WChar c;
00748 
00749   GetString(buffer, str, lastof(buffer));
00750 
00751   tmp = FormatStringLinebreaks(buffer, maxw);
00752   num = GB(tmp, 0, 16);
00753 
00754   mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
00755   total_height = (num + 1) * mt;
00756 
00757   if (maxh != -1 && (int)total_height > maxh) {
00758     /* Check there's room enough for at least one line. */
00759     if (maxh < mt) return 0;
00760 
00761     num = maxh / mt - 1;
00762     total_height = (num + 1) * mt;
00763   }
00764 
00765   src = buffer;
00766 
00767   for (;;) {
00768     char buf2[DRAW_STRING_BUFFER];
00769     strecpy(buf2, src, lastof(buf2));
00770     HandleBiDiAndArabicShapes(buf2, lastof(buf2));
00771     ReallyDoDrawString(buf2, x, y, 0xFE, true);
00772     _cur_fontsize = _last_fontsize;
00773 
00774     for (;;) {
00775       c = Utf8Consume(&src);
00776       if (c == 0) {
00777         y += mt;
00778         if (--num < 0) {
00779           _cur_fontsize = FS_NORMAL;
00780           return total_height;
00781         }
00782         break;
00783       } else if (c == SCC_SETX) {
00784         src++;
00785       } else if (c == SCC_SETXY) {
00786         src += 2;
00787       }
00788     }
00789   }
00790 }
00791 
00799 Dimension GetStringBoundingBox(const char *str)
00800 {
00801   FontSize size = _cur_fontsize;
00802   Dimension br;
00803   int max_width;
00804   WChar c;
00805 
00806   br.width = br.height = max_width = 0;
00807   for (;;) {
00808     c = Utf8Consume(&str);
00809     if (c == 0) break;
00810     if (IsPrintable(c)) {
00811       br.width += GetCharacterWidth(size, c);
00812     } else {
00813       switch (c) {
00814         case SCC_SETX: br.width += (byte)*str++; break;
00815         case SCC_SETXY:
00816           br.width += (byte)*str++;
00817           br.height += (byte)*str++;
00818           break;
00819         case SCC_TINYFONT: size = FS_SMALL; break;
00820         case SCC_BIGFONT:  size = FS_LARGE; break;
00821         case '\n':
00822           br.height += GetCharacterHeight(size);
00823           if (br.width > max_width) max_width = br.width;
00824           br.width = 0;
00825           break;
00826       }
00827     }
00828   }
00829   br.height += GetCharacterHeight(size);
00830 
00831   br.width  = max(br.width, max_width);
00832   return br;
00833 }
00834 
00842 void DrawCharCentered(WChar c, int x, int y, uint16 real_color)
00843 {
00844   FontSize size = FS_NORMAL;
00845   byte color = real_color & 0xFF;
00846   int w = GetCharacterWidth(size, c);
00847 
00848   _string_colorremap[1] = _string_colormap[_use_palette][color].text;
00849   _string_colorremap[2] = _string_colormap[_use_palette][color].shadow;
00850   _color_remap_ptr = _string_colorremap;
00851 
00852   GfxMainBlitter(GetGlyph(size, c), x - w / 2, y, BM_COLOUR_REMAP);
00853 }
00854 
00872 int DoDrawString(const char *string, int x, int y, uint16 real_colour, bool parse_string_also_when_clipped)
00873 {
00874   char buffer[DRAW_STRING_BUFFER];
00875   strecpy(buffer, string, lastof(buffer));
00876   HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00877 
00878   return ReallyDoDrawString(buffer, x, y, real_colour, parse_string_also_when_clipped);
00879 }
00880 
00898 static int ReallyDoDrawString(const char *string, int x, int y, uint16 real_colour, bool parse_string_also_when_clipped)
00899 {
00900   DrawPixelInfo *dpi = _cur_dpi;
00901   FontSize size = _cur_fontsize;
00902   WChar c;
00903   int xo = x, yo = y;
00904 
00905   byte colour = real_colour & 0xFF;  // extract the 8 bits colour index that is required for the mapping
00906   byte previous_colour = colour;
00907 
00908   if (!parse_string_also_when_clipped) {
00909     /* in "mode multiline", the available space have been verified. Not in regular one.
00910      * So if the string cannot be drawn, return the original start to say so.*/
00911     if (x >= dpi->left + dpi->width || y >= dpi->top + dpi->height) return x;
00912 
00913     if (colour != TC_INVALID) { // the invalid colour flag test should not  really occur.  But better be safe
00914 switch_colour:;
00915       if (real_colour & IS_PALETTE_COLOR) {
00916         _string_colorremap[1] = colour;
00917         _string_colorremap[2] = (_use_palette == PAL_DOS) ? 1 : 215;
00918       } else {
00919         _string_colorremap[1] = _string_colormap[_use_palette][colour].text;
00920         _string_colorremap[2] = _string_colormap[_use_palette][colour].shadow;
00921       }
00922       _color_remap_ptr = _string_colorremap;
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 = (byte)(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, uint16 color, 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, color);
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     _color_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     _color_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 = _color_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 colors to be rotated.
01119    * A few more for the DOS palette, because the water colors 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   if (_cursor.visible) {
01290     Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01291     _cursor.visible = false;
01292     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);
01293     _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
01294   }
01295 }
01296 
01297 void DrawMouseCursor()
01298 {
01299 #if defined(WINCE)
01300   /* Don't ever draw the mouse for WinCE, as we work with a stylus */
01301   return;
01302 #endif
01303 
01304