WinUAE/od-win32/gdirender.cpp

686 lines
15 KiB
C++

/* GDI graphics renderer */
#include <windows.h>
#include "resource.h"
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
#include "xwin.h"
#include "custom.h"
#include "drawing.h"
#include "render.h"
#include "win32.h"
#include "win32gfx.h"
#include "statusline.h"
#include "uae.h"
#include "direct3d.h"
#include "gfxfilter.h"
struct gdibm
{
bool active;
int x, y;
int width, height, depth;
int maxw, maxh;
HDC thdc;
HBITMAP hbm;
HGDIOBJ oldbm;
void *bits;
int pitch;
};
struct gdioverlay
{
struct gdioverlay *next;
int id;
int x, y;
struct gdibm tex;
};
struct gdistruct
{
int enabled;
int num;
int wwidth, wheight;
int depth;
HWND hwnd;
HDC hdc;
HGDIOBJ oldbm;
int statusbar_hx, statusbar_vx;
int ledwidth, ledheight;
struct gdibm bm;
struct gdibm buf;
struct gdibm osd;
struct gdibm cursor;
struct gdioverlay *extoverlays;
float cursor_x, cursor_y;
float cursor_mx, cursor_my;
bool cursor_v, cursor_scale;
RECT sr2, dr2, zr2;
int dmult, dmultxh, dmultxv, dmode;
int xoffset, yoffset;
float xmult, ymult;
int cursor_offset_x, cursor_offset_y;
int bmxoffset, bmyoffset;
int bmwidth, bmheight;
bool refreshneeded;
bool eraseneeded;
};
static struct gdistruct gdidata[MAX_AMIGAMONITORS];
static void gdi_restore(int monid, bool checkonly)
{
struct gdistruct *gdi = &gdidata[monid];
}
static void setupscenecoords(struct gdistruct *gdi, int monid)
{
RECT sr, dr, zr;
static RECT sr2[MAX_AMIGAMONITORS], dr2[MAX_AMIGAMONITORS], zr2[MAX_AMIGAMONITORS];
getfilterrect2(gdi->num, &dr, &sr, &zr, gdi->wwidth, gdi->wheight, gdi->bm.width / gdi->dmult, gdi->bm.height / gdi->dmult, gdi->dmult, &gdi->dmode, gdi->bm.width, gdi->bm.height);
if (memcmp(&sr, &sr2[monid], sizeof RECT) || memcmp(&dr, &dr2[monid], sizeof RECT) || memcmp(&zr, &zr2[monid], sizeof RECT)) {
write_log(_T("POS (%d %d %d %d) - (%d %d %d %d)[%d,%d] (%d %d) S=%d*%d B=%d*%d\n"),
dr.left, dr.top, dr.right, dr.bottom,
sr.left, sr.top, sr.right, sr.bottom,
sr.right - sr.left, sr.bottom - sr.top,
zr.left, zr.top,
gdi->wwidth, gdi->wheight,
gdi->bm.width, gdi->bm.height);
sr2[monid] = sr;
dr2[monid] = dr;
zr2[monid] = zr;
}
gdi->sr2 = sr;
gdi->dr2 = dr;
gdi->zr2 = zr;
float dw = (float)dr.right - dr.left;
float dh = (float)dr.bottom - dr.top;
float w = (float)sr.right - sr.left;
float h = (float)sr.bottom - sr.top;
int tx = ((dr.right - dr.left) * gdi->bm.width) / (gdi->wwidth * 2);
int ty = ((dr.bottom - dr.top) * gdi->bm.height) / (gdi->wheight * 2);
float sw = dw / gdi->wwidth;
float sh = dh / gdi->wheight;
int xshift = -zr.left - sr.left;
int yshift = -zr.top - sr.top;
xshift -= ((sr.right - sr.left) - gdi->wwidth) / 2;
yshift -= ((sr.bottom - sr.top) - gdi->wheight) / 2;
gdi->xoffset = tx + xshift - gdi->wwidth / 2;
gdi->yoffset = ty + yshift - gdi->wheight / 2;
gdi->xmult = filterrectmult(gdi->wwidth, w, gdi->dmode);
gdi->ymult = filterrectmult(gdi->wheight, h, gdi->dmode);
gdi->cursor_offset_x = -zr.left;
gdi->cursor_offset_y = -zr.top;
sw *= gdi->wwidth;
sh *= gdi->wheight;
gdi->bmwidth = (int)(gdi->bm.width * gdi->xmult);
gdi->bmheight = (int)(gdi->bm.height * gdi->ymult);
int positionX, positionY;
int bw2 = gdi->bm.width;
int bh2 = gdi->bm.height;
int sw2 = gdi->wwidth;
int sh2 = gdi->wheight;
positionX = (sw2 - bw2) / 2 + gdi->xoffset;
positionY = (sh2 - bh2) / 2 + gdi->yoffset;
float left = sw2 / -2.0f;
left += positionX;
float top = sh2 / -2.0f;
top += positionY;
left *= gdi->xmult;
top *= gdi->ymult;
left += gdi->wwidth / 2.0f;
top += gdi->wheight / 2.0f;
gdi->bmxoffset = (int)left;
gdi->bmyoffset = (int)top;
gdi->eraseneeded = true;
}
static void gdi_clear(int monid)
{
struct gdistruct *gdi = &gdidata[monid];
if (gdi->hdc) {
Rectangle(gdi->hdc, 0, 0, gdi->wwidth, gdi->wheight);
}
}
static void freesprite(struct gdistruct *gdi, struct gdibm *bm)
{
if (bm->thdc) {
if (bm->hbm) {
if (bm->oldbm) {
SelectObject(bm->thdc, bm->oldbm);
}
DeleteObject(bm->hbm);
}
bm->oldbm = NULL;
bm->hbm = NULL;
bm->bits = NULL;
DeleteDC(bm->thdc);
bm->thdc = NULL;
}
bm->active = false;
}
static void freetexture(int monid)
{
struct gdistruct *gdi = &gdidata[monid];
freesprite(gdi, &gdi->bm);
if (gdi->hdc) {
ReleaseDC(gdi->hwnd, gdi->hdc);
gdi->hdc = NULL;
}
}
static bool allocsprite(struct gdistruct *gdi, struct gdibm *bm, int w, int h)
{
bm->thdc = CreateCompatibleDC(gdi->hdc);
if (bm->thdc) {
BITMAPV4HEADER bmi = { 0 };
bmi.bV4Size = sizeof(BITMAPINFOHEADER);
bmi.bV4Width = w;
bmi.bV4Height = -h;
bmi.bV4Planes = 1;
bmi.bV4V4Compression = BI_RGB;
bmi.bV4BitCount = gdi->depth;
bm->width = w;
bm->height = h;
bm->depth = gdi->depth;
bm->pitch = ((w * bmi.bV4BitCount + 31) / 32) * 4;
bmi.bV4SizeImage = bm->pitch * h;
bm->hbm = CreateDIBSection(gdi->hdc, (const BITMAPINFO*)&bmi, DIB_RGB_COLORS, &bm->bits, NULL, 0);
if (bm->hbm) {
bm->oldbm = SelectObject(bm->thdc, bm->hbm);
SelectObject(bm->thdc, GetStockObject(DC_PEN));
SelectObject(bm->thdc, GetStockObject(DC_BRUSH));
SetDCPenColor(bm->thdc, RGB(0, 0, 0));
SetDCBrushColor(bm->thdc, RGB(0, 0, 0));
return true;
}
}
return false;
}
static bool gdi_alloctexture(int monid, int w, int h)
{
struct gdistruct *gdi = &gdidata[monid];
if (w < 0 || h < 0) {
if (gdi->bm.width == -w && gdi->bm.height == -h && gdi->hdc) {
return true;
}
return false;
}
freetexture(monid);
gdi->hdc = GetDC(gdi->hwnd);
if (gdi->hdc) {
SelectObject(gdi->hdc, GetStockObject(DC_PEN));
SelectObject(gdi->hdc, GetStockObject(DC_BRUSH));
SetDCPenColor(gdi->hdc, RGB(0, 0, 0));
SetDCBrushColor(gdi->hdc, RGB(0, 0, 0));
if (allocsprite(gdi, &gdi->bm, w, h)) {
gdi->dmult = S2X_getmult(monid);
setupscenecoords(gdi, monid);
return true;
}
}
freetexture(monid);
return false;
}
static void updateleds(struct gdistruct *gdi)
{
static uae_u32 rc[256], gc[256], bc[256], a[256];
static int done;
int osdx, osdy;
if (!done) {
for (int i = 0; i < 256; i++) {
rc[i] = i << 16;
gc[i] = i << 8;
bc[i] = i << 0;
a[i] = i << 24;
}
done = 1;
}
if (gdi->osd.bits == NULL || gdi != gdidata)
return;
statusline_getpos(gdi->num, &osdx, &osdy, gdi->wwidth, gdi->wheight);
gdi->osd.x = osdx;
gdi->osd.y = osdy;
for (int y = 0; y < gdi->osd.height; y++) {
uae_u8 *buf = (uae_u8*)gdi->osd.bits + y * gdi->osd.pitch;
statusline_single_erase(gdi->num, buf, gdi->osd.depth / 8, y, gdi->ledwidth);
}
statusline_render(gdi->num, (uae_u8*)gdi->osd.bits, gdi->osd.depth / 8, gdi->osd.pitch, gdi->ledwidth, gdi->ledheight, rc, gc, bc, a);
for (int y = 0; y < gdi->osd.height; y++) {
uae_u8 *buf = (uae_u8*)gdi->osd.bits + y * gdi->osd.pitch;
draw_status_line_single(gdi->num, buf, gdi->osd.depth / 8, y, gdi->ledwidth, rc, gc, bc, a);
}
}
static void gdi_guimode(int monid, int guion)
{
}
static uae_u8 *gdi_locktexture(int monid, int *pitch, int *width, int *height, int fullupdate)
{
struct gdistruct *gdi = &gdidata[monid];
if (gdi->bm.bits) {
*pitch = gdi->bm.pitch;
if (height)
*height = gdi->bm.height;
if (width)
*width = gdi->bm.width;
return (uae_u8*)gdi->bm.bits;
}
return NULL;
}
static void gdi_unlocktexture(int monid, int y_start, int y_end)
{
struct gdistruct *gdi = &gdidata[monid];
struct AmigaMonitor *mon = &AMonitors[monid];
bool rtg = WIN32GFX_IsPicassoScreen(mon);
if (((currprefs.leds_on_screen & STATUSLINE_CHIPSET) && !rtg) || ((currprefs.leds_on_screen & STATUSLINE_RTG) && rtg)) {
updateleds(gdi);
gdi->osd.active = true;
} else {
gdi->osd.active = false;
}
}
static void gdi_flushtexture(int monid, int miny, int maxy)
{
}
static bool gdi_renderframe(int monid, int mode, bool immediate)
{
struct gdistruct *gdi = &gdidata[monid];
if (gdi->bm.hbm) {
setupscenecoords(gdi, monid);
}
return gdi->bm.hbm != NULL;
}
static void gdi_paint(void)
{
for (int monid = 0; monid < MAX_AMIGAMONITORS; monid++) {
struct gdistruct *gdi = &gdidata[monid];
if (!gdi->refreshneeded) {
continue;
}
gdi->refreshneeded = false;
if (gdi->eraseneeded) {
Rectangle(gdi->buf.thdc, 0, 0, gdi->buf.width, gdi->buf.height);
gdi->eraseneeded = false;
}
if (gdi->bm.hbm) {
StretchBlt(gdi->buf.thdc, gdi->bmxoffset, gdi->bmyoffset, gdi->bmwidth, gdi->bmheight, gdi->bm.thdc, 0, 0, gdi->bm.width, gdi->bm.height, SRCCOPY);
}
if (gdi->cursor.active && gdi->cursor.hbm) {
int bx = gdi->bmxoffset;
int by = gdi->bmyoffset;
int x = gdi->cursor.x;
int y = gdi->cursor.y;
int cx = 0;
int cy = 0;
int cw = CURSORMAXWIDTH;
int ch = CURSORMAXHEIGHT;
if (gdi->cursor.x < bx) {
int d = bx - gdi->cursor.x;
cx += d;
cw -= d;
x += d;
}
if (gdi->cursor.y < by) {
int d = by - gdi->cursor.y;
cy += d;
ch -= d;
y += d;
}
if (x + cw > gdi->cursor.maxw) {
cw -= (x + cw) - gdi->cursor.maxw;
}
if (y + ch > gdi->cursor.maxh) {
ch -= (y + ch) - gdi->cursor.maxh;
}
if (cw > 0 && ch > 0) {
TransparentBlt(gdi->buf.thdc, x, y, (int)(cw * gdi->xmult), (int)(ch * gdi->ymult), gdi->cursor.thdc, cx, cy, cw, ch, 0xfe00fe);
}
}
if (gdi->osd.active && gdi->osd.hbm) {
TransparentBlt(gdi->buf.thdc, gdi->osd.x, gdi->osd.y, gdi->ledwidth, gdi->ledheight, gdi->osd.thdc, 0, 0, gdi->ledwidth, gdi->ledheight, 0x000000);
}
struct gdioverlay *ov = gdi->extoverlays;
while (ov) {
if (ov->tex.bits) {
TransparentBlt(gdi->buf.thdc, ov->x, ov->y, ov->tex.width, ov->tex.height, ov->tex.thdc, 0, 0, ov->tex.width, ov->tex.height, 0xfe00fe);
}
ov = ov->next;
}
BitBlt(gdi->hdc, 0, 0, gdi->wwidth, gdi->wheight, gdi->buf.thdc, 0, 0, SRCCOPY);
}
}
static void gdi_showframe(int monid)
{
struct gdistruct *gdi = &gdidata[monid];
gdi->refreshneeded = true;
RECT r;
r.left = 0;
r.top = 0;
r.right = gdi->wwidth;
r.bottom = gdi->wheight;
InvalidateRect(gdi->hwnd, &r, FALSE);
}
static void gdi_refresh(int monid)
{
gdi_clear(monid);
gdi_showframe(monid);
}
void gdi_free(int monid, bool immediate)
{
struct gdistruct *gdi = &gdidata[monid];
gdi->enabled = 0;
freetexture(monid);
freesprite(gdi, &gdi->osd);
freesprite(gdi, &gdi->cursor);
struct gdioverlay *ov = gdi->extoverlays;
while (ov) {
struct gdioverlay *next = ov->next;
if (ov->tex.bits) {
freesprite(gdi, &ov->tex);
}
xfree(ov);
ov = next;
}
gdi->extoverlays = NULL;
}
static const TCHAR *gdi_init(HWND ahwnd, int monid, int w_w, int w_h, int depth, int *freq, int mmulth, int mmultv, int *errp)
{
struct gdistruct *gdi = &gdidata[monid];
if (isfullscreen() > 0) {
*errp = 2;
return _T("GDI fullscreen not supported");
}
gdi->hwnd = ahwnd;
gdi->depth = depth;
gdi->wwidth = w_w;
gdi->wheight = w_h;
if (allocsprite(gdi, &gdi->buf, gdi->wwidth, gdi->wheight)) {
gdi->statusbar_hx = gdi->statusbar_vx = statusline_set_multiplier(monid, gdi->wwidth, gdi->wheight) / 100;
gdi->ledwidth = gdi->wwidth;
gdi->ledheight = TD_TOTAL_HEIGHT * gdi->statusbar_vx;
allocsprite(gdi, &gdi->osd, gdi->ledwidth, gdi->ledheight);
allocsprite(gdi, &gdi->cursor, CURSORMAXWIDTH, CURSORMAXHEIGHT);
gdi->enabled = 1;
write_log(_T("GDI mode initialized %d*%d*%d\n"), w_w, w_h, depth);
return NULL;
}
*errp = 1;
return _T("failed to allocate buffer");
}
static HDC gdi_getDC(int monid, HDC hdc)
{
struct gdistruct *gdi = &gdidata[monid];
if (!hdc) {
return gdi->hdc;
}
return NULL;
}
static int gdi_isenabled(int monid)
{
struct gdistruct *gdi = &gdidata[monid];
return gdi->enabled ? -1 : 0;
}
static bool gdi_setcursor(int monid, int x, int y, int width, int height, float mx, float my, bool visible, bool noscale)
{
struct gdistruct *gdi = &gdidata[monid];
int cx, cy;
if (gdi->depth < 32) {
return false;
}
if (width < 0 || height < 0) {
return true;
}
if (width && height) {
cx = (int)((float)x * mx * gdi->xmult + gdi->cursor_offset_x * gdi->ymult + 0.5f);
cy = (int)((float)y * my * gdi->ymult + gdi->cursor_offset_y * gdi->xmult + 0.5f);
} else {
cx = cy = 0;
}
if (cx < 0) {
cx = 0;
}
if (cy < 0) {
cy = 0;
}
gdi->cursor.x = cx;
gdi->cursor.y = cy;
gdi->cursor.maxw = (int)((float)gdi->bmwidth * mx * gdi->xmult + gdi->cursor_offset_x * gdi->ymult + 0.5f);
gdi->cursor.maxh = (int)((float)gdi->bmheight * my * gdi->ymult + gdi->cursor_offset_y * gdi->xmult + 0.5f);
gdi->cursor_scale = !noscale;
gdi->cursor.active = visible;
return true;
}
static uae_u8 *gdi_setcursorsurface(int monid, bool query, int *pitch)
{
struct gdistruct* gdi = &gdidata[monid];
if (query) {
return (uae_u8 *)gdi->cursor.bits;
}
if (gdi->depth < 32) {
return NULL;
}
if (pitch) {
*pitch = gdi->cursor.pitch;
return (uae_u8*)gdi->cursor.bits;
}
for (int y = 0; y < CURSORMAXHEIGHT; y++) {
for (int x = 0; x < CURSORMAXWIDTH; x++) {
uae_u32 *p = (uae_u32*)((uae_u8*)gdi->cursor.bits + gdi->cursor.pitch * y + x * 4);
uae_u32 v = *p;
if ((v & 0xff000000) == 0x00000000) {
*p = 0xfe00fe;
} else {
*p = v & 0xffffff;
}
}
}
return NULL;
}
static bool gdi_extoverlay(struct extoverlay *ext, int monid)
{
struct gdistruct *gdi = &gdidata[monid];
struct gdioverlay *ov, *ovprev, *ov2;
struct gdibm *s = NULL;
if (gdi->depth < 32) {
return false;
}
gdi->eraseneeded = true;
ov = gdi->extoverlays;
ovprev = NULL;
while (ov) {
if (ov->id == ext->idx) {
s = &ov->tex;
break;
}
ovprev = ov;
ov = ov->next;
}
if (!s && (ext->width <= 0 || ext->height <= 0))
return false;
if (!ext->data && s && (ext->width == 0 || ext->height == 0)) {
ov->x = ext->xpos;
ov->y = ext->ypos;
return true;
}
if (ov && s) {
if (ovprev) {
ovprev->next = ov->next;
} else {
gdi->extoverlays = ov->next;
}
freesprite(gdi, s);
xfree(ov);
if (ext->width <= 0 || ext->height <= 0)
return true;
}
if (ext->width <= 0 || ext->height <= 0 || !ext->data)
return false;
ov = xcalloc(gdioverlay, 1);
if (!ov) {
return false;
}
if (!allocsprite(gdi, &ov->tex, ext->width, ext->height)) {
xfree(ov);
return false;
}
s = &ov->tex;
ov->id = ext->idx;
ov2 = gdi->extoverlays;
ovprev = NULL;
for (;;) {
if (ov2 == NULL || ov2->id >= ov->id) {
if (ov2 == gdi->extoverlays) {
gdi->extoverlays = ov;
ov->next = ov2;
} else {
ov->next = ovprev->next;
ovprev->next = ov;
}
break;
}
ovprev = ov2;
ov2 = ov2->next;
}
ov->x = ext->xpos;
ov->y = ext->ypos;
for (int y = 0; y < ext->height; y++) {
for (int x = 0; x < ext->width; x++) {
uae_u32 *sp = (uae_u32*)(ext->data + ext->width * y * 4 + x * 4);
uae_u32 *p = (uae_u32*)((uae_u8*)s->bits + s->pitch * y + x * 4);
uae_u32 v = *sp;
if ((v & 0xff000000) == 0x00000000) {
*p = 0xfe00fe;
} else {
*p = v & 0xffffff;
}
}
}
return true;
}
void gdi_select(void)
{
for (int i = 0; i < MAX_AMIGAMONITORS; i++) {
gdidata[i].num = i;
}
D3D_free = gdi_free;
D3D_init = gdi_init;
D3D_renderframe = gdi_renderframe;
D3D_alloctexture = gdi_alloctexture;
D3D_refresh = gdi_refresh;
D3D_restore = gdi_restore;
D3D_locktexture = gdi_locktexture;
D3D_unlocktexture = gdi_unlocktexture;
D3D_flushtexture = gdi_flushtexture;
D3D_showframe = gdi_showframe;
D3D_showframe_special = NULL;
D3D_guimode = gdi_guimode;
D3D_getDC = gdi_getDC;
D3D_isenabled = gdi_isenabled;
D3D_clear = gdi_clear;
D3D_canshaders = NULL;
D3D_goodenough = NULL;
D3D_setcursor = gdi_setcursor;
D3D_setcursorsurface = gdi_setcursorsurface;
D3D_getrefreshrate = NULL;
D3D_resize = NULL;
D3D_change = NULL;
D3D_getscalerect = NULL;
D3D_run = NULL;
D3D_debug = NULL;
D3D_led = NULL;
D3D_getscanline = NULL;
D3D_extoverlay = gdi_extoverlay;
D3D_paint = gdi_paint;
}