00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "gfx_func.h"
00014 #include "fontcache.h"
00015 #include "progress.h"
00016 #include "zoom_func.h"
00017 #include "blitter/factory.hpp"
00018 #include "video/video_driver.hpp"
00019 #include "strings_func.h"
00020 #include "settings_type.h"
00021 #include "network/network.h"
00022 #include "network/network_func.h"
00023 #include "thread/thread.h"
00024 #include "window_func.h"
00025 #include "newgrf_debug.h"
00026
00027 #include "table/palettes.h"
00028 #include "table/sprites.h"
00029 #include "table/control_codes.h"
00030
00031 byte _dirkeys;
00032 bool _fullscreen;
00033 CursorVars _cursor;
00034 bool _ctrl_pressed;
00035 bool _shift_pressed;
00036 byte _fast_forward;
00037 bool _left_button_down;
00038 bool _left_button_clicked;
00039 bool _right_button_down;
00040 bool _right_button_clicked;
00041 DrawPixelInfo _screen;
00042 bool _screen_disable_anim = false;
00043 bool _exit_game;
00044 GameMode _game_mode;
00045 SwitchMode _switch_mode;
00046 PauseModeByte _pause_mode;
00047 int _pal_first_dirty;
00048 int _pal_count_dirty;
00049
00050 Colour _cur_palette[256];
00051
00052 static int _max_char_height;
00053 static int _max_char_width;
00054 static byte _stringwidth_table[FS_END][224];
00055 DrawPixelInfo *_cur_dpi;
00056 byte _colour_gradient[COLOUR_END][8];
00057
00058 static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL, SpriteID sprite_id = SPR_CURSOR_MOUSE);
00059 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL, SpriteID sprite_id = SPR_CURSOR_MOUSE, ZoomLevel zoom = ZOOM_LVL_NORMAL);
00060
00065 struct DrawStringParams {
00066 FontSize fontsize;
00067 TextColour cur_colour, prev_colour;
00068
00069 DrawStringParams(TextColour colour, FontSize fontsize) : fontsize(fontsize), cur_colour(colour), prev_colour(colour) {}
00070
00075 FORCEINLINE void SetColour(TextColour c)
00076 {
00077 assert(c >= TC_BLUE && c <= TC_BLACK);
00078 this->prev_colour = this->cur_colour;
00079 this->cur_colour = c;
00080 }
00081
00083 FORCEINLINE void SetPreviousColour()
00084 {
00085 Swap(this->cur_colour, this->prev_colour);
00086 }
00087
00092 FORCEINLINE void SetFontSize(FontSize f)
00093 {
00094 this->fontsize = f;
00095 }
00096 };
00097
00098 static ReusableBuffer<uint8> _cursor_backup;
00099
00107 static Rect _invalid_rect;
00108 static const byte *_colour_remap_ptr;
00109 static byte _string_colourremap[3];
00110
00111 static const uint DIRTY_BLOCK_HEIGHT = 8;
00112 static const uint DIRTY_BLOCK_WIDTH = 64;
00113
00114 static uint _dirty_bytes_per_line = 0;
00115 static byte *_dirty_blocks = NULL;
00116
00117 void GfxScroll(int left, int top, int width, int height, int xo, int yo)
00118 {
00119 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00120
00121 if (xo == 0 && yo == 0) return;
00122
00123 if (_cursor.visible) UndrawMouseCursor();
00124
00125 #ifdef ENABLE_NETWORK
00126 if (_networking) NetworkUndrawChatMessage();
00127 #endif
00128
00129 blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
00130
00131 _video_driver->MakeDirty(left, top, width, height);
00132 }
00133
00134
00149 void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
00150 {
00151 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00152 const DrawPixelInfo *dpi = _cur_dpi;
00153 void *dst;
00154 const int otop = top;
00155 const int oleft = left;
00156
00157 if (dpi->zoom != ZOOM_LVL_NORMAL) return;
00158 if (left > right || top > bottom) return;
00159 if (right < dpi->left || left >= dpi->left + dpi->width) return;
00160 if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
00161
00162 if ( (left -= dpi->left) < 0) left = 0;
00163 right = right - dpi->left + 1;
00164 if (right > dpi->width) right = dpi->width;
00165 right -= left;
00166 assert(right > 0);
00167
00168 if ( (top -= dpi->top) < 0) top = 0;
00169 bottom = bottom - dpi->top + 1;
00170 if (bottom > dpi->height) bottom = dpi->height;
00171 bottom -= top;
00172 assert(bottom > 0);
00173
00174 dst = blitter->MoveTo(dpi->dst_ptr, left, top);
00175
00176 switch (mode) {
00177 default:
00178 blitter->DrawRect(dst, right, bottom, (uint8)colour);
00179 break;
00180
00181 case FILLRECT_RECOLOUR:
00182 blitter->DrawColourMappingRect(dst, right, bottom, GB(colour, 0, PALETTE_WIDTH));
00183 break;
00184
00185 case FILLRECT_CHECKER: {
00186 byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
00187 do {
00188 for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8)colour);
00189 dst = blitter->MoveTo(dst, 0, 1);
00190 } while (--bottom > 0);
00191 break;
00192 }
00193 }
00194 }
00195
00196 void GfxDrawLine(int x, int y, int x2, int y2, int colour, int width)
00197 {
00198 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00199 DrawPixelInfo *dpi = _cur_dpi;
00200
00201 assert(width > 0);
00202
00203 x -= dpi->left;
00204 x2 -= dpi->left;
00205 y -= dpi->top;
00206 y2 -= dpi->top;
00207
00208
00209 if (x + width / 2 < 0 && x2 + width / 2 < 0 ) return;
00210 if (y + width / 2 < 0 && y2 + width / 2 < 0 ) return;
00211 if (x - width / 2 > dpi->width && x2 - width / 2 > dpi->width ) return;
00212 if (y - width / 2 > dpi->height && y2 - width / 2 > dpi->height) return;
00213
00214 blitter->DrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour, width);
00215 }
00216
00217 void GfxDrawLineUnscaled(int x, int y, int x2, int y2, int colour)
00218 {
00219 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00220 DrawPixelInfo *dpi = _cur_dpi;
00221
00222 x -= dpi->left;
00223 x2 -= dpi->left;
00224 y -= dpi->top;
00225 y2 -= dpi->top;
00226
00227
00228 if (x < 0 && x2 < 0) return;
00229 if (y < 0 && y2 < 0) return;
00230 if (x > dpi->width && x2 > dpi->width) return;
00231 if (y > dpi->height && y2 > dpi->height) return;
00232
00233 blitter->DrawLine(dpi->dst_ptr, UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
00234 UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
00235 UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), colour, 1);
00236 }
00237
00251 void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
00252 {
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268 static const byte colour = PC_WHITE;
00269
00270 GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
00271 GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
00272 GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
00273
00274 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
00275 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
00276 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
00277 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
00278 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
00279 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
00280 }
00281
00286 static void SetColourRemap(TextColour colour)
00287 {
00288 if (colour == TC_INVALID) return;
00289
00290
00291
00292 bool no_shade = (colour & TC_NO_SHADE) != 0 || colour == TC_BLACK;
00293 bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0;
00294 colour &= ~(TC_NO_SHADE | TC_IS_PALETTE_COLOUR);
00295
00296 _string_colourremap[1] = raw_colour ? (byte)colour : _string_colourmap[colour];
00297 _string_colourremap[2] = no_shade ? 0 : 1;
00298 _colour_remap_ptr = _string_colourremap;
00299 }
00300
00301 #if !defined(WITH_ICU)
00302 typedef WChar UChar;
00303 static UChar *HandleBiDiAndArabicShapes(UChar *text) { return text; }
00304 #else
00305 #include <unicode/ubidi.h>
00306 #include <unicode/ushape.h>
00307
00337 static UChar *HandleBiDiAndArabicShapes(UChar *buffer)
00338 {
00339 static UChar input_output[DRAW_STRING_BUFFER];
00340 UChar intermediate[DRAW_STRING_BUFFER];
00341
00342 UChar *t = buffer;
00343 size_t length = 0;
00344 while (*t != '\0' && length < lengthof(input_output) - 1) {
00345 input_output[length++] = *t++;
00346 }
00347 input_output[length] = 0;
00348
00349 UErrorCode err = U_ZERO_ERROR;
00350 UBiDi *para = ubidi_openSized((int32_t)length, 0, &err);
00351 if (para == NULL) return buffer;
00352
00353 ubidi_setPara(para, input_output, (int32_t)length, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, NULL, &err);
00354 ubidi_writeReordered(para, intermediate, (int32_t)length, UBIDI_REMOVE_BIDI_CONTROLS, &err);
00355 length = u_shapeArabic(intermediate, (int32_t)length, input_output, lengthof(input_output), U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE, &err);
00356 ubidi_close(para);
00357
00358 if (U_FAILURE(err)) return buffer;
00359
00360 input_output[length] = '\0';
00361 return input_output;
00362 }
00363 #endif
00364
00365
00375 static int TruncateString(char *str, int maxw, bool ignore_setxy, FontSize start_fontsize)
00376 {
00377 int w = 0;
00378 FontSize size = start_fontsize;
00379 int ddd, ddd_w;
00380
00381 WChar c;
00382 char *ddd_pos;
00383
00384 ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
00385
00386 for (ddd_pos = str; (c = Utf8Consume(const_cast<const char **>(&str))) != '\0'; ) {
00387 if (IsPrintable(c) && !IsTextDirectionChar(c)) {
00388 w += GetCharacterWidth(size, c);
00389
00390 if (w > maxw) {
00391
00392
00393 for (int i = 0; *ddd_pos != '\0' && i < 3; i++, ddd_pos++) *ddd_pos = '.';
00394 *ddd_pos = '\0';
00395 return ddd_w;
00396 }
00397 } else {
00398 if (c == SCC_SETX) {
00399 if (!ignore_setxy) w = *str;
00400 str++;
00401 } else if (c == SCC_SETXY) {
00402 if (!ignore_setxy) w = *str;
00403 str += 2;
00404 } else if (c == SCC_TINYFONT) {
00405 size = FS_SMALL;
00406 ddd = GetCharacterWidth(size, '.') * 3;
00407 } else if (c == SCC_BIGFONT) {
00408 size = FS_LARGE;
00409 ddd = GetCharacterWidth(size, '.') * 3;
00410 } else if (c == '\n') {
00411 DEBUG(misc, 0, "Drawing string using newlines with DrawString instead of DrawStringMultiLine. Please notify the developers of this: [%s]", str);
00412 }
00413 }
00414
00415
00416 if (w + ddd < maxw) {
00417 ddd_w = w + ddd;
00418 ddd_pos = str;
00419 }
00420 }
00421
00422 return w;
00423 }
00424
00425 static int ReallyDoDrawString(const UChar *string, int x, int y, DrawStringParams ¶ms, bool parse_string_also_when_clipped = false);
00426
00433 static int GetStringWidth(const UChar *str, FontSize start_fontsize)
00434 {
00435 FontSize size = start_fontsize;
00436 int max_width;
00437 int width;
00438 WChar c;
00439
00440 width = max_width = 0;
00441 for (;;) {
00442 c = *str++;
00443 if (c == 0) break;
00444 if (IsPrintable(c) && !IsTextDirectionChar(c)) {
00445 width += GetCharacterWidth(size, c);
00446 } else {
00447 switch (c) {
00448 case SCC_SETX:
00449 case SCC_SETXY:
00450
00451 NOT_REACHED();
00452 break;
00453 case SCC_TINYFONT: size = FS_SMALL; break;
00454 case SCC_BIGFONT: size = FS_LARGE; break;
00455 case '\n':
00456 max_width = max(max_width, width);
00457 break;
00458 }
00459 }
00460 }
00461
00462 return max(max_width, width);
00463 }
00464
00483 static int DrawString(int left, int right, int top, char *str, const char *last, DrawStringParams ¶ms, StringAlignment align, bool underline = false, bool truncate = true)
00484 {
00485
00486 int min_left = INT32_MAX;
00487 int max_right = INT32_MIN;
00488
00489 int initial_left = left;
00490 int initial_right = right;
00491 int initial_top = top;
00492
00493 if (truncate) TruncateString(str, right - left + 1, (align & SA_STRIP) == SA_STRIP, params.fontsize);
00494
00495
00496
00497
00498
00499
00500
00501 static SmallVector<UChar *, 4> setx_offsets;
00502 setx_offsets.Clear();
00503
00504 UChar draw_buffer[DRAW_STRING_BUFFER];
00505 UChar *p = draw_buffer;
00506
00507 *setx_offsets.Append() = p;
00508
00509 char *loc = str;
00510 for (;;) {
00511 WChar c;
00512
00513 size_t len = Utf8Decode(&c, loc);
00514 *p++ = c;
00515
00516 if (c == '\0') break;
00517 if (p >= lastof(draw_buffer) - 3) {
00518
00519 *p = '\0';
00520 break;
00521 }
00522 if (c != SCC_SETX && c != SCC_SETXY) {
00523 loc += len;
00524 continue;
00525 }
00526
00527 if (align & SA_STRIP) {
00528
00529
00530 *p-- = '\0';
00531 loc += len + (c == SCC_SETXY ? 2 : 1);
00532 continue;
00533 }
00534
00535 if ((align & SA_HOR_MASK) != SA_LEFT) {
00536 DEBUG(grf, 1, "Using SETX and/or SETXY when not aligned to the left. Fixing alignment...");
00537
00538
00539
00540
00541
00542 align = SA_LEFT | SA_FORCE;
00543 initial_left = left = max(left, (left + right - (int)GetStringBoundingBox(str).width) / 2);
00544 }
00545
00546
00547 if (p != draw_buffer) {
00548 *setx_offsets.Append() = p;
00549 p[-1] = '\0';
00550 *p++ = c;
00551 }
00552
00553
00554 loc += len;
00555
00556 *p++ = *loc++;
00557
00558 if (c == SCC_SETXY) *p++ = *loc++;
00559 }
00560
00561
00562 if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && !(align & SA_STRIP) && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
00563
00564 for (UChar **iter = setx_offsets.Begin(); iter != setx_offsets.End(); iter++) {
00565 UChar *to_draw = *iter;
00566 int offset = 0;
00567
00568
00569 if (*to_draw == SCC_SETX || *to_draw == SCC_SETXY) {
00570 to_draw++;
00571 offset = *to_draw++;
00572 if (*to_draw == SCC_SETXY) top = initial_top + *to_draw++;
00573 }
00574
00575 to_draw = HandleBiDiAndArabicShapes(to_draw);
00576 int w = GetStringWidth(to_draw, params.fontsize);
00577
00578
00579
00580
00581
00582
00583 switch (align & SA_HOR_MASK) {
00584 case SA_LEFT:
00585
00586 left = initial_left + offset;
00587 right = left + w - 1;
00588 break;
00589
00590 case SA_HOR_CENTER:
00591 left = RoundDivSU(initial_right + 1 + initial_left - w, 2);
00592
00593 right = left + w - 1;
00594 break;
00595
00596 case SA_RIGHT:
00597 left = initial_right + 1 - w - offset;
00598 break;
00599
00600 default:
00601 NOT_REACHED();
00602 }
00603
00604 min_left = min(left, min_left);
00605 max_right = max(right, max_right);
00606
00607 ReallyDoDrawString(to_draw, left, top, params, !truncate);
00608 if (underline) {
00609 GfxFillRect(left, top + FONT_HEIGHT_NORMAL, right, top + FONT_HEIGHT_NORMAL, _string_colourremap[1]);
00610 }
00611 }
00612
00613 return (align & SA_HOR_MASK) == SA_RIGHT ? min_left : max_right;
00614 }
00615
00630 int DrawString(int left, int right, int top, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
00631 {
00632 char buffer[DRAW_STRING_BUFFER];
00633 strecpy(buffer, str, lastof(buffer));
00634 DrawStringParams params(colour, fontsize);
00635 return DrawString(left, right, top, buffer, lastof(buffer), params, align, underline);
00636 }
00637
00652 int DrawString(int left, int right, int top, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
00653 {
00654 char buffer[DRAW_STRING_BUFFER];
00655 GetString(buffer, str, lastof(buffer));
00656 DrawStringParams params(colour, fontsize);
00657 return DrawString(left, right, top, buffer, lastof(buffer), params, align, underline);
00658 }
00659
00680 uint32 FormatStringLinebreaks(char *str, const char *last, int maxw, FontSize size)
00681 {
00682 int num = 0;
00683
00684 assert(maxw > 0);
00685
00686 for (;;) {
00687
00688 char *last_space = NULL;
00689 int w = 0;
00690
00691 for (;;) {
00692 WChar c = Utf8Consume(const_cast<const char **>(&str));
00693
00694 if (IsWhitespace(c)) last_space = str;
00695
00696 if (IsPrintable(c) && !IsTextDirectionChar(c)) {
00697 int char_w = GetCharacterWidth(size, c);
00698 w += char_w;
00699 if (w > maxw) {
00700
00701
00702 if (w == char_w) {
00703
00704
00705 return num + (size << 16);
00706 }
00707 if (last_space == NULL) {
00708
00709
00710
00711
00712
00713
00714 str = Utf8PrevChar(str);
00715 size_t len = strlen(str);
00716 char *terminator = str + len;
00717
00718
00719
00720
00721 assert(terminator <= last);
00722 assert(*terminator == '\0');
00723
00724
00725 if (terminator == last) {
00726
00727
00728 *Utf8PrevChar(terminator) = '\0';
00729 len = strlen(str);
00730 }
00731
00732 memmove(str + 1, str, len + 1);
00733 *str = '\0';
00734
00735 str++;
00736 } else {
00737
00738 str = last_space;
00739 }
00740 break;
00741 }
00742 } else {
00743 switch (c) {
00744 case '\0': return num + (size << 16);
00745 case SCC_SETX: str++; break;
00746 case SCC_SETXY: str += 2; break;
00747 case SCC_TINYFONT: size = FS_SMALL; break;
00748 case SCC_BIGFONT: size = FS_LARGE; break;
00749 case '\n': goto end_of_inner_loop;
00750 }
00751 }
00752 }
00753 end_of_inner_loop:
00754
00755
00756
00757 num++;
00758 char *s = Utf8PrevChar(str);
00759 *s++ = '\0';
00760
00761
00762 if (str - s >= 1) {
00763 for (; str[-1] != '\0';) *s++ = *str++;
00764 }
00765 }
00766 }
00767
00768
00777 static int GetMultilineStringHeight(const char *src, int num, FontSize start_fontsize)
00778 {
00779 int maxy = 0;
00780 int y = 0;
00781 int fh = GetCharacterHeight(start_fontsize);
00782
00783 for (;;) {
00784 WChar c = Utf8Consume(&src);
00785
00786 switch (c) {
00787 case 0: y += fh; if (--num < 0) return maxy; break;
00788 case '\n': y += fh; break;
00789 case SCC_SETX: src++; break;
00790 case SCC_SETXY: src++; y = (int)*src++; break;
00791 case SCC_TINYFONT: fh = GetCharacterHeight(FS_SMALL); break;
00792 case SCC_BIGFONT: fh = GetCharacterHeight(FS_LARGE); break;
00793 default: maxy = max<int>(maxy, y + fh); break;
00794 }
00795 }
00796 }
00797
00798
00805 int GetStringHeight(StringID str, int maxw)
00806 {
00807 char buffer[DRAW_STRING_BUFFER];
00808
00809 GetString(buffer, str, lastof(buffer));
00810
00811 uint32 tmp = FormatStringLinebreaks(buffer, lastof(buffer), maxw);
00812
00813 return GetMultilineStringHeight(buffer, GB(tmp, 0, 16), FS_NORMAL);
00814 }
00815
00822 Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion)
00823 {
00824 Dimension box = {suggestion.width, GetStringHeight(str, suggestion.width)};
00825 return box;
00826 }
00827
00844 static int DrawStringMultiLine(int left, int right, int top, int bottom, char *str, const char *last, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
00845 {
00846 int maxw = right - left + 1;
00847 int maxh = bottom - top + 1;
00848
00849
00850
00851 if (maxh <= 0) return top;
00852
00853 uint32 tmp = FormatStringLinebreaks(str, last, maxw);
00854 int num = GB(tmp, 0, 16) + 1;
00855
00856 int mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
00857 int total_height = num * mt;
00858
00859 int skip_lines = 0;
00860 if (total_height > maxh) {
00861 if (maxh < mt) return top;
00862 if ((align & SA_VERT_MASK) == SA_BOTTOM) {
00863 skip_lines = num;
00864 num = maxh / mt;
00865 skip_lines -= num;
00866 } else {
00867 num = maxh / mt;
00868 }
00869 total_height = num * mt;
00870 }
00871
00872 int y;
00873 switch (align & SA_VERT_MASK) {
00874 case SA_TOP:
00875 y = top;
00876 break;
00877
00878 case SA_VERT_CENTER:
00879 y = RoundDivSU(bottom + top - total_height, 2);
00880 break;
00881
00882 case SA_BOTTOM:
00883 y = bottom - total_height;
00884 break;
00885
00886 default: NOT_REACHED();
00887 }
00888
00889 const char *src = str;
00890 DrawStringParams params(colour, fontsize);
00891 int written_top = bottom;
00892 for (;;) {
00893 if (skip_lines == 0) {
00894 char buf2[DRAW_STRING_BUFFER];
00895 strecpy(buf2, src, lastof(buf2));
00896 DrawString(left, right, y, buf2, lastof(buf2), params, align, underline, false);
00897 if (written_top > y) written_top = y;
00898 y += mt;
00899 num--;
00900 }
00901
00902 for (;;) {
00903 WChar c = Utf8Consume(&src);
00904 if (c == 0) {
00905 break;
00906 } else if (c == SCC_SETX) {
00907 src++;
00908 } else if (c == SCC_SETXY) {
00909 src += 2;
00910 } else if (skip_lines > 0) {
00911
00912 if (c >= SCC_BLUE && c <= SCC_BLACK) {
00913 params.SetColour((TextColour)(c - SCC_BLUE));
00914 } else if (c == SCC_PREVIOUS_COLOUR) {
00915 params.SetPreviousColour();
00916 } else if (c == SCC_TINYFONT) {
00917 params.SetFontSize(FS_SMALL);
00918 } else if (c == SCC_BIGFONT) {
00919 params.SetFontSize(FS_LARGE);
00920 }
00921
00922 }
00923 }
00924 if (skip_lines > 0) skip_lines--;
00925 if (num == 0) return ((align & SA_VERT_MASK) == SA_BOTTOM) ? written_top : y;
00926 }
00927 }
00928
00944 int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
00945 {
00946 char buffer[DRAW_STRING_BUFFER];
00947 strecpy(buffer, str, lastof(buffer));
00948 return DrawStringMultiLine(left, right, top, bottom, buffer, lastof(buffer), colour, align, underline, fontsize);
00949 }
00950
00966 int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
00967 {
00968 char buffer[DRAW_STRING_BUFFER];
00969 GetString(buffer, str, lastof(buffer));
00970 return DrawStringMultiLine(left, right, top, bottom, buffer, lastof(buffer), colour, align, underline, fontsize);
00971 }
00972
00983 Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
00984 {
00985 FontSize size = start_fontsize;
00986 Dimension br;
00987 uint max_width;
00988 WChar c;
00989
00990 br.width = br.height = max_width = 0;
00991 for (;;) {
00992 c = Utf8Consume(&str);
00993 if (c == 0) break;
00994 if (IsPrintable(c) && !IsTextDirectionChar(c)) {
00995 br.width += GetCharacterWidth(size, c);
00996 } else {
00997 switch (c) {
00998 case SCC_SETX: br.width = max((uint)*str++, br.width); break;
00999 case SCC_SETXY:
01000 br.width = max((uint)*str++, br.width);
01001 br.height = max((uint)*str++, br.height);
01002 break;
01003 case SCC_TINYFONT: size = FS_SMALL; break;
01004 case SCC_BIGFONT: size = FS_LARGE; break;
01005 case '\n':
01006 br.height += GetCharacterHeight(size);
01007 if (br.width > max_width) max_width = br.width;
01008 br.width = 0;
01009 break;
01010 }
01011 }
01012 }
01013 br.height += GetCharacterHeight(size);
01014
01015 br.width = max(br.width, max_width);
01016 return br;
01017 }
01018
01025 Dimension GetStringBoundingBox(StringID strid)
01026 {
01027 char buffer[DRAW_STRING_BUFFER];
01028
01029 GetString(buffer, strid, lastof(buffer));
01030 return GetStringBoundingBox(buffer);
01031 }
01032
01040 void DrawCharCentered(WChar c, int x, int y, TextColour colour)
01041 {
01042 SetColourRemap(colour);
01043 GfxMainBlitter(GetGlyph(FS_NORMAL, c), x - GetCharacterWidth(FS_NORMAL, c) / 2, y, BM_COLOUR_REMAP);
01044 }
01045
01063 static int ReallyDoDrawString(const UChar *string, int x, int y, DrawStringParams ¶ms, bool parse_string_also_when_clipped)
01064 {
01065 DrawPixelInfo *dpi = _cur_dpi;
01066 bool draw_shadow = GetDrawGlyphShadow();
01067 UChar c;
01068 int xo = x;
01069
01070 if (!parse_string_also_when_clipped) {
01071
01072
01073 if (x >= dpi->left + dpi->width || y >= dpi->top + dpi->height) return x;
01074 }
01075
01076 switch_colour:;
01077 SetColourRemap(params.cur_colour);
01078
01079 check_bounds:
01080 if (y + _max_char_height <= dpi->top || dpi->top + dpi->height <= y) {
01081 skip_char:;
01082 for (;;) {
01083 c = *string++;
01084 if (!IsPrintable(c)) goto skip_cont;
01085 }
01086 }
01087
01088 for (;;) {
01089 c = *string++;
01090 skip_cont:;
01091 if (c == 0) {
01092 return x;
01093 }
01094 if (IsPrintable(c) && !IsTextDirectionChar(c)) {
01095 if (x >= dpi->left + dpi->width) goto skip_char;
01096 if (x + _max_char_width >= dpi->left) {
01097 const Sprite *glyph = GetGlyph(params.fontsize, c);
01098 if (draw_shadow && params.fontsize == FS_NORMAL && params.cur_colour != TC_BLACK && !(c >= SCC_SPRITE_START && c <= SCC_SPRITE_END)) {
01099 SetColourRemap(TC_BLACK);
01100 GfxMainBlitter(glyph, x + 1, y + 1, BM_COLOUR_REMAP);
01101 SetColourRemap(params.cur_colour);
01102 }
01103 GfxMainBlitter(glyph, x, y, BM_COLOUR_REMAP);
01104 }
01105 x += GetCharacterWidth(params.fontsize, c);
01106 } else if (c == '\n') {
01107 x = xo;
01108 y += GetCharacterHeight(params.fontsize);
01109 goto check_bounds;
01110 } else if (c >= SCC_BLUE && c <= SCC_BLACK) {
01111 params.SetColour((TextColour)(c - SCC_BLUE));
01112 goto switch_colour;
01113 } else if (c == SCC_PREVIOUS_COLOUR) {
01114 params.SetPreviousColour();
01115 goto switch_colour;
01116 } else if (c == SCC_SETX || c == SCC_SETXY) {
01117
01118 NOT_REACHED();
01119 } else if (c == SCC_TINYFONT) {
01120 params.SetFontSize(FS_SMALL);
01121 } else if (c == SCC_BIGFONT) {
01122 params.SetFontSize(FS_LARGE);
01123 } else if (!IsTextDirectionChar(c)) {
01124 DEBUG(misc, 0, "[utf8] unknown string command character %d", c);
01125 }
01126 }
01127 }
01128
01136 Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
01137 {
01138 const Sprite *sprite = GetSprite(sprid, ST_NORMAL);
01139
01140 if (offset != NULL) {
01141 offset->x = UnScaleByZoom(sprite->x_offs, zoom);
01142 offset->y = UnScaleByZoom(sprite->y_offs, zoom);
01143 }
01144
01145 Dimension d;
01146 d.width = max<int>(0, UnScaleByZoom(sprite->x_offs + sprite->width, zoom));
01147 d.height = max<int>(0, UnScaleByZoom(sprite->y_offs + sprite->height, zoom));
01148 return d;
01149 }
01150
01159 void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
01160 {
01161 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
01162 if (HasBit(img, PALETTE_MODIFIER_TRANSPARENT)) {
01163 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1;
01164 GfxMainBlitterViewport(GetSprite(real_sprite, ST_NORMAL), x, y, BM_TRANSPARENT, sub, real_sprite);
01165 } else if (pal != PAL_NONE) {
01166 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1;
01167 GfxMainBlitterViewport(GetSprite(real_sprite, ST_NORMAL), x, y, BM_COLOUR_REMAP, sub, real_sprite);
01168 } else {
01169 GfxMainBlitterViewport(GetSprite(real_sprite, ST_NORMAL), x, y, BM_NORMAL, sub, real_sprite);
01170 }
01171 }
01172
01182 void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
01183 {
01184 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
01185 if (HasBit(img, PALETTE_MODIFIER_TRANSPARENT)) {
01186 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1;
01187 GfxMainBlitter(GetSprite(real_sprite, ST_NORMAL), x, y, BM_TRANSPARENT, sub, real_sprite, zoom);
01188 } else if (pal != PAL_NONE) {
01189 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1;
01190 GfxMainBlitter(GetSprite(real_sprite, ST_NORMAL), x, y, BM_COLOUR_REMAP, sub, real_sprite, zoom);
01191 } else {
01192 GfxMainBlitter(GetSprite(real_sprite, ST_NORMAL), x, y, BM_NORMAL, sub, real_sprite, zoom);
01193 }
01194 }
01195
01196 static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id)
01197 {
01198 const DrawPixelInfo *dpi = _cur_dpi;
01199 Blitter::BlitterParams bp;
01200
01201
01202 int clip_left = (sub != NULL ? max(0, -sprite->x_offs + sub->left * ZOOM_LVL_BASE ) : 0);
01203 int clip_top = (sub != NULL ? max(0, -sprite->y_offs + sub->top * ZOOM_LVL_BASE ) : 0);
01204 int clip_right = (sub != NULL ? max(0, sprite->width - (-sprite->x_offs + (sub->right + 1) * ZOOM_LVL_BASE)) : 0);
01205 int clip_bottom = (sub != NULL ? max(0, sprite->height - (-sprite->y_offs + (sub->bottom + 1) * ZOOM_LVL_BASE)) : 0);
01206
01207 if (clip_left + clip_right >= sprite->width) return;
01208 if (clip_top + clip_bottom >= sprite->height) return;
01209
01210
01211 x += sprite->x_offs;
01212 y += sprite->y_offs;
01213
01214
01215 bp.sprite = sprite->data;
01216 bp.sprite_width = sprite->width;
01217 bp.sprite_height = sprite->height;
01218 bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, dpi->zoom);
01219 bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, dpi->zoom);
01220 bp.top = 0;
01221 bp.left = 0;
01222 bp.skip_left = UnScaleByZoomLower(clip_left, dpi->zoom);
01223 bp.skip_top = UnScaleByZoomLower(clip_top, dpi->zoom);
01224
01225 x += ScaleByZoom(bp.skip_left, dpi->zoom);
01226 y += ScaleByZoom(bp.skip_top, dpi->zoom);
01227
01228 bp.dst = dpi->dst_ptr;
01229 bp.pitch = dpi->pitch;
01230 bp.remap = _colour_remap_ptr;
01231
01232 assert(sprite->width > 0);
01233 assert(sprite->height > 0);
01234
01235 if (bp.width <= 0) return;
01236 if (bp.height <= 0) return;
01237
01238 y -= dpi->top;
01239
01240 if (y < 0) {
01241 bp.height -= -UnScaleByZoom(y, dpi->zoom);
01242 if (bp.height <= 0) return;
01243 bp.skip_top += -UnScaleByZoom(y, dpi->zoom);
01244 y = 0;
01245 } else {
01246 bp.top = UnScaleByZoom(y, dpi->zoom);
01247 }
01248
01249
01250 y += ScaleByZoom(bp.height, dpi->zoom) - dpi->height;
01251 if (y > 0) {
01252 bp.height -= UnScaleByZoom(y, dpi->zoom);
01253 if (bp.height <= 0) return;
01254 }
01255
01256 x -= dpi->left;
01257
01258 if (x < 0) {
01259 bp.width -= -UnScaleByZoom(x, dpi->zoom);
01260 if (bp.width <= 0) return;
01261 bp.skip_left += -UnScaleByZoom(x, dpi->zoom);
01262 x = 0;
01263 } else {
01264 bp.left = UnScaleByZoom(x, dpi->zoom);
01265 }
01266
01267
01268 x += ScaleByZoom(bp.width, dpi->zoom) - dpi->width;
01269 if (x > 0) {
01270 bp.width -= UnScaleByZoom(x, dpi->zoom);
01271 if (bp.width <= 0) return;
01272 }
01273
01274 assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, dpi->zoom));
01275 assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, dpi->zoom));
01276
01277
01278 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && sprite_id != SPR_CURSOR_MOUSE) {
01279 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01280 void *topleft = blitter->MoveTo(bp.dst, bp.left, bp.top);
01281 void *bottomright = blitter->MoveTo(topleft, bp.width - 1, bp.height - 1);
01282
01283 void *clicked = _newgrf_debug_sprite_picker.clicked_pixel;
01284
01285 if (topleft <= clicked && clicked <= bottomright) {
01286 uint offset = (((size_t)clicked - (size_t)topleft) / (blitter->GetScreenDepth() / 8)) % bp.pitch;
01287 if (offset < (uint)bp.width) {
01288 _newgrf_debug_sprite_picker.sprites.Include(sprite_id);
01289 }
01290 }
01291 }
01292
01293 BlitterFactoryBase::GetCurrentBlitter()->Draw(&bp, mode, dpi->zoom);
01294 }
01295
01296 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id, ZoomLevel zoom)
01297 {
01298 const DrawPixelInfo *dpi = _cur_dpi;
01299 Blitter::BlitterParams bp;
01300
01301
01302 int clip_left = (sub != NULL ? max(0, -sprite->x_offs + sub->left ) : 0);
01303 int clip_top = (sub != NULL ? max(0, -sprite->y_offs + sub->top ) : 0);
01304 int clip_right = (sub != NULL ? max(0, sprite->width - (-sprite->x_offs + sub->right + 1)) : 0);
01305 int clip_bottom = (sub != NULL ? max(0, sprite->height - (-sprite->y_offs + sub->bottom + 1)) : 0);
01306
01307 if (clip_left + clip_right >= sprite->width) return;
01308 if (clip_top + clip_bottom >= sprite->height) return;
01309
01310
01311 x = ScaleByZoom(x, zoom);
01312 y = ScaleByZoom(y, zoom);
01313
01314
01315 x += sprite->x_offs;
01316 y += sprite->y_offs;
01317
01318
01319 bp.sprite = sprite->data;
01320 bp.sprite_width = sprite->width;
01321 bp.sprite_height = sprite->height;
01322 bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, zoom);
01323 bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, zoom);
01324 bp.top = 0;
01325 bp.left = 0;
01326 bp.skip_left = UnScaleByZoomLower(clip_left, zoom);
01327 bp.skip_top = UnScaleByZoomLower(clip_top, zoom);
01328
01329 x += ScaleByZoom(bp.skip_left, zoom);
01330 y += ScaleByZoom(bp.skip_top, zoom);
01331
01332 bp.dst = dpi->dst_ptr;
01333 bp.pitch = dpi->pitch;
01334 bp.remap = _colour_remap_ptr;
01335
01336 assert(sprite->width > 0);
01337 assert(sprite->height > 0);
01338
01339 if (bp.width <= 0) return;
01340 if (bp.height <= 0) return;
01341
01342 y -= ScaleByZoom(dpi->top, zoom);
01343
01344 if (y < 0) {
01345 bp.height -= -UnScaleByZoom(y, zoom);
01346 if (bp.height <= 0) return;
01347 bp.skip_top += -UnScaleByZoom(y, zoom);
01348 y = 0;
01349 } else {
01350 bp.top = UnScaleByZoom(y, zoom);
01351 }
01352
01353
01354 y += ScaleByZoom(bp.height - dpi->height, zoom);
01355 if (y > 0) {
01356 bp.height -= UnScaleByZoom(y, zoom);
01357 if (bp.height <= 0) return;
01358 }
01359
01360 x -= ScaleByZoom(dpi->left, zoom);
01361
01362 if (x < 0) {
01363 bp.width -= -UnScaleByZoom(x, zoom);
01364 if (bp.width <= 0) return;
01365 bp.skip_left += -UnScaleByZoom(x, zoom);
01366 x = 0;
01367 } else {
01368 bp.left = UnScaleByZoom(x, zoom);
01369 }
01370
01371
01372 x += ScaleByZoom(bp.width - dpi->width, zoom);
01373 if (x > 0) {
01374 bp.width -= UnScaleByZoom(x, zoom);
01375 if (bp.width <= 0) return;
01376 }
01377
01378 assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, zoom));
01379 assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, zoom));
01380
01381
01382 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && sprite_id != SPR_CURSOR_MOUSE) {
01383 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01384 void *topleft = blitter->MoveTo(bp.dst, bp.left, bp.top);
01385 void *bottomright = blitter->MoveTo(topleft, bp.width - 1, bp.height - 1);
01386
01387 void *clicked = _newgrf_debug_sprite_picker.clicked_pixel;
01388
01389 if (topleft <= clicked && clicked <= bottomright) {
01390 uint offset = (((size_t)clicked - (size_t)topleft) / (blitter->GetScreenDepth() / 8)) % bp.pitch;
01391 if (offset < (uint)bp.width) {
01392 _newgrf_debug_sprite_picker.sprites.Include(sprite_id);
01393 }
01394 }
01395 }
01396
01397 BlitterFactoryBase::GetCurrentBlitter()->Draw(&bp, mode, zoom);
01398 }
01399
01400 void DoPaletteAnimations();
01401
01402 void GfxInitPalettes()
01403 {
01404 memcpy(_cur_palette, _palette, sizeof(_cur_palette));
01405
01406 DoPaletteAnimations();
01407 _pal_first_dirty = 0;
01408 _pal_count_dirty = 256;
01409 }
01410
01411 #define EXTR(p, q) (((uint16)(palette_animation_counter * (p)) * (q)) >> 16)
01412 #define EXTR2(p, q) (((uint16)(~palette_animation_counter * (p)) * (q)) >> 16)
01413
01414 void DoPaletteAnimations()
01415 {
01416
01417 static int palette_animation_counter = 0;
01418 palette_animation_counter += 8;
01419
01420 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01421 const Colour *s;
01422 const ExtraPaletteValues *ev = &_extra_palette_values;
01423 Colour old_val[PALETTE_ANIM_SIZE];
01424 const uint old_tc = palette_animation_counter;
01425 uint i;
01426 uint j;
01427
01428 if (blitter != NULL && blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_NONE) {
01429 palette_animation_counter = 0;
01430 }
01431
01432 Colour *palette_pos = &_cur_palette[PALETTE_ANIM_START];
01433
01434
01435 memcpy(old_val, palette_pos, sizeof(old_val));
01436
01437
01438 s = ev->fizzy_drink;
01439 j = EXTR2(512, EPV_CYCLES_FIZZY_DRINK);
01440 for (i = 0; i != EPV_CYCLES_FIZZY_DRINK; i++) {
01441 *palette_pos++ = s[j];
01442 j++;
01443 if (j == EPV_CYCLES_FIZZY_DRINK) j = 0;
01444 }
01445
01446
01447 s = ev->oil_refinery;
01448 j = EXTR2(512, EPV_CYCLES_OIL_REFINERY);
01449 for (i = 0; i != EPV_CYCLES_OIL_REFINERY; i++) {
01450 *palette_pos++ = s[j];
01451 j++;
01452 if (j == EPV_CYCLES_OIL_REFINERY) j = 0;
01453 }
01454
01455
01456 {
01457 byte i = (palette_animation_counter >> 1) & 0x7F;
01458 byte v;
01459
01460 if (i < 0x3f) {
01461 v = 255;
01462 } else if (i < 0x4A || i >= 0x75) {
01463 v = 128;
01464 } else {
01465 v = 20;
01466 }
01467 palette_pos->r = v;
01468 palette_pos->g = 0;
01469 palette_pos->b = 0;
01470 palette_pos++;
01471
01472 i ^= 0x40;
01473 if (i < 0x3f) {
01474 v = 255;
01475 } else if (i < 0x4A || i >= 0x75) {
01476 v = 128;
01477 } else {
01478 v = 20;
01479 }
01480 palette_pos->r = v;
01481 palette_pos->g = 0;
01482 palette_pos->b = 0;
01483 palette_pos++;
01484 }
01485
01486
01487 s = ev->lighthouse;
01488 j = EXTR(256, EPV_CYCLES_LIGHTHOUSE);
01489 for (i = 0; i != EPV_CYCLES_LIGHTHOUSE; i++) {
01490 *palette_pos++ = s[j];
01491 j++;
01492 if (j == EPV_CYCLES_LIGHTHOUSE) j = 0;
01493 }
01494
01495
01496 s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->dark_water_toyland : ev->dark_water;
01497 j = EXTR(320, EPV_CYCLES_DARK_WATER);
01498 for (i = 0; i != EPV_CYCLES_DARK_WATER; i++) {
01499 *palette_pos++ = s[j];
01500 j++;
01501 if (j == EPV_CYCLES_DARK_WATER) j = 0;
01502 }
01503
01504
01505 s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->glitter_water_toyland : ev->glitter_water;
01506 j = EXTR(128, EPV_CYCLES_GLITTER_WATER);
01507 for (i = 0; i != EPV_CYCLES_GLITTER_WATER / 3; i++) {
01508 *palette_pos++ = s[j];
01509 j += 3;
01510 if (j >= EPV_CYCLES_GLITTER_WATER) j -= EPV_CYCLES_GLITTER_WATER;
01511 }
01512
01513 if (blitter != NULL && blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_NONE) {
01514 palette_animation_counter = old_tc;
01515 } else {
01516 if (memcmp(old_val, &_cur_palette[PALETTE_ANIM_START], sizeof(old_val)) != 0) {
01517
01518 _pal_first_dirty = PALETTE_ANIM_START;
01519 _pal_count_dirty = PALETTE_ANIM_SIZE;
01520 }
01521 }
01522 }
01523
01524
01526 void LoadStringWidthTable()
01527 {
01528 _max_char_height = 0;
01529 _max_char_width = 0;
01530
01531 for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
01532 _max_char_height = max<int>(_max_char_height, GetCharacterHeight(fs));
01533 for (uint i = 0; i != 224; i++) {
01534 _stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
01535 _max_char_width = max<int>(_max_char_width, _stringwidth_table[fs][i]);
01536 }
01537 }
01538
01539
01540 _max_char_height++;
01541 _max_char_width++;
01542
01543 ReInitAllWindows();
01544 }
01545
01552 byte GetCharacterWidth(FontSize size, WChar key)
01553 {
01554
01555 if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
01556
01557 return GetGlyphWidth(size, key);
01558 }
01559
01565 byte GetDigitWidth(FontSize size)
01566 {
01567 byte width = 0;
01568 for (char c = '0'; c <= '9'; c++) {
01569 width = max(GetCharacterWidth(size, c), width);
01570 }
01571 return width;
01572 }
01573
01574
01575 void ScreenSizeChanged()
01576 {
01577 _dirty_bytes_per_line = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH);
01578 _dirty_blocks = ReallocT<byte>(_dirty_blocks, _dirty_bytes_per_line * CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT));
01579
01580
01581 if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
01582 if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
01583
01584
01585 _cursor.visible = false;
01586 }
01587
01588 void UndrawMouseCursor()
01589 {
01590
01591 if (_screen.dst_ptr == NULL) return;
01592
01593 if (_cursor.visible) {
01594 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01595 _cursor.visible = false;
01596 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);
01597 _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
01598 }
01599 }
01600
01601 void DrawMouseCursor()
01602 {
01603 #if defined(WINCE)
01604
01605 return;
01606 #endif
01607
01608
01609 if (_screen.dst_ptr == NULL) return;
01610
01611 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01612 int x;
01613 int y;
01614 int w;
01615 int h;
01616
01617
01618 if (!_cursor.in_window) return;
01619
01620
01621 if (_cursor.visible) {
01622 if (!_cursor.dirty) return;
01623 UndrawMouseCursor();
01624 }
01625
01626 w = _cursor.size.x;
01627 x = _cursor.pos.x + _cursor.offs.x + _cursor.short_vehicle_offset;
01628 if (x < 0) {
01629 w += x;
01630 x = 0;
01631 }
01632 if (w > _screen.width - x) w = _screen.width - x;
01633 if (w <= 0) return;
01634 _cursor.draw_pos.x = x;
01635 _cursor.draw_size.x = w;
01636
01637 h = _cursor.size.y;
01638 y = _cursor.pos.y + _cursor.offs.y;
01639 if (y < 0) {
01640 h += y;
01641 y = 0;
01642 }
01643 if (h > _screen.height - y) h = _screen.height - y;
01644 if (h <= 0) return;
01645 _cursor.draw_pos.y = y;
01646 _cursor.draw_size.y = h;
01647
01648 uint8 *buffer = _cursor_backup.Allocate(blitter->BufferSize(w, h));
01649
01650
01651 blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), buffer, _cursor.draw_size.x, _cursor.draw_size.y);
01652
01653
01654 _cur_dpi = &_screen;
01655 DrawSprite(_cursor.sprite, _cursor.pal, _cursor.pos.x + _cursor.short_vehicle_offset, _cursor.pos.y);
01656
01657 _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
01658
01659 _cursor.visible = true;
01660 _cursor.dirty = false;
01661 }
01662
01663 void RedrawScreenRect(int left, int top, int right, int bottom)
01664 {
01665 assert(right <= _screen.width && bottom <= _screen.height);
01666 if (_cursor.visible) {
01667 if (right > _cursor.draw_pos.x &&
01668 left < _cursor.draw_pos.x + _cursor.draw_size.x &&
01669 bottom > _cursor.draw_pos.y &&
01670 top < _cursor.draw_pos.y + _cursor.draw_size.y) {
01671 UndrawMouseCursor();
01672 }
01673 }
01674
01675 #ifdef ENABLE_NETWORK
01676 if (_networking) NetworkUndrawChatMessage();
01677 #endif
01678
01679 DrawOverlappedWindowForAll(left, top, right, bottom);
01680
01681 _video_driver->MakeDirty(left, top, right - left, bottom - top);
01682 }
01683
01689 void DrawDirtyBlocks()
01690 {
01691 byte *b = _dirty_blocks;
01692 const int w = Align(_screen.width, DIRTY_BLOCK_WIDTH);
01693 const int h = Align(_screen.height, DIRTY_BLOCK_HEIGHT);
01694 int x;
01695 int y;
01696
01697 if (HasModalProgress()) {
01698
01699
01700 _modal_progress_paint_mutex->EndCritical();
01701 _modal_progress_work_mutex->EndCritical();
01702
01703
01704 if (!IsFirstModalProgressLoop()) CSleep(MODAL_PROGRESS_REDRAW_TIMEOUT);
01705 _realtime_tick += MODAL_PROGRESS_REDRAW_TIMEOUT;
01706 _modal_progress_paint_mutex->BeginCritical();
01707 _modal_progress_work_mutex->BeginCritical();
01708
01709 extern void SwitchToMode(SwitchMode new_mode);
01710 if (_switch_mode != SM_NONE && !HasModalProgress()) {
01711 SwitchToMode(_switch_mode);
01712 _switch_mode = SM_NONE;
01713 }
01714 }
01715
01716 y = 0;
01717 do {
01718 x = 0;
01719 do {
01720 if (*b != 0) {
01721 int left;
01722 int top;
01723 int right = x + DIRTY_BLOCK_WIDTH;
01724 int bottom = y;
01725 byte *p = b;
01726 int h2;
01727
01728
01729 do {
01730 *p = 0;
01731 p += _dirty_bytes_per_line;
01732 bottom += DIRTY_BLOCK_HEIGHT;
01733 } while (bottom != h && *p != 0);
01734
01735
01736 h2 = (bottom - y) / DIRTY_BLOCK_HEIGHT;
01737 assert(h2 > 0);
01738 p = b;
01739
01740 while (right != w) {
01741 byte *p2 = ++p;
01742 int h = h2;
01743
01744 do {
01745 if (!*p2) goto no_more_coalesc;
01746 p2 += _dirty_bytes_per_line;
01747 } while (--h != 0);
01748
01749
01750
01751 right += DIRTY_BLOCK_WIDTH;
01752
01753 h = h2;
01754 p2 = p;
01755 do {
01756 *p2 = 0;
01757 p2 += _dirty_bytes_per_line;
01758 } while (--h != 0);
01759 }
01760 no_more_coalesc:
01761
01762 left = x;
01763 top = y;
01764
01765 if (left < _invalid_rect.left ) left = _invalid_rect.left;
01766 if (top < _invalid_rect.top ) top = _invalid_rect.top;
01767 if (right > _invalid_rect.right ) right = _invalid_rect.right;
01768 if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
01769
01770 if (left < right && top < bottom) {
01771 RedrawScreenRect(left, top, right, bottom);
01772 }
01773
01774 }
01775 } while (b++, (x += DIRTY_BLOCK_WIDTH) != w);
01776 } while (b += -(int)(w / DIRTY_BLOCK_WIDTH) + _dirty_bytes_per_line, (y += DIRTY_BLOCK_HEIGHT) != h);
01777
01778 _invalid_rect.left = w;
01779 _invalid_rect.top = h;
01780 _invalid_rect.right = 0;
01781 _invalid_rect.bottom = 0;
01782 }
01783
01799 void SetDirtyBlocks(int left, int top, int right, int bottom)
01800 {
01801 byte *b;
01802 int width;
01803 int height;
01804
01805 if (left < 0) left = 0;
01806 if (top < 0) top = 0;
01807 if (right > _screen.width) right = _screen.width;
01808 if (bottom > _screen.height) bottom = _screen.height;
01809
01810 if (left >= right || top >= bottom) return;
01811
01812 if (left < _invalid_rect.left ) _invalid_rect.left = left;
01813 if (top < _invalid_rect.top ) _invalid_rect.top = top;
01814 if (right > _invalid_rect.right ) _invalid_rect.right = right;
01815 if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
01816
01817 left /= DIRTY_BLOCK_WIDTH;
01818 top /= DIRTY_BLOCK_HEIGHT;
01819
01820 b = _dirty_blocks + top * _dirty_bytes_per_line + left;
01821
01822 width = ((right - 1) / DIRTY_BLOCK_WIDTH) - left + 1;
01823 height = ((bottom - 1) / DIRTY_BLOCK_HEIGHT) - top + 1;
01824
01825 assert(width > 0 && height > 0);
01826
01827 do {
01828 int i = width;
01829
01830 do b[--i] = 0xFF; while (i != 0);
01831
01832 b += _dirty_bytes_per_line;
01833 } while (--height != 0);
01834 }
01835
01842 void MarkWholeScreenDirty()
01843 {
01844 SetDirtyBlocks(0, 0, _screen.width, _screen.height);
01845 }
01846
01861 bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
01862 {
01863 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01864 const DrawPixelInfo *o = _cur_dpi;
01865
01866 n->zoom = ZOOM_LVL_NORMAL;
01867
01868 assert(width > 0);
01869 assert(height > 0);
01870
01871 if ((left -= o->left) < 0) {
01872 width += left;
01873 if (width <= 0) return false;
01874 n->left = -left;
01875 left = 0;
01876 } else {
01877 n->left = 0;
01878 }
01879
01880 if (width > o->width - left) {
01881 width = o->width - left;
01882 if (width <= 0) return false;
01883 }
01884 n->width = width;
01885
01886 if ((top -= o->top) < 0) {
01887 height += top;
01888 if (height <= 0) return false;
01889 n->top = -top;
01890 top = 0;
01891 } else {
01892 n->top = 0;
01893 }
01894
01895 n->dst_ptr = blitter->MoveTo(o->dst_ptr, left, top);
01896 n->pitch = o->pitch;
01897
01898 if (height > o->height - top) {
01899 height = o->height - top;
01900 if (height <= 0) return false;
01901 }
01902 n->height = height;
01903
01904 return true;
01905 }
01906
01911 void UpdateCursorSize()
01912 {
01913 CursorVars *cv = &_cursor;
01914 const Sprite *p = GetSprite(GB(cv->sprite, 0, SPRITE_WIDTH), ST_NORMAL);
01915
01916 cv->size.y = UnScaleByZoom(p->height, ZOOM_LVL_GUI);
01917 cv->size.x = UnScaleByZoom(p->width, ZOOM_LVL_GUI);
01918 cv->offs.x = UnScaleByZoom(p->x_offs, ZOOM_LVL_GUI);
01919 cv->offs.y = UnScaleByZoom(p->y_offs, ZOOM_LVL_GUI);
01920
01921 cv->dirty = true;
01922 }
01923
01929 static void SetCursorSprite(CursorID cursor, PaletteID pal)
01930 {
01931 CursorVars *cv = &_cursor;
01932 if (cv->sprite == cursor) return;
01933
01934 cv->sprite = cursor;
01935 cv->pal = pal;
01936 UpdateCursorSize();
01937
01938 cv->short_vehicle_offset = 0;
01939 }
01940
01941 static void SwitchAnimatedCursor()
01942 {
01943 const AnimCursor *cur = _cursor.animate_cur;
01944
01945 if (cur == NULL || cur->sprite == AnimCursor::LAST) cur = _cursor.animate_list;
01946
01947 SetCursorSprite(cur->sprite, _cursor.pal);
01948
01949 _cursor.animate_timeout = cur->display_time;
01950 _cursor.animate_cur = cur + 1;
01951 }
01952
01953 void CursorTick()
01954 {
01955 if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0) {
01956 SwitchAnimatedCursor();
01957 }
01958 }
01959
01966 void SetMouseCursor(CursorID sprite, PaletteID pal)
01967 {
01968
01969 _cursor.animate_timeout = 0;
01970
01971 SetCursorSprite(sprite, pal);
01972 }
01973
01979 void SetAnimatedMouseCursor(const AnimCursor *table)
01980 {
01981 _cursor.animate_list = table;
01982 _cursor.animate_cur = NULL;
01983 _cursor.pal = PAL_NONE;
01984 SwitchAnimatedCursor();
01985 }
01986
01987 bool ChangeResInGame(int width, int height)
01988 {
01989 return (_screen.width == width && _screen.height == height) || _video_driver->ChangeResolution(width, height);
01990 }
01991
01992 bool ToggleFullScreen(bool fs)
01993 {
01994 bool result = _video_driver->ToggleFullscreen(fs);
01995 if (_fullscreen != fs && _num_resolutions == 0) {
01996 DEBUG(driver, 0, "Could not find a suitable fullscreen resolution");
01997 }
01998 return result;
01999 }
02000
02001 static int CDECL compare_res(const Dimension *pa, const Dimension *pb)
02002 {
02003 int x = pa->width - pb->width;
02004 if (x != 0) return x;
02005 return pa->height - pb->height;
02006 }
02007
02008 void SortResolutions(int count)
02009 {
02010 QSortT(_resolutions, count, &compare_res);
02011 }