70 static const byte *_colour_remap_ptr;
73 static const uint DIRTY_BLOCK_HEIGHT = 8;
74 static const uint DIRTY_BLOCK_WIDTH = 64;
76 static uint _dirty_bytes_per_line = 0;
77 static byte *_dirty_blocks = NULL;
78 extern uint _dirty_block_colour;
80 void GfxScroll(
int left,
int top,
int width,
int height,
int xo,
int yo)
84 if (xo == 0 && yo == 0)
return;
86 if (_cursor.
visible) UndrawMouseCursor();
92 blitter->
ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
117 const int otop = top;
118 const int oleft = left;
121 if (left > right || top > bottom)
return;
122 if (right < dpi->left || left >= dpi->left + dpi->width)
return;
123 if (bottom < dpi->top || top >= dpi->top + dpi->height)
return;
125 if ( (left -= dpi->left) < 0) left = 0;
126 right = right - dpi->left + 1;
127 if (right > dpi->width) right = dpi->width;
131 if ( (top -= dpi->top) < 0) top = 0;
132 bottom = bottom - dpi->top + 1;
133 if (bottom > dpi->height) bottom = dpi->height;
137 dst = blitter->
MoveTo(dpi->dst_ptr, left, top);
141 blitter->
DrawRect(dst, right, bottom, (uint8)colour);
149 byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
151 for (
int i = (bo ^= 1); i < right; i += 2) blitter->
SetPixel(dst, i, 0, (uint8)colour);
152 dst = blitter->
MoveTo(dst, 0, 1);
153 }
while (--bottom > 0);
173 static inline void GfxDoDrawLine(
void *video,
int x,
int y,
int x2,
int y2,
int screen_width,
int screen_height, uint8 colour,
int width,
int dash = 0)
179 if (y2 == y || x2 == x) {
181 blitter->
DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
185 int grade_y = y2 - y;
186 int grade_x = x2 - x;
189 int extra = (int)
CeilDiv(3 * width, 4);
190 Rect clip = { -extra, -extra, screen_width - 1 + extra, screen_height - 1 + extra };
194 while (INT_MAX /
abs(grade_y) <
max(
abs(clip.left - x),
abs(clip.right - x))) {
204 int left_isec_y = y + (clip.left - x) * grade_y / grade_x;
205 int right_isec_y = y + (clip.right - x) * grade_y / grade_x;
206 if ((left_isec_y > clip.bottom + margin && right_isec_y > clip.bottom + margin) ||
207 (left_isec_y < clip.top - margin && right_isec_y < clip.top - margin)) {
217 blitter->
DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
239 if (x + width / 2 < 0 && x2 + width / 2 < 0 )
return false;
240 if (y + width / 2 < 0 && y2 + width / 2 < 0 )
return false;
241 if (x - width / 2 > dpi->width && x2 - width / 2 > dpi->width )
return false;
242 if (y - width / 2 > dpi->height && y2 - width / 2 > dpi->height)
return false;
246 void GfxDrawLine(
int x,
int y,
int x2,
int y2,
int colour,
int width,
int dash)
250 GfxDoDrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour, width, dash);
254 void GfxDrawLineUnscaled(
int x,
int y,
int x2,
int y2,
int colour)
278 void DrawBox(
int x,
int y,
int dx1,
int dy1,
int dx2,
int dy2,
int dx3,
int dy3)
295 static const byte colour =
PC_WHITE;
297 GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
298 GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
299 GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
301 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
302 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
303 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
304 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
305 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
306 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
315 if (colour == TC_INVALID)
return;
319 bool no_shade = (colour &
TC_NO_SHADE) != 0 || colour == TC_BLACK;
345 if (line->CountRuns() == 0)
return 0;
347 int w = line->GetWidth();
348 int h = line->GetLeading();
362 int max_w = right - left + 1;
368 truncation &= max_w < w;
370 const Sprite *dot_sprite = NULL;
379 FontCache *fc = ((
const Font*)line->GetVisualRun(0)->GetFont())->fc;
382 dot_sprite = fc->
GetGlyph(dot_glyph);
385 min_x += 3 * dot_width;
386 offset_x = w - 3 * dot_width - max_w;
388 max_x -= 3 * dot_width;
402 switch (align & SA_HOR_MASK) {
405 right = left + w - 1;
411 right = left + w - 1;
415 left = right + 1 - w;
423 bool draw_shadow =
false;
424 for (
int run_index = 0; run_index < line->CountRuns(); run_index++) {
426 const Font *f = (
const Font*)run->GetFont();
433 int dpi_left = dpi->left;
434 int dpi_right = dpi->left + dpi->width - 1;
438 for (
int i = 0; i < run->GetGlyphCount(); i++) {
439 GlyphID glyph = run->GetGlyphs()[i];
442 if (glyph == 0xFFFF)
continue;
444 int begin_x = (int)run->GetPositions()[i * 2] + left - offset_x;
445 int end_x = (int)run->GetPositions()[i * 2 + 2] + left - offset_x - 1;
446 int top = (int)run->GetPositions()[i * 2 + 1] + y;
449 if (truncation && (begin_x < min_x || end_x > max_x))
continue;
453 if (begin_x + sprite->
x_offs > dpi_right || begin_x + sprite->
x_offs + sprite->
width < dpi_left)
continue;
455 if (draw_shadow && (glyph & SPRITE_GLYPH) == 0) {
466 for (
int i = 0; i < 3; i++, x += dot_width) {
480 return (align & SA_HOR_MASK) ==
SA_RIGHT ? left : right;
505 int extra = max_height / 2;
507 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > top + max_height + extra ||
508 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
512 Layouter layout(str, INT32_MAX, colour, fontsize);
513 if (layout.
Length() == 0)
return 0;
537 GetString(buffer, str,
lastof(buffer));
538 return DrawString(left, right, top, buffer, colour, align, underline, fontsize);
549 Layouter layout(str, maxw, TC_FROMSTRING, fontsize);
562 GetString(buffer, str,
lastof(buffer));
575 GetString(buffer, str,
lastof(buffer));
622 int maxw = right - left + 1;
623 int maxh = bottom - top + 1;
627 if (maxh <= 0)
return top;
629 Layouter layout(str, maxw, colour, fontsize);
630 int total_height = layout.
GetBounds().height;
638 y =
RoundDivSU(bottom + top - total_height, 2);
642 y = bottom - total_height;
645 default: NOT_REACHED();
649 int first_line = bottom;
654 int line_height = line->GetLeading();
655 if (y >= top && y < bottom) {
656 last_line = y + line_height;
657 if (first_line > y) first_line = y;
664 return ((align & SA_VERT_MASK) ==
SA_BOTTOM) ? first_line : last_line;
685 GetString(buffer, str,
lastof(buffer));
686 return DrawStringMultiLine(left, right, top, bottom, buffer, colour, align, underline, fontsize);
701 Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
715 GetString(buffer, strid,
lastof(buffer));
729 Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
742 if (x < 0)
return NULL;
744 Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
772 if (offset != NULL) {
812 }
else if (pal != PAL_NONE) {
820 GfxMainBlitterViewport(GetSprite(real_sprite,
ST_NORMAL), x, y,
BM_NORMAL, sub, real_sprite);
839 }
else if (pal != PAL_NONE) {
847 GfxMainBlitter(GetSprite(real_sprite,
ST_NORMAL), x, y,
BM_NORMAL, sub, real_sprite, zoom);
862 template <
int ZOOM_BASE,
bool SCALED_XY>
886 int clip_left =
max(0, -sprite->
x_offs + sub->left * ZOOM_BASE );
887 int clip_top =
max(0, -sprite->
y_offs + sub->top * ZOOM_BASE );
888 int clip_right =
max(0, sprite->
width - (-sprite->
x_offs + (sub->right + 1) * ZOOM_BASE));
889 int clip_bottom =
max(0, sprite->
height - (-sprite->
y_offs + (sub->bottom + 1) * ZOOM_BASE));
891 if (clip_left + clip_right >= sprite->
width)
return;
892 if (clip_top + clip_bottom >= sprite->
height)
return;
910 bp.
dst = dpi->dst_ptr;
911 bp.
pitch = dpi->pitch;
912 bp.
remap = _colour_remap_ptr;
914 assert(sprite->
width > 0);
915 assert(sprite->
height > 0);
917 if (bp.
width <= 0)
return;
918 if (bp.
height <= 0)
return;
920 y -= SCALED_XY ?
ScaleByZoom(dpi->top, zoom) : dpi->top;
925 if (bp.
height <= 0)
return;
936 if (bp.
height <= 0)
return;
939 x -= SCALED_XY ?
ScaleByZoom(dpi->left, zoom) : dpi->left;
943 bp.
width -= -x_unscaled;
944 if (bp.
width <= 0)
return;
948 bp.
left = x_unscaled;
955 if (bp.
width <= 0)
return;
969 if (topleft <= clicked && clicked <= bottomright) {
970 uint offset = (((size_t)clicked - (
size_t)topleft) / (blitter->
GetScreenDepth() / 8)) % bp.
pitch;
971 if (offset < (uint)bp.
width) {
982 GfxBlitter<ZOOM_LVL_BASE, false>(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom);
987 GfxBlitter<1, true>(sprite, x, y, mode, sub, sprite_id, zoom);
990 void DoPaletteAnimations();
992 void GfxInitPalettes()
994 memcpy(&_cur_palette, &
_palette,
sizeof(_cur_palette));
995 DoPaletteAnimations();
998 #define EXTR(p, q) (((uint16)(palette_animation_counter * (p)) * (q)) >> 16)
999 #define EXTR2(p, q) (((uint16)(~palette_animation_counter * (p)) * (q)) >> 16)
1001 void DoPaletteAnimations()
1004 static int palette_animation_counter = 0;
1005 palette_animation_counter += 8;
1011 const uint old_tc = palette_animation_counter;
1016 palette_animation_counter = 0;
1022 memcpy(old_val, palette_pos,
sizeof(old_val));
1028 *palette_pos++ = s[j];
1037 *palette_pos++ = s[j];
1044 byte i = (palette_animation_counter >> 1) & 0x7F;
1049 }
else if (i < 0x4A || i >= 0x75) {
1062 }
else if (i < 0x4A || i >= 0x75) {
1077 *palette_pos++ = s[j];
1086 *palette_pos++ = s[j];
1095 *palette_pos++ = s[j];
1101 palette_animation_counter = old_tc;
1121 uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114;
1123 return sq1000_brightness < 128 * 128 * 1000 ? TC_WHITE : TC_BLACK;
1133 for (uint i = 0; i != 224; i++) {
1164 for (
char c =
'0'; c <=
'9'; c++) {
1179 for (
char c =
'9'; c >=
'0'; c--) {
1184 if (c !=
'0') *front = c -
'0';
1189 void ScreenSizeChanged()
1191 _dirty_bytes_per_line =
CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH);
1192 _dirty_blocks = ReallocT<byte>(_dirty_blocks, _dirty_bytes_per_line *
CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT));
1195 if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
1196 if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
1202 void UndrawMouseCursor()
1205 if (_screen.dst_ptr == NULL)
return;
1215 void DrawMouseCursor()
1223 if (_screen.dst_ptr == NULL)
return;
1232 if (!_cursor.
dirty)
return;
1233 UndrawMouseCursor();
1237 int left = _cursor.
pos.x + _cursor.total_offs.x;
1243 if (left + width > _screen.width) {
1244 width = _screen.width - left;
1246 if (width <= 0)
return;
1248 int top = _cursor.
pos.y + _cursor.total_offs.y;
1254 if (top + height > _screen.height) {
1255 height = _screen.height - top;
1257 if (height <= 0)
return;
1259 _cursor.draw_pos.x = left;
1260 _cursor.draw_pos.y = top;
1270 _cur_dpi = &_screen;
1278 _cursor.
dirty =
false;
1281 void RedrawScreenRect(
int left,
int top,
int right,
int bottom)
1283 assert(right <= _screen.width && bottom <= _screen.height);
1285 if (right > _cursor.draw_pos.x &&
1286 left < _cursor.draw_pos.x + _cursor.
draw_size.x &&
1287 bottom > _cursor.draw_pos.y &&
1288 top < _cursor.draw_pos.y + _cursor.
draw_size.y) {
1289 UndrawMouseCursor();
1293 #ifdef ENABLE_NETWORK
1309 byte *b = _dirty_blocks;
1310 const int w =
Align(_screen.width, DIRTY_BLOCK_WIDTH);
1311 const int h =
Align(_screen.height, DIRTY_BLOCK_HEIGHT);
1341 int right = x + DIRTY_BLOCK_WIDTH;
1349 p += _dirty_bytes_per_line;
1350 bottom += DIRTY_BLOCK_HEIGHT;
1351 }
while (bottom != h && *p != 0);
1354 h2 = (bottom - y) / DIRTY_BLOCK_HEIGHT;
1358 while (right != w) {
1363 if (!*p2)
goto no_more_coalesc;
1364 p2 += _dirty_bytes_per_line;
1369 right += DIRTY_BLOCK_WIDTH;
1375 p2 += _dirty_bytes_per_line;
1383 if (left < _invalid_rect.left ) left = _invalid_rect.left;
1384 if (top < _invalid_rect.top ) top = _invalid_rect.top;
1385 if (right > _invalid_rect.right ) right = _invalid_rect.right;
1386 if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
1388 if (left < right && top < bottom) {
1389 RedrawScreenRect(left, top, right, bottom);
1393 }
while (b++, (x += DIRTY_BLOCK_WIDTH) != w);
1394 }
while (b += -(
int)(w / DIRTY_BLOCK_WIDTH) + _dirty_bytes_per_line, (y += DIRTY_BLOCK_HEIGHT) != h);
1396 ++_dirty_block_colour;
1397 _invalid_rect.left = w;
1398 _invalid_rect.top = h;
1399 _invalid_rect.right = 0;
1400 _invalid_rect.bottom = 0;
1424 if (left < 0) left = 0;
1425 if (top < 0) top = 0;
1426 if (right > _screen.width) right = _screen.width;
1427 if (bottom > _screen.height) bottom = _screen.height;
1429 if (left >= right || top >= bottom)
return;
1431 if (left < _invalid_rect.left ) _invalid_rect.left = left;
1432 if (top < _invalid_rect.top ) _invalid_rect.top = top;
1433 if (right > _invalid_rect.right ) _invalid_rect.right = right;
1434 if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
1436 left /= DIRTY_BLOCK_WIDTH;
1437 top /= DIRTY_BLOCK_HEIGHT;
1439 b = _dirty_blocks + top * _dirty_bytes_per_line + left;
1441 width = ((right - 1) / DIRTY_BLOCK_WIDTH) - left + 1;
1442 height = ((bottom - 1) / DIRTY_BLOCK_HEIGHT) - top + 1;
1444 assert(width > 0 && height > 0);
1449 do b[--i] = 0xFF;
while (i != 0);
1451 b += _dirty_bytes_per_line;
1452 }
while (--height != 0);
1490 if ((left -= o->left) < 0) {
1492 if (width <= 0)
return false;
1499 if (width > o->width - left) {
1500 width = o->width - left;
1501 if (width <= 0)
return false;
1505 if ((top -= o->top) < 0) {
1507 if (height <= 0)
return false;
1514 n->dst_ptr = blitter->
MoveTo(o->dst_ptr, left, top);
1515 n->pitch = o->pitch;
1517 if (height > o->height - top) {
1518 height = o->height - top;
1519 if (height <= 0)
return false;
1546 _cursor.total_offs = offs;
1549 int right =
max(_cursor.total_offs.x + _cursor.
total_size.x, offs.x + size.x);
1550 int bottom =
max(_cursor.total_offs.y + _cursor.
total_size.y, offs.y + size.y);
1551 if (offs.x < _cursor.total_offs.x) _cursor.total_offs.x = offs.x;
1552 if (offs.y < _cursor.total_offs.y) _cursor.total_offs.y = offs.y;
1553 _cursor.
total_size.x = right - _cursor.total_offs.x;
1554 _cursor.
total_size.y = bottom - _cursor.total_offs.y;
1558 _cursor.
dirty =
true;
1579 static void SwitchAnimatedCursor()
1594 SwitchAnimatedCursor();
1635 SwitchAnimatedCursor();
1658 if (x == this->
pos.x && y == this->pos.y) {
1660 this->queued_warp =
false;
1663 this->
delta.x = x - (this->queued_warp ? this->last_position.x : this->
pos.x);
1664 this->
delta.y = y - (this->queued_warp ? this->last_position.y : this->
pos.y);
1666 this->last_position.x = x;
1667 this->last_position.y = y;
1669 bool need_warp =
false;
1671 if (this->
delta.x != 0 || this->delta.y != 0) {
1676 this->queued_warp = queued_warp;
1679 }
else if (this->
pos.x != x || this->pos.y != y) {
1680 this->queued_warp =
false;
1688 bool ChangeResInGame(
int width,
int height)
1693 bool ToggleFullScreen(
bool fs)
1697 DEBUG(driver, 0,
"Could not find a suitable fullscreen resolution");
1704 int x = pa->width - pb->width;
1705 if (x != 0)
return x;
1706 return pa->height - pb->height;
1709 void SortResolutions(
int count)