src/gfx.cpp
changeset 5835 e0ff603ae0b7
parent 5818 f0cccdf91665
child 5650 aefc131bf5ce
equal deleted inserted replaced
5834:7bf92d5a5a0f 5835:e0ff603ae0b7
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "functions.h"
       
     6 #include "macros.h"
       
     7 #include "spritecache.h"
       
     8 #include "strings.h"
       
     9 #include "string.h"
       
    10 #include "gfx.h"
       
    11 #include "table/palettes.h"
       
    12 #include "table/sprites.h"
       
    13 #include "hal.h"
       
    14 #include "variables.h"
       
    15 #include "table/control_codes.h"
       
    16 #include "fontcache.h"
       
    17 #include "genworld.h"
       
    18 #include "debug.h"
       
    19 
       
    20 #ifdef _DEBUG
       
    21 bool _dbg_screen_rect;
       
    22 #endif
       
    23 
       
    24 Colour _cur_palette[256];
       
    25 byte _stringwidth_table[FS_END][224];
       
    26 
       
    27 typedef enum BlitterModes {
       
    28 	BM_NORMAL,
       
    29 	BM_COLOUR_REMAP,
       
    30 	BM_TRANSPARENT,
       
    31 } BlitterMode;
       
    32 
       
    33 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode);
       
    34 
       
    35 FontSize _cur_fontsize;
       
    36 static FontSize _last_fontsize;
       
    37 static Pixel _cursor_backup[64 * 64];
       
    38 static Rect _invalid_rect;
       
    39 static const byte *_color_remap_ptr;
       
    40 static byte _string_colorremap[3];
       
    41 
       
    42 #define DIRTY_BYTES_PER_LINE (MAX_SCREEN_WIDTH / 64)
       
    43 static byte _dirty_blocks[DIRTY_BYTES_PER_LINE * MAX_SCREEN_HEIGHT / 8];
       
    44 
       
    45 void memcpy_pitch(void *dst, void *src, int w, int h, int srcpitch, int dstpitch)
       
    46 {
       
    47 	byte *dstp = (byte*)dst;
       
    48 	byte *srcp = (byte*)src;
       
    49 
       
    50 	assert(h >= 0);
       
    51 	for (; h != 0; --h) {
       
    52 		memcpy(dstp, srcp, w);
       
    53 		dstp += dstpitch;
       
    54 		srcp += srcpitch;
       
    55 	}
       
    56 }
       
    57 
       
    58 void GfxScroll(int left, int top, int width, int height, int xo, int yo)
       
    59 {
       
    60 	const Pixel *src;
       
    61 	Pixel *dst;
       
    62 	int p;
       
    63 	int ht;
       
    64 
       
    65 	if (xo == 0 && yo == 0) return;
       
    66 
       
    67 	if (_cursor.visible) UndrawMouseCursor();
       
    68 	UndrawTextMessage();
       
    69 
       
    70 	p = _screen.pitch;
       
    71 
       
    72 	if (yo > 0) {
       
    73 		// Calculate pointers
       
    74 		dst = _screen.dst_ptr + (top + height - 1) * p + left;
       
    75 		src = dst - yo * p;
       
    76 
       
    77 		// Decrease height and increase top
       
    78 		top += yo;
       
    79 		height -= yo;
       
    80 		assert(height > 0);
       
    81 
       
    82 		// Adjust left & width
       
    83 		if (xo >= 0) {
       
    84 			dst += xo;
       
    85 			left += xo;
       
    86 			width -= xo;
       
    87 		} else {
       
    88 			src -= xo;
       
    89 			width += xo;
       
    90 		}
       
    91 
       
    92 		for (ht = height; ht > 0; --ht) {
       
    93 			memcpy(dst, src, width);
       
    94 			src -= p;
       
    95 			dst -= p;
       
    96 		}
       
    97 	} else {
       
    98 		// Calculate pointers
       
    99 		dst = _screen.dst_ptr + top * p + left;
       
   100 		src = dst - yo * p;
       
   101 
       
   102 		// Decrese height. (yo is <=0).
       
   103 		height += yo;
       
   104 		assert(height > 0);
       
   105 
       
   106 		// Adjust left & width
       
   107 		if (xo >= 0) {
       
   108 			dst += xo;
       
   109 			left += xo;
       
   110 			width -= xo;
       
   111 		} else {
       
   112 			src -= xo;
       
   113 			width += xo;
       
   114 		}
       
   115 
       
   116 		// the y-displacement may be 0 therefore we have to use memmove,
       
   117 		// because source and destination may overlap
       
   118 		for (ht = height; ht > 0; --ht) {
       
   119 			memmove(dst, src, width);
       
   120 			src += p;
       
   121 			dst += p;
       
   122 		}
       
   123 	}
       
   124 	// This part of the screen is now dirty.
       
   125 	_video_driver->make_dirty(left, top, width, height);
       
   126 }
       
   127 
       
   128 
       
   129 void GfxFillRect(int left, int top, int right, int bottom, int color)
       
   130 {
       
   131 	const DrawPixelInfo* dpi = _cur_dpi;
       
   132 	Pixel *dst;
       
   133 	const int otop = top;
       
   134 	const int oleft = left;
       
   135 
       
   136 	if (dpi->zoom != 0) return;
       
   137 	if (left > right || top > bottom) return;
       
   138 	if (right < dpi->left || left >= dpi->left + dpi->width) return;
       
   139 	if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
       
   140 
       
   141 	if ( (left -= dpi->left) < 0) left = 0;
       
   142 	right = right - dpi->left + 1;
       
   143 	if (right > dpi->width) right = dpi->width;
       
   144 	right -= left;
       
   145 	assert(right > 0);
       
   146 
       
   147 	if ( (top -= dpi->top) < 0) top = 0;
       
   148 	bottom = bottom - dpi->top + 1;
       
   149 	if (bottom > dpi->height) bottom = dpi->height;
       
   150 	bottom -= top;
       
   151 	assert(bottom > 0);
       
   152 
       
   153 	dst = dpi->dst_ptr + top * dpi->pitch + left;
       
   154 
       
   155 	if (!(color & PALETTE_MODIFIER_GREYOUT)) {
       
   156 		if (!(color & USE_COLORTABLE)) {
       
   157 			do {
       
   158 				memset(dst, color, right);
       
   159 				dst += dpi->pitch;
       
   160 			} while (--bottom);
       
   161 		} else {
       
   162 			/* use colortable mode */
       
   163 			const byte* ctab = GetNonSprite(color & COLORTABLE_MASK) + 1;
       
   164 
       
   165 			do {
       
   166 				int i;
       
   167 				for (i = 0; i != right; i++) dst[i] = ctab[dst[i]];
       
   168 				dst += dpi->pitch;
       
   169 			} while (--bottom);
       
   170 		}
       
   171 	} else {
       
   172 		byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
       
   173 		do {
       
   174 			int i;
       
   175 			for (i = (bo ^= 1); i < right; i += 2) dst[i] = (byte)color;
       
   176 			dst += dpi->pitch;
       
   177 		} while (--bottom > 0);
       
   178 	}
       
   179 }
       
   180 
       
   181 static void GfxSetPixel(int x, int y, int color)
       
   182 {
       
   183 	const DrawPixelInfo* dpi = _cur_dpi;
       
   184 	if ((x-=dpi->left) < 0 || x>=dpi->width || (y-=dpi->top)<0 || y>=dpi->height)
       
   185 		return;
       
   186 	dpi->dst_ptr[y * dpi->pitch + x] = color;
       
   187 }
       
   188 
       
   189 void GfxDrawLine(int x, int y, int x2, int y2, int color)
       
   190 {
       
   191 	int dy;
       
   192 	int dx;
       
   193 	int stepx;
       
   194 	int stepy;
       
   195 	int frac;
       
   196 
       
   197 	// Check clipping first
       
   198 	{
       
   199 		DrawPixelInfo *dpi = _cur_dpi;
       
   200 		int t;
       
   201 
       
   202 		if (x < dpi->left && x2 < dpi->left) return;
       
   203 
       
   204 		if (y < dpi->top && y2 < dpi->top) return;
       
   205 
       
   206 		t = dpi->left + dpi->width;
       
   207 		if (x > t && x2 > t) return;
       
   208 
       
   209 		t = dpi->top + dpi->height;
       
   210 		if (y > t && y2 > t) return;
       
   211 	}
       
   212 
       
   213 	dy = (y2 - y) * 2;
       
   214 	if (dy < 0) {
       
   215 		dy = -dy;
       
   216 		stepy = -1;
       
   217 	} else {
       
   218 		stepy = 1;
       
   219 	}
       
   220 
       
   221 	dx = (x2 - x) * 2;
       
   222 	if (dx < 0) {
       
   223 		dx = -dx;
       
   224 		stepx = -1;
       
   225 	} else {
       
   226 		stepx = 1;
       
   227 	}
       
   228 
       
   229 	GfxSetPixel(x, y, color);
       
   230 	if (dx > dy) {
       
   231 		frac = dy - (dx >> 1);
       
   232 		while (x != x2) {
       
   233 			if (frac >= 0) {
       
   234 				y += stepy;
       
   235 				frac -= dx;
       
   236 			}
       
   237 			x += stepx;
       
   238 			frac += dy;
       
   239 			GfxSetPixel(x, y, color);
       
   240 		}
       
   241 	} else {
       
   242 		frac = dx - (dy >> 1);
       
   243 		while (y != y2) {
       
   244 			if (frac >= 0) {
       
   245 				x += stepx;
       
   246 				frac -= dy;
       
   247 			}
       
   248 			y += stepy;
       
   249 			frac += dx;
       
   250 			GfxSetPixel(x, y, color);
       
   251 		}
       
   252 	}
       
   253 }
       
   254 
       
   255 
       
   256 /** Truncate a given string to a maximum width if neccessary.
       
   257  * If the string is truncated, add three dots ('...') to show this.
       
   258  * @param *dest string that is checked and possibly truncated
       
   259  * @param maxw maximum width in pixels of the string
       
   260  * @return new width of (truncated) string */
       
   261 static int TruncateString(char *str, int maxw)
       
   262 {
       
   263 	int w = 0;
       
   264 	FontSize size = _cur_fontsize;
       
   265 	int ddd, ddd_w;
       
   266 
       
   267 	WChar c;
       
   268 	char *ddd_pos;
       
   269 
       
   270 	ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
       
   271 
       
   272 	for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
       
   273 		if (IsPrintable(c)) {
       
   274 			w += GetCharacterWidth(size, c);
       
   275 
       
   276 			if (w >= maxw) {
       
   277 				// string got too big... insert dotdotdot
       
   278 				ddd_pos[0] = ddd_pos[1] = ddd_pos[2] = '.';
       
   279 				ddd_pos[3] = 0;
       
   280 				return ddd_w;
       
   281 			}
       
   282 		} else {
       
   283 			if (c == SCC_SETX) str++;
       
   284 			else if (c == SCC_SETXY) str += 2;
       
   285 			else if (c == SCC_TINYFONT) {
       
   286 				size = FS_SMALL;
       
   287 				ddd = GetCharacterWidth(size, '.') * 3;
       
   288 			} else if (c == SCC_BIGFONT) {
       
   289 				size = FS_LARGE;
       
   290 				ddd = GetCharacterWidth(size, '.') * 3;
       
   291 			}
       
   292 		}
       
   293 
       
   294 		// Remember the last position where three dots fit.
       
   295 		if (w + ddd < maxw) {
       
   296 			ddd_w = w + ddd;
       
   297 			ddd_pos = str;
       
   298 		}
       
   299 	}
       
   300 
       
   301 	return w;
       
   302 }
       
   303 
       
   304 static inline int TruncateStringID(StringID src, char *dest, int maxw, const char* last)
       
   305 {
       
   306 	GetString(dest, src, last);
       
   307 	return TruncateString(dest, maxw);
       
   308 }
       
   309 
       
   310 /* returns right coordinate */
       
   311 int DrawString(int x, int y, StringID str, uint16 color)
       
   312 {
       
   313 	char buffer[512];
       
   314 
       
   315 	GetString(buffer, str, lastof(buffer));
       
   316 	return DoDrawString(buffer, x, y, color);
       
   317 }
       
   318 
       
   319 int DrawStringTruncated(int x, int y, StringID str, uint16 color, uint maxw)
       
   320 {
       
   321 	char buffer[512];
       
   322 	TruncateStringID(str, buffer, maxw, lastof(buffer));
       
   323 	return DoDrawString(buffer, x, y, color);
       
   324 }
       
   325 
       
   326 
       
   327 int DrawStringRightAligned(int x, int y, StringID str, uint16 color)
       
   328 {
       
   329 	char buffer[512];
       
   330 	int w;
       
   331 
       
   332 	GetString(buffer, str, lastof(buffer));
       
   333 	w = GetStringBoundingBox(buffer).width;
       
   334 	DoDrawString(buffer, x - w, y, color);
       
   335 
       
   336 	return w;
       
   337 }
       
   338 
       
   339 void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, uint maxw)
       
   340 {
       
   341 	char buffer[512];
       
   342 
       
   343 	TruncateStringID(str, buffer, maxw, lastof(buffer));
       
   344 	DoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, color);
       
   345 }
       
   346 
       
   347 void DrawStringRightAlignedUnderline(int x, int y, StringID str, uint16 color)
       
   348 {
       
   349 	int w = DrawStringRightAligned(x, y, str, color);
       
   350 	GfxFillRect(x - w, y + 10, x, y + 10, _string_colorremap[1]);
       
   351 }
       
   352 
       
   353 
       
   354 int DrawStringCentered(int x, int y, StringID str, uint16 color)
       
   355 {
       
   356 	char buffer[512];
       
   357 	int w;
       
   358 
       
   359 	GetString(buffer, str, lastof(buffer));
       
   360 
       
   361 	w = GetStringBoundingBox(buffer).width;
       
   362 	DoDrawString(buffer, x - w / 2, y, color);
       
   363 
       
   364 	return w;
       
   365 }
       
   366 
       
   367 int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 color)
       
   368 {
       
   369 	char buffer[512];
       
   370 	int w = TruncateStringID(str, buffer, xr - xl, lastof(buffer));
       
   371 	return DoDrawString(buffer, (xl + xr - w) / 2, y, color);
       
   372 }
       
   373 
       
   374 int DoDrawStringCentered(int x, int y, const char *str, uint16 color)
       
   375 {
       
   376 	int w = GetStringBoundingBox(str).width;
       
   377 	DoDrawString(str, x - w / 2, y, color);
       
   378 	return w;
       
   379 }
       
   380 
       
   381 void DrawStringCenterUnderline(int x, int y, StringID str, uint16 color)
       
   382 {
       
   383 	int w = DrawStringCentered(x, y, str, color);
       
   384 	GfxFillRect(x - (w >> 1), y + 10, x - (w >> 1) + w, y + 10, _string_colorremap[1]);
       
   385 }
       
   386 
       
   387 void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, uint16 color)
       
   388 {
       
   389 	int w = DrawStringCenteredTruncated(xl, xr, y, str, color);
       
   390 	GfxFillRect((xl + xr - w) / 2, y + 10, (xl + xr + w) / 2, y + 10, _string_colorremap[1]);
       
   391 }
       
   392 
       
   393 /** 'Correct' a string to a maximum length. Longer strings will be cut into
       
   394  * additional lines at whitespace characters if possible. The string parameter
       
   395  * is modified with terminating characters mid-string which are the
       
   396  * placeholders for the newlines.<br/>
       
   397  * The string WILL be truncated if there was no whitespace for the current
       
   398  * line's maximum width.
       
   399  *
       
   400  * @note To know if the the terminating '\0' is the string end or just a
       
   401  * newline, the returned 'num' value should be consulted. The num'th '\0',
       
   402  * starting with index 0 is the real string end.
       
   403  *
       
   404  * @param str string to check and correct for length restrictions
       
   405  * @param maxw the maximum width the string can have on one line
       
   406  * @return return a 32bit wide number consisting of 2 packed values:
       
   407  *  0 - 15 the number of lines ADDED to the string
       
   408  * 16 - 31 the fontsize in which the length calculation was done at */
       
   409 uint32 FormatStringLinebreaks(char *str, int maxw)
       
   410 {
       
   411 	FontSize size = _cur_fontsize;
       
   412 	int num = 0;
       
   413 
       
   414 	assert(maxw > 0);
       
   415 
       
   416 	for (;;) {
       
   417 		char *last_space = NULL;
       
   418 		int w = 0;
       
   419 
       
   420 		for (;;) {
       
   421 			WChar c = Utf8Consume((const char **)&str);
       
   422 			/* whitespace is where we will insert the line-break */
       
   423 			if (c == ' ') last_space = str;
       
   424 
       
   425 			if (IsPrintable(c)) {
       
   426 				w += GetCharacterWidth(size, c);
       
   427 				/* string is longer than maximum width so we need to decide what to
       
   428 				 * do. We can do two things:
       
   429 				 * 1. If no whitespace was found at all up until now (on this line) then
       
   430 				 *    we will truncate the string and bail out.
       
   431 				 * 2. In all other cases force a linebreak at the last seen whitespace */
       
   432 				if (w > maxw) {
       
   433 					if (last_space == NULL) {
       
   434 						str[-1] = '\0';
       
   435 						return num + (size << 16);
       
   436 					}
       
   437 					str = last_space;
       
   438 					break;
       
   439 				}
       
   440 			} else {
       
   441 				switch (c) {
       
   442 					case '\0': return num + (size << 16); break;
       
   443 					case SCC_SETX:  str++; break;
       
   444 					case SCC_SETXY: str +=2; break;
       
   445 					case SCC_TINYFONT: size = FS_SMALL; break;
       
   446 					case SCC_BIGFONT:  size = FS_LARGE; break;
       
   447 					case '\n': goto end_of_inner_loop;
       
   448 				}
       
   449 			}
       
   450 		}
       
   451 end_of_inner_loop:
       
   452 		/* string didn't fit on line, so 'dummy' terminate and increase linecount */
       
   453 		num++;
       
   454 		str[-1] = '\0';
       
   455 	}
       
   456 }
       
   457 
       
   458 /** Draw a given string with the centre around the given x coordinates
       
   459  * @param x Centre the string around this pixel width
       
   460  * @param y Draw the string at this pixel height (first line's bottom)
       
   461  * @param str String to draw
       
   462  * @param max Maximum width the string can have before it is wrapped */
       
   463 void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
       
   464 {
       
   465 	char buffer[512];
       
   466 	uint32 tmp;
       
   467 	int num, w, mt;
       
   468 	const char *src;
       
   469 	WChar c;
       
   470 
       
   471 	GetString(buffer, str, lastof(buffer));
       
   472 
       
   473 	tmp = FormatStringLinebreaks(buffer, maxw);
       
   474 	num = GB(tmp, 0, 16);
       
   475 
       
   476 	mt = GetCharacterHeight(GB(tmp, 16, 16));
       
   477 
       
   478 	y -= (mt >> 1) * num;
       
   479 
       
   480 	src = buffer;
       
   481 
       
   482 	for (;;) {
       
   483 		w = GetStringBoundingBox(src).width;
       
   484 		DoDrawString(src, x - (w>>1), y, 0xFE);
       
   485 		_cur_fontsize = _last_fontsize;
       
   486 
       
   487 		for (;;) {
       
   488 			c = Utf8Consume(&src);
       
   489 			if (c == 0) {
       
   490 				y += mt;
       
   491 				if (--num < 0) {
       
   492 					_cur_fontsize = FS_NORMAL;
       
   493 					return;
       
   494 				}
       
   495 				break;
       
   496 			} else if (c == SCC_SETX) {
       
   497 				src++;
       
   498 			} else if (c == SCC_SETXY) {
       
   499 				src+=2;
       
   500 			}
       
   501 		}
       
   502 	}
       
   503 }
       
   504 
       
   505 
       
   506 uint DrawStringMultiLine(int x, int y, StringID str, int maxw)
       
   507 {
       
   508 	char buffer[512];
       
   509 	uint32 tmp;
       
   510 	int num, mt;
       
   511 	uint total_height;
       
   512 	const char *src;
       
   513 	WChar c;
       
   514 
       
   515 	GetString(buffer, str, lastof(buffer));
       
   516 
       
   517 	tmp = FormatStringLinebreaks(buffer, maxw);
       
   518 	num = GB(tmp, 0, 16);
       
   519 
       
   520 	mt = GetCharacterHeight(GB(tmp, 16, 16));
       
   521 	total_height = (num + 1) * mt;
       
   522 
       
   523 	src = buffer;
       
   524 
       
   525 	for (;;) {
       
   526 		DoDrawString(src, x, y, 0xFE);
       
   527 		_cur_fontsize = _last_fontsize;
       
   528 
       
   529 		for (;;) {
       
   530 			c = Utf8Consume(&src);
       
   531 			if (c == 0) {
       
   532 				y += mt;
       
   533 				if (--num < 0) {
       
   534 					_cur_fontsize = FS_NORMAL;
       
   535 					return total_height;
       
   536 				}
       
   537 				break;
       
   538 			} else if (c == SCC_SETX) {
       
   539 				src++;
       
   540 			} else if (c == SCC_SETXY) {
       
   541 				src+=2;
       
   542 			}
       
   543 		}
       
   544 	}
       
   545 }
       
   546 
       
   547 /** Return the string dimension in pixels. The height and width are returned
       
   548  * in a single BoundingRect value. TINYFONT, BIGFONT modifiers are only
       
   549  * supported as the first character of the string. The returned dimensions
       
   550  * are therefore a rough estimation correct for all the current strings
       
   551  * but not every possible combination
       
   552  * @param str string to calculate pixel-width
       
   553  * @return string width and height in pixels */
       
   554 BoundingRect GetStringBoundingBox(const char *str)
       
   555 {
       
   556 	FontSize size = _cur_fontsize;
       
   557 	BoundingRect br;
       
   558 	int max_width;
       
   559 	WChar c;
       
   560 
       
   561 	br.width = br.height = max_width = 0;
       
   562 	for (;;) {
       
   563 		c = Utf8Consume(&str);
       
   564 		if (c == 0) break;
       
   565 		if (IsPrintable(c)) {
       
   566 			br.width += GetCharacterWidth(size, c);
       
   567 		} else {
       
   568 			switch (c) {
       
   569 				case SCC_SETX: br.width += (byte)*str++; break;
       
   570 				case SCC_SETXY:
       
   571 					br.width += (byte)*str++;
       
   572 					br.height += (byte)*str++;
       
   573 					break;
       
   574 				case SCC_TINYFONT: size = FS_SMALL; break;
       
   575 				case SCC_BIGFONT:  size = FS_LARGE; break;
       
   576 				case '\n':
       
   577 					br.height += GetCharacterHeight(size);
       
   578 					if (br.width > max_width) max_width = br.width;
       
   579 					br.width = 0;
       
   580 					break;
       
   581 			}
       
   582 		}
       
   583 	}
       
   584 	br.height += GetCharacterHeight(size);
       
   585 
       
   586 	br.width  = max(br.width, max_width);
       
   587 	return br;
       
   588 }
       
   589 
       
   590 /** Draw a string at the given coordinates with the given colour
       
   591  * @param string the string to draw
       
   592  * @param x offset from left side of the screen, if negative offset from the right side
       
   593  * @param x offset from top side of the screen, if negative offset from the bottom
       
   594  * @param real_color colour of the string, see _string_colormap in
       
   595  * table/palettes.h or docs/ottd-colourtext-palette.png
       
   596  * @return the x-coordinates where the drawing has finished. If nothing is drawn
       
   597  * the originally passed x-coordinate is returned */
       
   598 int DoDrawString(const char *string, int x, int y, uint16 real_color)
       
   599 {
       
   600 	DrawPixelInfo *dpi = _cur_dpi;
       
   601 	FontSize size = _cur_fontsize;
       
   602 	WChar c;
       
   603 	byte color;
       
   604 	int xo = x, yo = y;
       
   605 
       
   606 	color = real_color & 0xFF;
       
   607 
       
   608 	if (color != 0xFE) {
       
   609 		if (x >= dpi->left + dpi->width ||
       
   610 				x + _screen.width*2 <= dpi->left ||
       
   611 				y >= dpi->top + dpi->height ||
       
   612 				y + _screen.height <= dpi->top)
       
   613 					return x;
       
   614 
       
   615 		if (color != 0xFF) {
       
   616 switch_color:;
       
   617 			if (real_color & IS_PALETTE_COLOR) {
       
   618 				_string_colorremap[1] = color;
       
   619 				_string_colorremap[2] = 215;
       
   620 			} else {
       
   621 				_string_colorremap[1] = _string_colormap[color].text;
       
   622 				_string_colorremap[2] = _string_colormap[color].shadow;
       
   623 			}
       
   624 			_color_remap_ptr = _string_colorremap;
       
   625 		}
       
   626 	}
       
   627 
       
   628 check_bounds:
       
   629 	if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
       
   630 skip_char:;
       
   631 		for (;;) {
       
   632 			c = Utf8Consume(&string);
       
   633 			if (!IsPrintable(c)) goto skip_cont;
       
   634 		}
       
   635 	}
       
   636 
       
   637 	for (;;) {
       
   638 		c = Utf8Consume(&string);
       
   639 skip_cont:;
       
   640 		if (c == 0) {
       
   641 			_last_fontsize = size;
       
   642 			return x;
       
   643 		}
       
   644 		if (IsPrintable(c)) {
       
   645 			if (x >= dpi->left + dpi->width) goto skip_char;
       
   646 			if (x + 26 >= dpi->left) {
       
   647 				GfxMainBlitter(GetGlyph(size, c), x, y, BM_COLOUR_REMAP);
       
   648 			}
       
   649 			x += GetCharacterWidth(size, c);
       
   650 		} else if (c == '\n') { // newline = {}
       
   651 			x = xo;
       
   652 			y += GetCharacterHeight(size);
       
   653 			goto check_bounds;
       
   654 		} else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change color?
       
   655 			color = (byte)(c - SCC_BLUE);
       
   656 			goto switch_color;
       
   657 		} else if (c == SCC_SETX) { // {SETX}
       
   658 			x = xo + (byte)*string++;
       
   659 		} else if (c == SCC_SETXY) {// {SETXY}
       
   660 			x = xo + (byte)*string++;
       
   661 			y = yo + (byte)*string++;
       
   662 		} else if (c == SCC_TINYFONT) { // {TINYFONT}
       
   663 			size = FS_SMALL;
       
   664 		} else if (c == SCC_BIGFONT) { // {BIGFONT}
       
   665 			size = FS_LARGE;
       
   666 		} else {
       
   667 			DEBUG(misc, 0, "[utf8] unknown string command character %d", c);
       
   668 		}
       
   669 	}
       
   670 }
       
   671 
       
   672 int DoDrawStringTruncated(const char *str, int x, int y, uint16 color, uint maxw)
       
   673 {
       
   674 	char buffer[512];
       
   675 	ttd_strlcpy(buffer, str, sizeof(buffer));
       
   676 	TruncateString(buffer, maxw);
       
   677 	return DoDrawString(buffer, x, y, color);
       
   678 }
       
   679 
       
   680 void DrawSprite(uint32 img, int x, int y)
       
   681 {
       
   682 	if (img & PALETTE_MODIFIER_COLOR) {
       
   683 		_color_remap_ptr = GetNonSprite(GB(img, PALETTE_SPRITE_START, PALETTE_SPRITE_WIDTH)) + 1;
       
   684 		GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, BM_COLOUR_REMAP);
       
   685 	} else if (img & PALETTE_MODIFIER_TRANSPARENT) {
       
   686 		_color_remap_ptr = GetNonSprite(GB(img, PALETTE_SPRITE_START, PALETTE_SPRITE_WIDTH)) + 1;
       
   687 		GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, BM_TRANSPARENT);
       
   688 	} else {
       
   689 		GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, BM_NORMAL);
       
   690 	}
       
   691 }
       
   692 
       
   693 typedef struct BlitterParams {
       
   694 	int start_x, start_y;
       
   695 	const byte *sprite;
       
   696 	Pixel *dst;
       
   697 	BlitterMode mode;
       
   698 	int width, height;
       
   699 	int width_org;
       
   700 	int pitch;
       
   701 } BlitterParams;
       
   702 
       
   703 static void GfxBlitTileZoomIn(BlitterParams *bp)
       
   704 {
       
   705 	const byte *src_o = bp->sprite;
       
   706 	const byte *src;
       
   707 	int num, skip;
       
   708 	byte done;
       
   709 	Pixel *dst;
       
   710 	const byte *ctab;
       
   711 
       
   712 	src_o += ReadLE16Aligned(src_o + bp->start_y * 2);
       
   713 	switch (bp->mode) {
       
   714 		case BM_COLOUR_REMAP:
       
   715 			do {
       
   716 				do {
       
   717 					done = src_o[0];
       
   718 					num = done & 0x7F;
       
   719 					skip = src_o[1];
       
   720 					src = src_o + 2;
       
   721 					src_o += num + 2;
       
   722 
       
   723 					dst = bp->dst;
       
   724 
       
   725 					if ( (skip -= bp->start_x) > 0) {
       
   726 						dst += skip;
       
   727 					} else {
       
   728 						src -= skip;
       
   729 						num += skip;
       
   730 						if (num <= 0) continue;
       
   731 						skip = 0;
       
   732 					}
       
   733 
       
   734 					skip = skip + num - bp->width;
       
   735 					if (skip > 0) {
       
   736 						num -= skip;
       
   737 						if (num <= 0) continue;
       
   738 					}
       
   739 
       
   740 					ctab = _color_remap_ptr;
       
   741 
       
   742 					for (; num >= 4; num -=4) {
       
   743 						dst[3] = ctab[src[3]];
       
   744 						dst[2] = ctab[src[2]];
       
   745 						dst[1] = ctab[src[1]];
       
   746 						dst[0] = ctab[src[0]];
       
   747 						dst += 4;
       
   748 						src += 4;
       
   749 					}
       
   750 					for (; num != 0; num--) *dst++ = ctab[*src++];
       
   751 				} while (!(done & 0x80));
       
   752 
       
   753 				bp->dst += bp->pitch;
       
   754 			} while (--bp->height != 0);
       
   755 			break;
       
   756 
       
   757 		case BM_TRANSPARENT:
       
   758 			do {
       
   759 				do {
       
   760 					done = src_o[0];
       
   761 					num = done & 0x7F;
       
   762 					skip = src_o[1];
       
   763 					src_o += num + 2;
       
   764 
       
   765 					dst = bp->dst;
       
   766 
       
   767 					if ( (skip -= bp->start_x) > 0) {
       
   768 						dst += skip;
       
   769 					} else {
       
   770 						num += skip;
       
   771 						if (num <= 0) continue;
       
   772 						skip = 0;
       
   773 					}
       
   774 
       
   775 					skip = skip + num - bp->width;
       
   776 					if (skip > 0) {
       
   777 						num -= skip;
       
   778 						if (num <= 0) continue;
       
   779 					}
       
   780 
       
   781 					ctab = _color_remap_ptr;
       
   782 					for (; num != 0; num--) {
       
   783 						*dst = ctab[*dst];
       
   784 						dst++;
       
   785 					}
       
   786 				} while (!(done & 0x80));
       
   787 
       
   788 				bp->dst += bp->pitch;
       
   789 			} while (--bp->height != 0);
       
   790 			break;
       
   791 
       
   792 		default:
       
   793 			do {
       
   794 				do {
       
   795 					done = src_o[0];
       
   796 					num = done & 0x7F;
       
   797 					skip = src_o[1];
       
   798 					src = src_o + 2;
       
   799 					src_o += num + 2;
       
   800 
       
   801 					dst = bp->dst;
       
   802 
       
   803 					if ( (skip -= bp->start_x) > 0) {
       
   804 						dst += skip;
       
   805 					} else {
       
   806 						src -= skip;
       
   807 						num += skip;
       
   808 						if (num <= 0) continue;
       
   809 						skip = 0;
       
   810 					}
       
   811 
       
   812 					skip = skip + num - bp->width;
       
   813 					if (skip > 0) {
       
   814 						num -= skip;
       
   815 						if (num <= 0) continue;
       
   816 					}
       
   817 #if defined(_WIN32)
       
   818 					if (num & 1) *dst++ = *src++;
       
   819 					if (num & 2) { *(uint16*)dst = *(uint16*)src; dst += 2; src += 2; }
       
   820 					if (num >>= 2) {
       
   821 						do {
       
   822 							*(uint32*)dst = *(uint32*)src;
       
   823 							dst += 4;
       
   824 							src += 4;
       
   825 						} while (--num != 0);
       
   826 					}
       
   827 #else
       
   828 					memcpy(dst, src, num);
       
   829 #endif
       
   830 				} while (!(done & 0x80));
       
   831 
       
   832 				bp->dst += bp->pitch;
       
   833 			} while (--bp->height != 0);
       
   834 			break;
       
   835 	}
       
   836 }
       
   837 
       
   838 static void GfxBlitZoomInUncomp(BlitterParams *bp)
       
   839 {
       
   840 	const byte *src = bp->sprite;
       
   841 	Pixel *dst = bp->dst;
       
   842 	int height = bp->height;
       
   843 	int width = bp->width;
       
   844 	int i;
       
   845 
       
   846 	assert(height > 0);
       
   847 	assert(width > 0);
       
   848 
       
   849 	switch (bp->mode) {
       
   850 		case BM_COLOUR_REMAP: {
       
   851 			const byte *ctab = _color_remap_ptr;
       
   852 
       
   853 			do {
       
   854 				for (i = 0; i != width; i++) {
       
   855 					byte b = ctab[src[i]];
       
   856 
       
   857 					if (b != 0) dst[i] = b;
       
   858 				}
       
   859 				src += bp->width_org;
       
   860 				dst += bp->pitch;
       
   861 			} while (--height != 0);
       
   862 			break;
       
   863 		}
       
   864 
       
   865 		case BM_TRANSPARENT: {
       
   866 			const byte *ctab = _color_remap_ptr;
       
   867 
       
   868 			do {
       
   869 				for (i = 0; i != width; i++)
       
   870 					if (src[i] != 0) dst[i] = ctab[dst[i]];
       
   871 				src += bp->width_org;
       
   872 				dst += bp->pitch;
       
   873 			} while (--height != 0);
       
   874 			break;
       
   875 		}
       
   876 
       
   877 		default:
       
   878 			do {
       
   879 				int n = width;
       
   880 
       
   881 				for (; n >= 4; n -= 4) {
       
   882 					if (src[0] != 0) dst[0] = src[0];
       
   883 					if (src[1] != 0) dst[1] = src[1];
       
   884 					if (src[2] != 0) dst[2] = src[2];
       
   885 					if (src[3] != 0) dst[3] = src[3];
       
   886 
       
   887 					dst += 4;
       
   888 					src += 4;
       
   889 				}
       
   890 
       
   891 				for (; n != 0; n--) {
       
   892 					if (src[0] != 0) dst[0] = src[0];
       
   893 					src++;
       
   894 					dst++;
       
   895 				}
       
   896 
       
   897 				src += bp->width_org - width;
       
   898 				dst += bp->pitch - width;
       
   899 			} while (--height != 0);
       
   900 			break;
       
   901 	}
       
   902 }
       
   903 
       
   904 static void GfxBlitTileZoomMedium(BlitterParams *bp)
       
   905 {
       
   906 	const byte *src_o = bp->sprite;
       
   907 	const byte *src;
       
   908 	int num, skip;
       
   909 	byte done;
       
   910 	Pixel *dst;
       
   911 	const byte *ctab;
       
   912 
       
   913 	src_o += ReadLE16Aligned(src_o + bp->start_y * 2);
       
   914 	switch (bp->mode) {
       
   915 		case BM_COLOUR_REMAP:
       
   916 			do {
       
   917 				do {
       
   918 					done = src_o[0];
       
   919 					num = done & 0x7F;
       
   920 					skip = src_o[1];
       
   921 					src = src_o + 2;
       
   922 					src_o += num + 2;
       
   923 
       
   924 					dst = bp->dst;
       
   925 
       
   926 					if (skip & 1) {
       
   927 						skip++;
       
   928 						src++;
       
   929 						if (--num == 0) continue;
       
   930 					}
       
   931 
       
   932 					if ( (skip -= bp->start_x) > 0) {
       
   933 						dst += skip >> 1;
       
   934 					} else {
       
   935 						src -= skip;
       
   936 						num += skip;
       
   937 						if (num <= 0) continue;
       
   938 						skip = 0;
       
   939 					}
       
   940 
       
   941 					skip = skip + num - bp->width;
       
   942 					if (skip > 0) {
       
   943 						num -= skip;
       
   944 						if (num <= 0) continue;
       
   945 					}
       
   946 
       
   947 					ctab = _color_remap_ptr;
       
   948 					num = (num + 1) >> 1;
       
   949 					for (; num != 0; num--) {
       
   950 							*dst = ctab[*src];
       
   951 							dst++;
       
   952 							src += 2;
       
   953 					}
       
   954 				} while (!(done & 0x80));
       
   955 				bp->dst += bp->pitch;
       
   956 				if (--bp->height == 0) return;
       
   957 
       
   958 				do {
       
   959 					done = src_o[0];
       
   960 					src_o += (done & 0x7F) + 2;
       
   961 				} while (!(done & 0x80));
       
   962 			} while (--bp->height != 0);
       
   963 			break;
       
   964 
       
   965 		case BM_TRANSPARENT:
       
   966 			do {
       
   967 				do {
       
   968 					done = src_o[0];
       
   969 					num = done & 0x7F;
       
   970 					skip = src_o[1];
       
   971 					src_o += num + 2;
       
   972 
       
   973 					dst = bp->dst;
       
   974 
       
   975 					if (skip & 1) {
       
   976 						skip++;
       
   977 						if (--num == 0) continue;
       
   978 					}
       
   979 
       
   980 					if ( (skip -= bp->start_x) > 0) {
       
   981 						dst += skip >> 1;
       
   982 					} else {
       
   983 						num += skip;
       
   984 						if (num <= 0) continue;
       
   985 						skip = 0;
       
   986 					}
       
   987 
       
   988 					skip = skip + num - bp->width;
       
   989 					if (skip > 0) {
       
   990 						num -= skip;
       
   991 						if (num <= 0) continue;
       
   992 					}
       
   993 
       
   994 					ctab = _color_remap_ptr;
       
   995 					num = (num + 1) >> 1;
       
   996 					for (; num != 0; num--) {
       
   997 							*dst = ctab[*dst];
       
   998 							dst++;
       
   999 					}
       
  1000 				} while (!(done & 0x80));
       
  1001 				bp->dst += bp->pitch;
       
  1002 				if (--bp->height == 0) return;
       
  1003 
       
  1004 				do {
       
  1005 					done = src_o[0];
       
  1006 					src_o += (done & 0x7F) + 2;
       
  1007 				} while (!(done & 0x80));
       
  1008 			} while (--bp->height != 0);
       
  1009 			break;
       
  1010 
       
  1011 		default:
       
  1012 			do {
       
  1013 				do {
       
  1014 					done = src_o[0];
       
  1015 					num = done & 0x7F;
       
  1016 					skip = src_o[1];
       
  1017 					src = src_o + 2;
       
  1018 					src_o += num + 2;
       
  1019 
       
  1020 					dst = bp->dst;
       
  1021 
       
  1022 					if (skip & 1) {
       
  1023 						skip++;
       
  1024 						src++;
       
  1025 						if (--num == 0) continue;
       
  1026 					}
       
  1027 
       
  1028 					if ( (skip -= bp->start_x) > 0) {
       
  1029 						dst += skip >> 1;
       
  1030 					} else {
       
  1031 						src -= skip;
       
  1032 						num += skip;
       
  1033 						if (num <= 0) continue;
       
  1034 						skip = 0;
       
  1035 					}
       
  1036 
       
  1037 					skip = skip + num - bp->width;
       
  1038 					if (skip > 0) {
       
  1039 						num -= skip;
       
  1040 						if (num <= 0) continue;
       
  1041 					}
       
  1042 
       
  1043 					num = (num + 1) >> 1;
       
  1044 
       
  1045 					for (; num != 0; num--) {
       
  1046 							*dst = *src;
       
  1047 							dst++;
       
  1048 							src += 2;
       
  1049 					}
       
  1050 
       
  1051 				} while (!(done & 0x80));
       
  1052 
       
  1053 				bp->dst += bp->pitch;
       
  1054 				if (--bp->height == 0) return;
       
  1055 
       
  1056 				do {
       
  1057 					done = src_o[0];
       
  1058 					src_o += (done & 0x7F) + 2;
       
  1059 				} while (!(done & 0x80));
       
  1060 			} while (--bp->height != 0);
       
  1061 			break;
       
  1062 	}
       
  1063 }
       
  1064 
       
  1065 static void GfxBlitZoomMediumUncomp(BlitterParams *bp)
       
  1066 {
       
  1067 	const byte *src = bp->sprite;
       
  1068 	Pixel *dst = bp->dst;
       
  1069 	int height = bp->height;
       
  1070 	int width = bp->width;
       
  1071 	int i;
       
  1072 
       
  1073 	assert(height > 0);
       
  1074 	assert(width > 0);
       
  1075 
       
  1076 	switch (bp->mode) {
       
  1077 		case BM_COLOUR_REMAP: {
       
  1078 			const byte *ctab = _color_remap_ptr;
       
  1079 
       
  1080 			for (height >>= 1; height != 0; height--) {
       
  1081 				for (i = 0; i != width >> 1; i++) {
       
  1082 					byte b = ctab[src[i * 2]];
       
  1083 
       
  1084 					if (b != 0) dst[i] = b;
       
  1085 				}
       
  1086 				src += bp->width_org * 2;
       
  1087 				dst += bp->pitch;
       
  1088 			}
       
  1089 			break;
       
  1090 		}
       
  1091 
       
  1092 		case BM_TRANSPARENT: {
       
  1093 			const byte *ctab = _color_remap_ptr;
       
  1094 
       
  1095 			for (height >>= 1; height != 0; height--) {
       
  1096 				for (i = 0; i != width >> 1; i++)
       
  1097 					if (src[i * 2] != 0) dst[i] = ctab[dst[i]];
       
  1098 				src += bp->width_org * 2;
       
  1099 				dst += bp->pitch;
       
  1100 			}
       
  1101 			break;
       
  1102 		}
       
  1103 
       
  1104 		default:
       
  1105 			for (height >>= 1; height != 0; height--) {
       
  1106 				for (i = 0; i != width >> 1; i++)
       
  1107 					if (src[i * 2] != 0) dst[i] = src[i * 2];
       
  1108 				src += bp->width_org * 2;
       
  1109 				dst += bp->pitch;
       
  1110 			}
       
  1111 			break;
       
  1112 	}
       
  1113 }
       
  1114 
       
  1115 static void GfxBlitTileZoomOut(BlitterParams *bp)
       
  1116 {
       
  1117 	const byte *src_o = bp->sprite;
       
  1118 	const byte *src;
       
  1119 	int num, skip;
       
  1120 	byte done;
       
  1121 	Pixel *dst;
       
  1122 	const byte *ctab;
       
  1123 
       
  1124 	src_o += ReadLE16Aligned(src_o + bp->start_y * 2);
       
  1125 	switch (bp->mode) {
       
  1126 		case BM_COLOUR_REMAP:
       
  1127 			for (;;) {
       
  1128 				do {
       
  1129 					done = src_o[0];
       
  1130 					num = done & 0x7F;
       
  1131 					skip = src_o[1];
       
  1132 					src = src_o + 2;
       
  1133 					src_o += num + 2;
       
  1134 
       
  1135 					dst = bp->dst;
       
  1136 
       
  1137 					if (skip & 1) {
       
  1138 						skip++;
       
  1139 						src++;
       
  1140 						if (--num == 0) continue;
       
  1141 					}
       
  1142 
       
  1143 					if (skip & 2) {
       
  1144 						skip += 2;
       
  1145 						src += 2;
       
  1146 						num -= 2;
       
  1147 						if (num <= 0) continue;
       
  1148 					}
       
  1149 
       
  1150 					if ( (skip -= bp->start_x) > 0) {
       
  1151 						dst += skip >> 2;
       
  1152 					} else {
       
  1153 						src -= skip;
       
  1154 						num += skip;
       
  1155 						if (num <= 0) continue;
       
  1156 						skip = 0;
       
  1157 					}
       
  1158 
       
  1159 					skip = skip + num - bp->width;
       
  1160 					if (skip > 0) {
       
  1161 						num -= skip;
       
  1162 						if (num <= 0) continue;
       
  1163 					}
       
  1164 
       
  1165 					ctab = _color_remap_ptr;
       
  1166 					num = (num + 3) >> 2;
       
  1167 					for (; num != 0; num--) {
       
  1168 							*dst = ctab[*src];
       
  1169 							dst++;
       
  1170 							src += 4;
       
  1171 					}
       
  1172 				} while (!(done & 0x80));
       
  1173 				bp->dst += bp->pitch;
       
  1174 				if (--bp->height == 0) return;
       
  1175 
       
  1176 				do {
       
  1177 					done = src_o[0];
       
  1178 					src_o += (done & 0x7F) + 2;
       
  1179 				} while (!(done & 0x80));
       
  1180 				if (--bp->height == 0) return;
       
  1181 
       
  1182 				do {
       
  1183 					done = src_o[0];
       
  1184 					src_o += (done & 0x7F) + 2;
       
  1185 				} while (!(done & 0x80));
       
  1186 				if (--bp->height == 0) return;
       
  1187 
       
  1188 				do {
       
  1189 					done = src_o[0];
       
  1190 					src_o += (done & 0x7F) + 2;
       
  1191 				} while (!(done & 0x80));
       
  1192 				if (--bp->height == 0) return;
       
  1193 			}
       
  1194 			break;
       
  1195 
       
  1196 		case BM_TRANSPARENT:
       
  1197 			for (;;) {
       
  1198 				do {
       
  1199 					done = src_o[0];
       
  1200 					num = done & 0x7F;
       
  1201 					skip = src_o[1];
       
  1202 					src_o += num + 2;
       
  1203 
       
  1204 					dst = bp->dst;
       
  1205 
       
  1206 					if (skip & 1) {
       
  1207 						skip++;
       
  1208 						if (--num == 0) continue;
       
  1209 					}
       
  1210 
       
  1211 					if (skip & 2) {
       
  1212 						skip += 2;
       
  1213 						num -= 2;
       
  1214 						if (num <= 0) continue;
       
  1215 					}
       
  1216 
       
  1217 					if ( (skip -= bp->start_x) > 0) {
       
  1218 						dst += skip >> 2;
       
  1219 					} else {
       
  1220 						num += skip;
       
  1221 						if (num <= 0) continue;
       
  1222 						skip = 0;
       
  1223 					}
       
  1224 
       
  1225 					skip = skip + num - bp->width;
       
  1226 					if (skip > 0) {
       
  1227 						num -= skip;
       
  1228 						if (num <= 0) continue;
       
  1229 					}
       
  1230 
       
  1231 					ctab = _color_remap_ptr;
       
  1232 					num = (num + 3) >> 2;
       
  1233 					for (; num != 0; num--) {
       
  1234 							*dst = ctab[*dst];
       
  1235 							dst++;
       
  1236 					}
       
  1237 
       
  1238 				} while (!(done & 0x80));
       
  1239 				bp->dst += bp->pitch;
       
  1240 				if (--bp->height == 0) return;
       
  1241 
       
  1242 				do {
       
  1243 					done = src_o[0];
       
  1244 					src_o += (done & 0x7F) + 2;
       
  1245 				} while (!(done & 0x80));
       
  1246 				if (--bp->height == 0) return;
       
  1247 
       
  1248 				do {
       
  1249 					done = src_o[0];
       
  1250 					src_o += (done & 0x7F) + 2;
       
  1251 				} while (!(done & 0x80));
       
  1252 				if (--bp->height == 0) return;
       
  1253 
       
  1254 				do {
       
  1255 					done = src_o[0];
       
  1256 					src_o += (done & 0x7F) + 2;
       
  1257 				} while (!(done & 0x80));
       
  1258 				if (--bp->height == 0) return;
       
  1259 			}
       
  1260 			break;
       
  1261 
       
  1262 		default:
       
  1263 			for (;;) {
       
  1264 				do {
       
  1265 					done = src_o[0];
       
  1266 					num = done & 0x7F;
       
  1267 					skip = src_o[1];
       
  1268 					src = src_o + 2;
       
  1269 					src_o += num + 2;
       
  1270 
       
  1271 					dst = bp->dst;
       
  1272 
       
  1273 					if (skip & 1) {
       
  1274 						skip++;
       
  1275 						src++;
       
  1276 						if (--num == 0) continue;
       
  1277 					}
       
  1278 
       
  1279 					if (skip & 2) {
       
  1280 						skip += 2;
       
  1281 						src += 2;
       
  1282 						num -= 2;
       
  1283 						if (num <= 0) continue;
       
  1284 					}
       
  1285 
       
  1286 					if ( (skip -= bp->start_x) > 0) {
       
  1287 						dst += skip >> 2;
       
  1288 					} else {
       
  1289 						src -= skip;
       
  1290 						num += skip;
       
  1291 						if (num <= 0) continue;
       
  1292 						skip = 0;
       
  1293 					}
       
  1294 
       
  1295 					skip = skip + num - bp->width;
       
  1296 					if (skip > 0) {
       
  1297 						num -= skip;
       
  1298 						if (num <= 0) continue;
       
  1299 					}
       
  1300 
       
  1301 					num = (num + 3) >> 2;
       
  1302 
       
  1303 					for (; num != 0; num--) {
       
  1304 							*dst = *src;
       
  1305 							dst++;
       
  1306 							src += 4;
       
  1307 					}
       
  1308 				} while (!(done & 0x80));
       
  1309 
       
  1310 				bp->dst += bp->pitch;
       
  1311 				if (--bp->height == 0) return;
       
  1312 
       
  1313 				do {
       
  1314 					done = src_o[0];
       
  1315 					src_o += (done & 0x7F) + 2;
       
  1316 				} while (!(done & 0x80));
       
  1317 				if (--bp->height == 0) return;
       
  1318 
       
  1319 				do {
       
  1320 					done = src_o[0];
       
  1321 					src_o += (done & 0x7F) + 2;
       
  1322 				} while (!(done & 0x80));
       
  1323 				if (--bp->height == 0) return;
       
  1324 
       
  1325 				do {
       
  1326 					done = src_o[0];
       
  1327 					src_o += (done & 0x7F) + 2;
       
  1328 				} while (!(done & 0x80));
       
  1329 				if (--bp->height == 0) return;
       
  1330 			}
       
  1331 			break;
       
  1332 	}
       
  1333 }
       
  1334 
       
  1335 static void GfxBlitZoomOutUncomp(BlitterParams *bp)
       
  1336 {
       
  1337 	const byte *src = bp->sprite;
       
  1338 	Pixel *dst = bp->dst;
       
  1339 	int height = bp->height;
       
  1340 	int width = bp->width;
       
  1341 	int i;
       
  1342 
       
  1343 	assert(height > 0);
       
  1344 	assert(width > 0);
       
  1345 
       
  1346 	switch (bp->mode) {
       
  1347 		case BM_COLOUR_REMAP: {
       
  1348 			const byte *ctab = _color_remap_ptr;
       
  1349 
       
  1350 			for (height >>= 2; height != 0; height--) {
       
  1351 				for (i = 0; i != width >> 2; i++) {
       
  1352 					byte b = ctab[src[i * 4]];
       
  1353 
       
  1354 					if (b != 0) dst[i] = b;
       
  1355 				}
       
  1356 				src += bp->width_org * 4;
       
  1357 				dst += bp->pitch;
       
  1358 			}
       
  1359 			break;
       
  1360 		}
       
  1361 
       
  1362 		case BM_TRANSPARENT: {
       
  1363 			const byte *ctab = _color_remap_ptr;
       
  1364 
       
  1365 			for (height >>= 2; height != 0; height--) {
       
  1366 				for (i = 0; i != width >> 2; i++)
       
  1367 					if (src[i * 4] != 0) dst[i] = ctab[dst[i]];
       
  1368 				src += bp->width_org * 4;
       
  1369 				dst += bp->pitch;
       
  1370 			}
       
  1371 			break;
       
  1372 		}
       
  1373 
       
  1374 		default:
       
  1375 			for (height >>= 2; height != 0; height--) {
       
  1376 				for (i = 0; i != width >> 2; i++)
       
  1377 					if (src[i * 4] != 0) dst[i] = src[i * 4];
       
  1378 				src += bp->width_org * 4;
       
  1379 				dst += bp->pitch;
       
  1380 			}
       
  1381 			break;
       
  1382 	}
       
  1383 }
       
  1384 
       
  1385 
       
  1386 static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode)
       
  1387 {
       
  1388 	const DrawPixelInfo *dpi = _cur_dpi;
       
  1389 	int start_x, start_y;
       
  1390 	BlitterParams bp;
       
  1391 	int zoom_mask = ~((1 << dpi->zoom) - 1);
       
  1392 
       
  1393 	/* decode sprite header */
       
  1394 	x += sprite->x_offs;
       
  1395 	y += sprite->y_offs;
       
  1396 	bp.width_org = bp.width = sprite->width;
       
  1397 	bp.height = sprite->height;
       
  1398 	bp.sprite = sprite->data;
       
  1399 	bp.dst = dpi->dst_ptr;
       
  1400 	bp.mode = mode;
       
  1401 	bp.pitch = dpi->pitch;
       
  1402 
       
  1403 	assert(bp.height > 0);
       
  1404 	assert(bp.width > 0);
       
  1405 
       
  1406 	if (sprite->info & 8) {
       
  1407 		/* tile blit */
       
  1408 		start_y = 0;
       
  1409 
       
  1410 		if (dpi->zoom > 0) {
       
  1411 			start_y += bp.height & ~zoom_mask;
       
  1412 			bp.height &= zoom_mask;
       
  1413 			if (bp.height == 0) return;
       
  1414 			y &= zoom_mask;
       
  1415 		}
       
  1416 
       
  1417 		if ( (y -= dpi->top) < 0) {
       
  1418 			bp.height += y;
       
  1419 			if (bp.height <= 0) return;
       
  1420 			start_y -= y;
       
  1421 			y = 0;
       
  1422 		} else {
       
  1423 			bp.dst += bp.pitch * (y >> dpi->zoom);
       
  1424 		}
       
  1425 		bp.start_y = start_y;
       
  1426 
       
  1427 		if ( (y = y + bp.height - dpi->height) > 0) {
       
  1428 			bp.height -= y;
       
  1429 			if (bp.height <= 0) return;
       
  1430 		}
       
  1431 
       
  1432 		start_x = 0;
       
  1433 		x &= zoom_mask;
       
  1434 		if ( (x -= dpi->left) < 0) {
       
  1435 			bp.width += x;
       
  1436 			if (bp.width <= 0) return;
       
  1437 			start_x -= x;
       
  1438 			x = 0;
       
  1439 		}
       
  1440 		bp.start_x = start_x;
       
  1441 		bp.dst += x >> dpi->zoom;
       
  1442 
       
  1443 		if ( (x = x + bp.width - dpi->width) > 0) {
       
  1444 			bp.width -= x;
       
  1445 			if (bp.width <= 0) return;
       
  1446 		}
       
  1447 
       
  1448 		switch (dpi->zoom) {
       
  1449 			default: NOT_REACHED();
       
  1450 			case 0: GfxBlitTileZoomIn(&bp);     break;
       
  1451 			case 1: GfxBlitTileZoomMedium(&bp); break;
       
  1452 			case 2: GfxBlitTileZoomOut(&bp);    break;
       
  1453 		}
       
  1454 	} else {
       
  1455 		bp.sprite += bp.width * (bp.height & ~zoom_mask);
       
  1456 		bp.height &= zoom_mask;
       
  1457 		if (bp.height == 0) return;
       
  1458 
       
  1459 		y &= zoom_mask;
       
  1460 
       
  1461 		if ( (y -= dpi->top) < 0) {
       
  1462 			bp.height += y;
       
  1463 			if (bp.height <= 0) return;
       
  1464 			bp.sprite -= bp.width * y;
       
  1465 			y = 0;
       
  1466 		} else {
       
  1467 			bp.dst += bp.pitch * (y >> dpi->zoom);
       
  1468 		}
       
  1469 
       
  1470 		if (bp.height > dpi->height - y) {
       
  1471 			bp.height = dpi->height - y;
       
  1472 			if (bp.height <= 0) return;
       
  1473 		}
       
  1474 
       
  1475 		x &= zoom_mask;
       
  1476 
       
  1477 		if ( (x -= dpi->left) < 0) {
       
  1478 			bp.width += x;
       
  1479 			if (bp.width <= 0) return;
       
  1480 			bp.sprite -= x;
       
  1481 			x = 0;
       
  1482 		}
       
  1483 		bp.dst += x >> dpi->zoom;
       
  1484 
       
  1485 		if (bp.width > dpi->width - x) {
       
  1486 			bp.width = dpi->width - x;
       
  1487 			if (bp.width <= 0) return;
       
  1488 		}
       
  1489 
       
  1490 		switch (dpi->zoom) {
       
  1491 			default: NOT_REACHED();
       
  1492 			case 0: GfxBlitZoomInUncomp(&bp);     break;
       
  1493 			case 1: GfxBlitZoomMediumUncomp(&bp); break;
       
  1494 			case 2: GfxBlitZoomOutUncomp(&bp);    break;
       
  1495 		}
       
  1496 	}
       
  1497 }
       
  1498 
       
  1499 void DoPaletteAnimations(void);
       
  1500 
       
  1501 void GfxInitPalettes(void)
       
  1502 {
       
  1503 	memcpy(_cur_palette, _palettes[_use_dos_palette ? 1 : 0], sizeof(_cur_palette));
       
  1504 
       
  1505 	_pal_first_dirty = 0;
       
  1506 	_pal_last_dirty = 255;
       
  1507 	DoPaletteAnimations();
       
  1508 }
       
  1509 
       
  1510 #define EXTR(p, q) (((uint16)(_timer_counter * (p)) * (q)) >> 16)
       
  1511 #define EXTR2(p, q) (((uint16)(~_timer_counter * (p)) * (q)) >> 16)
       
  1512 
       
  1513 void DoPaletteAnimations(void)
       
  1514 {
       
  1515 	const Colour *s;
       
  1516 	Colour *d;
       
  1517 	/* Amount of colors to be rotated.
       
  1518 	 * A few more for the DOS palette, because the water colors are
       
  1519 	 * 245-254 for DOS and 217-226 for Windows.  */
       
  1520 	const ExtraPaletteValues *ev = &_extra_palette_values;
       
  1521 	int c = _use_dos_palette ? 38 : 28;
       
  1522 	Colour old_val[38]; // max(38, 28)
       
  1523 	uint i;
       
  1524 	uint j;
       
  1525 
       
  1526 	d = &_cur_palette[217];
       
  1527 	memcpy(old_val, d, c * sizeof(*old_val));
       
  1528 
       
  1529 	// Dark blue water
       
  1530 	s = (_opt.landscape == LT_CANDY) ? ev->ac : ev->a;
       
  1531 	j = EXTR(320, 5);
       
  1532 	for (i = 0; i != 5; i++) {
       
  1533 		*d++ = s[j];
       
  1534 		j++;
       
  1535 		if (j == 5) j = 0;
       
  1536 	}
       
  1537 
       
  1538 	// Glittery water
       
  1539 	s = (_opt.landscape == LT_CANDY) ? ev->bc : ev->b;
       
  1540 	j = EXTR(128, 15);
       
  1541 	for (i = 0; i != 5; i++) {
       
  1542 		*d++ = s[j];
       
  1543 		j += 3;
       
  1544 		if (j >= 15) j -= 15;
       
  1545 	}
       
  1546 
       
  1547 	s = ev->e;
       
  1548 	j = EXTR2(512, 5);
       
  1549 	for (i = 0; i != 5; i++) {
       
  1550 		*d++ = s[j];
       
  1551 		j++;
       
  1552 		if (j == 5) j = 0;
       
  1553 	}
       
  1554 
       
  1555 	// Oil refinery fire animation
       
  1556 	s = ev->oil_ref;
       
  1557 	j = EXTR2(512, 7);
       
  1558 	for (i = 0; i != 7; i++) {
       
  1559 		*d++ = s[j];
       
  1560 		j++;
       
  1561 		if (j == 7) j = 0;
       
  1562 	}
       
  1563 
       
  1564 	// Radio tower blinking
       
  1565 	{
       
  1566 		byte i = (_timer_counter >> 1) & 0x7F;
       
  1567 		byte v;
       
  1568 
       
  1569 		(v = 255, i < 0x3f) ||
       
  1570 		(v = 128, i < 0x4A || i >= 0x75) ||
       
  1571 		(v = 20);
       
  1572 		d->r = v;
       
  1573 		d->g = 0;
       
  1574 		d->b = 0;
       
  1575 		d++;
       
  1576 
       
  1577 		i ^= 0x40;
       
  1578 		(v = 255, i < 0x3f) ||
       
  1579 		(v = 128, i < 0x4A || i >= 0x75) ||
       
  1580 		(v = 20);
       
  1581 		d->r = v;
       
  1582 		d->g = 0;
       
  1583 		d->b = 0;
       
  1584 		d++;
       
  1585 	}
       
  1586 
       
  1587 	// Handle lighthouse and stadium animation
       
  1588 	s = ev->lighthouse;
       
  1589 	j = EXTR(256, 4);
       
  1590 	for (i = 0; i != 4; i++) {
       
  1591 		*d++ = s[j];
       
  1592 		j++;
       
  1593 		if (j == 4) j = 0;
       
  1594 	}
       
  1595 
       
  1596 	// Animate water for old DOS graphics
       
  1597 	if (_use_dos_palette) {
       
  1598 		// Dark blue water DOS
       
  1599 		s = (_opt.landscape == LT_CANDY) ? ev->ac : ev->a;
       
  1600 		j = EXTR(320, 5);
       
  1601 		for (i = 0; i != 5; i++) {
       
  1602 			*d++ = s[j];
       
  1603 			j++;
       
  1604 			if (j == 5) j = 0;
       
  1605 		}
       
  1606 
       
  1607 		// Glittery water DOS
       
  1608 		s = (_opt.landscape == LT_CANDY) ? ev->bc : ev->b;
       
  1609 		j = EXTR(128, 15);
       
  1610 		for (i = 0; i != 5; i++) {
       
  1611 			*d++ = s[j];
       
  1612 			j += 3;
       
  1613 			if (j >= 15) j -= 15;
       
  1614 		}
       
  1615 	}
       
  1616 
       
  1617 	if (memcmp(old_val, &_cur_palette[217], c * sizeof(*old_val)) != 0) {
       
  1618 		if (_pal_first_dirty > 217) _pal_first_dirty = 217;
       
  1619 		if (_pal_last_dirty < 217 + c) _pal_last_dirty = 217 + c;
       
  1620 	}
       
  1621 }
       
  1622 
       
  1623 
       
  1624 void LoadStringWidthTable(void)
       
  1625 {
       
  1626 	uint i;
       
  1627 
       
  1628 	/* Normal font */
       
  1629 	for (i = 0; i != 224; i++) {
       
  1630 		_stringwidth_table[FS_NORMAL][i] = GetGlyphWidth(FS_NORMAL, i + 32);
       
  1631 	}
       
  1632 
       
  1633 	/* Small font */
       
  1634 	for (i = 0; i != 224; i++) {
       
  1635 		_stringwidth_table[FS_SMALL][i] = GetGlyphWidth(FS_SMALL, i + 32);
       
  1636 	}
       
  1637 
       
  1638 	/* Large font */
       
  1639 	for (i = 0; i != 224; i++) {
       
  1640 		_stringwidth_table[FS_LARGE][i] = GetGlyphWidth(FS_LARGE, i + 32);
       
  1641 	}
       
  1642 }
       
  1643 
       
  1644 
       
  1645 byte GetCharacterWidth(FontSize size, WChar key)
       
  1646 {
       
  1647 	if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
       
  1648 
       
  1649 	return GetGlyphWidth(size, key);
       
  1650 }
       
  1651 
       
  1652 
       
  1653 void ScreenSizeChanged(void)
       
  1654 {
       
  1655 	// check the dirty rect
       
  1656 	if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
       
  1657 	if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
       
  1658 
       
  1659 	// screen size changed and the old bitmap is invalid now, so we don't want to undraw it
       
  1660 	_cursor.visible = false;
       
  1661 }
       
  1662 
       
  1663 void UndrawMouseCursor(void)
       
  1664 {
       
  1665 	if (_cursor.visible) {
       
  1666 		_cursor.visible = false;
       
  1667 		memcpy_pitch(
       
  1668 			_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
       
  1669 			_cursor_backup,
       
  1670 			_cursor.draw_size.x, _cursor.draw_size.y, _cursor.draw_size.x, _screen.pitch);
       
  1671 
       
  1672 		_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
       
  1673 	}
       
  1674 }
       
  1675 
       
  1676 void DrawMouseCursor(void)
       
  1677 {
       
  1678 	int x;
       
  1679 	int y;
       
  1680 	int w;
       
  1681 	int h;
       
  1682 
       
  1683 	/* Redraw mouse cursor but only when it's inside the window */
       
  1684 	if (!_cursor.in_window) return;
       
  1685 
       
  1686 	// Don't draw the mouse cursor if it's already drawn
       
  1687 	if (_cursor.visible) {
       
  1688 		if (!_cursor.dirty) return;
       
  1689 		UndrawMouseCursor();
       
  1690 	}
       
  1691 
       
  1692 	w = _cursor.size.x;
       
  1693 	x = _cursor.pos.x + _cursor.offs.x;
       
  1694 	if (x < 0) {
       
  1695 		w += x;
       
  1696 		x = 0;
       
  1697 	}
       
  1698 	if (w > _screen.width - x) w = _screen.width - x;
       
  1699 	if (w <= 0) return;
       
  1700 	_cursor.draw_pos.x = x;
       
  1701 	_cursor.draw_size.x = w;
       
  1702 
       
  1703 	h = _cursor.size.y;
       
  1704 	y = _cursor.pos.y + _cursor.offs.y;
       
  1705 	if (y < 0) {
       
  1706 		h += y;
       
  1707 		y = 0;
       
  1708 	}
       
  1709 	if (h > _screen.height - y) h = _screen.height - y;
       
  1710 	if (h <= 0) return;
       
  1711 	_cursor.draw_pos.y = y;
       
  1712 	_cursor.draw_size.y = h;
       
  1713 
       
  1714 	assert(w * h < (int)sizeof(_cursor_backup));
       
  1715 
       
  1716 	// Make backup of stuff below cursor
       
  1717 	memcpy_pitch(
       
  1718 		_cursor_backup,
       
  1719 		_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
       
  1720 		_cursor.draw_size.x, _cursor.draw_size.y, _screen.pitch, _cursor.draw_size.x);
       
  1721 
       
  1722 	// Draw cursor on screen
       
  1723 	_cur_dpi = &_screen;
       
  1724 	DrawSprite(_cursor.sprite, _cursor.pos.x, _cursor.pos.y);
       
  1725 
       
  1726 	_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
       
  1727 
       
  1728 	_cursor.visible = true;
       
  1729 	_cursor.dirty = false;
       
  1730 }
       
  1731 
       
  1732 #if defined(_DEBUG)
       
  1733 static void DbgScreenRect(int left, int top, int right, int bottom)
       
  1734 {
       
  1735 	DrawPixelInfo dp;
       
  1736 	DrawPixelInfo *old;
       
  1737 
       
  1738 	old = _cur_dpi;
       
  1739 	_cur_dpi = &dp;
       
  1740 	dp = _screen;
       
  1741 	GfxFillRect(left, top, right - 1, bottom - 1, rand() & 255);
       
  1742 	_cur_dpi = old;
       
  1743 }
       
  1744 #endif
       
  1745 
       
  1746 void RedrawScreenRect(int left, int top, int right, int bottom)
       
  1747 {
       
  1748 	assert(right <= _screen.width && bottom <= _screen.height);
       
  1749 	if (_cursor.visible) {
       
  1750 		if (right > _cursor.draw_pos.x &&
       
  1751 				left < _cursor.draw_pos.x + _cursor.draw_size.x &&
       
  1752 				bottom > _cursor.draw_pos.y &&
       
  1753 				top < _cursor.draw_pos.y + _cursor.draw_size.y) {
       
  1754 			UndrawMouseCursor();
       
  1755 		}
       
  1756 	}
       
  1757 	UndrawTextMessage();
       
  1758 
       
  1759 #if defined(_DEBUG)
       
  1760 	if (_dbg_screen_rect)
       
  1761 		DbgScreenRect(left, top, right, bottom);
       
  1762 	else
       
  1763 #endif
       
  1764 		DrawOverlappedWindowForAll(left, top, right, bottom);
       
  1765 
       
  1766 	_video_driver->make_dirty(left, top, right - left, bottom - top);
       
  1767 }
       
  1768 
       
  1769 void DrawDirtyBlocks(void)
       
  1770 {
       
  1771 	byte *b = _dirty_blocks;
       
  1772 	const int w = ALIGN(_screen.width, 64);
       
  1773 	const int h = ALIGN(_screen.height, 8);
       
  1774 	int x;
       
  1775 	int y;
       
  1776 
       
  1777 	if (IsGeneratingWorld() && !IsGeneratingWorldReadyForPaint()) return;
       
  1778 
       
  1779 	y = 0;
       
  1780 	do {
       
  1781 		x = 0;
       
  1782 		do {
       
  1783 			if (*b != 0) {
       
  1784 				int left;
       
  1785 				int top;
       
  1786 				int right = x + 64;
       
  1787 				int bottom = y;
       
  1788 				byte *p = b;
       
  1789 				int h2;
       
  1790 
       
  1791 				// First try coalescing downwards
       
  1792 				do {
       
  1793 					*p = 0;
       
  1794 					p += DIRTY_BYTES_PER_LINE;
       
  1795 					bottom += 8;
       
  1796 				} while (bottom != h && *p != 0);
       
  1797 
       
  1798 				// Try coalescing to the right too.
       
  1799 				h2 = (bottom - y) >> 3;
       
  1800 				assert(h2 > 0);
       
  1801 				p = b;
       
  1802 
       
  1803 				while (right != w) {
       
  1804 					byte *p2 = ++p;
       
  1805 					int h = h2;
       
  1806 					// Check if a full line of dirty flags is set.
       
  1807 					do {
       
  1808 						if (!*p2) goto no_more_coalesc;
       
  1809 						p2 += DIRTY_BYTES_PER_LINE;
       
  1810 					} while (--h != 0);
       
  1811 
       
  1812 					// Wohoo, can combine it one step to the right!
       
  1813 					// Do that, and clear the bits.
       
  1814 					right += 64;
       
  1815 
       
  1816 					h = h2;
       
  1817 					p2 = p;
       
  1818 					do {
       
  1819 						*p2 = 0;
       
  1820 						p2 += DIRTY_BYTES_PER_LINE;
       
  1821 					} while (--h != 0);
       
  1822 				}
       
  1823 				no_more_coalesc:
       
  1824 
       
  1825 				left = x;
       
  1826 				top = y;
       
  1827 
       
  1828 				if (left   < _invalid_rect.left  ) left   = _invalid_rect.left;
       
  1829 				if (top    < _invalid_rect.top   ) top    = _invalid_rect.top;
       
  1830 				if (right  > _invalid_rect.right ) right  = _invalid_rect.right;
       
  1831 				if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
       
  1832 
       
  1833 				if (left < right && top < bottom) {
       
  1834 					RedrawScreenRect(left, top, right, bottom);
       
  1835 				}
       
  1836 
       
  1837 			}
       
  1838 		} while (b++, (x += 64) != w);
       
  1839 	} while (b += -(w >> 6) + DIRTY_BYTES_PER_LINE, (y += 8) != h);
       
  1840 
       
  1841 	_invalid_rect.left = w;
       
  1842 	_invalid_rect.top = h;
       
  1843 	_invalid_rect.right = 0;
       
  1844 	_invalid_rect.bottom = 0;
       
  1845 
       
  1846 	/* If we are generating a world, and waiting for a paint run, mark it here
       
  1847 	 *  as done painting, so we can continue generating. */
       
  1848 	if (IsGeneratingWorld() && IsGeneratingWorldReadyForPaint()) {
       
  1849 		SetGeneratingWorldPaintStatus(false);
       
  1850 	}
       
  1851 }
       
  1852 
       
  1853 
       
  1854 void SetDirtyBlocks(int left, int top, int right, int bottom)
       
  1855 {
       
  1856 	byte *b;
       
  1857 	int width;
       
  1858 	int height;
       
  1859 
       
  1860 	if (left < 0) left = 0;
       
  1861 	if (top < 0) top = 0;
       
  1862 	if (right > _screen.width) right = _screen.width;
       
  1863 	if (bottom > _screen.height) bottom = _screen.height;
       
  1864 
       
  1865 	if (left >= right || top >= bottom) return;
       
  1866 
       
  1867 	if (left   < _invalid_rect.left  ) _invalid_rect.left   = left;
       
  1868 	if (top    < _invalid_rect.top   ) _invalid_rect.top    = top;
       
  1869 	if (right  > _invalid_rect.right ) _invalid_rect.right  = right;
       
  1870 	if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
       
  1871 
       
  1872 	left >>= 6;
       
  1873 	top  >>= 3;
       
  1874 
       
  1875 	b = _dirty_blocks + top * DIRTY_BYTES_PER_LINE + left;
       
  1876 
       
  1877 	width  = ((right  - 1) >> 6) - left + 1;
       
  1878 	height = ((bottom - 1) >> 3) - top  + 1;
       
  1879 
       
  1880 	assert(width > 0 && height > 0);
       
  1881 
       
  1882 	do {
       
  1883 		int i = width;
       
  1884 
       
  1885 		do b[--i] = 0xFF; while (i);
       
  1886 
       
  1887 		b += DIRTY_BYTES_PER_LINE;
       
  1888 	} while (--height != 0);
       
  1889 }
       
  1890 
       
  1891 void MarkWholeScreenDirty(void)
       
  1892 {
       
  1893 	SetDirtyBlocks(0, 0, _screen.width, _screen.height);
       
  1894 }
       
  1895 
       
  1896 /** Set up a clipping area for only drawing into a certain area. To do this,
       
  1897  * Fill a DrawPixelInfo object with the supplied relative rectangle, backup
       
  1898  * the original (calling) _cur_dpi and assign the just returned DrawPixelInfo
       
  1899  * _cur_dpi. When you are done, give restore _cur_dpi's original value
       
  1900  * @param *n the DrawPixelInfo that will be the clipping rectangle box allowed
       
  1901  * for drawing
       
  1902  * @param left,top,width,height the relative coordinates of the clipping
       
  1903  * rectangle relative to the current _cur_dpi. This will most likely be the
       
  1904  * offset from the calling window coordinates
       
  1905  * @return return false if the requested rectangle is not possible with the
       
  1906  * current dpi pointer. Only continue of the return value is true, or you'll
       
  1907  * get some nasty results */
       
  1908 bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
       
  1909 {
       
  1910 	const DrawPixelInfo *o = _cur_dpi;
       
  1911 
       
  1912 	n->zoom = 0;
       
  1913 
       
  1914 	assert(width > 0);
       
  1915 	assert(height > 0);
       
  1916 
       
  1917 	if ((left -= o->left) < 0) {
       
  1918 		width += left;
       
  1919 		if (width <= 0) return false;
       
  1920 		n->left = -left;
       
  1921 		left = 0;
       
  1922 	} else {
       
  1923 		n->left = 0;
       
  1924 	}
       
  1925 
       
  1926 	if (width > o->width - left) {
       
  1927 		width = o->width - left;
       
  1928 		if (width <= 0) return false;
       
  1929 	}
       
  1930 	n->width = width;
       
  1931 
       
  1932 	if ((top -= o->top) < 0) {
       
  1933 		height += top;
       
  1934 		if (height <= 0) return false;
       
  1935 		n->top = -top;
       
  1936 		top = 0;
       
  1937 	} else {
       
  1938 		n->top = 0;
       
  1939 	}
       
  1940 
       
  1941 	n->dst_ptr = o->dst_ptr + left + top * (n->pitch = o->pitch);
       
  1942 
       
  1943 	if (height > o->height - top) {
       
  1944 		height = o->height - top;
       
  1945 		if (height <= 0) return false;
       
  1946 	}
       
  1947 	n->height = height;
       
  1948 
       
  1949 	return true;
       
  1950 }
       
  1951 
       
  1952 static void SetCursorSprite(CursorID cursor)
       
  1953 {
       
  1954 	CursorVars *cv = &_cursor;
       
  1955 	const Sprite *p;
       
  1956 
       
  1957 	if (cv->sprite == cursor) return;
       
  1958 
       
  1959 	p = GetSprite(cursor & SPRITE_MASK);
       
  1960 	cv->sprite = cursor;
       
  1961 	cv->size.y = p->height;
       
  1962 	cv->size.x = p->width;
       
  1963 	cv->offs.x = p->x_offs;
       
  1964 	cv->offs.y = p->y_offs;
       
  1965 
       
  1966 	cv->dirty = true;
       
  1967 }
       
  1968 
       
  1969 static void SwitchAnimatedCursor(void)
       
  1970 {
       
  1971 	CursorVars *cv = &_cursor;
       
  1972 	const CursorID *cur = cv->animate_cur;
       
  1973 	CursorID sprite;
       
  1974 
       
  1975 	// ANIM_CURSOR_END is 0xFFFF in table/animcursors.h
       
  1976 	if (cur == NULL || *cur == 0xFFFF) cur = cv->animate_list;
       
  1977 
       
  1978 	sprite = cur[0];
       
  1979 	cv->animate_timeout = cur[1];
       
  1980 	cv->animate_cur = cur + 2;
       
  1981 
       
  1982 	SetCursorSprite(sprite);
       
  1983 }
       
  1984 
       
  1985 void CursorTick(void)
       
  1986 {
       
  1987 	if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0)
       
  1988 		SwitchAnimatedCursor();
       
  1989 }
       
  1990 
       
  1991 void SetMouseCursor(CursorID cursor)
       
  1992 {
       
  1993 	// Turn off animation
       
  1994 	_cursor.animate_timeout = 0;
       
  1995 	// Set cursor
       
  1996 	SetCursorSprite(cursor);
       
  1997 }
       
  1998 
       
  1999 void SetAnimatedMouseCursor(const CursorID *table)
       
  2000 {
       
  2001 	_cursor.animate_list = table;
       
  2002 	_cursor.animate_cur = NULL;
       
  2003 	SwitchAnimatedCursor();
       
  2004 }
       
  2005 
       
  2006 bool ChangeResInGame(int w, int h)
       
  2007 {
       
  2008 	return
       
  2009 		(_screen.width == w && _screen.height == h) ||
       
  2010 		_video_driver->change_resolution(w, h);
       
  2011 }
       
  2012 
       
  2013 void ToggleFullScreen(bool fs)
       
  2014 {
       
  2015 	_video_driver->toggle_fullscreen(fs);
       
  2016 	if (_fullscreen != fs && _num_resolutions == 0) {
       
  2017 		DEBUG(driver, 0, "Could not find a suitable fullscreen resolution");
       
  2018 	}
       
  2019 }
       
  2020 
       
  2021 static int CDECL compare_res(const void *pa, const void *pb)
       
  2022 {
       
  2023 	int x = ((const uint16*)pa)[0] - ((const uint16*)pb)[0];
       
  2024 	if (x != 0) return x;
       
  2025 	return ((const uint16*)pa)[1] - ((const uint16*)pb)[1];
       
  2026 }
       
  2027 
       
  2028 void SortResolutions(int count)
       
  2029 {
       
  2030 	qsort(_resolutions, count, sizeof(_resolutions[0]), compare_res);
       
  2031 }