mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-17 21:25:40 +00:00
a77a211ef7
- Fix: There would be duplicate entries in the resolutions dropdown box. Copy SDL method or removing duplicates and sort the list. - Feature: in the settings menu, you don't have to click on the arrows anymore, clicking on the dropdown box itself has the same effect. Consistent with other dropdowns in the game
2002 lines
38 KiB
C
2002 lines
38 KiB
C
#include "stdafx.h"
|
|
#include "ttd.h"
|
|
#include "spritecache.h"
|
|
#include "strings.h"
|
|
#include "gfx.h"
|
|
#include "table/palettes.h"
|
|
#include "hal.h"
|
|
|
|
static void GfxMainBlitter(const Sprite *sprite, int x, int y, int mode);
|
|
|
|
static int _stringwidth_out;
|
|
static byte _cursor_backup[64*64];
|
|
static Rect _invalid_rect;
|
|
static const byte *_color_remap_ptr;
|
|
static byte _string_colorremap[3];
|
|
|
|
#define DIRTY_BYTES_PER_LINE (MAX_SCREEN_WIDTH/64)
|
|
static byte _dirty_blocks[DIRTY_BYTES_PER_LINE * MAX_SCREEN_HEIGHT / 8];
|
|
|
|
|
|
|
|
void memcpy_pitch(void *d, void *s, int w, int h, int spitch, int dpitch)
|
|
{
|
|
byte *dp = (byte*)d;
|
|
byte *sp = (byte*)s;
|
|
|
|
assert(h >= 0);
|
|
for (; h != 0; --h) {
|
|
memcpy(dp, sp, w);
|
|
dp += dpitch;
|
|
sp += spitch;
|
|
}
|
|
}
|
|
|
|
|
|
void GfxScroll(int left, int top, int width, int height, int xo, int yo) {
|
|
byte *src, *dst;
|
|
int p;
|
|
int ht;
|
|
|
|
if (xo == 0 && yo == 0)
|
|
return;
|
|
|
|
if (_cursor.visible)
|
|
UndrawMouseCursor();
|
|
UndrawTextMessage();
|
|
|
|
p = _screen.pitch;
|
|
|
|
if (yo > 0) {
|
|
// Calculate pointers
|
|
dst = _screen.dst_ptr + (top + height - 1) * p + left;
|
|
src = dst - yo * p;
|
|
|
|
// Decrease height and increase top
|
|
top += yo;
|
|
height -= yo;
|
|
assert(height > 0);
|
|
|
|
// Adjust left & width
|
|
if (xo >= 0) {
|
|
dst += xo;
|
|
left += xo;
|
|
width -= xo;
|
|
} else {
|
|
src -= xo;
|
|
width += xo;
|
|
}
|
|
|
|
for (ht = height; ht > 0; --ht) {
|
|
memcpy(dst, src, width);
|
|
src -= p;
|
|
dst -= p;
|
|
}
|
|
} else {
|
|
// Calculate pointers
|
|
dst = _screen.dst_ptr + top * p + left;
|
|
src = dst - yo * p;
|
|
|
|
// Decrese height. (yo is <=0).
|
|
height += yo;
|
|
assert(height > 0);
|
|
|
|
// Adjust left & width
|
|
if (xo >= 0) {
|
|
dst += xo;
|
|
left += xo;
|
|
width -= xo;
|
|
} else {
|
|
src -= xo;
|
|
width += xo;
|
|
}
|
|
|
|
// the y-displacement may be 0 therefore we have to use memmove,
|
|
// because source and destination may overlap
|
|
for (ht = height; ht > 0; --ht) {
|
|
memmove(dst, src, width);
|
|
src += p;
|
|
dst += p;
|
|
}
|
|
}
|
|
// This part of the screen is now dirty.
|
|
_video_driver->make_dirty(left, top, width, height);
|
|
}
|
|
|
|
|
|
void GfxFillRect(int left, int top, int right, int bottom, int color) {
|
|
DrawPixelInfo *dpi = _cur_dpi;
|
|
byte *dst;
|
|
const int otop = top;
|
|
const int oleft = left;
|
|
|
|
if (dpi->zoom != 0)
|
|
return;
|
|
|
|
if (left > right || top > bottom)
|
|
return;
|
|
|
|
if (right < dpi->left || left >= dpi->left + dpi->width)
|
|
return;
|
|
|
|
if (bottom < dpi->top || top >= dpi->top + dpi->height)
|
|
return;
|
|
|
|
if ( (left -= dpi->left) < 0) left = 0;
|
|
right = right - dpi->left + 1;
|
|
if (right > dpi->width) right=dpi->width;
|
|
right -= left;
|
|
assert(right > 0);
|
|
|
|
if ( (top -= dpi->top) < 0) top = 0;
|
|
bottom = bottom - dpi->top + 1;
|
|
if (bottom > dpi->height) bottom=dpi->height;
|
|
bottom -= top;
|
|
assert(bottom > 0);
|
|
|
|
dst = dpi->dst_ptr + top * dpi->pitch + left;
|
|
|
|
if (!(color & 0x8000)) {
|
|
if (!(color & 0x4000)) {
|
|
do {
|
|
memset(dst, color, right);
|
|
dst += dpi->pitch;
|
|
} while (--bottom);
|
|
} else {
|
|
/* use colortable mode */
|
|
const byte* ctab = GetNonSprite(color & 0x3FFF) + 1;
|
|
|
|
do {
|
|
int i;
|
|
for(i=0; i!=right;i++)
|
|
dst[i] = ctab[dst[i]];
|
|
dst += dpi->pitch;
|
|
} while (--bottom);
|
|
}
|
|
} else {
|
|
byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
|
|
do {
|
|
int i;
|
|
for (i = (bo ^= 1); i < right; i += 2)
|
|
dst[i] = (byte)color;
|
|
dst += dpi->pitch;
|
|
} while (--bottom > 0);
|
|
}
|
|
}
|
|
|
|
static void GfxSetPixel(int x, int y, int color)
|
|
{
|
|
DrawPixelInfo *dpi = _cur_dpi;
|
|
if ((x-=dpi->left) < 0 || x>=dpi->width || (y-=dpi->top)<0 || y>=dpi->height)
|
|
return;
|
|
dpi->dst_ptr[y*dpi->pitch+x] = color;
|
|
}
|
|
|
|
void GfxDrawLine(int x, int y, int x2, int y2, int color)
|
|
{
|
|
int dy;
|
|
int dx;
|
|
int stepx, stepy;
|
|
int frac;
|
|
|
|
// Check clipping first
|
|
{
|
|
DrawPixelInfo *dpi = _cur_dpi;
|
|
int t;
|
|
|
|
if (x < dpi->left && x2 < dpi->left)
|
|
return;
|
|
|
|
if (y < dpi->top && y2 < dpi->top)
|
|
return;
|
|
|
|
t = dpi->left + dpi->width;
|
|
if (x > t && x2 > t)
|
|
return;
|
|
|
|
t = dpi->top + dpi->height;
|
|
if (y > t && y2 > t)
|
|
return;
|
|
}
|
|
|
|
dy = (y2 - y) * 2;
|
|
if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }
|
|
|
|
dx = (x2 - x) * 2;
|
|
if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }
|
|
|
|
GfxSetPixel(x, y, color);
|
|
if (dx > dy) {
|
|
frac = dy - (dx >> 1);
|
|
while (x != x2) {
|
|
if (frac >= 0) {
|
|
y += stepy;
|
|
frac -= dx;
|
|
}
|
|
x += stepx;
|
|
frac += dy;
|
|
GfxSetPixel(x, y, color);
|
|
}
|
|
} else {
|
|
frac = dx - (dy >> 1);
|
|
while (y != y2) {
|
|
if (frac >= 0) {
|
|
x += stepx;
|
|
frac -= dy;
|
|
}
|
|
y += stepy;
|
|
frac += dx;
|
|
GfxSetPixel(x, y, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ASSIGNMENT OF ASCII LETTERS < 32
|
|
// 0 - end of string
|
|
// 1 - SETX <BYTE>
|
|
// 2 - SETXY <BYTE> <BYTE>
|
|
// 3-7 -
|
|
// 8 - TINYFONT
|
|
// 9 - BIGFONT
|
|
// 10 - newline
|
|
// 11-14 -
|
|
// 15-31 - 17 colors
|
|
|
|
|
|
enum {
|
|
ASCII_SETX = 1,
|
|
ASCII_SETXY = 2,
|
|
|
|
ASCII_TINYFONT = 8,
|
|
ASCII_BIGFONT = 9,
|
|
ASCII_NL = 10,
|
|
|
|
ASCII_COLORSTART = 15,
|
|
};
|
|
|
|
|
|
/* returns right coordinate */
|
|
int DrawString(int x, int y, uint16 str, uint16 color)
|
|
{
|
|
char buffer[512];
|
|
|
|
GetString(buffer, str);
|
|
return DoDrawString(buffer, x, y, color);
|
|
}
|
|
|
|
|
|
void DrawStringRightAligned(int x, int y, uint16 str, uint16 color)
|
|
{
|
|
char buffer[512];
|
|
|
|
GetString(buffer, str);
|
|
DoDrawString(buffer, x - GetStringWidth(buffer), y, color);
|
|
}
|
|
|
|
|
|
int DrawStringCentered(int x, int y, uint16 str, uint16 color)
|
|
{
|
|
char buffer[512];
|
|
int w;
|
|
|
|
GetString(buffer, str);
|
|
|
|
w = GetStringWidth(buffer);
|
|
DoDrawString(buffer, x - w / 2, y, color);
|
|
|
|
return w;
|
|
}
|
|
|
|
void DrawStringCenterUnderline(int x, int y, uint16 str, uint16 color)
|
|
{
|
|
int w = DrawStringCentered(x, y, str, color);
|
|
GfxFillRect(x-(w>>1), y+10, x-(w>>1)+w, y+10, _string_colorremap[1]);
|
|
}
|
|
|
|
static uint32 FormatStringLinebreaks(char *str, int maxw)
|
|
{
|
|
int num = 0;
|
|
int base = _stringwidth_base;
|
|
int w;
|
|
char *last_space;
|
|
byte c;
|
|
|
|
for(;;) {
|
|
w = 0;
|
|
last_space = NULL;
|
|
|
|
for(;;) {
|
|
c = *str++;
|
|
if (c == ASCII_LETTERSTART) last_space = str;
|
|
|
|
if (c >= ASCII_LETTERSTART) {
|
|
w += GetCharacterWidth(base + (byte)c);
|
|
if (w > maxw) {
|
|
str = last_space;
|
|
if (str == NULL)
|
|
return num + (base << 16);
|
|
break;
|
|
}
|
|
} else {
|
|
if (c == 0) return num + (base << 16);
|
|
if (c == ASCII_NL) break;
|
|
|
|
if (c == ASCII_SETX) str++;
|
|
else if (c == ASCII_SETXY) str += 2;
|
|
else if (c == ASCII_TINYFONT) base = 224;
|
|
else if (c == ASCII_BIGFONT) base = 448;
|
|
}
|
|
}
|
|
|
|
num++;
|
|
str[-1] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void DrawStringMultiCenter(int x, int y, uint16 str, int maxw)
|
|
{
|
|
char buffer[512];
|
|
uint32 tmp;
|
|
int num, w, mt, t;
|
|
const char *src;
|
|
byte c;
|
|
|
|
GetString(buffer, str);
|
|
|
|
tmp = FormatStringLinebreaks(buffer, maxw);
|
|
num = (uint16)tmp;
|
|
t = tmp >> 16;
|
|
|
|
mt = 10;
|
|
if (t != 0) {
|
|
mt = 6;
|
|
if (t != 244) mt = 18;
|
|
}
|
|
|
|
y -= (mt >> 1) * num;
|
|
|
|
src = buffer;
|
|
|
|
for(;;) {
|
|
w = GetStringWidth(src);
|
|
DoDrawString(src, x - (w>>1), y, 0xFE);
|
|
_stringwidth_base = _stringwidth_out;
|
|
|
|
for(;;) {
|
|
c = *src++;
|
|
if (c == 0) {
|
|
y += mt;
|
|
if (--num < 0) {
|
|
_stringwidth_base = 0;
|
|
return;
|
|
}
|
|
break;
|
|
} else if (c == ASCII_SETX) {
|
|
src++;
|
|
} else if (c == ASCII_SETXY) {
|
|
src+=2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawStringMultiLine(int x, int y, uint16 str, int maxw) {
|
|
char buffer[512];
|
|
uint32 tmp;
|
|
int num, w, mt, t;
|
|
const char *src;
|
|
byte c;
|
|
|
|
GetString(buffer, str);
|
|
|
|
tmp = FormatStringLinebreaks(buffer, maxw);
|
|
num = (uint16)tmp;
|
|
t = tmp >> 16;
|
|
mt = 10;
|
|
if (t != 0) {
|
|
mt = 6;
|
|
if (t != 244) mt = 18;
|
|
}
|
|
|
|
src = buffer;
|
|
|
|
for(;;) {
|
|
w = GetStringWidth(src);
|
|
DoDrawString(src, x, y, 0xFE);
|
|
_stringwidth_base = _stringwidth_out;
|
|
|
|
for(;;) {
|
|
c = *src++;
|
|
if (c == 0) {
|
|
y += mt;
|
|
if (--num < 0) {
|
|
_stringwidth_base = 0;
|
|
return;
|
|
}
|
|
break;
|
|
} else if (c == ASCII_SETX) {
|
|
src++;
|
|
} else if (c == ASCII_SETXY) {
|
|
src+=2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int GetStringWidth(const char *str)
|
|
{
|
|
int w = 0;
|
|
byte c;
|
|
int base = _stringwidth_base;
|
|
for (c = *str; c != '\0'; c = *(++str)) {
|
|
if (c >= ASCII_LETTERSTART) {
|
|
w += GetCharacterWidth(base + c);
|
|
} else {
|
|
if (c == ASCII_SETX) str++;
|
|
else if (c == ASCII_SETXY) str += 2;
|
|
else if (c == ASCII_TINYFONT) base = 224;
|
|
else if (c == ASCII_BIGFONT) base = 448;
|
|
}
|
|
}
|
|
return w;
|
|
}
|
|
|
|
void DrawFrameRect(int left, int top, int right, int bottom, int ctab, int flags) {
|
|
byte color_2 = _color_list[ctab].window_color_1a;
|
|
byte color_interior = _color_list[ctab].window_color_bga;
|
|
byte color_3 = _color_list[ctab].window_color_bgb;
|
|
byte color = _color_list[ctab].window_color_2;
|
|
|
|
if (!(flags & 0x8)) {
|
|
if (!(flags & 0x20)) {
|
|
GfxFillRect(left, top, left, bottom-1, color);
|
|
GfxFillRect(left+1, top, right-1, top, color);
|
|
GfxFillRect(right, top, right, bottom-1, color_2);
|
|
GfxFillRect(left, bottom, right, bottom, color_2);
|
|
if (!(flags & 0x10)) {
|
|
GfxFillRect(left+1, top+1, right-1, bottom-1, color_interior);
|
|
}
|
|
} else {
|
|
GfxFillRect(left, top, left, bottom, color_2);
|
|
GfxFillRect(left+1, top, right, top, color_2);
|
|
GfxFillRect(right, top+1, right, bottom-1, color);
|
|
GfxFillRect(left+1, bottom, right, bottom, color);
|
|
if (!(flags & 0x10)) {
|
|
GfxFillRect(left+1, top+1, right-1, bottom-1,
|
|
flags&0x40 ? color_interior : color_3);
|
|
}
|
|
}
|
|
} else if (flags & 0x1) {
|
|
// transparency
|
|
GfxFillRect(left, top, right, bottom, 0x4322);
|
|
} else {
|
|
GfxFillRect(left, top, right, bottom, color_interior);
|
|
}
|
|
}
|
|
|
|
int DoDrawString(const char *string, int x, int y, uint16 real_color)
|
|
{
|
|
DrawPixelInfo *dpi = _cur_dpi;
|
|
int base = _stringwidth_base;
|
|
byte c;
|
|
byte color;
|
|
int xo = x, yo = y;
|
|
|
|
color = real_color & 0xFF;
|
|
|
|
if (color != 0xFE) {
|
|
if (x >= dpi->left + dpi->width ||
|
|
x + _screen.width*2 <= dpi->left ||
|
|
y >= dpi->top + dpi->height ||
|
|
y + _screen.height <= dpi->top)
|
|
return x;
|
|
|
|
if (color != 0xFF) {
|
|
switch_color:;
|
|
if (real_color & IS_PALETTE_COLOR) {
|
|
_string_colorremap[1] = color;
|
|
_string_colorremap[2] = 215;
|
|
} else {
|
|
_string_colorremap[1] = _string_colormap[color].text;
|
|
_string_colorremap[2] = _string_colormap[color].shadow;
|
|
}
|
|
_color_remap_ptr = _string_colorremap;
|
|
}
|
|
}
|
|
|
|
check_bounds:
|
|
if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
|
|
skip_char:;
|
|
for(;;) {
|
|
c = *string++;
|
|
if (c < ASCII_LETTERSTART) goto skip_cont;
|
|
}
|
|
}
|
|
|
|
for(;;) {
|
|
c = *string++;
|
|
skip_cont:;
|
|
if (c == 0) {
|
|
_stringwidth_out = base;
|
|
return x;
|
|
}
|
|
if (c >= ASCII_LETTERSTART) {
|
|
if (x >= dpi->left + dpi->width) goto skip_char;
|
|
if (x + 26 >= dpi->left) {
|
|
GfxMainBlitter(GetSprite(base + 2 + c - ASCII_LETTERSTART), x, y, 1);
|
|
}
|
|
x += GetCharacterWidth(base + c);
|
|
} else if (c == ASCII_NL) { // newline = {}
|
|
x = xo;
|
|
y += 10;
|
|
if (base != 0) {
|
|
y -= 4;
|
|
if (base != 0xE0)
|
|
y += 12;
|
|
}
|
|
goto check_bounds;
|
|
} else if (c >= ASCII_COLORSTART) { // change color?
|
|
color = (byte)(c - ASCII_COLORSTART);
|
|
goto switch_color;
|
|
} else if (c == ASCII_SETX) { // {SETX}
|
|
x = xo + (byte)*string++;
|
|
} else if (c == ASCII_SETXY) {// {SETXY}
|
|
x = xo + (byte)*string++;
|
|
y = yo + (byte)*string++;
|
|
} else if (c == ASCII_TINYFONT) { // {TINYFONT}
|
|
base = 0xE0;
|
|
} else if (c == ASCII_BIGFONT) { // {BIGFONT}
|
|
base = 0x1C0;
|
|
} else {
|
|
printf("Unknown string command character %d\n", c);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawSprite(uint32 img, int x, int y) {
|
|
if (img & 0x8000) {
|
|
_color_remap_ptr = GetNonSprite(img >> 16) + 1;
|
|
GfxMainBlitter(GetSprite(img & 0x3FFF), x, y, 1);
|
|
} else if (img & 0x4000) {
|
|
_color_remap_ptr = GetNonSprite(img >> 16) + 1;
|
|
GfxMainBlitter(GetSprite(img & 0x3FFF), x, y, 2);
|
|
} else {
|
|
GfxMainBlitter(GetSprite(img & 0x3FFF), x, y, 0);
|
|
}
|
|
}
|
|
|
|
typedef struct BlitterParams {
|
|
int start_x, start_y;
|
|
const byte* sprite;
|
|
const byte* sprite_org;
|
|
byte *dst;
|
|
int mode;
|
|
int width, height;
|
|
int width_org, height_org;
|
|
int pitch;
|
|
byte info;
|
|
} BlitterParams;
|
|
|
|
static void GfxBlitTileZoomIn(BlitterParams *bp)
|
|
{
|
|
const byte* src_o = bp->sprite;
|
|
const byte* src;
|
|
int num, skip;
|
|
byte done;
|
|
byte *dst;
|
|
const byte* ctab;
|
|
|
|
if (bp->mode & 1) {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
|
|
do {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src = src_o + 2;
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip;
|
|
} else {
|
|
src -= skip;
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
|
|
ctab = _color_remap_ptr;
|
|
|
|
while (num >= 4) {
|
|
dst[3] = ctab[src[3]];
|
|
dst[2] = ctab[src[2]];
|
|
dst[1] = ctab[src[1]];
|
|
dst[0] = ctab[src[0]];
|
|
dst += 4;
|
|
src += 4;
|
|
num -= 4;
|
|
}
|
|
while (num) {
|
|
*dst++ = ctab[*src++];
|
|
num--;
|
|
}
|
|
} while (!(done & 0x80));
|
|
|
|
bp->dst += bp->pitch;
|
|
} while (--bp->height);
|
|
} else if (bp->mode & 2) {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
do {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip;
|
|
} else {
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
|
|
ctab = _color_remap_ptr;
|
|
while (num) {
|
|
*dst = ctab[*dst];
|
|
dst++;
|
|
num--;
|
|
}
|
|
} while (!(done & 0x80));
|
|
|
|
bp->dst += bp->pitch;
|
|
} while (--bp->height);
|
|
|
|
} else {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
do {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src = src_o + 2;
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip;
|
|
} else {
|
|
src -= skip;
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
#if defined(_WIN32)
|
|
if (num & 1) *dst++ = *src++;
|
|
if (num & 2) { *(uint16*)dst = *(uint16*)src; dst += 2; src += 2; }
|
|
if (num >>= 2) {
|
|
do {
|
|
*(uint32*)dst = *(uint32*)src;
|
|
dst += 4;
|
|
src += 4;
|
|
} while (--num);
|
|
}
|
|
#else
|
|
memcpy(dst, src, num);
|
|
#endif
|
|
} while (!(done & 0x80));
|
|
|
|
bp->dst += bp->pitch;
|
|
} while (--bp->height);
|
|
}
|
|
}
|
|
|
|
static void GfxBlitZoomInUncomp(BlitterParams *bp)
|
|
{
|
|
const byte *src = bp->sprite;
|
|
byte *dst = bp->dst;
|
|
int height = bp->height;
|
|
int width = bp->width;
|
|
int i;
|
|
|
|
assert(height > 0);
|
|
assert(width > 0);
|
|
|
|
if (bp->mode & 1) {
|
|
if (bp->info & 1) {
|
|
const byte *ctab = _color_remap_ptr;
|
|
byte b;
|
|
|
|
do {
|
|
for(i=0; i!=width; i++) {
|
|
if ((b=ctab[src[i]]) != 0)
|
|
dst[i] = b;
|
|
}
|
|
src += bp->width_org;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
}
|
|
} else if (bp->mode & 2) {
|
|
if (bp->info & 1) {
|
|
const byte *ctab = _color_remap_ptr;
|
|
|
|
do {
|
|
for(i=0; i!=width; i++)
|
|
if (src[i])
|
|
dst[i] = ctab[dst[i]];
|
|
src += bp->width_org;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
}
|
|
} else {
|
|
if (!(bp->info & 1)) {
|
|
do {
|
|
memcpy(dst, src, width);
|
|
src += bp->width_org;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
} else {
|
|
do {
|
|
int n = width;
|
|
while (n >= 4) {
|
|
if (src[0]) dst[0] = src[0];
|
|
if (src[1]) dst[1] = src[1];
|
|
if (src[2]) dst[2] = src[2];
|
|
if (src[3]) dst[3] = src[3];
|
|
|
|
dst += 4;
|
|
src += 4;
|
|
n -= 4;
|
|
}
|
|
|
|
while (n) {
|
|
if (src[0]) dst[0] = src[0];
|
|
src++;
|
|
dst++;
|
|
n--;
|
|
}
|
|
|
|
src += bp->width_org - width;
|
|
dst += bp->pitch - width;
|
|
|
|
} while (--height);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GfxBlitTileZoomMedium(BlitterParams *bp)
|
|
{
|
|
const byte* src_o = bp->sprite;
|
|
const byte* src;
|
|
int num, skip;
|
|
byte done;
|
|
byte *dst;
|
|
const byte* ctab;
|
|
|
|
if (bp->mode & 1) {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
do {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src = src_o + 2;
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if (skip & 1) {
|
|
skip++;
|
|
src++;
|
|
if (!--num)
|
|
continue;
|
|
}
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip >> 1;
|
|
} else {
|
|
src -= skip;
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
|
|
ctab = _color_remap_ptr;
|
|
num = (num + 1) >> 1;
|
|
if (num) {
|
|
do {
|
|
*dst = ctab[*src];
|
|
dst++;
|
|
src+=2;
|
|
} while (--num);
|
|
}
|
|
} while (!(done & 0x80));
|
|
bp->dst += bp->pitch;
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
} while (--bp->height);
|
|
} else if (bp->mode & 2) {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
do {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if (skip & 1) {
|
|
skip++;
|
|
if (!--num)
|
|
continue;
|
|
}
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip >> 1;
|
|
} else {
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
|
|
ctab = _color_remap_ptr;
|
|
num = (num + 1) >> 1;
|
|
if (num) {
|
|
do {
|
|
*dst = ctab[*dst];
|
|
dst++;
|
|
} while (--num);
|
|
}
|
|
|
|
} while (!(done & 0x80));
|
|
bp->dst += bp->pitch;
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
} while (--bp->height);
|
|
|
|
} else {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
do {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src = src_o + 2;
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if (skip & 1) {
|
|
skip++;
|
|
src++;
|
|
if (!--num)
|
|
continue;
|
|
}
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip >> 1;
|
|
} else {
|
|
src -= skip;
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
|
|
num = (num + 1) >> 1;
|
|
|
|
if (num) {
|
|
do {
|
|
*dst = *src;
|
|
dst++;
|
|
src+=2;
|
|
} while (--num);
|
|
}
|
|
|
|
} while (!(done & 0x80));
|
|
|
|
bp->dst += bp->pitch;
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
|
|
} while (--bp->height);
|
|
}
|
|
}
|
|
|
|
static void GfxBlitZoomMediumUncomp(BlitterParams *bp)
|
|
{
|
|
const byte *src = bp->sprite;
|
|
byte *dst = bp->dst;
|
|
int height = bp->height;
|
|
int width = bp->width;
|
|
int i;
|
|
|
|
assert(height > 0);
|
|
assert(width > 0);
|
|
|
|
if (bp->mode & 1) {
|
|
if (bp->info & 1) {
|
|
const byte *ctab = _color_remap_ptr;
|
|
byte b;
|
|
|
|
height >>= 1;
|
|
if (height) do {
|
|
for(i=0; i!=width>>1; i++)
|
|
if ((b=ctab[src[i*2]]) != 0)
|
|
dst[i] = b;
|
|
src += bp->width_org * 2;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
}
|
|
} else if (bp->mode & 2) {
|
|
if (bp->info & 1) {
|
|
const byte *ctab = _color_remap_ptr;
|
|
|
|
height >>= 1;
|
|
if (height) do {
|
|
for(i=0; i!=width>>1; i++)
|
|
if (src[i*2])
|
|
dst[i] = ctab[dst[i]];
|
|
src += bp->width_org * 2;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
}
|
|
} else {
|
|
if (bp->info & 1) {
|
|
height >>= 1;
|
|
if (height) do {
|
|
for(i=0; i!=width>>1; i++)
|
|
if (src[i*2])
|
|
dst[i] = src[i*2];
|
|
src += bp->width_org * 2;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GfxBlitTileZoomOut(BlitterParams *bp)
|
|
{
|
|
const byte* src_o = bp->sprite;
|
|
const byte* src;
|
|
int num, skip;
|
|
byte done;
|
|
byte *dst;
|
|
const byte* ctab;
|
|
|
|
if (bp->mode & 1) {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
for(;;) {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src = src_o + 2;
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if (skip & 1) {
|
|
skip++;
|
|
src++;
|
|
if (!--num)
|
|
continue;
|
|
}
|
|
|
|
if (skip & 2) {
|
|
skip+=2;
|
|
src+=2;
|
|
if ((num-=2) <= 0)
|
|
continue;
|
|
}
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip >> 2;
|
|
} else {
|
|
src -= skip;
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
|
|
ctab = _color_remap_ptr;
|
|
num = (num + 3) >> 2;
|
|
if (num) {
|
|
do {
|
|
*dst = ctab[*src];
|
|
dst++;
|
|
src+=4;
|
|
} while (--num);
|
|
}
|
|
} while (!(done & 0x80));
|
|
bp->dst += bp->pitch;
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
}
|
|
} else if (bp->mode & 2) {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
for(;;) {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if (skip & 1) {
|
|
skip++;
|
|
if (!--num)
|
|
continue;
|
|
}
|
|
|
|
if (skip & 2) {
|
|
skip+=2;
|
|
if ((num-=2) <= 0)
|
|
continue;
|
|
}
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip >> 2;
|
|
} else {
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
|
|
ctab = _color_remap_ptr;
|
|
num = (num + 3) >> 2;
|
|
if (num) {
|
|
do {
|
|
*dst = ctab[*dst];
|
|
dst++;
|
|
} while (--num);
|
|
}
|
|
|
|
} while (!(done & 0x80));
|
|
bp->dst += bp->pitch;
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
}
|
|
} else {
|
|
src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
|
|
for(;;) {
|
|
do {
|
|
done = src_o[0];
|
|
num = done & 0x7F;
|
|
skip = src_o[1];
|
|
src = src_o + 2;
|
|
src_o += num + 2;
|
|
|
|
dst = bp->dst;
|
|
|
|
if (skip & 1) {
|
|
skip++;
|
|
src++;
|
|
if (!--num)
|
|
continue;
|
|
}
|
|
|
|
if (skip & 2) {
|
|
skip+=2;
|
|
src+=2;
|
|
if ((num-=2) <= 0)
|
|
continue;
|
|
}
|
|
|
|
if ( (skip -= bp->start_x) > 0) {
|
|
dst += skip >> 2;
|
|
} else {
|
|
src -= skip;
|
|
num += skip;
|
|
if (num <= 0)
|
|
continue;
|
|
skip = 0;
|
|
}
|
|
|
|
skip = skip + num - bp->width;
|
|
if (skip > 0) {
|
|
num -= skip;
|
|
if (num <= 0)
|
|
continue;
|
|
}
|
|
|
|
num = (num + 3) >> 2;
|
|
|
|
if (num) {
|
|
do {
|
|
*dst = *src;
|
|
dst++;
|
|
src+=4;
|
|
} while (--num);
|
|
}
|
|
|
|
} while (!(done & 0x80));
|
|
|
|
bp->dst += bp->pitch;
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
|
|
do {
|
|
done = src_o[0];
|
|
src_o += (done & 0x7F) + 2;
|
|
} while (!(done & 0x80));
|
|
if (!--bp->height)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GfxBlitZoomOutUncomp(BlitterParams *bp)
|
|
{
|
|
const byte* src = bp->sprite;
|
|
byte *dst = bp->dst;
|
|
int height = bp->height;
|
|
int width = bp->width;
|
|
int i;
|
|
|
|
assert(height > 0);
|
|
assert(width > 0);
|
|
|
|
if (bp->mode & 1) {
|
|
if (bp->info & 1) {
|
|
const byte *ctab = _color_remap_ptr;
|
|
byte b;
|
|
|
|
height >>= 2;
|
|
if (height) do {
|
|
for(i=0; i!=width>>2; i++)
|
|
if ((b=ctab[src[i*4]]) != 0)
|
|
dst[i] = b;
|
|
src += bp->width_org * 4;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
}
|
|
} else if (bp->mode & 2) {
|
|
if (bp->info & 1) {
|
|
const byte *ctab = _color_remap_ptr;
|
|
|
|
height >>= 2;
|
|
if (height) do {
|
|
for(i=0; i!=width>>2; i++)
|
|
if (src[i*4])
|
|
dst[i] = ctab[dst[i]];
|
|
src += bp->width_org * 4;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
}
|
|
} else {
|
|
if (bp->info & 1) {
|
|
height >>= 2;
|
|
if (height) do {
|
|
for(i=0; i!=width>>2; i++)
|
|
if (src[i*4])
|
|
dst[i] = src[i*4];
|
|
src += bp->width_org * 4;
|
|
dst += bp->pitch;
|
|
} while (--height);
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef void (*BlitZoomFunc)(BlitterParams *bp);
|
|
|
|
static void GfxMainBlitter(const Sprite* sprite, int x, int y, int mode)
|
|
{
|
|
DrawPixelInfo *dpi = _cur_dpi;
|
|
int start_x, start_y;
|
|
byte info;
|
|
BlitterParams bp;
|
|
int zoom_mask = ~((1 << (dpi->zoom))-1);
|
|
|
|
static const BlitZoomFunc zf_tile[3] =
|
|
{
|
|
GfxBlitTileZoomIn,
|
|
GfxBlitTileZoomMedium,
|
|
GfxBlitTileZoomOut
|
|
};
|
|
static const BlitZoomFunc zf_uncomp[3] =
|
|
{
|
|
GfxBlitZoomInUncomp,
|
|
GfxBlitZoomMediumUncomp,
|
|
GfxBlitZoomOutUncomp
|
|
};
|
|
|
|
/* decode sprite header */
|
|
x += sprite->x_offs;
|
|
y += sprite->y_offs;
|
|
bp.width_org = bp.width = sprite->width;
|
|
bp.height_org = bp.height = sprite->height;
|
|
info = sprite->info;
|
|
bp.info = info;
|
|
bp.sprite_org = bp.sprite = sprite->data;
|
|
bp.dst = dpi->dst_ptr;
|
|
bp.mode = mode;
|
|
bp.pitch = dpi->pitch;
|
|
|
|
assert(bp.height > 0);
|
|
assert(bp.width > 0);
|
|
|
|
if (info & 8) {
|
|
/* tile blit */
|
|
start_y = 0;
|
|
|
|
if (dpi->zoom > 0) {
|
|
start_y += bp.height &~ zoom_mask;
|
|
bp.height &= zoom_mask;
|
|
if (bp.height == 0) return;
|
|
y&=zoom_mask;
|
|
}
|
|
|
|
if ( (y -= dpi->top) < 0) {
|
|
if ((bp.height += y) <= 0)
|
|
return;
|
|
start_y -= y;
|
|
y = 0;
|
|
} else {
|
|
bp.dst += bp.pitch * (y>>(dpi->zoom));
|
|
}
|
|
bp.start_y = start_y;
|
|
|
|
if ( (y = y + bp.height - dpi->height) > 0) {
|
|
if ( (bp.height -= y) <= 0)
|
|
return;
|
|
}
|
|
|
|
start_x = 0;
|
|
x &= zoom_mask;
|
|
if ( (x -= dpi->left) < 0) {
|
|
if ((bp.width += x) <= 0)
|
|
return;
|
|
start_x -= x;
|
|
x = 0;
|
|
}
|
|
bp.start_x = start_x;
|
|
bp.dst += x>>(dpi->zoom);
|
|
|
|
if ( (x = x + bp.width - dpi->width) > 0) {
|
|
if ( (bp.width -= x) <= 0)
|
|
return;
|
|
}
|
|
|
|
zf_tile[dpi->zoom](&bp);
|
|
} else {
|
|
|
|
bp.sprite += bp.width * (bp.height & ~zoom_mask);
|
|
bp.height &= zoom_mask;
|
|
if (bp.height == 0)
|
|
return;
|
|
|
|
y &= zoom_mask;
|
|
|
|
if ( (y -= dpi->top) < 0) {
|
|
if ((bp.height += y) <= 0)
|
|
return;
|
|
bp.sprite -= bp.width * y;
|
|
y = 0;
|
|
} else {
|
|
bp.dst += bp.pitch * (y>>(dpi->zoom));
|
|
}
|
|
|
|
if ( (y = y + bp.height - dpi->height) > 0) {
|
|
if ( (bp.height -= y) <= 0)
|
|
return;
|
|
}
|
|
|
|
start_x = 0;
|
|
|
|
x &= zoom_mask;
|
|
|
|
if ( (x -= dpi->left) < 0) {
|
|
if ((bp.width += x) <= 0)
|
|
return;
|
|
start_x -= x;
|
|
bp.sprite -= x;
|
|
x = 0;
|
|
}
|
|
bp.dst += x>>(dpi->zoom);
|
|
|
|
if ( (x = x + bp.width - dpi->width) > 0) {
|
|
if ( (bp.width -= x) <= 0)
|
|
return;
|
|
start_x += x;
|
|
}
|
|
bp.start_x = start_x;
|
|
|
|
if (info&2) {
|
|
int totpix = bp.height_org * bp.width_org;
|
|
byte *dst = (byte*)alloca(totpix);
|
|
const byte *src = bp.sprite_org;
|
|
signed char b;
|
|
|
|
bp.sprite = dst + (bp.sprite - bp.sprite_org);
|
|
|
|
while (totpix != 0) {
|
|
assert(totpix > 0);
|
|
b = *src++;
|
|
if (b >= 0) {
|
|
int i, count=b;
|
|
for(i=0; i!=count; i++)
|
|
dst[i] = src[i];
|
|
dst += count;
|
|
src += count;
|
|
totpix -= count;
|
|
} else {
|
|
byte *tmp = dst- (((b&7)<<8)|(*src++));
|
|
int i;
|
|
int count = -(b >> 3);
|
|
|
|
for(i=0; i!=count; i++)
|
|
dst[i] = tmp[i];
|
|
dst += count;
|
|
totpix -= count;
|
|
}
|
|
}
|
|
}
|
|
zf_uncomp[dpi->zoom](&bp);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void GfxScalePalette(int pal, byte scaling)
|
|
{
|
|
byte *dst, *src;
|
|
size_t count;
|
|
|
|
GfxInitPalettes();
|
|
|
|
dst = _cur_palette;
|
|
src = GET_PALETTE(pal);
|
|
count = 256;
|
|
do {
|
|
dst[0] = (byte)(src[0] * scaling >> 8);
|
|
dst[1] = (byte)(src[1] * scaling >> 8);
|
|
dst[2] = (byte)(src[2] * scaling >> 8);
|
|
dst += 3;
|
|
src += 3;
|
|
} while (--count);
|
|
}
|
|
#endif
|
|
|
|
void DoPaletteAnimations(void);
|
|
|
|
void GfxInitPalettes(void)
|
|
{
|
|
int pal = _use_dos_palette?1:0;
|
|
memcpy(_cur_palette, _palettes[pal], 256*3);
|
|
|
|
_pal_first_dirty = 0;
|
|
_pal_last_dirty = 255;
|
|
DoPaletteAnimations();
|
|
}
|
|
|
|
#define EXTR(p,q) (((uint16)(_timer_counter * (p)) * (q)) >> 16)
|
|
#define EXTR2(p,q) (((uint16)(~_timer_counter * (p)) * (q)) >> 16)
|
|
#define COPY_TRIPLET do {d[0]=s[0+j]; d[1]=s[1+j]; d[2]=s[2+j];d+=3;}while(0)
|
|
|
|
void DoPaletteAnimations(void)
|
|
{
|
|
const byte *s;
|
|
byte *d;
|
|
/* Amount of colors to be rotated.
|
|
* A few more for the DOS palette, because the water colors are
|
|
* 245-254 for DOS and 217-226 for Windows. */
|
|
int c = _use_dos_palette?38:28;
|
|
int j;
|
|
int i;
|
|
const ExtraPaletteValues *ev = &_extra_palette_values;
|
|
byte old_val[114]; // max(c*(38:28)) = 114
|
|
|
|
d = _cur_palette + 217*3;
|
|
memcpy(old_val, d, c*3);
|
|
|
|
// Dark blue water
|
|
s = ev->a;
|
|
if (_opt.landscape == LT_CANDY) s = ev->ac;
|
|
j = EXTR(320,5) * 3;
|
|
for(i=0; i!=5; i++) {
|
|
COPY_TRIPLET;
|
|
j+=3;
|
|
if (j == 15) j = 0;
|
|
}
|
|
|
|
// Glittery water
|
|
s = ev->b;
|
|
if (_opt.landscape == LT_CANDY) s = ev->bc;
|
|
j = EXTR(128, 15) * 3;
|
|
for(i=0; i!=5; i++) {
|
|
COPY_TRIPLET;
|
|
j += 9;
|
|
if (j >= 45) j -= 45;
|
|
}
|
|
|
|
s = ev->e;
|
|
j = EXTR2(512, 5) * 3;
|
|
for(i=0; i!=5; i++) {
|
|
COPY_TRIPLET;
|
|
j += 3;
|
|
if (j == 3*5) j = 0;
|
|
}
|
|
|
|
// Oil refinery fire animation
|
|
s = ev->oil_ref;
|
|
j = EXTR2(512, 7) * 3;
|
|
for(i=0; i!=7; i++) {
|
|
COPY_TRIPLET;
|
|
j += 3;
|
|
if (j == 3*7) j = 0;
|
|
}
|
|
|
|
// Radio tower blinking
|
|
{
|
|
byte i,v;
|
|
i = (_timer_counter >> 1) & 0x7F;
|
|
(v = 255, i < 0x3f) ||
|
|
(v = 128, i < 0x4A || i >= 0x75) ||
|
|
(v = 20);
|
|
d[0] = v;
|
|
d[1] = d[2] = 0;
|
|
d += 3;
|
|
|
|
i ^= 0x40;
|
|
(v = 255, i < 0x3f) ||
|
|
(v = 128, i < 0x4A || i >= 0x75) ||
|
|
(v = 20);
|
|
d[0] = v;
|
|
|
|
d[1] = d[2] = 0;
|
|
d += 3;
|
|
}
|
|
|
|
// Handle lighthouse and stadium animation
|
|
s = ev->lighthouse;
|
|
j = EXTR(256, 4) * 3;
|
|
for(i=0; i!=4; i++) {
|
|
COPY_TRIPLET;
|
|
j += 3;
|
|
if (j == 3*4) j = 0;
|
|
}
|
|
|
|
// Animate water for old DOS graphics
|
|
if(_use_dos_palette) {
|
|
// Dark blue water DOS
|
|
s = ev->a;
|
|
if (_opt.landscape == LT_CANDY) s = ev->ac;
|
|
j = EXTR(320,5) * 3;
|
|
for(i=0; i!=5; i++) {
|
|
COPY_TRIPLET;
|
|
j+=3;
|
|
if (j == 15) j = 0;
|
|
}
|
|
|
|
// Glittery water DOS
|
|
s = ev->b;
|
|
if (_opt.landscape == LT_CANDY) s = ev->bc;
|
|
j = EXTR(128, 15) * 3;
|
|
for(i=0; i!=5; i++) {
|
|
COPY_TRIPLET;
|
|
j += 9;
|
|
if (j >= 45) j -= 45;
|
|
}
|
|
}
|
|
|
|
if (memcmp(old_val, _cur_palette + 217*3, c*3)) {
|
|
if (_pal_first_dirty > 217) _pal_first_dirty = 217;
|
|
if (_pal_last_dirty < 217+c) _pal_last_dirty = 217+c;
|
|
}
|
|
}
|
|
|
|
|
|
void LoadStringWidthTable(void)
|
|
{
|
|
int i;
|
|
byte *b;
|
|
|
|
b = _stringwidth_table;
|
|
|
|
// 2 equals space.
|
|
for(i=2; i != 0xE2; i++) {
|
|
*b++ = (byte)((i < 93 || i >= 129 || i == 98) ? GetSprite(i)->width : 0);
|
|
}
|
|
|
|
for(i=0xE2; i != 0x1C2; i++) {
|
|
*b++ = (byte)((i < 317 || i >= 353) ? GetSprite(i)->width + 1 : 0);
|
|
}
|
|
|
|
for(i=0x1C2; i != 0x2A2; i++) {
|
|
*b++ = (byte)((i < 541 || i >= 577) ? GetSprite(i)->width + 1 : 0);
|
|
}
|
|
}
|
|
|
|
void ScreenSizeChanged(void)
|
|
{
|
|
// check the dirty rect
|
|
if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
|
|
if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
|
|
|
|
// screen size changed and the old bitmap is invalid now, so we don't want to undraw it
|
|
_cursor.visible = false;
|
|
}
|
|
|
|
void UndrawMouseCursor(void)
|
|
{
|
|
if (_cursor.visible) {
|
|
_cursor.visible = false;
|
|
memcpy_pitch(
|
|
_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
|
|
_cursor_backup,
|
|
_cursor.draw_size.x, _cursor.draw_size.y, _cursor.draw_size.x, _screen.pitch);
|
|
|
|
_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
|
|
}
|
|
}
|
|
|
|
void DrawMouseCursor(void)
|
|
{
|
|
int x,y,w,h;
|
|
|
|
// Don't draw the mouse cursor if it's already drawn
|
|
if (_cursor.visible) {
|
|
if (!_cursor.dirty)
|
|
return;
|
|
UndrawMouseCursor();
|
|
}
|
|
|
|
w = _cursor.size.x;
|
|
x = _cursor.pos.x + _cursor.offs.x;
|
|
if (x < 0) { w += x; x=0; }
|
|
if (w>_screen.width-x) { w = _screen.width-x; }
|
|
if (w <= 0) return;
|
|
_cursor.draw_pos.x = x;
|
|
_cursor.draw_size.x = w;
|
|
|
|
h = _cursor.size.y;
|
|
y = _cursor.pos.y + _cursor.offs.y;
|
|
if (y < 0) { h += y; y=0; }
|
|
if (h>_screen.height-y) { h = _screen.height-y; }
|
|
if (h <= 0) return;
|
|
_cursor.draw_pos.y = y;
|
|
_cursor.draw_size.y = h;
|
|
|
|
assert(w*h < (int) sizeof(_cursor_backup));
|
|
|
|
// Make backup of stuff below cursor
|
|
memcpy_pitch(
|
|
_cursor_backup,
|
|
_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
|
|
_cursor.draw_size.x, _cursor.draw_size.y, _screen.pitch, _cursor.draw_size.x);
|
|
|
|
// Draw cursor on screen
|
|
_cur_dpi = &_screen;
|
|
DrawSprite(_cursor.sprite, _cursor.pos.x, _cursor.pos.y);
|
|
|
|
_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
|
|
|
|
_cursor.visible = true;
|
|
_cursor.dirty = false;
|
|
}
|
|
|
|
#if defined(_DEBUG)
|
|
static void DbgScreenRect(int left, int top, int right, int bottom)
|
|
{
|
|
DrawPixelInfo dp,*old;
|
|
|
|
old = _cur_dpi;
|
|
_cur_dpi = &dp;
|
|
dp = _screen;
|
|
GfxFillRect(left, top, right-1, bottom-1, rand() & 255);
|
|
_cur_dpi = old;
|
|
}
|
|
#endif
|
|
|
|
extern bool _dbg_screen_rect;
|
|
|
|
void RedrawScreenRect(int left, int top, int right, int bottom)
|
|
{
|
|
assert(right <= _screen.width && bottom <= _screen.height);
|
|
if (_cursor.visible) {
|
|
if (right > _cursor.draw_pos.x &&
|
|
left < _cursor.draw_pos.x + _cursor.draw_size.x &&
|
|
bottom > _cursor.draw_pos.y &&
|
|
top < _cursor.draw_pos.y + _cursor.draw_size.y) {
|
|
UndrawMouseCursor();
|
|
}
|
|
}
|
|
UndrawTextMessage();
|
|
|
|
#if defined(_DEBUG)
|
|
if (_dbg_screen_rect)
|
|
DbgScreenRect(left, top, right, bottom);
|
|
else
|
|
#endif
|
|
DrawOverlappedWindowForAll(left, top, right, bottom);
|
|
|
|
_video_driver->make_dirty(left, top, right-left, bottom-top);
|
|
}
|
|
|
|
void DrawDirtyBlocks(void)
|
|
{
|
|
byte *b = _dirty_blocks;
|
|
int x=0,y=0;
|
|
const int w = (_screen.width + 63) & ~63;
|
|
const int h = (_screen.height + 7) & ~7;
|
|
|
|
do {
|
|
if (*b != 0) {
|
|
int left,top;
|
|
int right = x + 64;
|
|
int bottom = y;
|
|
byte *p = b;
|
|
int h2;
|
|
// First try coalescing downwards
|
|
do {
|
|
*p = 0;
|
|
p += DIRTY_BYTES_PER_LINE;
|
|
bottom += 8;
|
|
} while (bottom != h && *p);
|
|
|
|
// Try coalescing to the right too.
|
|
h2 = (bottom - y) >> 3;
|
|
assert(h2>0);
|
|
p = b;
|
|
|
|
while (right != w) {
|
|
byte *p2 = ++p;
|
|
int h = h2;
|
|
// Check if a full line of dirty flags is set.
|
|
do {
|
|
if (!*p2) goto no_more_coalesc;
|
|
p2 += DIRTY_BYTES_PER_LINE;
|
|
} while (--h);
|
|
|
|
// Wohoo, can combine it one step to the right!
|
|
// Do that, and clear the bits.
|
|
right += 64;
|
|
|
|
h = h2;
|
|
p2 = p;
|
|
do {
|
|
*p2 = 0;
|
|
p2 += DIRTY_BYTES_PER_LINE;
|
|
} while (--h);
|
|
}
|
|
no_more_coalesc:;
|
|
|
|
left = x;
|
|
top = y;
|
|
|
|
if (left < _invalid_rect.left)left = _invalid_rect.left;
|
|
if (top < _invalid_rect.top) top = _invalid_rect.top;
|
|
if (right > _invalid_rect.right)right = _invalid_rect.right;
|
|
if (bottom > _invalid_rect.bottom)bottom = _invalid_rect.bottom;
|
|
|
|
if (left < right && top < bottom) {
|
|
RedrawScreenRect(left, top, right, bottom);
|
|
}
|
|
|
|
}
|
|
} while (b++, (x+=64) != w || (x=0,b+=-(w>>6)+DIRTY_BYTES_PER_LINE,(y+=8) != h));
|
|
|
|
_invalid_rect.left = w;
|
|
_invalid_rect.top = h;
|
|
_invalid_rect.right = 0;
|
|
_invalid_rect.bottom = 0;
|
|
}
|
|
|
|
|
|
void SetDirtyBlocks(int left, int top, int right, int bottom)
|
|
{
|
|
byte *b;
|
|
int width,height,i;
|
|
|
|
if (left < 0) left = 0;
|
|
if (top < 0) top = 0;
|
|
if (right > _screen.width) right = _screen.width;
|
|
if (bottom > _screen.height) bottom = _screen.height;
|
|
|
|
if (left >= right || top >= bottom)
|
|
return;
|
|
|
|
if (left < _invalid_rect.left) _invalid_rect.left = left;
|
|
if (top < _invalid_rect.top) _invalid_rect.top = top;
|
|
if (right > _invalid_rect.right)_invalid_rect.right = right;
|
|
if (bottom > _invalid_rect.bottom)_invalid_rect.bottom = bottom;
|
|
|
|
left >>= 6;
|
|
top >>= 3;
|
|
|
|
b = _dirty_blocks + top * DIRTY_BYTES_PER_LINE + left;
|
|
|
|
width = ((right-1) >> 6) - left + 1;
|
|
height = ((bottom-1) >> 3) - top + 1;
|
|
|
|
assert(width > 0 && height > 0);
|
|
|
|
do {
|
|
i=width;
|
|
do b[--i] = 0xFF; while (i);
|
|
|
|
b += DIRTY_BYTES_PER_LINE;
|
|
} while (--height);
|
|
}
|
|
|
|
void MarkWholeScreenDirty(void)
|
|
{
|
|
SetDirtyBlocks(0, 0, _screen.width, _screen.height);
|
|
}
|
|
|
|
bool FillDrawPixelInfo(DrawPixelInfo *n, DrawPixelInfo *o, int left, int top, int width, int height)
|
|
{
|
|
int t;
|
|
|
|
if (o == NULL)
|
|
o = _cur_dpi;
|
|
|
|
n->zoom = 0;
|
|
|
|
assert(width > 0);
|
|
assert(height > 0);
|
|
|
|
n->left = 0;
|
|
if ((left -= o->left) < 0) {
|
|
if ((width += left) < 0)
|
|
return false;
|
|
n->left = -left;
|
|
left = 0;
|
|
}
|
|
|
|
if ((t=width + left - o->width) > 0) {
|
|
if ((width -= t) < 0)
|
|
return false;
|
|
}
|
|
n->width = width;
|
|
|
|
n->top = 0;
|
|
if ((top -= o->top) < 0) {
|
|
if ((height += top) < 0)
|
|
return false;
|
|
n->top = -top;
|
|
top = 0;
|
|
}
|
|
|
|
n->dst_ptr = o->dst_ptr + left + top * (n->pitch = o->pitch);
|
|
|
|
if ((t=height + top - o->height) > 0) {
|
|
if ((height-=t) < 0)
|
|
return false;
|
|
}
|
|
n->height = height;
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
static void SetCursorSprite(uint cursor)
|
|
{
|
|
CursorVars *cv = &_cursor;
|
|
const Sprite *p;
|
|
|
|
if (cv->sprite == cursor)
|
|
return;
|
|
|
|
p = GetSprite(cursor & 0x3FFF);
|
|
cv->sprite = cursor;
|
|
cv->size.y = p->height;
|
|
cv->size.x = p->width;
|
|
cv->offs.x = p->x_offs;
|
|
cv->offs.y = p->y_offs;
|
|
|
|
cv->dirty = true;
|
|
}
|
|
|
|
static void SwitchAnimatedCursor(void)
|
|
{
|
|
CursorVars *cv = &_cursor;
|
|
const uint16 *cur;
|
|
uint sprite;
|
|
|
|
cur = cv->animate_cur;
|
|
if (cur == NULL || *cur == 0xFFFF)
|
|
cur = cv->animate_list;
|
|
sprite = cur[0];
|
|
cv->animate_timeout = cur[1];
|
|
cv->animate_cur = cur + 2;
|
|
|
|
SetCursorSprite(sprite);
|
|
}
|
|
|
|
void CursorTick(void)
|
|
{
|
|
CursorVars *cv = &_cursor;
|
|
if (cv->animate_timeout && !--cv->animate_timeout)
|
|
SwitchAnimatedCursor();
|
|
}
|
|
|
|
void SetMouseCursor(uint cursor)
|
|
{
|
|
// Turn off animation
|
|
_cursor.animate_timeout = 0;
|
|
// Set cursor
|
|
SetCursorSprite(cursor);
|
|
}
|
|
|
|
void SetAnimatedMouseCursor(const uint16 *table)
|
|
{
|
|
_cursor.animate_list = table;
|
|
_cursor.animate_cur = NULL;
|
|
SwitchAnimatedCursor();
|
|
}
|
|
|
|
bool ChangeResInGame(int w, int h)
|
|
{
|
|
if ((_screen.width != w || _screen.height != h) && !_video_driver->change_resolution(w, h))
|
|
return false;
|
|
|
|
_cur_resolution[0] = w;
|
|
_cur_resolution[1] = h;
|
|
return true;
|
|
}
|
|
|
|
static int CDECL compare_res(const void *pa, const void *pb)
|
|
{
|
|
int x = ((const uint16*)pa)[0] - ((const uint16*)pb)[0];
|
|
if (x != 0) return x;
|
|
return ((const uint16*)pa)[1] - ((const uint16*)pb)[1];
|
|
}
|
|
|
|
void SortResolutions(int count)
|
|
{
|
|
qsort(_resolutions, count, sizeof(_resolutions[0]), compare_res);
|
|
}
|
|
|
|
uint16 GetDrawStringPlayerColor(byte player)
|
|
{
|
|
// Get the color for DrawString-subroutines which matches the color
|
|
// of the player
|
|
if (player == OWNER_SPECTATOR || player == OWNER_SPECTATOR - 1)
|
|
return 1;
|
|
return (_color_list[_player_colors[player]].window_color_1b) | IS_PALETTE_COLOR;
|
|
}
|