|
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 } |