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