/* GDI graphics renderer */ #include #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; }