#include "std.h" #include "gxgraphics.h" #include "gxruntime.h" extern gxRuntime *gx_runtime; gxGraphics::gxGraphics(gxRuntime *rt, IDirectDraw7 *dd, IDirectDrawSurface7 *fs, IDirectDrawSurface7 *bs, bool d3d) : runtime(rt), dirDraw(dd), dir3d(0), dir3dDev(0), def_font(0), gfx_lost(false), dummy_mesh(0) { dirDraw->QueryInterface(IID_IDirectDraw, (void**)&ds_dirDraw); front_canvas = new gxCanvas(this, fs, 0); back_canvas = new gxCanvas(this, bs, 0); front_canvas->cls(); back_canvas->cls(); def_font = loadFont("courier", 12, 0); front_canvas->setFont(def_font); back_canvas->setFont(def_font); memset(&primFmt, 0, sizeof(primFmt)); primFmt.dwSize = sizeof(primFmt); fs->GetPixelFormat(&primFmt); //are we fullscreen? _gamma = 0; if (fs != bs) { if (fs->QueryInterface(IID_IDirectDrawGammaControl, (void**)&_gamma) >= 0) { if (_gamma->GetGammaRamp(0, &_gammaRamp) < 0) _gamma = 0; } } if (!_gamma) { for (int k = 0; k < 256; ++k) _gammaRamp.red[k] = _gammaRamp.blue[k] = _gammaRamp.green[k] = k; } } gxGraphics::~gxGraphics() { if (_gamma) _gamma->Release(); while (scene_set.size()) freeScene(*scene_set.begin()); while (movie_set.size()) closeMovie(*movie_set.begin()); while (font_set.size()) freeFont(*font_set.begin()); while (canvas_set.size()) freeCanvas(*canvas_set.begin()); set::iterator it; for (it = font_res.begin(); it != font_res.end(); ++it) RemoveFontResource((*it).c_str()); font_res.clear(); delete back_canvas; delete front_canvas; ds_dirDraw->Release(); dirDraw->RestoreDisplayMode(); dirDraw->Release(); } void gxGraphics::setGamma(int r, int g, int b, float dr, float dg, float db) { _gammaRamp.red[r & 255] = dr*257.0f; _gammaRamp.green[g & 255] = dg*257.0f; _gammaRamp.blue[b & 255] = db*257.0f; } void gxGraphics::updateGamma(bool calibrate) { if (!_gamma) return; _gamma->SetGammaRamp(calibrate ? DDSGR_CALIBRATE : 0, &_gammaRamp); } void gxGraphics::getGamma(int r, int g, int b, float *dr, float *dg, float *db) { *dr = _gammaRamp.red[r & 255] / 257.0f; *dg = _gammaRamp.green[g & 255] / 257.0f; *db = _gammaRamp.blue[b & 255] / 257.0f; } void gxGraphics::backup() { } bool gxGraphics::restore() { while (dirDraw->TestCooperativeLevel() != DD_OK) { if (dirDraw->TestCooperativeLevel() == DDERR_WRONGMODE) return false; Sleep(100); } if (back_canvas->getSurface()->IsLost() == DD_OK) return true; dirDraw->RestoreAllSurfaces(); //restore all canvases set::iterator it; for (it = canvas_set.begin(); it != canvas_set.end(); ++it) { (*it)->restore(); } //restore all meshes (b3d surfaces) set::iterator mesh_it; for (mesh_it = mesh_set.begin(); mesh_it != mesh_set.end(); ++mesh_it) { (*mesh_it)->restore(); } if (dir3d) dir3d->EvictManagedTextures(); return true; } gxCanvas *gxGraphics::getFrontCanvas()const { return front_canvas; } gxCanvas *gxGraphics::getBackCanvas()const { return back_canvas; } gxFont *gxGraphics::getDefaultFont()const { return def_font; } void gxGraphics::vwait() { dirDraw->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); } void gxGraphics::flip(bool v) { runtime->flip(v); } void gxGraphics::copy(gxCanvas *dest, int dx, int dy, int dw, int dh, gxCanvas *src, int sx, int sy, int sw, int sh) { RECT r = { dx,dy,dx + dw,dy + dh }; ddUtil::copy(dest->getSurface(), dx, dy, dw, dh, src->getSurface(), sx, sy, sw, sh); dest->damage(r); } int gxGraphics::getScanLine()const { DWORD t = 0; dirDraw->GetScanLine(&t); return t; } int gxGraphics::getTotalVidmem()const { DDCAPS caps = { sizeof(caps) }; dirDraw->GetCaps(&caps, 0); return caps.dwVidMemTotal; } int gxGraphics::getAvailVidmem()const { DDCAPS caps = { sizeof(caps) }; dirDraw->GetCaps(&caps, 0); return caps.dwVidMemFree; } gxMovie *gxGraphics::openMovie(const string &file, int flags) { /*IAMMultiMediaStream *iam_stream; if (CoCreateInstance( CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, (void **)&iam_stream) == S_OK) { if (iam_stream->Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL) == S_OK) { if (iam_stream->AddMediaStream(ds_dirDraw, &MSPID_PrimaryVideo, 0, NULL) == S_OK) { iam_stream->AddMediaStream(NULL, &MSPID_PrimaryAudio, AMMSF_ADDDEFAULTRENDERER, NULL); WCHAR *path = new WCHAR[file.size() + 1]; MultiByteToWideChar(CP_ACP, 0, file.c_str(), -1, path, sizeof(WCHAR)*(file.size() + 1)); int n = iam_stream->OpenFile(path, 0); delete path; if (n == S_OK) { gxMovie *movie = new gxMovie(this, iam_stream); movie_set.insert(movie); return movie; } } } iam_stream->Release(); }*/ return 0; } gxMovie *gxGraphics::verifyMovie(gxMovie *m) { return movie_set.count(m) ? m : 0; } void gxGraphics::closeMovie(gxMovie *m) { if (movie_set.erase(m)) delete m; } gxCanvas *gxGraphics::createCanvas(int w, int h, int flags) { IDirectDrawSurface7 *s = ddUtil::createSurface(w, h, flags, this); if (!s) return 0; gxCanvas *c = new gxCanvas(this, s, flags); canvas_set.insert(c); c->cls(); return c; } gxCanvas *gxGraphics::loadCanvas(const string &f, int flags) { IDirectDrawSurface7 *s = ddUtil::loadSurface(f, flags, this); if (!s) return 0; gxCanvas *c = new gxCanvas(this, s, flags); canvas_set.insert(c); return c; } gxCanvas *gxGraphics::verifyCanvas(gxCanvas *c) { return canvas_set.count(c) || c == front_canvas || c == back_canvas ? c : 0; } void gxGraphics::freeCanvas(gxCanvas *c) { if (canvas_set.erase(c)) delete c; } int gxGraphics::getWidth()const { return front_canvas->getWidth(); } int gxGraphics::getHeight()const { return front_canvas->getHeight(); } int gxGraphics::getDepth()const { return front_canvas->getDepth(); } gxFont *gxGraphics::loadFont(const string &f, int height, int flags) { int bold = flags & gxFont::FONT_BOLD ? FW_BOLD : FW_REGULAR; int italic = flags & gxFont::FONT_ITALIC ? 1 : 0; int underline = flags & gxFont::FONT_UNDERLINE ? 1 : 0; int strikeout = 0; string t; int n = f.find('.'); if (n != string::npos) { t = fullfilename(f); if (!font_res.count(t) && AddFontResource(t.c_str())) font_res.insert(t); t = filenamefile(f.substr(0, n)); } else { t = f; } //save and turn off font smoothing.... BOOL smoothing = FALSE; SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &smoothing, 0); SystemParametersInfo(SPI_SETFONTSMOOTHING, FALSE, 0, 0); HFONT hfont = CreateFont( height, 0, 0, 0, bold, italic, underline, strikeout, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, t.c_str()); if (!hfont) { //restore font smoothing SystemParametersInfo(SPI_SETFONTSMOOTHING, smoothing, 0, 0); return 0; } HDC hdc = CreateCompatibleDC(0); HFONT pfont = (HFONT)SelectObject(hdc, hfont); TEXTMETRIC tm = { 0 }; if (!GetTextMetrics(hdc, &tm)) { SelectObject(hdc, pfont); DeleteDC(hdc); DeleteObject(hfont); SystemParametersInfo(SPI_SETFONTSMOOTHING, smoothing, 0, 0); return 0; } height = tm.tmHeight; int first = tm.tmFirstChar, last = tm.tmLastChar; int sz = last - first + 1; int *offs = new int[sz]; int *widths = new int[sz]; int *as = new int[sz]; //calc size of canvas to hold font. int x = 0, y = 0, max_x = 0; for (int k = 0; k < sz; ++k) { char t = k + first; SIZE sz; GetTextExtentPoint32(hdc, &t, 1, &sz); int w = sz.cx; as[k] = 0; ABC abc; if (GetCharABCWidths(hdc, t, t, &abc)) { if (abc.abcA < 0) { as[k] = ceil(-abc.abcA); w += as[k]; } if (abc.abcC < 0) w += (int)ceil(-abc.abcC); } if (x && x + w > getWidth()) { x = 0; y += height; } offs[k] = (x << 16) | y; widths[k] = w; x += w; if (x > max_x) max_x = x; } SelectObject(hdc, pfont); DeleteDC(hdc); int cw = max_x, ch = y + height; if (gxCanvas *c = createCanvas(cw, ch, 0)) { IDirectDrawSurface7 *surf = c->getSurface(); HDC surf_hdc; if (surf->GetDC(&surf_hdc) >= 0) { HFONT pfont = (HFONT)SelectObject(surf_hdc, hfont); SetBkColor(surf_hdc, 0x000000); SetTextColor(surf_hdc, 0xffffff); for (int k = 0; k < sz; ++k) { int x = (offs[k] >> 16) & 0xffff, y = offs[k] & 0xffff; char t = k + first; RECT rect = { x,y,x + widths[k],y + height }; ExtTextOut(surf_hdc, x + as[k], y, ETO_CLIPPED, &rect, &t, 1, 0); } SelectObject(surf_hdc, pfont); surf->ReleaseDC(surf_hdc); DeleteObject(hfont); delete[] as; c->backup(); gxFont *font = new gxFont(this, c, tm.tmMaxCharWidth, height, first, last + 1, tm.tmDefaultChar, offs, widths); font_set.insert(font); //restore font smoothing SystemParametersInfo(SPI_SETFONTSMOOTHING, smoothing, 0, 0); return font; } else { } freeCanvas(c); } else { } DeleteObject(hfont); delete[] as; delete[] widths; delete[] offs; //restore font smoothing SystemParametersInfo(SPI_SETFONTSMOOTHING, smoothing, 0, 0); return 0; } gxFont *gxGraphics::verifyFont(gxFont *f) { return font_set.count(f) ? f : 0; } void gxGraphics::freeFont(gxFont *f) { if (font_set.erase(f)) delete f; } ////////////// // 3D STUFF // ////////////// static int maxDevType; static HRESULT CALLBACK enumDevice(char *desc, char *name, D3DDEVICEDESC7 *devDesc, void *context) { gxGraphics *g = (gxGraphics*)context; int t = 0; GUID guid = devDesc->deviceGUID; if (guid == IID_IDirect3DRGBDevice) t = 1; else if (guid == IID_IDirect3DHALDevice) t = 2; else if (guid == IID_IDirect3DTnLHalDevice) t = 3; if (t > maxDevType) { g->dir3dDevDesc = *devDesc; maxDevType = t; } return D3DENUMRET_OK; } static HRESULT CALLBACK enumZbuffFormat(LPDDPIXELFORMAT format, void *context) { gxGraphics *g = (gxGraphics*)context; if (format->dwZBufferBitDepth == g->primFmt.dwRGBBitCount) { g->zbuffFmt = *format; return D3DENUMRET_CANCEL; } if (format->dwZBufferBitDepth > g->zbuffFmt.dwZBufferBitDepth) { if (format->dwZBufferBitDepth < g->primFmt.dwRGBBitCount) { g->zbuffFmt = *format; } } return D3DENUMRET_OK; } struct TexFmt { DDPIXELFORMAT fmt; int bits, a_bits, rgb_bits; }; static int cntBits(int mask) { int n = 0; for (int k = 0; k < 32; ++k) { if (mask & (1 << k)) ++n; } return n; } static vector tex_fmts; static HRESULT CALLBACK enumTextureFormat(DDPIXELFORMAT *fmt, void *p) { TexFmt t; t.fmt = *fmt; t.bits = fmt->dwRGBBitCount; t.a_bits = (fmt->dwFlags & DDPF_ALPHAPIXELS) ? cntBits(fmt->dwRGBAlphaBitMask) : 0; t.rgb_bits = (fmt->dwFlags & DDPF_RGB) ? cntBits(fmt->dwRBitMask | fmt->dwGBitMask | fmt->dwBBitMask) : 0; tex_fmts.push_back(t); return D3DENUMRET_OK; } static string itobin(int n) { string t; for (int k = 0; k < 32; n <<= 1, ++k) { t += (n & 0x80000000) ? '1' : '0'; } return t; } static void debugPF(const DDPIXELFORMAT &pf) { string t; t = "Bits:" + itoa(pf.dwRGBBitCount); gx_runtime->debugLog(t.c_str()); t = "R Mask:" + itobin(pf.dwRBitMask); gx_runtime->debugLog(t.c_str()); t = "G Mask:" + itobin(pf.dwGBitMask); gx_runtime->debugLog(t.c_str()); t = "B Mask:" + itobin(pf.dwBBitMask); gx_runtime->debugLog(t.c_str()); t = "A Mask:" + itobin(pf.dwRGBAlphaBitMask); gx_runtime->debugLog(t.c_str()); } static void pickTexFmts(gxGraphics *g, int hi) { //texRGBFmt. { int pick = -1, max = 0, bits; for (int d = g->primFmt.dwRGBBitCount; d <= 32; d += 8) { for (int k = 0; k < tex_fmts.size(); ++k) { const TexFmt &t = tex_fmts[k]; if (t.bits > d || !t.rgb_bits || t.rgb_bits < max) continue; if (t.rgb_bits == max && t.bits >= bits) continue; pick = k; max = t.rgb_bits; bits = t.bits; } if (!hi && pick >= 0) break; } if (pick < 0) g->texRGBFmt[hi] = g->primFmt; else g->texRGBFmt[hi] = tex_fmts[pick].fmt; } //texAlphaFmt { int pick = -1, max = 0, bits; for (int d = g->primFmt.dwRGBBitCount; d <= 32; d += 8) { for (int k = 0; k < tex_fmts.size(); ++k) { const TexFmt &t = tex_fmts[k]; if (t.bits > d || !t.a_bits || t.a_bits < max) continue; if (t.a_bits == max && t.bits >= bits) continue; pick = k; max = t.a_bits; bits = t.bits; } if (!hi && pick >= 0) break; } if (pick < 0) g->texAlphaFmt[hi] = g->primFmt; else g->texAlphaFmt[hi] = tex_fmts[pick].fmt; } //texRGBAlphaFmt { int pick = -1, a8rgb8 = -1, max = 0, bits; for (int d = g->primFmt.dwRGBBitCount; d <= 32; d += 8) { for (int k = 0; k < tex_fmts.size(); ++k) { const TexFmt &t = tex_fmts[k]; if (t.a_bits == 8 && t.bits == 16) { a8rgb8 = k; continue; } if (t.bits > d || !t.a_bits || !t.rgb_bits || t.a_bits < max) continue; if (t.a_bits == max && t.bits >= bits) continue; pick = k; max = t.a_bits; bits = t.bits; } if (!hi && pick >= 0) break; } if (pick < 0) pick = a8rgb8; if (pick < 0) g->texRGBAlphaFmt[hi] = g->primFmt; else g->texRGBAlphaFmt[hi] = tex_fmts[pick].fmt; } //texRGBMaskFmt... { int pick = -1, max = 0, bits; for (int d = g->primFmt.dwRGBBitCount; d <= 32; d += 8) { for (int k = 0; k < tex_fmts.size(); ++k) { const TexFmt &t = tex_fmts[k]; if (!t.a_bits || !t.rgb_bits || t.rgb_bits < max) continue; if (t.rgb_bits == max && t.bits >= bits) continue; pick = k; max = t.rgb_bits; bits = t.bits; } if (!hi && pick >= 0) break; } if (pick < 0) g->texRGBMaskFmt[hi] = g->primFmt; else g->texRGBMaskFmt[hi] = tex_fmts[pick].fmt; } } gxScene *gxGraphics::createScene(int flags) { if (scene_set.size()) return 0; //get d3d if (dirDraw->QueryInterface(IID_IDirect3D7, (void**)&dir3d) >= 0) { //enum devices maxDevType = 0; if (dir3d->EnumDevices(enumDevice, this) >= 0 && maxDevType > 1) { //enum zbuffer formats zbuffFmt.dwZBufferBitDepth = 0; if (dir3d->EnumZBufferFormats(dir3dDevDesc.deviceGUID, enumZbuffFormat, this) >= 0) { //create zbuff for back buffer if (back_canvas->attachZBuffer()) { //create 3d device if (dir3d->CreateDevice(dir3dDevDesc.deviceGUID, back_canvas->getSurface(), &dir3dDev) >= 0) { //enum texture formats tex_fmts.clear(); if (dir3dDev->EnumTextureFormats(enumTextureFormat, this) >= 0) { pickTexFmts(this, 0); pickTexFmts(this, 1); tex_fmts.clear(); #ifdef BETA gx_runtime->debugLog("Texture RGB format:"); debugPF(texRGBFmt); gx_runtime->debugLog("Texture Alpha format:"); debugPF(texAlphaFmt); gx_runtime->debugLog("Texture RGB Alpha format:"); debugPF(texRGBAlphaFmt); gx_runtime->debugLog("Texture RGB Mask format:"); debugPF(texRGBMaskFmt); gx_runtime->debugLog("Texture Primary format:"); debugPF(primFmt); string ts = "ZBuffer Bit Depth:" + itoa(zbuffFmt.dwZBufferBitDepth); gx_runtime->debugLog(ts.c_str()); #endif gxScene *scene = new gxScene(this, back_canvas); scene_set.insert(scene); dummy_mesh = createMesh(8, 12, 0); return scene; } dir3dDev->Release(); dir3dDev = 0; } back_canvas->releaseZBuffer(); } } } dir3d->Release(); dir3d = 0; } return 0; } gxScene *gxGraphics::verifyScene(gxScene *s) { return scene_set.count(s) ? s : 0; } void gxGraphics::freeScene(gxScene *scene) { if (!scene_set.erase(scene)) return; dummy_mesh = 0; while (mesh_set.size()) freeMesh(*mesh_set.begin()); back_canvas->releaseZBuffer(); if (dir3dDev) { dir3dDev->Release(); dir3dDev = 0; } if (dir3d) { dir3d->Release(); dir3d = 0; } delete scene; } gxMesh *gxGraphics::createMesh(int max_verts, int max_tris, int flags) { static const int VTXFMT = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2 | D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE2(1); int vbflags = 0; //XP or less? /*if (runtime->osinfo.dwMajorVersion < 6) { vbflags |= D3DVBCAPS_WRITEONLY; }*/ D3DVERTEXBUFFERDESC desc = { sizeof(desc),vbflags,VTXFMT,max_verts }; IDirect3DVertexBuffer7 *buff; if (dir3d->CreateVertexBuffer(&desc, &buff, 0) < 0) return 0; WORD *indices = new WORD[max_tris * 3]; gxMesh *mesh = new gxMesh(this, buff, indices, max_verts, max_tris); mesh_set.insert(mesh); return mesh; } gxMesh *gxGraphics::verifyMesh(gxMesh *m) { return mesh_set.count(m) ? m : 0; } void gxGraphics::freeMesh(gxMesh *mesh) { if (mesh_set.erase(mesh)) delete mesh; }