00001
00002
00005 #include "stdafx.h"
00006 #include "gfx_func.h"
00007 #include "variables.h"
00008 #include "spritecache.h"
00009 #include "fontcache.h"
00010 #include "genworld.h"
00011 #include "zoom_func.h"
00012 #include "blitter/factory.hpp"
00013 #include "video/video_driver.hpp"
00014 #include "strings_func.h"
00015 #include "settings_type.h"
00016 #include "core/alloc_func.hpp"
00017 #include "core/sort_func.hpp"
00018 #include "landscape_type.h"
00019 #include "network/network_func.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[COLOUR_END][8];
00048
00049 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL);
00050 static int ReallyDoDrawString(const char *string, int x, int y, TextColour colour, bool parse_string_also_when_clipped = false);
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 *_colour_remap_ptr;
00065 static byte _string_colourremap[3];
00066
00067 enum {
00068 DIRTY_BLOCK_HEIGHT = 8,
00069 DIRTY_BLOCK_WIDTH = 64,
00070 };
00071 static uint _dirty_bytes_per_line = 0;
00072 static byte *_dirty_blocks = NULL;
00073
00074 void GfxScroll(int left, int top, int width, int height, int xo, int yo)
00075 {
00076 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00077
00078 if (xo == 0 && yo == 0) return;
00079
00080 if (_cursor.visible) UndrawMouseCursor();
00081
00082 #ifdef ENABLE_NETWORK
00083 NetworkUndrawChatMessage();
00084 #endif
00085
00086 blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
00087
00088 _video_driver->MakeDirty(left, top, width, height);
00089 }
00090
00091
00106 void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
00107 {
00108 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00109 const DrawPixelInfo *dpi = _cur_dpi;
00110 void *dst;
00111 const int otop = top;
00112 const int oleft = left;
00113
00114 if (dpi->zoom != ZOOM_LVL_NORMAL) return;
00115 if (left > right || top > bottom) return;
00116 if (right < dpi->left || left >= dpi->left + dpi->width) return;
00117 if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
00118
00119 if ( (left -= dpi->left) < 0) left = 0;
00120 right = right - dpi->left + 1;
00121 if (right > dpi->width) right = dpi->width;
00122 right -= left;
00123 assert(right > 0);
00124
00125 if ( (top -= dpi->top) < 0) top = 0;
00126 bottom = bottom - dpi->top + 1;
00127 if (bottom > dpi->height) bottom = dpi->height;
00128 bottom -= top;
00129 assert(bottom > 0);
00130
00131 dst = blitter->MoveTo(dpi->dst_ptr, left, top);
00132
00133 switch (mode) {
00134 default:
00135 blitter->DrawRect(dst, right, bottom, (uint8)colour);
00136 break;
00137
00138 case FILLRECT_RECOLOUR:
00139 blitter->DrawColourMappingRect(dst, right, bottom, GB(colour, 0, PALETTE_WIDTH));
00140 break;
00141
00142 case FILLRECT_CHECKER: {
00143 byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
00144 do {
00145 for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8)colour);
00146 dst = blitter->MoveTo(dst, 0, 1);
00147 } while (--bottom > 0);
00148 break;
00149 }
00150 }
00151 }
00152
00153 void GfxDrawLine(int x, int y, int x2, int y2, int colour)
00154 {
00155 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00156 DrawPixelInfo *dpi = _cur_dpi;
00157
00158 x -= dpi->left;
00159 x2 -= dpi->left;
00160 y -= dpi->top;
00161 y2 -= dpi->top;
00162
00163
00164 if (x < 0 && x2 < 0) return;
00165 if (y < 0 && y2 < 0) return;
00166 if (x > dpi->width && x2 > dpi->width) return;
00167 if (y > dpi->height && y2 > dpi->height) return;
00168
00169 blitter->DrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour);
00170 }
00171
00172 void GfxDrawLineUnscaled(int x, int y, int x2, int y2, int colour)
00173 {
00174 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00175 DrawPixelInfo *dpi = _cur_dpi;
00176
00177 x -= dpi->left;
00178 x2 -= dpi->left;
00179 y -= dpi->top;
00180 y2 -= dpi->top;
00181
00182
00183 if (x < 0 && x2 < 0) return;
00184 if (y < 0 && y2 < 0) return;
00185 if (x > dpi->width && x2 > dpi->width) return;
00186 if (y > dpi->height && y2 > dpi->height) return;
00187
00188 blitter->DrawLine(dpi->dst_ptr, UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
00189 UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
00190 UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), colour);
00191 }
00192
00206 void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
00207 {
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223 static const byte colour = 255;
00224
00225 GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
00226 GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
00227 GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
00228
00229 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
00230 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
00231 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
00232 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
00233 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
00234 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
00235 }
00236
00241 static void SetColourRemap(TextColour colour)
00242 {
00243 if (colour == TC_INVALID) return;
00244
00245 if (colour & IS_PALETTE_COLOUR) {
00246 _string_colourremap[1] = colour & ~IS_PALETTE_COLOUR;
00247 _string_colourremap[2] = (_use_palette == PAL_DOS) ? 1 : 215;
00248 } else {
00249 _string_colourremap[1] = _string_colourmap[_use_palette][colour].text;
00250 _string_colourremap[2] = _string_colourmap[_use_palette][colour].shadow;
00251 }
00252 _colour_remap_ptr = _string_colourremap;
00253 }
00254
00255 #if !defined(WITH_ICU)
00256 static void HandleBiDiAndArabicShapes(char *text, const char *lastof) {}
00257 #else
00258 #include <unicode/ubidi.h>
00259 #include <unicode/ushape.h>
00260
00289 static void HandleBiDiAndArabicShapes(char *buffer, const char *lastof)
00290 {
00291 UChar input_output[DRAW_STRING_BUFFER];
00292 UChar intermediate[DRAW_STRING_BUFFER];
00293
00294 char *t = buffer;
00295 size_t length = 0;
00296 while (*t != '\0' && length < lengthof(input_output)) {
00297 WChar tmp;
00298 t += Utf8Decode(&tmp, t);
00299 input_output[length++] = tmp;
00300 }
00301 input_output[length] = 0;
00302
00303 UErrorCode err = U_ZERO_ERROR;
00304 UBiDi *para = ubidi_openSized((int32_t)length, 0, &err);
00305 if (para == NULL) return;
00306
00307 ubidi_setPara(para, input_output, (int32_t)length, _dynlang.text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, NULL, &err);
00308 ubidi_writeReordered(para, intermediate, (int32_t)length, 0, &err);
00309 length = u_shapeArabic(intermediate, (int32_t)length, input_output, lengthof(input_output), U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE, &err);
00310 ubidi_close(para);
00311
00312 if (U_FAILURE(err)) return;
00313
00314 t = buffer;
00315 for (size_t i = 0; i < length && t < (lastof - 4); i++) {
00316 t += Utf8Encode(t, input_output[i]);
00317 }
00318 *t = '\0';
00319 }
00320 #endif
00321
00322
00328 static int TruncateString(char *str, int maxw)
00329 {
00330 int w = 0;
00331 FontSize size = _cur_fontsize;
00332 int ddd, ddd_w;
00333
00334 WChar c;
00335 char *ddd_pos;
00336
00337 ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
00338
00339 for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
00340 if (IsPrintable(c)) {
00341 w += GetCharacterWidth(size, c);
00342
00343 if (w >= maxw) {
00344
00345
00346 for (int i = 0; *ddd_pos != '\0' && i < 3; i++, ddd_pos++) *ddd_pos = '.';
00347 *ddd_pos = '\0';
00348 return ddd_w;
00349 }
00350 } else {
00351 if (c == SCC_SETX) {
00352 w = *str;
00353 str++;
00354 } else if (c == SCC_SETXY) {
00355 w = *str;
00356 str += 2;
00357 } else if (c == SCC_TINYFONT) {
00358 size = FS_SMALL;
00359 ddd = GetCharacterWidth(size, '.') * 3;
00360 } else if (c == SCC_BIGFONT) {
00361 size = FS_LARGE;
00362 ddd = GetCharacterWidth(size, '.') * 3;
00363 }
00364 }
00365
00366
00367 if (w + ddd < maxw) {
00368 ddd_w = w + ddd;
00369 ddd_pos = str;
00370 }
00371 }
00372
00373 return w;
00374 }
00375
00386 static inline int TruncateStringID(StringID src, char *dest, int maxw, const char *last)
00387 {
00388 GetString(dest, src, last);
00389 return TruncateString(dest, maxw);
00390 }
00391
00402 int DrawString(int x, int y, StringID str, TextColour colour)
00403 {
00404 char buffer[DRAW_STRING_BUFFER];
00405
00406 GetString(buffer, str, lastof(buffer));
00407 HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00408 return ReallyDoDrawString(buffer, x, y, colour);
00409 }
00410
00422 int DrawStringTruncated(int x, int y, StringID str, TextColour colour, uint maxw)
00423 {
00424 char buffer[DRAW_STRING_BUFFER];
00425 TruncateStringID(str, buffer, maxw, lastof(buffer));
00426 HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00427 return ReallyDoDrawString(buffer, x, y, colour);
00428 }
00429
00440 int DrawStringRightAligned(int x, int y, StringID str, TextColour colour)
00441 {
00442 char buffer[DRAW_STRING_BUFFER];
00443 int w;
00444
00445 GetString(buffer, str, lastof(buffer));
00446 HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00447
00448 w = GetStringBoundingBox(buffer).width;
00449 ReallyDoDrawString(buffer, x - w, y, colour);
00450
00451 return w;
00452 }
00453
00463 void DrawStringRightAlignedTruncated(int x, int y, StringID str, TextColour colour, uint maxw)
00464 {
00465 char buffer[DRAW_STRING_BUFFER];
00466
00467 TruncateStringID(str, buffer, maxw, lastof(buffer));
00468 HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00469 ReallyDoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, colour);
00470 }
00471
00480 void DrawStringRightAlignedUnderline(int x, int y, StringID str, TextColour colour)
00481 {
00482 int w = DrawStringRightAligned(x, y, str, colour);
00483 GfxFillRect(x - w, y + 10, x, y + 10, _string_colourremap[1]);
00484 }
00485
00496 int DrawStringCentered(int x, int y, StringID str, TextColour colour)
00497 {
00498 char buffer[DRAW_STRING_BUFFER];
00499 int w;
00500
00501 GetString(buffer, str, lastof(buffer));
00502 HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00503
00504 w = GetStringBoundingBox(buffer).width;
00505 ReallyDoDrawString(buffer, x - w / 2, y, colour);
00506
00507 return w;
00508 }
00509
00521 int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, TextColour colour)
00522 {
00523 char buffer[DRAW_STRING_BUFFER];
00524 TruncateStringID(str, buffer, xr - xl, lastof(buffer));
00525 HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00526
00527 int w = GetStringBoundingBox(buffer).width;
00528 return ReallyDoDrawString(buffer, (xl + xr - w) / 2, y, colour);
00529 }
00530
00541 int DoDrawStringCentered(int x, int y, const char *str, TextColour colour)
00542 {
00543 char buffer[DRAW_STRING_BUFFER];
00544 strecpy(buffer, str, lastof(buffer));
00545 HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00546
00547 int w = GetStringBoundingBox(buffer).width;
00548 ReallyDoDrawString(buffer, x - w / 2, y, colour);
00549 return w;
00550 }
00551
00560 void DrawStringCenterUnderline(int x, int y, StringID str, TextColour colour)
00561 {
00562 int w = DrawStringCentered(x, y, str, colour);
00563 GfxFillRect(x - (w >> 1), y + 10, x - (w >> 1) + w, y + 10, _string_colourremap[1]);
00564 }
00565
00575 void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, TextColour colour)
00576 {
00577 int w = DrawStringCenteredTruncated(xl, xr, y, str, colour);
00578 GfxFillRect((xl + xr - w) / 2, y + 10, (xl + xr + w) / 2, y + 10, _string_colourremap[1]);
00579 }
00580
00599 uint32 FormatStringLinebreaks(char *str, int maxw)
00600 {
00601 FontSize size = _cur_fontsize;
00602 int num = 0;
00603
00604 assert(maxw > 0);
00605
00606 for (;;) {
00607 char *last_space = NULL;
00608 int w = 0;
00609
00610 for (;;) {
00611 WChar c = Utf8Consume((const char **)&str);
00612
00613 if (IsWhitespace(c)) last_space = str;
00614
00615 if (IsPrintable(c)) {
00616 w += GetCharacterWidth(size, c);
00617
00618
00619
00620
00621
00622 if (w > maxw) {
00623 if (last_space == NULL) {
00624 *Utf8PrevChar(str) = '\0';
00625 return num + (size << 16);
00626 }
00627 str = last_space;
00628 break;
00629 }
00630 } else {
00631 switch (c) {
00632 case '\0': return num + (size << 16); break;
00633 case SCC_SETX: str++; break;
00634 case SCC_SETXY: str += 2; break;
00635 case SCC_TINYFONT: size = FS_SMALL; break;
00636 case SCC_BIGFONT: size = FS_LARGE; break;
00637 case '\n': goto end_of_inner_loop;
00638 }
00639 }
00640 }
00641 end_of_inner_loop:
00642
00643
00644
00645 num++;
00646 char *s = Utf8PrevChar(str);
00647 *s++ = '\0';
00648
00649
00650 if (str - s >= 1) {
00651 for (; str[-1] != '\0';) *s++ = *str++;
00652 }
00653 }
00654 }
00655
00656
00663 static int GetMultilineStringHeight(const char *src, int num)
00664 {
00665 int maxy = 0;
00666 int y = 0;
00667 int fh = GetCharacterHeight(_cur_fontsize);
00668
00669 for (;;) {
00670 WChar c = Utf8Consume(&src);
00671
00672 switch (c) {
00673 case 0: y += fh; if (--num < 0) return maxy; break;
00674 case '\n': y += fh; break;
00675 case SCC_SETX: src++; break;
00676 case SCC_SETXY: src++; y = (int)*src++; break;
00677 case SCC_TINYFONT: fh = GetCharacterHeight(FS_SMALL); break;
00678 case SCC_BIGFONT: fh = GetCharacterHeight(FS_LARGE); break;
00679 default: maxy = max<int>(maxy, y + fh); break;
00680 }
00681 }
00682 }
00683
00684
00690 int GetStringHeight(StringID str, int maxw)
00691 {
00692 char buffer[DRAW_STRING_BUFFER];
00693
00694 GetString(buffer, str, lastof(buffer));
00695
00696 uint32 tmp = FormatStringLinebreaks(buffer, maxw);
00697
00698 return GetMultilineStringHeight(buffer, GB(tmp, 0, 16));
00699 }
00700
00701
00707 void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
00708 {
00709 char buffer[DRAW_STRING_BUFFER];
00710 uint32 tmp;
00711 int num, mt;
00712 const char *src;
00713 WChar c;
00714
00715 GetString(buffer, str, lastof(buffer));
00716
00717 tmp = FormatStringLinebreaks(buffer, maxw);
00718 num = GB(tmp, 0, 16);
00719
00720 mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
00721
00722 y -= (mt >> 1) * num;
00723
00724 src = buffer;
00725
00726 for (;;) {
00727 char buf2[DRAW_STRING_BUFFER];
00728 strecpy(buf2, src, lastof(buf2));
00729 HandleBiDiAndArabicShapes(buf2, lastof(buf2));
00730 int w = GetStringBoundingBox(buf2).width;
00731 ReallyDoDrawString(buf2, x - (w >> 1), y, TC_FROMSTRING, true);
00732 _cur_fontsize = _last_fontsize;
00733
00734 for (;;) {
00735 c = Utf8Consume(&src);
00736 if (c == 0) {
00737 y += mt;
00738 if (--num < 0) {
00739 _cur_fontsize = FS_NORMAL;
00740 return;
00741 }
00742 break;
00743 } else if (c == SCC_SETX) {
00744 src++;
00745 } else if (c == SCC_SETXY) {
00746 src += 2;
00747 }
00748 }
00749 }
00750 }
00751
00752
00753 uint DrawStringMultiLine(int x, int y, StringID str, int maxw, int maxh)
00754 {
00755 char buffer[DRAW_STRING_BUFFER];
00756 uint32 tmp;
00757 int num, mt;
00758 uint total_height;
00759 const char *src;
00760 WChar c;
00761
00762 GetString(buffer, str, lastof(buffer));
00763
00764 tmp = FormatStringLinebreaks(buffer, maxw);
00765 num = GB(tmp, 0, 16);
00766
00767 mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
00768 total_height = (num + 1) * mt;
00769
00770 if (maxh != -1 && (int)total_height > maxh) {
00771
00772 if (maxh < mt) return 0;
00773
00774 num = maxh / mt - 1;
00775 total_height = (num + 1) * mt;
00776 }
00777
00778 src = buffer;
00779
00780 for (;;) {
00781 char buf2[DRAW_STRING_BUFFER];
00782 strecpy(buf2, src, lastof(buf2));
00783 HandleBiDiAndArabicShapes(buf2, lastof(buf2));
00784 ReallyDoDrawString(buf2, x, y, TC_FROMSTRING, true);
00785 _cur_fontsize = _last_fontsize;
00786
00787 for (;;) {
00788 c = Utf8Consume(&src);
00789 if (c == 0) {
00790 y += mt;
00791 if (--num < 0) {
00792 _cur_fontsize = FS_NORMAL;
00793 return total_height;
00794 }
00795 break;
00796 } else if (c == SCC_SETX) {
00797 src++;
00798 } else if (c == SCC_SETXY) {
00799 src += 2;
00800 }
00801 }
00802 }
00803 }
00804
00812 Dimension GetStringBoundingBox(const char *str)
00813 {
00814 FontSize size = _cur_fontsize;
00815 Dimension br;
00816 int max_width;
00817 WChar c;
00818
00819 br.width = br.height = max_width = 0;
00820 for (;;) {
00821 c = Utf8Consume(&str);
00822 if (c == 0) break;
00823 if (IsPrintable(c)) {
00824 br.width += GetCharacterWidth(size, c);
00825 } else {
00826 switch (c) {
00827 case SCC_SETX: br.width += (byte)*str++; break;
00828 case SCC_SETXY:
00829 br.width += (byte)*str++;
00830 br.height += (byte)*str++;
00831 break;
00832 case SCC_TINYFONT: size = FS_SMALL; break;
00833 case SCC_BIGFONT: size = FS_LARGE; break;
00834 case '\n':
00835 br.height += GetCharacterHeight(size);
00836 if (br.width > max_width) max_width = br.width;
00837 br.width = 0;
00838 break;
00839 }
00840 }
00841 }
00842 br.height += GetCharacterHeight(size);
00843
00844 br.width = max(br.width, max_width);
00845 return br;
00846 }
00847
00855 void DrawCharCentered(WChar c, int x, int y, TextColour colour)
00856 {
00857 SetColourRemap(colour);
00858 GfxMainBlitter(GetGlyph(FS_NORMAL, c), x - GetCharacterWidth(FS_NORMAL, c) / 2, y, BM_COLOUR_REMAP);
00859 }
00860
00878 int DoDrawString(const char *string, int x, int y, TextColour colour, bool parse_string_also_when_clipped)
00879 {
00880 char buffer[DRAW_STRING_BUFFER];
00881 strecpy(buffer, string, lastof(buffer));
00882 HandleBiDiAndArabicShapes(buffer, lastof(buffer));
00883
00884 return ReallyDoDrawString(buffer, x, y, colour, parse_string_also_when_clipped);
00885 }
00886
00904 static int ReallyDoDrawString(const char *string, int x, int y, TextColour colour, bool parse_string_also_when_clipped)
00905 {
00906 DrawPixelInfo *dpi = _cur_dpi;
00907 FontSize size = _cur_fontsize;
00908 WChar c;
00909 int xo = x, yo = y;
00910
00911 TextColour previous_colour = colour;
00912
00913 if (!parse_string_also_when_clipped) {
00914
00915
00916 if (x >= dpi->left + dpi->width || y >= dpi->top + dpi->height) return x;
00917
00918 if (colour != TC_INVALID) {
00919 switch_colour:;
00920 SetColourRemap(colour);
00921 }
00922 }
00923
00924 check_bounds:
00925 if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
00926 skip_char:;
00927 for (;;) {
00928 c = Utf8Consume(&string);
00929 if (!IsPrintable(c)) goto skip_cont;
00930 }
00931 }
00932
00933 for (;;) {
00934 c = Utf8Consume(&string);
00935 skip_cont:;
00936 if (c == 0) {
00937 _last_fontsize = size;
00938 return x;
00939 }
00940 if (IsPrintable(c)) {
00941 if (x >= dpi->left + dpi->width) goto skip_char;
00942 if (x + 26 >= dpi->left) {
00943 GfxMainBlitter(GetGlyph(size, c), x, y, BM_COLOUR_REMAP);
00944 }
00945 x += GetCharacterWidth(size, c);
00946 } else if (c == '\n') {
00947 x = xo;
00948 y += GetCharacterHeight(size);
00949 goto check_bounds;
00950 } else if (c >= SCC_BLUE && c <= SCC_BLACK) {
00951 previous_colour = colour;
00952 colour = (TextColour)(c - SCC_BLUE);
00953 goto switch_colour;
00954 } else if (c == SCC_PREVIOUS_COLOUR) {
00955 Swap(colour, previous_colour);
00956 goto switch_colour;
00957 } else if (c == SCC_SETX) {
00958 x = xo + (byte)*string++;
00959 } else if (c == SCC_SETXY) {
00960 x = xo + (byte)*string++;
00961 y = yo + (byte)*string++;
00962 } else if (c == SCC_TINYFONT) {
00963 size = FS_SMALL;
00964 } else if (c == SCC_BIGFONT) {
00965 size = FS_LARGE;
00966 } else {
00967 DEBUG(misc, 0, "[utf8] unknown string command character %d", c);
00968 }
00969 }
00970 }
00971
00984 int DoDrawStringTruncated(const char *str, int x, int y, TextColour colour, uint maxw)
00985 {
00986 char buffer[DRAW_STRING_BUFFER];
00987 strecpy(buffer, str, lastof(buffer));
00988 TruncateString(buffer, maxw);
00989 return DoDrawString(buffer, x, y, colour);
00990 }
00991
01000 void DrawSprite(SpriteID img, SpriteID pal, int x, int y, const SubSprite *sub)
01001 {
01002 if (HasBit(img, PALETTE_MODIFIER_TRANSPARENT)) {
01003 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1;
01004 GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH), ST_NORMAL), x, y, BM_TRANSPARENT, sub);
01005 } else if (pal != PAL_NONE) {
01006 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1;
01007 GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH), ST_NORMAL), x, y, BM_COLOUR_REMAP, sub);
01008 } else {
01009 GfxMainBlitter(GetSprite(GB(img, 0, SPRITE_WIDTH), ST_NORMAL), x, y, BM_NORMAL, sub);
01010 }
01011 }
01012
01013 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub)
01014 {
01015 const DrawPixelInfo *dpi = _cur_dpi;
01016 Blitter::BlitterParams bp;
01017
01018
01019 int clip_left = (sub != NULL ? max(0, -sprite->x_offs + sub->left ) : 0);
01020 int clip_top = (sub != NULL ? max(0, -sprite->y_offs + sub->top ) : 0);
01021 int clip_right = (sub != NULL ? max(0, sprite->width - (-sprite->x_offs + sub->right + 1)) : 0);
01022 int clip_bottom = (sub != NULL ? max(0, sprite->height - (-sprite->y_offs + sub->bottom + 1)) : 0);
01023
01024 if (clip_left + clip_right >= sprite->width) return;
01025 if (clip_top + clip_bottom >= sprite->height) return;
01026
01027
01028 x += sprite->x_offs;
01029 y += sprite->y_offs;
01030
01031
01032 bp.sprite = sprite->data;
01033 bp.sprite_width = sprite->width;
01034 bp.sprite_height = sprite->height;
01035 bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, dpi->zoom);
01036 bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, dpi->zoom);
01037 bp.top = 0;
01038 bp.left = 0;
01039 bp.skip_left = UnScaleByZoomLower(clip_left, dpi->zoom);
01040 bp.skip_top = UnScaleByZoomLower(clip_top, dpi->zoom);
01041
01042 x += ScaleByZoom(bp.skip_left, dpi->zoom);
01043 y += ScaleByZoom(bp.skip_top, dpi->zoom);
01044
01045 bp.dst = dpi->dst_ptr;
01046 bp.pitch = dpi->pitch;
01047 bp.remap = _colour_remap_ptr;
01048
01049 assert(sprite->width > 0);
01050 assert(sprite->height > 0);
01051
01052 if (bp.width <= 0) return;
01053 if (bp.height <= 0) return;
01054
01055 y -= dpi->top;
01056
01057 if (y < 0) {
01058 bp.height -= -UnScaleByZoom(y, dpi->zoom);
01059 if (bp.height <= 0) return;
01060 bp.skip_top += -UnScaleByZoom(y, dpi->zoom);
01061 y = 0;
01062 } else {
01063 bp.top = UnScaleByZoom(y, dpi->zoom);
01064 }
01065
01066
01067 y += ScaleByZoom(bp.height, dpi->zoom) - dpi->height;
01068 if (y > 0) {
01069 bp.height -= UnScaleByZoom(y, dpi->zoom);
01070 if (bp.height <= 0) return;
01071 }
01072
01073 x -= dpi->left;
01074
01075 if (x < 0) {
01076 bp.width -= -UnScaleByZoom(x, dpi->zoom);
01077 if (bp.width <= 0) return;
01078 bp.skip_left += -UnScaleByZoom(x, dpi->zoom);
01079 x = 0;
01080 } else {
01081 bp.left = UnScaleByZoom(x, dpi->zoom);
01082 }
01083
01084
01085 x += ScaleByZoom(bp.width, dpi->zoom) - dpi->width;
01086 if (x > 0) {
01087 bp.width -= UnScaleByZoom(x, dpi->zoom);
01088 if (bp.width <= 0) return;
01089 }
01090
01091 assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, dpi->zoom));
01092 assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, dpi->zoom));
01093
01094 BlitterFactoryBase::GetCurrentBlitter()->Draw(&bp, mode, dpi->zoom);
01095 }
01096
01097 void DoPaletteAnimations();
01098
01099 void GfxInitPalettes()
01100 {
01101 memcpy(_cur_palette, _palettes[_use_palette], sizeof(_cur_palette));
01102
01103 DoPaletteAnimations();
01104 _pal_first_dirty = 0;
01105 _pal_count_dirty = 256;
01106 }
01107
01108 #define EXTR(p, q) (((uint16)(_palette_animation_counter * (p)) * (q)) >> 16)
01109 #define EXTR2(p, q) (((uint16)(~_palette_animation_counter * (p)) * (q)) >> 16)
01110
01111 void DoPaletteAnimations()
01112 {
01113 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01114 const Colour *s;
01115 const ExtraPaletteValues *ev = &_extra_palette_values;
01116
01117
01118
01119 const int colour_rotation_amount = (_use_palette == PAL_DOS) ? PALETTE_ANIM_SIZE_DOS : PALETTE_ANIM_SIZE_WIN;
01120 Colour old_val[PALETTE_ANIM_SIZE_DOS];
01121 const int oldval_size = colour_rotation_amount * sizeof(*old_val);
01122 const uint old_tc = _palette_animation_counter;
01123 uint i;
01124 uint j;
01125
01126 if (blitter != NULL && blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_NONE) {
01127 _palette_animation_counter = 0;
01128 }
01129
01130 Colour *palette_pos = &_cur_palette[PALETTE_ANIM_SIZE_START];
01131
01132
01133 memcpy(old_val, palette_pos, oldval_size);
01134
01135
01136 s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->dark_water_TOY : ev->dark_water;
01137 j = EXTR(320, 5);
01138 for (i = 0; i != 5; i++) {
01139 *palette_pos++ = s[j];
01140 j++;
01141 if (j == 5) j = 0;
01142 }
01143
01144
01145 s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->glitter_water_TOY : ev->glitter_water;
01146 j = EXTR(128, 15);
01147 for (i = 0; i != 5; i++) {
01148 *palette_pos++ = s[j];
01149 j += 3;
01150 if (j >= 15) j -= 15;
01151 }
01152
01153
01154 s = ev->fizzy_drink;
01155 j = EXTR2(512, 5);
01156 for (i = 0; i != 5; i++) {
01157 *palette_pos++ = s[j];
01158 j++;
01159 if (j == 5) j = 0;
01160 }
01161
01162
01163 s = ev->oil_ref;
01164 j = EXTR2(512, 7);
01165 for (i = 0; i != 7; i++) {
01166 *palette_pos++ = s[j];
01167 j++;
01168 if (j == 7) j = 0;
01169 }
01170
01171
01172 {
01173 byte i = (_palette_animation_counter >> 1) & 0x7F;
01174 byte v;
01175
01176 (v = 255, i < 0x3f) ||
01177 (v = 128, i < 0x4A || i >= 0x75) ||
01178 (v = 20);
01179 palette_pos->r = v;
01180 palette_pos->g = 0;
01181 palette_pos->b = 0;
01182 palette_pos++;
01183
01184 i ^= 0x40;
01185 (v = 255, i < 0x3f) ||
01186 (v = 128, i < 0x4A || i >= 0x75) ||
01187 (v = 20);
01188 palette_pos->r = v;
01189 palette_pos->g = 0;
01190 palette_pos->b = 0;
01191 palette_pos++;
01192 }
01193
01194
01195 s = ev->lighthouse;
01196 j = EXTR(256, 4);
01197 for (i = 0; i != 4; i++) {
01198 *palette_pos++ = s[j];
01199 j++;
01200 if (j == 4) j = 0;
01201 }
01202
01203
01204 if (_use_palette == PAL_DOS) {
01205
01206 s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->dark_water_TOY : ev->dark_water;
01207 j = EXTR(320, 5);
01208 for (i = 0; i != 5; i++) {
01209 *palette_pos++ = s[j];
01210 j++;
01211 if (j == 5) j = 0;
01212 }
01213
01214
01215 s = (_settings_game.game_creation.landscape == LT_TOYLAND) ? ev->glitter_water_TOY : ev->glitter_water;
01216 j = EXTR(128, 15);
01217 for (i = 0; i != 5; i++) {
01218 *palette_pos++ = s[j];
01219 j += 3;
01220 if (j >= 15) j -= 15;
01221 }
01222 }
01223
01224 if (blitter != NULL && blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_NONE) {
01225 _palette_animation_counter = old_tc;
01226 } else {
01227 if (memcmp(old_val, &_cur_palette[PALETTE_ANIM_SIZE_START], oldval_size) != 0) {
01228
01229 _pal_first_dirty = PALETTE_ANIM_SIZE_START;
01230 _pal_count_dirty = colour_rotation_amount;
01231 }
01232 }
01233 }
01234
01235
01237 void LoadStringWidthTable()
01238 {
01239 uint i;
01240
01241
01242 for (i = 0; i != 224; i++) {
01243 _stringwidth_table[FS_NORMAL][i] = GetGlyphWidth(FS_NORMAL, i + 32);
01244 }
01245
01246
01247 for (i = 0; i != 224; i++) {
01248 _stringwidth_table[FS_SMALL][i] = GetGlyphWidth(FS_SMALL, i + 32);
01249 }
01250
01251
01252 for (i = 0; i != 224; i++) {
01253 _stringwidth_table[FS_LARGE][i] = GetGlyphWidth(FS_LARGE, i + 32);
01254 }
01255 }
01256
01263 byte GetCharacterWidth(FontSize size, WChar key)
01264 {
01265
01266 if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
01267
01268 return GetGlyphWidth(size, key);
01269 }
01270
01271
01272 void ScreenSizeChanged()
01273 {
01274 _dirty_bytes_per_line = (_screen.width + DIRTY_BLOCK_WIDTH - 1) / DIRTY_BLOCK_WIDTH;
01275 _dirty_blocks = ReallocT<byte>(_dirty_blocks, _dirty_bytes_per_line * ((_screen.height + DIRTY_BLOCK_HEIGHT - 1) / DIRTY_BLOCK_HEIGHT));
01276
01277
01278 if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
01279 if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
01280
01281
01282 _cursor.visible = false;
01283 }
01284
01285 void UndrawMouseCursor()
01286 {
01287 if (_cursor.visible) {
01288 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01289 _cursor.visible = false;
01290 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);
01291 _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
01292 }
01293 }
01294
01295 void DrawMouseCursor()
01296 {
01297 #if defined(WINCE)
01298
01299 return;
01300 #endif
01301
01302 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01303 int x;
01304 int y;
01305 int w;
01306 int h;
01307
01308
01309 if (!_cursor.in_window) return;
01310
01311
01312 if (_cursor.visible) {
01313 if (!_cursor.dirty) return;
01314 UndrawMouseCursor();
01315 }
01316
01317 w = _cursor.size.x;
01318 x = _cursor.pos.x + _cursor.offs.x + _cursor.short_vehicle_offset;
01319 if (x < 0) {
01320 w += x;
01321 x = 0;
01322 }
01323 if (w > _screen.width - x) w = _screen.width - x;
01324 if (w <= 0) return;
01325 _cursor.draw_pos.x = x;
01326 _cursor.draw_size.x = w;
01327
01328 h = _cursor.size.y;
01329 y = _cursor.pos.y + _cursor.offs.y;
01330 if (y < 0) {
01331 h += y;
01332 y = 0;
01333 }
01334 if (h > _screen.height - y) h = _screen.height - y;
01335 if (h <= 0) return;
01336 _cursor.draw_pos.y = y;
01337 _cursor.draw_size.y = h;
01338
01339 assert(blitter->BufferSize(w, h) < (int)sizeof(_cursor_backup));
01340
01341
01342 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);
01343
01344
01345 _cur_dpi = &_screen;
01346 DrawSprite(_cursor.sprite, _cursor.pal, _cursor.pos.x + _cursor.short_vehicle_offset, _cursor.pos.y);
01347
01348 _video_driver->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
01349
01350 _cursor.visible = true;
01351 _cursor.dirty = false;
01352 }
01353
01354 void RedrawScreenRect(int left, int top, int right, int bottom)
01355 {
01356 assert(right <= _screen.width && bottom <= _screen.height);
01357 if (_cursor.visible) {
01358 if (right > _cursor.draw_pos.x &&
01359 left < _cursor.draw_pos.x + _cursor.draw_size.x &&
01360 bottom > _cursor.draw_pos.y &&
01361 top < _cursor.draw_pos.y + _cursor.draw_size.y) {
01362 UndrawMouseCursor();
01363 }
01364 }
01365
01366 #ifdef ENABLE_NETWORK
01367 NetworkUndrawChatMessage();
01368 #endif
01369
01370 DrawOverlappedWindowForAll(left, top, right, bottom);
01371
01372 _video_driver->MakeDirty(left, top, right - left, bottom - top);
01373 }
01374
01380 void DrawDirtyBlocks()
01381 {
01382 byte *b = _dirty_blocks;
01383 const int w = Align(_screen.width, DIRTY_BLOCK_WIDTH);
01384 const int h = Align(_screen.height, DIRTY_BLOCK_HEIGHT);
01385 int x;
01386 int y;
01387
01388 if (IsGeneratingWorld() && !IsGeneratingWorldReadyForPaint()) return;
01389
01390 y = 0;
01391 do {
01392 x = 0;
01393 do {
01394 if (*b != 0) {
01395 int left;
01396 int top;
01397 int right = x + DIRTY_BLOCK_WIDTH;
01398 int bottom = y;
01399 byte *p = b;
01400 int h2;
01401
01402
01403 do {
01404 *p = 0;
01405 p += _dirty_bytes_per_line;
01406 bottom += DIRTY_BLOCK_HEIGHT;
01407 } while (bottom != h && *p != 0);
01408
01409
01410 h2 = (bottom - y) / DIRTY_BLOCK_HEIGHT;
01411 assert(h2 > 0);
01412 p = b;
01413
01414 while (right != w) {
01415 byte *p2 = ++p;
01416 int h = h2;
01417
01418 do {
01419 if (!*p2) goto no_more_coalesc;
01420 p2 += _dirty_bytes_per_line;
01421 } while (--h != 0);
01422
01423
01424
01425 right += DIRTY_BLOCK_WIDTH;
01426
01427 h = h2;
01428 p2 = p;
01429 do {
01430 *p2 = 0;
01431 p2 += _dirty_bytes_per_line;
01432 } while (--h != 0);
01433 }
01434 no_more_coalesc:
01435
01436 left = x;
01437 top = y;
01438
01439 if (left < _invalid_rect.left ) left = _invalid_rect.left;
01440 if (top < _invalid_rect.top ) top = _invalid_rect.top;
01441 if (right > _invalid_rect.right ) right = _invalid_rect.right;
01442 if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
01443
01444 if (left < right && top < bottom) {
01445 RedrawScreenRect(left, top, right, bottom);
01446 }
01447
01448 }
01449 } while (b++, (x += DIRTY_BLOCK_WIDTH) != w);
01450 } while (b += -(w / DIRTY_BLOCK_WIDTH) + _dirty_bytes_per_line, (y += DIRTY_BLOCK_HEIGHT) != h);
01451
01452 _invalid_rect.left = w;
01453 _invalid_rect.top = h;
01454 _invalid_rect.right = 0;
01455 _invalid_rect.bottom = 0;
01456
01457
01458
01459 if (IsGeneratingWorld() && IsGeneratingWorldReadyForPaint()) {
01460 SetGeneratingWorldPaintStatus(false);
01461 }
01462 }
01463
01479 void SetDirtyBlocks(int left, int top, int right, int bottom)
01480 {
01481 byte *b;
01482 int width;
01483 int height;
01484
01485 if (left < 0) left = 0;
01486 if (top < 0) top = 0;
01487 if (right > _screen.width) right = _screen.width;
01488 if (bottom > _screen.height) bottom = _screen.height;
01489
01490 if (left >= right || top >= bottom) return;
01491
01492 if (left < _invalid_rect.left ) _invalid_rect.left = left;
01493 if (top < _invalid_rect.top ) _invalid_rect.top = top;
01494 if (right > _invalid_rect.right ) _invalid_rect.right = right;
01495 if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
01496
01497 left /= DIRTY_BLOCK_WIDTH;
01498 top /= DIRTY_BLOCK_HEIGHT;
01499
01500 b = _dirty_blocks + top * _dirty_bytes_per_line + left;
01501
01502 width = ((right - 1) / DIRTY_BLOCK_WIDTH) - left + 1;
01503 height = ((bottom - 1) / DIRTY_BLOCK_HEIGHT) - top + 1;
01504
01505 assert(width > 0 && height > 0);
01506
01507 do {
01508 int i = width;
01509
01510 do b[--i] = 0xFF; while (i);
01511
01512 b += _dirty_bytes_per_line;
01513 } while (--height != 0);
01514 }
01515
01521 void MarkWholeScreenDirty()
01522 {
01523 SetDirtyBlocks(0, 0, _screen.width, _screen.height);
01524 }
01525
01538 bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
01539 {
01540 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
01541 const DrawPixelInfo *o = _cur_dpi;
01542
01543 n->zoom = ZOOM_LVL_NORMAL;
01544
01545 assert(width > 0);
01546 assert(height > 0);
01547
01548 if ((left -= o->left) < 0) {
01549 width += left;
01550 if (width <= 0) return false;
01551 n->left = -left;
01552 left = 0;
01553 } else {
01554 n->left = 0;
01555 }
01556
01557 if (width > o->width - left) {
01558 width = o->width - left;
01559 if (width <= 0) return false;
01560 }
01561 n->width = width;
01562
01563 if ((top -= o->top) < 0) {
01564 height += top;
01565 if (height <= 0) return false;
01566 n->top = -top;
01567 top = 0;
01568 } else {
01569 n->top = 0;
01570 }
01571
01572 n->dst_ptr = blitter->MoveTo(o->dst_ptr, left, top);
01573 n->pitch = o->pitch;
01574
01575 if (height > o->height - top) {
01576 height = o->height - top;
01577 if (height <= 0) return false;
01578 }
01579 n->height = height;
01580
01581 return true;
01582 }
01583
01584 static void SetCursorSprite(SpriteID cursor, SpriteID pal)
01585 {
01586 CursorVars *cv = &_cursor;
01587 const Sprite *p;
01588
01589 if (cv->sprite == cursor) return;
01590
01591 p = GetSprite(GB(cursor, 0, SPRITE_WIDTH), ST_NORMAL);
01592 cv->sprite = cursor;
01593 cv->pal = pal;
01594 cv->size.y = p->height;
01595 cv->size.x = p->width;
01596 cv->offs.x = p->x_offs;
01597 cv->offs.y = p->y_offs;
01598
01599 cv->dirty = true;
01600 cv->short_vehicle_offset = 0;
01601 }
01602
01603 static void SwitchAnimatedCursor()
01604 {
01605 const AnimCursor *cur = _cursor.animate_cur;
01606
01607 if (cur == NULL || cur->sprite == AnimCursor::LAST) cur = _cursor.animate_list;
01608
01609 SetCursorSprite(cur->sprite, _cursor.pal);
01610
01611 _cursor.animate_timeout = cur->display_time;
01612 _cursor.animate_cur = cur + 1;
01613 }
01614
01615 void CursorTick()
01616 {
01617 if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0)
01618 SwitchAnimatedCursor();
01619 }
01620
01621 void SetMouseCursor(SpriteID sprite, SpriteID pal)
01622 {
01623
01624 _cursor.animate_timeout = 0;
01625
01626 SetCursorSprite(sprite, pal);
01627 }
01628
01629 void SetAnimatedMouseCursor(const AnimCursor *table)
01630 {
01631 _cursor.animate_list = table;
01632 _cursor.animate_cur = NULL;
01633 _cursor.pal = PAL_NONE;
01634 SwitchAnimatedCursor();
01635 }
01636
01637 bool ChangeResInGame(int width, int height)
01638 {
01639 return (_screen.width == width && _screen.height == height) || _video_driver->ChangeResolution(width, height);
01640 }
01641
01642 bool ToggleFullScreen(bool fs)
01643 {
01644 bool result = _video_driver->ToggleFullscreen(fs);
01645 if (_fullscreen != fs && _num_resolutions == 0) {
01646 DEBUG(driver, 0, "Could not find a suitable fullscreen resolution");
01647 }
01648 return result;
01649 }
01650
01651 static int CDECL compare_res(const Dimension *pa, const Dimension *pb)
01652 {
01653 int x = pa->width - pb->width;
01654 if (x != 0) return x;
01655 return pa->height - pb->height;
01656 }
01657
01658 void SortResolutions(int count)
01659 {
01660 QSortT(_resolutions, count, &compare_res);
01661 }