69 static const byte *_colour_remap_ptr;
72 static const uint DIRTY_BLOCK_HEIGHT = 8;
73 static const uint DIRTY_BLOCK_WIDTH = 64;
75 static uint _dirty_bytes_per_line = 0;
76 static byte *_dirty_blocks = NULL;
77 extern uint _dirty_block_colour;
79 void GfxScroll(
int left,
int top,
int width,
int height,
int xo,
int yo)
83 if (xo == 0 && yo == 0)
return;
85 if (_cursor.
visible) UndrawMouseCursor();
91 blitter->
ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
116 const int otop = top;
117 const int oleft = left;
120 if (left > right || top > bottom)
return;
121 if (right < dpi->left || left >= dpi->left + dpi->width)
return;
122 if (bottom < dpi->top || top >= dpi->top + dpi->height)
return;
124 if ( (left -= dpi->left) < 0) left = 0;
125 right = right - dpi->left + 1;
126 if (right > dpi->width) right = dpi->width;
130 if ( (top -= dpi->top) < 0) top = 0;
131 bottom = bottom - dpi->top + 1;
132 if (bottom > dpi->height) bottom = dpi->height;
136 dst = blitter->
MoveTo(dpi->dst_ptr, left, top);
140 blitter->
DrawRect(dst, right, bottom, (uint8)colour);
148 byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
150 for (
int i = (bo ^= 1); i < right; i += 2) blitter->
SetPixel(dst, i, 0, (uint8)colour);
151 dst = blitter->
MoveTo(dst, 0, 1);
152 }
while (--bottom > 0);
172 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)
178 if (y2 == y || x2 == x) {
180 blitter->
DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
184 int grade_y = y2 - y;
185 int grade_x = x2 - x;
188 uint extra =
CeilDiv(3 * width, 4);
189 Rect clip = { -extra, -extra, screen_width - 1 + extra, screen_height - 1 + extra };
193 while (INT_MAX /
abs(grade_y) <
max(
abs(clip.left - x),
abs(clip.right - x))) {
203 int left_isec_y = y + (clip.left - x) * grade_y / grade_x;
204 int right_isec_y = y + (clip.right - x) * grade_y / grade_x;
205 if ((left_isec_y > clip.bottom + margin && right_isec_y > clip.bottom + margin) ||
206 (left_isec_y < clip.top - margin && right_isec_y < clip.top - margin)) {
216 blitter->
DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
238 if (x + width / 2 < 0 && x2 + width / 2 < 0 )
return false;
239 if (y + width / 2 < 0 && y2 + width / 2 < 0 )
return false;
240 if (x - width / 2 > dpi->width && x2 - width / 2 > dpi->width )
return false;
241 if (y - width / 2 > dpi->height && y2 - width / 2 > dpi->height)
return false;
245 void GfxDrawLine(
int x,
int y,
int x2,
int y2,
int colour,
int width,
int dash)
249 GfxDoDrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour, width, dash);
253 void GfxDrawLineUnscaled(
int x,
int y,
int x2,
int y2,
int colour)
277 void DrawBox(
int x,
int y,
int dx1,
int dy1,
int dx2,
int dy2,
int dx3,
int dy3)
294 static const byte colour =
PC_WHITE;
296 GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
297 GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
298 GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
300 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
301 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
302 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
303 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
304 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
305 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
314 if (colour == TC_INVALID)
return;
318 bool no_shade = (colour &
TC_NO_SHADE) != 0 || colour == TC_BLACK;
344 if (line->CountRuns() == 0)
return 0;
346 int w = line->GetWidth();
347 int h = line->GetLeading();
361 int max_w = right - left + 1;
367 truncation &= max_w < w;
369 const Sprite *dot_sprite = NULL;
378 FontCache *fc = ((
const Font*)line->GetVisualRun(0)->GetFont())->fc;
381 dot_sprite = fc->
GetGlyph(dot_glyph);
384 min_x += 3 * dot_width;
385 offset_x = w - 3 * dot_width - max_w;
387 max_x -= 3 * dot_width;
401 switch (align & SA_HOR_MASK) {
404 right = left + w - 1;
410 right = left + w - 1;
414 left = right + 1 - w;
422 bool draw_shadow =
false;
423 for (
int run_index = 0; run_index < line->CountRuns(); run_index++) {
425 const Font *f = (
const Font*)run->GetFont();
432 int dpi_left = dpi->left;
433 int dpi_right = dpi->left + dpi->width - 1;
437 for (
int i = 0; i < run->GetGlyphCount(); i++) {
438 GlyphID glyph = run->GetGlyphs()[i];
441 if (glyph == 0xFFFF)
continue;
443 int begin_x = (int)run->GetPositions()[i * 2] + left - offset_x;
444 int end_x = (int)run->GetPositions()[i * 2 + 2] + left - offset_x - 1;
445 int top = (int)run->GetPositions()[i * 2 + 1] + y;
448 if (truncation && (begin_x < min_x || end_x > max_x))
continue;
452 if (begin_x + sprite->
x_offs > dpi_right || begin_x + sprite->
x_offs + sprite->
width < dpi_left)
continue;
454 if (draw_shadow && (glyph & SPRITE_GLYPH) == 0) {
465 for (
int i = 0; i < 3; i++, x += dot_width) {
479 return (align & SA_HOR_MASK) ==
SA_RIGHT ? left : right;
504 int extra = max_height / 2;
506 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > top + max_height + extra ||
507 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
511 Layouter layout(str, INT32_MAX, colour, fontsize);
512 if (layout.
Length() == 0)
return 0;
536 GetString(buffer, str,
lastof(buffer));
537 return DrawString(left, right, top, buffer, colour, align, underline, fontsize);
548 Layouter layout(str, maxw, TC_FROMSTRING, fontsize);
561 GetString(buffer, str,
lastof(buffer));
574 GetString(buffer, str,
lastof(buffer));
621 int maxw = right - left + 1;
622 int maxh = bottom - top + 1;
626 if (maxh <= 0)
return top;
628 Layouter layout(str, maxw, colour, fontsize);
629 int total_height = layout.
GetBounds().height;
637 y =
RoundDivSU(bottom + top - total_height, 2);
641 y = bottom - total_height;
644 default: NOT_REACHED();
648 int first_line = bottom;
653 int line_height = line->GetLeading();
654 if (y >= top && y < bottom) {
655 last_line = y + line_height;
656 if (first_line > y) first_line = y;
663 return ((align & SA_VERT_MASK) ==
SA_BOTTOM) ? first_line : last_line;
684 GetString(buffer, str,
lastof(buffer));
685 return DrawStringMultiLine(left, right, top, bottom, buffer, colour, align, underline, fontsize);
700 Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
714 GetString(buffer, strid,
lastof(buffer));
728 Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
741 if (x < 0)
return NULL;
743 Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
771 if (offset != NULL) {
811 }
else if (pal != PAL_NONE) {
819 GfxMainBlitterViewport(GetSprite(real_sprite,
ST_NORMAL), x, y,
BM_NORMAL, sub, real_sprite);
838 }
else if (pal != PAL_NONE) {
846 GfxMainBlitter(GetSprite(real_sprite,
ST_NORMAL), x, y,
BM_NORMAL, sub, real_sprite, zoom);
861 template <
int ZOOM_BASE,
bool SCALED_XY>
885 int clip_left =
max(0, -sprite->
x_offs + sub->left * ZOOM_BASE );
886 int clip_top =
max(0, -sprite->
y_offs + sub->top * ZOOM_BASE );
887 int clip_right =
max(0, sprite->
width - (-sprite->
x_offs + (sub->right + 1) * ZOOM_BASE));
888 int clip_bottom =
max(0, sprite->
height - (-sprite->
y_offs + (sub->bottom + 1) * ZOOM_BASE));
890 if (clip_left + clip_right >= sprite->
width)
return;
891 if (clip_top + clip_bottom >= sprite->
height)
return;
909 bp.
dst = dpi->dst_ptr;
910 bp.
pitch = dpi->pitch;
911 bp.
remap = _colour_remap_ptr;
913 assert(sprite->
width > 0);
914 assert(sprite->
height > 0);
916 if (bp.
width <= 0)
return;
917 if (bp.
height <= 0)
return;
919 y -= SCALED_XY ?
ScaleByZoom(dpi->top, zoom) : dpi->top;
924 if (bp.
height <= 0)
return;
935 if (bp.
height <= 0)
return;
938 x -= SCALED_XY ?
ScaleByZoom(dpi->left, zoom) : dpi->left;
942 bp.
width -= -x_unscaled;
943 if (bp.
width <= 0)
return;
947 bp.
left = x_unscaled;
954 if (bp.
width <= 0)
return;
968 if (topleft <= clicked && clicked <= bottomright) {
969 uint offset = (((size_t)clicked - (
size_t)topleft) / (blitter->
GetScreenDepth() / 8)) % bp.
pitch;
970 if (offset < (uint)bp.
width) {
981 GfxBlitter<ZOOM_LVL_BASE, false>(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom);
986 GfxBlitter<1, true>(sprite, x, y, mode, sub, sprite_id, zoom);
989 void DoPaletteAnimations();
991 void GfxInitPalettes()
993 memcpy(&_cur_palette, &
_palette,
sizeof(_cur_palette));
994 DoPaletteAnimations();
997 #define EXTR(p, q) (((uint16)(palette_animation_counter * (p)) * (q)) >> 16)
998 #define EXTR2(p, q) (((uint16)(~palette_animation_counter * (p)) * (q)) >> 16)
1000 void DoPaletteAnimations()
1003 static int palette_animation_counter = 0;
1004 palette_animation_counter += 8;
1010 const uint old_tc = palette_animation_counter;
1015 palette_animation_counter = 0;
1021 memcpy(old_val, palette_pos,
sizeof(old_val));
1027 *palette_pos++ = s[j];
1036 *palette_pos++ = s[j];
1043 byte i = (palette_animation_counter >> 1) & 0x7F;
1048 }
else if (i < 0x4A || i >= 0x75) {
1061 }
else if (i < 0x4A || i >= 0x75) {
1076 *palette_pos++ = s[j];
1085 *palette_pos++ = s[j];
1094 *palette_pos++ = s[j];
1100 palette_animation_counter = old_tc;
1120 uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114;
1122 return sq1000_brightness < 128 * 128 * 1000 ? TC_WHITE : TC_BLACK;
1132 for (uint i = 0; i != 224; i++) {
1163 for (
char c =
'0'; c <=
'9'; c++) {
1178 for (
char c =
'9'; c >=
'0'; c--) {
1183 if (c !=
'0') *front = c -
'0';
1188 void ScreenSizeChanged()
1190 _dirty_bytes_per_line =
CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH);
1191 _dirty_blocks = ReallocT<byte>(_dirty_blocks, _dirty_bytes_per_line *
CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT));
1194 if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
1195 if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
1201 void UndrawMouseCursor()
1204 if (_screen.dst_ptr == NULL)
return;
1214 void DrawMouseCursor()
1222 if (_screen.dst_ptr == NULL)
return;
1235 if (!_cursor.
dirty)
return;
1236 UndrawMouseCursor();
1245 if (w > _screen.width - x) w = _screen.width - x;
1247 _cursor.draw_pos.x = x;
1251 y = _cursor.pos.y + _cursor.offs.y;
1256 if (h > _screen.height - y) h = _screen.height - y;
1258 _cursor.draw_pos.y = y;
1267 _cur_dpi = &_screen;
1273 _cursor.
dirty =
false;
1276 void RedrawScreenRect(
int left,
int top,
int right,
int bottom)
1278 assert(right <= _screen.width && bottom <= _screen.height);
1280 if (right > _cursor.draw_pos.x &&
1281 left < _cursor.draw_pos.x + _cursor.
draw_size.x &&
1282 bottom > _cursor.draw_pos.y &&
1283 top < _cursor.draw_pos.y + _cursor.
draw_size.y) {
1284 UndrawMouseCursor();
1288 #ifdef ENABLE_NETWORK
1304 byte *b = _dirty_blocks;
1305 const int w =
Align(_screen.width, DIRTY_BLOCK_WIDTH);
1306 const int h =
Align(_screen.height, DIRTY_BLOCK_HEIGHT);
1336 int right = x + DIRTY_BLOCK_WIDTH;
1344 p += _dirty_bytes_per_line;
1345 bottom += DIRTY_BLOCK_HEIGHT;
1346 }
while (bottom != h && *p != 0);
1349 h2 = (bottom - y) / DIRTY_BLOCK_HEIGHT;
1353 while (right != w) {
1358 if (!*p2)
goto no_more_coalesc;
1359 p2 += _dirty_bytes_per_line;
1364 right += DIRTY_BLOCK_WIDTH;
1370 p2 += _dirty_bytes_per_line;
1378 if (left < _invalid_rect.left ) left = _invalid_rect.left;
1379 if (top < _invalid_rect.top ) top = _invalid_rect.top;
1380 if (right > _invalid_rect.right ) right = _invalid_rect.right;
1381 if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
1383 if (left < right && top < bottom) {
1384 RedrawScreenRect(left, top, right, bottom);
1388 }
while (b++, (x += DIRTY_BLOCK_WIDTH) != w);
1389 }
while (b += -(
int)(w / DIRTY_BLOCK_WIDTH) + _dirty_bytes_per_line, (y += DIRTY_BLOCK_HEIGHT) != h);
1391 ++_dirty_block_colour;
1392 _invalid_rect.left = w;
1393 _invalid_rect.top = h;
1394 _invalid_rect.right = 0;
1395 _invalid_rect.bottom = 0;
1419 if (left < 0) left = 0;
1420 if (top < 0) top = 0;
1421 if (right > _screen.width) right = _screen.width;
1422 if (bottom > _screen.height) bottom = _screen.height;
1424 if (left >= right || top >= bottom)
return;
1426 if (left < _invalid_rect.left ) _invalid_rect.left = left;
1427 if (top < _invalid_rect.top ) _invalid_rect.top = top;
1428 if (right > _invalid_rect.right ) _invalid_rect.right = right;
1429 if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
1431 left /= DIRTY_BLOCK_WIDTH;
1432 top /= DIRTY_BLOCK_HEIGHT;
1434 b = _dirty_blocks + top * _dirty_bytes_per_line + left;
1436 width = ((right - 1) / DIRTY_BLOCK_WIDTH) - left + 1;
1437 height = ((bottom - 1) / DIRTY_BLOCK_HEIGHT) - top + 1;
1439 assert(width > 0 && height > 0);
1444 do b[--i] = 0xFF;
while (i != 0);
1446 b += _dirty_bytes_per_line;
1447 }
while (--height != 0);
1485 if ((left -= o->left) < 0) {
1487 if (width <= 0)
return false;
1494 if (width > o->width - left) {
1495 width = o->width - left;
1496 if (width <= 0)
return false;
1500 if ((top -= o->top) < 0) {
1502 if (height <= 0)
return false;
1509 n->dst_ptr = blitter->
MoveTo(o->dst_ptr, left, top);
1510 n->pitch = o->pitch;
1512 if (height > o->height - top) {
1513 height = o->height - top;
1514 if (height <= 0)
return false;
1546 if (cv->
sprite == cursor)
return;
1555 static void SwitchAnimatedCursor()
1570 SwitchAnimatedCursor();
1597 _cursor.pal = PAL_NONE;
1598 SwitchAnimatedCursor();
1621 if (x == this->pos.x && y == this->pos.y) {
1623 this->queued_warp =
false;
1626 this->
delta.x = x - (this->queued_warp ? this->last_position.x : this->pos.x);
1627 this->
delta.y = y - (this->queued_warp ? this->last_position.y : this->pos.y);
1629 this->last_position.x = x;
1630 this->last_position.y = y;
1632 bool need_warp =
false;
1634 if (this->
delta.x != 0 || this->delta.y != 0) {
1639 this->queued_warp = queued_warp;
1642 }
else if (this->pos.x != x || this->pos.y != y) {
1643 this->queued_warp =
false;
1651 bool ChangeResInGame(
int width,
int height)
1656 bool ToggleFullScreen(
bool fs)
1660 DEBUG(driver, 0,
"Could not find a suitable fullscreen resolution");
1667 int x = pa->width - pb->width;
1668 if (x != 0)
return x;
1669 return pa->height - pb->height;
1672 void SortResolutions(
int count)