bool GetFormatMSE(const D3DXIMAGE_INFO& info, LPDIRECT3DSURFACE8 pSrcSurf, D3DFORMAT fmt, double& CMSE, double& AMSE) { LPDIRECT3DSURFACE8 pCompSurf = 0, pDstSurf = 0; HRESULT hr; // Compress int Width = PadPow2(info.Width), Height = PadPow2(info.Height); hr = pD3DDevice->CreateImageSurface(Width, Height, fmt, &pCompSurf); CheckHR(hr); hr = D3DXLoadSurfaceFromSurface(pCompSurf, NULL, NULL, pSrcSurf, NULL, NULL, D3DX_FILTER_NONE, 0); CheckHR(hr); // Decompress hr = pD3DDevice->CreateImageSurface(Width, Height, D3DFMT_A8R8G8B8, &pDstSurf); CheckHR(hr); hr = D3DXLoadSurfaceFromSurface(pDstSurf, NULL, NULL, pCompSurf, NULL, NULL, D3DX_FILTER_NONE, 0); CheckHR(hr); pCompSurf->Release(); pCompSurf = 0; // calculate mean square error D3DLOCKED_RECT slr, dlr; hr = pSrcSurf->LockRect(&slr, NULL, D3DLOCK_READONLY); CheckHR(hr); hr = pDstSurf->LockRect(&dlr, NULL, D3DLOCK_READONLY); CheckHR(hr); double CTSE = 0.0; // total colour square error double ATSE = 0.0; // total alpha square error RGBCOLOUR* src = (RGBCOLOUR*)slr.pBits; RGBCOLOUR* dst = (RGBCOLOUR*)dlr.pBits; for (UINT y = 0; y < info.Height; ++y) { for (UINT x = 0; x < info.Width; ++x) { CTSE += (src->b - dst->b) * (src->b - dst->b); CTSE += (src->g - dst->g) * (src->g - dst->g); CTSE += (src->r - dst->r) * (src->r - dst->r); ATSE += (src->a - dst->a) * (src->a - dst->a); ++src; ++dst; } src += (slr.Pitch - info.Width*sizeof(RGBCOLOUR)) / sizeof(RGBCOLOUR); dst += (dlr.Pitch - info.Width*sizeof(RGBCOLOUR)) / sizeof(RGBCOLOUR); } CMSE = CTSE / double(info.Width * info.Height * 3); AMSE = ATSE / double(info.Width * info.Height); pSrcSurf->UnlockRect(); pDstSurf->UnlockRect(); pDstSurf->Release(); pDstSurf = 0; return true; }
int LoadBitmapToSurface(char* PathName, LPDIRECT3DSURFACE8* ppSurface, LPDIRECT3DDEVICE8 pDevice){ HRESULT r; HBITMAP hBitmap; BITMAP Bitmap; hBitmap = (HBITMAP)LoadImage(NULL, PathName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION); if(hBitmap == NULL){ SetError("Unable to load bitmap"); return E_FAIL; } GetObject(hBitmap, sizeof(BITMAP), &Bitmap); DeleteObject(hBitmap);//we only needed it for the header info to create a D3D surface //create surface for bitmap r=pDevice->CreateImageSurface(Bitmap.bmWidth, Bitmap.bmHeight, D3DFMT_X8R8G8B8, ppSurface); if(FAILED(r)){ SetError("Unable to create surface for bitmap load"); return E_FAIL; } //load bitmap onto surface r = D3DXLoadSurfaceFromFile(*ppSurface, NULL, NULL, PathName, NULL, D3DX_FILTER_NONE, 0, NULL); if(FAILED(r)){ SetError("Unable to laod file to surface"); return E_FAIL; } return S_OK; }
RageSurface* RageDisplay_D3D::CreateScreenshot() { #if defined(XBOX) return NULL; #else /* Get the back buffer. */ IDirect3DSurface8* pSurface; g_pd3dDevice->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pSurface ); /* Get the back buffer description. */ D3DSURFACE_DESC desc; pSurface->GetDesc( &desc ); /* Copy the back buffer into a surface of a type we support. */ IDirect3DSurface8* pCopy; g_pd3dDevice->CreateImageSurface( desc.Width, desc.Height, D3DFMT_A8R8G8B8, &pCopy ); D3DXLoadSurfaceFromSurface( pCopy, NULL, NULL, pSurface, NULL, NULL, D3DX_DEFAULT, 0 ); pSurface->Release(); /* Update desc from the copy. */ pCopy->GetDesc( &desc ); D3DLOCKED_RECT lr; { RECT rect; rect.left = 0; rect.top = 0; rect.right = desc.Width; rect.bottom = desc.Height; pCopy->LockRect( &lr, &rect, D3DLOCK_READONLY ); } RageSurface *surface = CreateSurfaceFromPixfmt( FMT_RGBA8, lr.pBits, desc.Width, desc.Height, lr.Pitch); ASSERT( surface ); /* We need to make a copy, since lr.pBits will go away when we call UnlockRect(). */ RageSurface *SurfaceCopy = CreateSurface( surface->w, surface->h, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, surface->format->Amask ); RageSurfaceUtils::CopySurface( surface, SurfaceCopy ); delete surface; pCopy->UnlockRect(); pCopy->Release(); return SurfaceCopy; #endif }
RageSurface* RageDisplay_D3D::CreateScreenshot() { // Get the back buffer. IDirect3DSurface8* pSurface; g_pd3dDevice->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pSurface ); // Get the back buffer description. D3DSURFACE_DESC desc; pSurface->GetDesc( &desc ); // Copy the back buffer into a surface of a type we support. IDirect3DSurface8* pCopy; g_pd3dDevice->CreateImageSurface( desc.Width, desc.Height, D3DFMT_A8R8G8B8, &pCopy ); // Aldo_MX: D3DXLoadSurfaceFromSurface requires d3dx8core.h, I replaced it with CopyRects to // remove this dependency so its possible to compile SM with any DirectX SDK up to Aug 2007 D3DXLoadSurfaceFromSurface( pCopy, NULL, NULL, pSurface, NULL, NULL, D3DX_DEFAULT, 0 ); //g_pd3dDevice->CopyRects( pSurface, NULL, 0, pCopy, NULL ); pSurface->Release(); // Update desc from the copy. pCopy->GetDesc( &desc ); D3DLOCKED_RECT lr; { RECT rect; rect.left = 0; rect.top = 0; rect.right = desc.Width; rect.bottom = desc.Height; pCopy->LockRect( &lr, &rect, D3DLOCK_READONLY ); } RageSurface *surface = CreateSurfaceFromPixfmt( PixelFormat_RGBA8, lr.pBits, desc.Width, desc.Height, lr.Pitch); ASSERT( surface ); // We need to make a copy, since lr.pBits will go away when we call UnlockRect(). RageSurface *SurfaceCopy = CreateSurface( surface->w, surface->h, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, surface->format->Amask ); RageSurfaceUtils::CopySurface( surface, SurfaceCopy ); delete surface; pCopy->UnlockRect(); pCopy->Release(); return SurfaceCopy; }
// only works for gifs or other 256-colour anims void ConvertAnim(const char* Dir, const char* Filename, double MaxMSE) { HRESULT hr; LPDIRECT3DSURFACE8 pSrcSurf = NULL; char OutFilename[52]; if (Dir) _snprintf(OutFilename, 52, "%s\\%s", Dir, Filename); else _snprintf(OutFilename, 52, "%s", Filename); OutFilename[51] = 0; printf("%s: ", OutFilename); TRACE1("%s:\n", OutFilename); int n = strlen(OutFilename); if (n < 40) printf("%*c", 40-n, ' '); // Load up the file CAnimatedGifSet Anim; int nImages = Anim.LoadGIF(Filename); if (!nImages) { puts("ERROR: Unable to load gif (file corrupt?)"); return; } if (nImages > 65535) { printf("ERROR: Too many frames in gif (%d > 65535)\n", nImages); return; } PrintAnimInfo(Anim); UINT Width = PadPow2(Anim.FrameWidth); UINT Height = PadPow2(Anim.FrameHeight); D3DXIMAGE_INFO info; info.Width = Anim.FrameWidth; info.Height = Anim.FrameHeight; info.MipLevels = 1; info.Depth = 0; info.ResourceType = D3DRTYPE_SURFACE; info.Format = D3DFMT_P8; info.ImageFileFormat = D3DXIFF_PNG; PALETTEENTRY pal[256]; memcpy(pal, Anim.m_vecimg[0]->Palette, 256 * sizeof(PALETTEENTRY)); for (int i = 0; i < 256; i++) pal[i].peFlags = 0xff; // alpha if (Anim.m_vecimg[0]->Transparency && Anim.m_vecimg[0]->Transparent >= 0) memset(&pal[Anim.m_vecimg[0]->Transparent], 0, sizeof(PALETTEENTRY)); // setup xpr header WriteXPRHeader((DWORD*)pal, nImages); if (nImages > 1) { XPRFile.AnimInfo->RealSize = (info.Width & 0xffff) | ((info.Height & 0xffff) << 16); XPRFile.AnimInfo->nLoops = Anim.nLoops; } int nActualImages = 0; TotalSrcPixels += info.Width * info.Height * nImages; TotalDstPixels += Width * Height * nImages; float Waste = 100.f * (float)(Width * Height - info.Width * info.Height) / (float)(Width * Height); // alloc hash buffer BYTE (*HashBuf)[20] = new BYTE[nImages][20]; for (int i = 0; i < nImages; ++i) { if (pSrcSurf) pSrcSurf->Release(); pSrcSurf = NULL; printf("%3d%%\b\b\b\b", 100 * i / nImages); UncompressedSize += Width * Height; CAnimatedGif* pGif = Anim.m_vecimg[i]; if (nImages > 1) XPRFile.Texture[i].RealSize = pGif->Delay; // generate sha1 hash SHA1((BYTE*)pGif->Raster, pGif->BytesPerRow * pGif->Height, HashBuf[i]); // duplicate scan int j; for (j = 0; j < i; ++j) { if (!memcmp(HashBuf[j], HashBuf[i], 20)) { // duplicate image! TRACE2(" %03d: Duplicate of %03d\n", i, j); AppendXPRImageLink(j); break; } } if (j < i) continue; ++nActualImages; // DXT1 for P8s if lossless hr = pD3DDevice->CreateImageSurface(Width, Height, D3DFMT_A8R8G8B8, &pSrcSurf); CheckHR(hr); D3DLOCKED_RECT slr; hr = pSrcSurf->LockRect(&slr, NULL, D3DLOCK_READONLY); CheckHR(hr); BYTE* src = (BYTE*)pGif->Raster; DWORD* dst = (DWORD*)slr.pBits; DWORD* dwPal = (DWORD*)pal; for (int y = 0; y < pGif->Height; ++y) { for (UINT x = 0; x < Width; ++x) *dst++ = dwPal[*src++]; } memset(dst, 0, (Height - pGif->Height) * slr.Pitch); pSrcSurf->UnlockRect(); double CMSE, AMSE; TRACE1(" %03d: Checking DXT1: ", i); if (!GetFormatMSE(info, pSrcSurf, D3DFMT_DXT1, CMSE, AMSE)) return; TRACE2("CMSE=%05.2f, AMSE=%07.2f\n", CMSE, AMSE); if (CMSE <= 1e-6 && AMSE <= 1e-6) { TRACE1(" %03d: Selected Format: DXT1\n", i); AppendXPRImage(info, pSrcSurf, XB_D3DFMT_DXT1); } else { pSrcSurf->Release(); hr = pD3DDevice->CreateImageSurface(Width, Height, D3DFMT_P8, &pSrcSurf); CheckHR(hr); hr = pSrcSurf->LockRect(&slr, NULL, D3DLOCK_READONLY); CheckHR(hr); memcpy((BYTE*)slr.pBits, pGif->Raster, pGif->Height * slr.Pitch); memset((BYTE*)slr.pBits + pGif->Height * slr.Pitch, pGif->Transparent, (Height - pGif->Height) * slr.Pitch); pSrcSurf->UnlockRect(); TRACE1(" %03d: Selected Format: P8\n", i); AppendXPRImage(info, pSrcSurf, XB_D3DFMT_P8); } } delete [] HashBuf; printf("(%5df) %4dx%-4d (%5.2f%% waste)\n", nActualImages, Width, Height, Waste); CommitXPR(OutFilename); if (pSrcSurf) pSrcSurf->Release(); }
void ConvertFile(const char* Dir, const char* Filename, double MaxMSE) { HRESULT hr; LPDIRECT3DSURFACE8 pSrcSurf = NULL; char OutFilename[52]; if (Dir) _snprintf(OutFilename, 52, "%s\\%s", Dir, Filename); else _snprintf(OutFilename, 52, "%s", Filename); OutFilename[51] = 0; printf("%s: ", OutFilename); TRACE1("%s:\n", OutFilename); int n = strlen(OutFilename); if (n < 40) printf("%*c", 40-n, ' '); if (pSrcSurf) pSrcSurf->Release(); pSrcSurf = NULL; // Load up the file D3DXIMAGE_INFO info; hr = D3DXGetImageInfoFromFile(Filename, &info); CheckHR(hr); PrintImageInfo(info); UINT Width = PadPow2(info.Width); UINT Height = PadPow2(info.Height); float Waste = 100.f * (float)(Width * Height - info.Width * info.Height) / (float)(Width * Height); UncompressedSize += Width * Height * 4; TotalSrcPixels += info.Width * info.Height; TotalDstPixels += Width * Height; // Special case for 256-colour files - just directly drop into a P8 xpr if (info.Format == D3DFMT_P8) { hr = pD3DDevice->CreateImageSurface(Width, Height, D3DFMT_A8R8G8B8, &pSrcSurf); CheckHR(hr); hr = D3DXLoadSurfaceFromFile(pSrcSurf, NULL, NULL, Filename, NULL, D3DX_FILTER_NONE, 0, NULL); CheckHR(hr); FixTransparency(pSrcSurf); if (Width * Height > 4096) { // DXT1 for P8s if lossless and more than 4k image LPDIRECT3DSURFACE8 pTempSurf; hr = pD3DDevice->CreateImageSurface(Width, Height, D3DFMT_A8R8G8B8, &pTempSurf); CheckHR(hr); hr = D3DXLoadSurfaceFromSurface(pTempSurf, NULL, NULL, pSrcSurf, NULL, NULL, D3DX_FILTER_NONE, 0); CheckHR(hr); double CMSE, AMSE; TRACE0(" Checking DXT1: "); if (!GetFormatMSE(info, pTempSurf, D3DFMT_DXT1, CMSE, AMSE)) { pTempSurf->Release(); return; } TRACE2("CMSE=%05.2f, AMSE=%07.2f\n", CMSE, AMSE); if (CMSE <= 1e-6 && AMSE <= 1e-6) { printf("DXT1 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: DXT1\n"); WriteXPR(OutFilename, info, pTempSurf, XB_D3DFMT_DXT1, NULL); pTempSurf->Release(); return; } pTempSurf->Release(); } printf("P8 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: P8\n"); LPDIRECT3DSURFACE8 pTempSurf; DWORD pal[256]; ConvertP8(pSrcSurf, pTempSurf, pal, info); WriteXPR(OutFilename, info, pTempSurf, XB_D3DFMT_P8, pal); pTempSurf->Release(); return; } // test linear format versus non-linear format // Linear format requires 64 pixel aligned width, whereas // Non-linear format requires power of 2 width and height bool useLinearFormat(false); UINT linearWidth = (info.Width + 0x3f) & ~0x3f; if (AllowLinear && linearWidth * info.Height < Width * Height) useLinearFormat = true; hr = pD3DDevice->CreateImageSurface(Width, Height, D3DFMT_A8R8G8B8, &pSrcSurf); CheckHR(hr); hr = D3DXLoadSurfaceFromFile(pSrcSurf, NULL, NULL, Filename, NULL, D3DX_FILTER_NONE, 0, NULL); CheckHR(hr); // create the linear version as well LPDIRECT3DSURFACE8 pLinearSrcSurf = NULL; if (useLinearFormat) { hr = pD3DDevice->CreateImageSurface(linearWidth, info.Height, D3DFMT_A8R8G8B8, &pLinearSrcSurf); CheckHR(hr); hr = D3DXLoadSurfaceFromFile(pLinearSrcSurf, NULL, NULL, Filename, NULL, D3DX_FILTER_NONE, 0, NULL); CheckHR(hr); } // special case for small files - all textures are alloced on page granularity so just output uncompressed // dxt is crap on small files anyway if (Width * Height <= 1024) { if (useLinearFormat) { // correct sizing amounts UncompressedSize -= Width * Height * 4; UncompressedSize += linearWidth * info.Height * 4; TotalDstPixels -= Width * Height; TotalDstPixels += linearWidth * info.Height; Waste = 100.f * (float)(linearWidth * info.Height - info.Width * info.Height) / (float)(linearWidth * info.Height); printf("LIN_A8R8G8B8 %4dx%-4d (%5.2f%% waste)\n", linearWidth, info.Height, Waste); TRACE0(" Selected Format: LIN_A8R8G8B8\n"); WriteXPR(OutFilename, info, pLinearSrcSurf, XB_D3DFMT_LIN_A8R8G8B8, NULL); } else { printf("A8R8G8B8 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: A8R8G8B8\n"); WriteXPR(OutFilename, info, pSrcSurf, XB_D3DFMT_A8R8G8B8, NULL); } return; } FixTransparency(pSrcSurf); // Find the best format within specified tolerance double CMSE, AMSE[2]; // DXT1 is the preferred format as it's smallest TRACE0(" Checking DXT1: "); if (!GetFormatMSE(info, pSrcSurf, D3DFMT_DXT1, CMSE, AMSE[0])) return; TRACE2("CMSE=%05.2f, AMSE=%07.2f\n", CMSE, AMSE[0]); if (CMSE <= MaxMSE && AMSE[0] <= MaxMSE) { printf("DXT1 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: DXT1\n"); WriteXPR(OutFilename, info, pSrcSurf, XB_D3DFMT_DXT1, NULL); return; } // Use P8 is possible as it's lossless LPDIRECT3DSURFACE8 pTempSurf; DWORD pal[256]; if (ConvertP8(pSrcSurf, pTempSurf, pal, info)) { printf("P8 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: P8\n"); WriteXPR(OutFilename, info, pTempSurf, XB_D3DFMT_P8, pal); pTempSurf->Release(); return; } // DXT3/5 are the same size so use whichever is better if good enough // CMSE will be equal for both TRACE0(" Checking DXT3: "); if (!GetFormatMSE(info, pSrcSurf, D3DFMT_DXT3, CMSE, AMSE[0])) return; TRACE2("CMSE=%05.2f, AMSE=%07.2f\n", CMSE, AMSE[0]); TRACE0(" Checking DXT5: "); if (!GetFormatMSE(info, pSrcSurf, D3DFMT_DXT5, CMSE, AMSE[1])) return; TRACE2("CMSE=%05.2f, AMSE=%07.2f\n", CMSE, AMSE[1]); if (AMSE[0] <= AMSE[1]) { if (CMSE <= MaxMSE && AMSE[0] <= MaxMSE) { printf("DXT3 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: DXT3\n"); WriteXPR(OutFilename, info, pSrcSurf, XB_D3DFMT_DXT3, NULL); return; } } else { if (CMSE <= MaxMSE && AMSE[1] <= MaxMSE) { printf("DXT5 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: DXT5\n"); WriteXPR(OutFilename, info, pSrcSurf, XB_D3DFMT_DXT5, NULL); return; } } // No good compressed format so use uncompressed // A1R5G5B5 is worth a try I guess... TRACE0(" Checking A1R5G5B5: "); if (!GetFormatMSE(info, pSrcSurf, D3DFMT_A1R5G5B5, CMSE, AMSE[0])) return; TRACE2("CMSE=%05.2f, AMSE=%07.2f\n", CMSE, AMSE[0]); if (CMSE <= MaxMSE && AMSE[0] <= MaxMSE) { printf("A1R5G5B5 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: A1R5G5B5\n"); LPDIRECT3DSURFACE8 pTempSurf; hr = pD3DDevice->CreateImageSurface(Width, Height, D3DFMT_A1R5G5B5, &pTempSurf); CheckHR(hr); hr = D3DXLoadSurfaceFromSurface(pTempSurf, NULL, NULL, pSrcSurf, NULL, NULL, D3DX_FILTER_NONE, 0); CheckHR(hr); WriteXPR(OutFilename, info, pTempSurf, XB_D3DFMT_A1R5G5B5, NULL); pTempSurf->Release(); return; } // Use A8R8G8B8 if (useLinearFormat) { // correct sizing information UncompressedSize -= Width * Height * 4; UncompressedSize += linearWidth * info.Height * 4; TotalDstPixels -= Width * Height; TotalDstPixels += linearWidth * info.Height; Waste = 100.f * (float)(linearWidth * info.Height - info.Width * info.Height) / (float)(linearWidth * info.Height); printf("LIN_A8R8G8B8 %4dx%-4d (%5.2f%% waste)\n", linearWidth, info.Height, Waste); TRACE0(" Selected Format: LIN_A8R8G8B8\n"); WriteXPR(OutFilename, info, pLinearSrcSurf, XB_D3DFMT_LIN_A8R8G8B8, NULL); } else { printf("A8R8G8B8 %4dx%-4d (%5.2f%% waste)\n", Width, Height, Waste); TRACE0(" Selected Format: A8R8G8B8\n"); WriteXPR(OutFilename, info, pSrcSurf, XB_D3DFMT_A8R8G8B8, NULL); } if (pSrcSurf) pSrcSurf->Release(); }
// Converts to P8 format is colours <= 256 bool ConvertP8(LPDIRECT3DSURFACE8 pSrcSurf, LPDIRECT3DSURFACE8& pDstSurf, DWORD* pal, D3DXIMAGE_INFO &info) { pDstSurf = 0; D3DSURFACE_DESC desc; pSrcSurf->GetDesc(&desc); // convert to p8 UINT Width = PadPow2(desc.Width); UINT Height = PadPow2(desc.Height); HRESULT hr = pD3DDevice->CreateImageSurface(Width, Height, D3DFMT_A8R8G8B8, &pDstSurf); CheckHR(hr); D3DLOCKED_RECT slr, dlr; hr = pDstSurf->LockRect(&dlr, NULL, 0); CheckHR(hr); hr = pSrcSurf->LockRect(&slr, NULL, D3DLOCK_READONLY); CheckHR(hr); DWORD* src = (DWORD*)slr.pBits; BYTE* dst = (BYTE*)dlr.pBits; int n = 0, i; for (UINT y = 0; y < info.Height; ++y) { for (UINT x = 0; x < info.Width; ++x) { for (i = 0; i < n; ++i) { if (pal[i] == *src) break; } if (i == n) { if (n >= 256) { TRACE0(" Too many colours for P8\n"); pSrcSurf->UnlockRect(); pDstSurf->UnlockRect(); pDstSurf->Release(); return false; } pal[n++] = *src; } *dst++ = i; ++src; } for (UINT x = info.Width; x < Width; ++x) { *dst++ = 0; // we don't care about the colour outside of our real image ++src; } } for (UINT y = info.Height; y < Height; ++y) { for (UINT x = 0; x < Width; ++x) { *dst++ = 0; // we don't care about the colour outside of our real image ++src; } } TRACE1(" Colours Used: %d\n", n); pDstSurf->UnlockRect(); pSrcSurf->UnlockRect(); return true; }