/////////////////////////////////////////////////////////////////////////////// // Copyright (C) Microsoft Corporation, 1998. // // texmap.cpp // // Direct3D Reference Rasterizer - Texture Map Access Methods // /////////////////////////////////////////////////////////////////////////////// #include "pch.cpp" #pragma hdrstop //---------------------------------------------------------------------------- // // FindOutSurfFormat // // Converts a DDPIXELFORMAT to RDSurfaceFormat. // //---------------------------------------------------------------------------- HRESULT FASTCALL FindOutSurfFormat(LPDDPIXELFORMAT pDdPixFmt, RDSurfaceFormat* pFmt, BOOL* pbIsDepth) { if( pbIsDepth ) *pbIsDepth = FALSE; if (pDdPixFmt->dwFourCC == D3DFMT_Q8W8V8U8 ) { *pFmt = RD_SF_U8V8W8Q8; } else if (pDdPixFmt->dwFourCC == D3DFMT_W11V11U10 ) { *pFmt = RD_SF_U10V11W11; } else if (pDdPixFmt->dwFourCC == D3DFMT_V16U16 ) { *pFmt = RD_SF_U16V16; } else if (pDdPixFmt->dwFourCC == D3DFMT_R8G8B8 ) { *pFmt = RD_SF_B8G8R8; } else if (pDdPixFmt->dwFourCC == D3DFMT_A8 ) { *pFmt = RD_SF_A8; } else if (pDdPixFmt->dwFourCC == D3DFMT_A8P8 ) { *pFmt = RD_SF_P8A8; } else if (pDdPixFmt->dwFourCC == D3DFMT_X4R4G4B4 ) { *pFmt = RD_SF_B4G4R4X4; } else if (pDdPixFmt->dwFourCC == D3DFMT_A2B10G10R10) { *pFmt = RD_SF_R10G10B10A2; } #if 0 else if (pDdPixFmt->dwFourCC == D3DFMT_A8B8G8R8) { *pFmt = RD_SF_R8G8B8A8; } else if (pDdPixFmt->dwFourCC == D3DFMT_X8B8G8R8) { *pFmt = RD_SF_R8G8B8X8; } else if (pDdPixFmt->dwFourCC == D3DFMT_W10V11U11) { *pFmt = RD_SF_U11V11W10; } else if (pDdPixFmt->dwFourCC == D3DFMT_A8X8V8U8) { *pFmt = RD_SF_U8V8X8A8; } else if (pDdPixFmt->dwFourCC == D3DFMT_L8X8V8U8) { *pFmt = RD_SF_U8V8X8L8; } #endif else if (pDdPixFmt->dwFourCC == D3DFMT_G16R16) { *pFmt = RD_SF_R16G16; } else if (pDdPixFmt->dwFourCC == D3DFMT_A2W10V10U10) { *pFmt = RD_SF_U10V10W10A2; } else if (pDdPixFmt->dwFourCC == MAKEFOURCC('U', 'Y', 'V', 'Y')) { *pFmt = RD_SF_UYVY; } else if (pDdPixFmt->dwFourCC == MAKEFOURCC('Y', 'U', 'Y', '2')) { *pFmt = RD_SF_YUY2; } else if (pDdPixFmt->dwFourCC == MAKEFOURCC('D', 'X', 'T', '1')) { *pFmt = RD_SF_DXT1; } else if (pDdPixFmt->dwFourCC == MAKEFOURCC('D', 'X', 'T', '2')) { *pFmt = RD_SF_DXT2; } else if (pDdPixFmt->dwFourCC == MAKEFOURCC('D', 'X', 'T', '3')) { *pFmt = RD_SF_DXT3; } else if (pDdPixFmt->dwFourCC == MAKEFOURCC('D', 'X', 'T', '4')) { *pFmt = RD_SF_DXT4; } else if (pDdPixFmt->dwFourCC == MAKEFOURCC('D', 'X', 'T', '5')) { *pFmt = RD_SF_DXT5; } else if (pDdPixFmt->dwFourCC == 0xFF000004) { // This is an example of a IHV-specific format // The HIWORD must be the PCI-ID of the IHV // and the third byte must be zero. // In this case, we're using a sample PCI-ID of // FF00, and we're denoting the 4th format // by that PCI-ID *pFmt = RD_SF_Z32S0; } else if (pDdPixFmt->dwFlags & DDPF_ZBUFFER) { if( pbIsDepth ) *pbIsDepth = TRUE; switch(pDdPixFmt->dwZBitMask) { default: case 0x0000FFFF: *pFmt = RD_SF_Z16S0; break; case 0xFFFFFF00: switch(pDdPixFmt->dwStencilBitMask) { default: case 0x00000000: *pFmt = RD_SF_Z24X8; break; case 0x000000FF: *pFmt = RD_SF_Z24S8; break; case 0x0000000F: *pFmt = RD_SF_Z24X4S4; break; } break; case 0x00FFFFFF: switch(pDdPixFmt->dwStencilBitMask) { default: case 0x00000000: *pFmt = RD_SF_X8Z24; break; case 0xFF000000: *pFmt = RD_SF_S8Z24; break; case 0x0F000000: *pFmt = RD_SF_X4S4Z24; break; } break; case 0x0000FFFE: *pFmt = RD_SF_Z15S1; break; case 0x00007FFF: *pFmt = RD_SF_S1Z15; break; case 0xFFFFFFFF: *pFmt = RD_SF_Z32S0; break; } } else if (pDdPixFmt->dwFlags & DDPF_BUMPDUDV) { UINT uFmt = pDdPixFmt->dwBumpDvBitMask; switch (uFmt) { case 0x0000ff00: switch (pDdPixFmt->dwRGBBitCount) { case 32: *pFmt = RD_SF_U8V8L8X8; break; case 16: *pFmt = RD_SF_U8V8; break; } break; case 0x000003e0: *pFmt = RD_SF_U5V5L6; break; } } else if (pDdPixFmt->dwFlags & DDPF_PALETTEINDEXED8) { if (pDdPixFmt->dwFlags & DDPF_ALPHAPIXELS) { *pFmt = RD_SF_P8A8; } else { *pFmt = RD_SF_PALETTE8; } } else if (pDdPixFmt->dwFlags & DDPF_PALETTEINDEXED4) { *pFmt = RD_SF_PALETTE4; } else if (pDdPixFmt->dwFlags & DDPF_ALPHA) { if (pDdPixFmt->dwAlphaBitDepth == 8) { *pFmt = RD_SF_A8; } else { *pFmt = RD_SF_NULL; } } else { UINT uFmt = pDdPixFmt->dwGBitMask | pDdPixFmt->dwRBitMask; if (pDdPixFmt->dwFlags & DDPF_ALPHAPIXELS) { uFmt |= pDdPixFmt->dwRGBAlphaBitMask; } switch (uFmt) { case 0x00ffff00: switch (pDdPixFmt->dwRGBBitCount) { case 32: *pFmt = RD_SF_B8G8R8X8; break; case 24: *pFmt = RD_SF_B8G8R8; break; } break; case 0xffffff00: *pFmt = RD_SF_B8G8R8A8; break; case 0xffe0: if (pDdPixFmt->dwFlags & DDPF_ALPHAPIXELS) { *pFmt = RD_SF_B5G5R5A1; } else { *pFmt = RD_SF_B5G6R5; } break; case 0x07fe0: *pFmt = RD_SF_B5G5R5X1; break; case 0xff0: *pFmt = RD_SF_B4G4R4X4; break; case 0xfff0: *pFmt = RD_SF_B4G4R4A4; break; case 0xff: if (pDdPixFmt->dwFlags & DDPF_ALPHAPIXELS) { *pFmt = RD_SF_L4A4; } else { *pFmt = RD_SF_L8; } break; case 0xffff: *pFmt = RD_SF_L8A8; break; case 0xfc: *pFmt = RD_SF_B2G3R3; break; case 0xfffc: *pFmt = RD_SF_B2G3R3A8; break; default: *pFmt = RD_SF_NULL; break; } } return D3D_OK; } //---------------------------------------------------------------------------- // // ValidMipmapSize // // Computes size of next smallest mipmap level, clamping at 1 // //---------------------------------------------------------------------------- BOOL FASTCALL ValidMipmapSize(INT16 iPreSize, INT16 iSize) { if (iPreSize == 1) { if (iSize == 1) { return TRUE; } else { return FALSE; } } else { return ((iPreSize >> 1) == iSize); } } ////////////////////////////////////////////////////////////////////////////// // // RDPalette // ////////////////////////////////////////////////////////////////////////////// const DWORD RDPalette::RDPAL_ALPHAINPALETTE = (1 << 0); const DWORD RDPalette::m_dwNumEntries = 256; HRESULT RDPalette::Update( WORD StartIndex, WORD wNumEntries, PALETTEENTRY* pEntries ) { _ASSERT( StartIndex < m_dwNumEntries, "Bad StartIndex\n" ); _ASSERT( StartIndex+wNumEntries <= m_dwNumEntries, "Too many entries\n" ); for( WORD i = 0; i < wNumEntries; i++ ) { m_Entries[StartIndex+i] = D3DCOLOR_ARGB( pEntries[i].peFlags, pEntries[i].peRed, pEntries[i].peGreen, pEntries[i].peBlue ); } return S_OK; } //----------------------------------------------------------------------------- // // Constructor/Destructor // //----------------------------------------------------------------------------- RDSurface2D::RDSurface2D( void ) { m_pRefDev = NULL; m_uFlags = 0; m_iWidth = 0; m_iHeight = 0; m_iDepth = 0; m_cLOD = 0; m_SurfFormat = RD_SF_NULL; m_dwColorKey = 0; m_dwEmptyFaceColor = 0; m_pPalette = 0; m_pPalObj = NULL; m_cLODDDS = 0; m_hTex = 0; m_bHasAlpha = 0; memset(m_pBits, 0, sizeof(m_pBits)); memset(m_iPitch, 0, sizeof(m_iPitch)); memset(m_iSlicePitch, 0, sizeof(m_iSlicePitch)); memset(m_pDDSLcl, 0, sizeof(m_pDDSLcl)); m_cDimension = 0; memset(m_fTexels, 0, sizeof(m_fTexels)); memset(m_cTexels, 0, sizeof(m_cTexels)); } //----------------------------------------------------------------------------- RDSurface2D::~RDSurface2D( void ) { } DWORD RDSurface2D::ComputePitch( LPDDRAWI_DDRAWSURFACE_LCL pLcl, RDSurfaceFormat SurfFormat, DWORD width, DWORD height ) const { if ((SurfFormat == RD_SF_DXT1) || (SurfFormat == RD_SF_DXT2) || (SurfFormat == RD_SF_DXT3) || (SurfFormat == RD_SF_DXT4) || (SurfFormat == RD_SF_DXT5)) { // Note, here is the assumption that: // 1) width and height are reported correctly by the runtime. // 2) The allocation of the memory is contiguous (as done by hel) return (((width+3)>>2) * g_DXTBlkSize[(int)SurfFormat - (int)RD_SF_DXT1]); } #if 0 else if( (SurfFormat == RD_SF_YUY2) || (SurfFormat == RD_SF_UYVY) ) { // Same assumptions as for DXTn. return (DDSurf_Pitch(pLcl)/height); } #endif else { return DDSurf_Pitch(pLcl); } } DWORD RDSurface2D::ComputePitch( LPDDRAWI_DDRAWSURFACE_LCL pLcl ) const { return ComputePitch( pLcl, m_SurfFormat, m_iWidth, m_iHeight ); } //----------------------------------------------------------------------------- // // RDSurface2D::Initialize() // //----------------------------------------------------------------------------- HRESULT RDSurface2D::Initialize( LPDDRAWI_DDRAWSURFACE_LCL pLcl ) { HRESULT hr = D3D_OK; RDSurfaceFormat SurfFormat; DDSCAPS2 ddscaps; LPDDRAWI_DDRAWSURFACE_GBL pGbl = pLcl->lpGbl; LPDDRAWI_DDRAWSURFACE_MORE pMore = pLcl->lpSurfMore; memset(&ddscaps, 0, sizeof(ddscaps)); UINT wMultiSampleCount = 0xfL & pMore->ddsCapsEx.dwCaps3; //Older than DX8 runtimes place a zero in this field if (wMultiSampleCount == 0) wMultiSampleCount = 1; if( pLcl->ddsCaps.dwCaps & DDSCAPS_TEXTURE ) m_SurfType |= RR_ST_TEXTURE; if( pLcl->ddsCaps.dwCaps & DDSCAPS_ZBUFFER ) { m_iSamples = wMultiSampleCount; m_SurfType |= RR_ST_RENDERTARGETDEPTH; } if( pLcl->ddsCaps.dwCaps & DDSCAPS_3DDEVICE ) { m_iSamples = wMultiSampleCount; m_SurfType |= RR_ST_RENDERTARGETCOLOR; } m_iWidth = DDSurf_Width(pLcl); m_iHeight = DDSurf_Height(pLcl); HR_RET(FindOutSurfFormat(&(DDSurf_PixFmt(pLcl)), &SurfFormat, NULL)); m_SurfFormat = SurfFormat; if (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_VOLUME) { // low word of ddsCaps.ddsCapsEx.dwCaps4 has depth // (volume texture only). m_iDepth = LOWORD(pMore->ddsCapsEx.dwCaps4); } else { m_iDepth = 0; } m_cTexels[0][0] = m_iWidth; m_cTexels[0][1] = m_iHeight; m_cTexels[0][2] = m_iDepth; m_fTexels[0][0] = (float)m_cTexels[0][0]; m_fTexels[0][1] = (float)m_cTexels[0][1]; m_fTexels[0][2] = (float)m_cTexels[0][2]; m_cLOD = 0; if( wMultiSampleCount > 1 ) { RDCREATESURFPRIVATE* pPriv = (RDCREATESURFPRIVATE*)pGbl->dwReserved1; m_pBits[0] = pPriv->pMultiSampleBits; m_iPitch[0] = pPriv->dwMultiSamplePitch; } else { m_pBits[0] = (BYTE *)SURFACE_MEMORY(pLcl); m_iPitch[0] = ComputePitch( pLcl ); } if (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_VOLUME) { // set slice pitch (volume texture only). m_iSlicePitch[0] = pGbl->lSlicePitch; } else { m_iSlicePitch[0] = 0; } // If the surface is not a texture early out. if( (pLcl->ddsCaps.dwCaps & DDSCAPS_TEXTURE) == 0 ) { SetInitialized(); return S_OK; } // Set the transparent bit and the transparent color with pDDS[0] if ((pLcl->dwFlags & DDRAWISURF_HASCKEYSRCBLT) != 0) { m_uFlags |= RR_TEXTURE_HAS_CK; m_dwColorKey = pLcl->ddckCKSrcBlt.dwColorSpaceLowValue; } else { m_uFlags &= ~RR_TEXTURE_HAS_CK; } // set the empty face color with pDDS[0] // note that ddckCKDestOverlay is unioned with dwEmptyFaceColor, // but not in the internal structure m_dwEmptyFaceColor = pLcl->ddckCKDestOverlay.dwColorSpaceLowValue; if (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_VOLUME) { m_uFlags |= RR_TEXTURE_VOLUME; m_cDimension = 3; } else { m_cDimension = 2; } // Compute sizes and pitches // We need to gather info on all surfaces under the top-level // mipmap face (This test is DX7+ runtime dependent) if ((0 == (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_MIPMAPSUBLEVEL)) && (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEX) ) { m_uFlags |= RR_TEXTURE_CUBEMAP; LPDDRAWI_DDRAWSURFACE_LCL pDDSNextLcl; ddscaps.dwCaps = DDSCAPS_TEXTURE; m_pDDSLcl[0] = pLcl; m_pBits[0] = (BYTE *)SURFACE_MEMORY(m_pDDSLcl[0]); m_iPitch[0] = ComputePitch( m_pDDSLcl[0] ); m_iSlicePitch[0] = 0; // get rest of top level surfaces, in order for (INT i = 1; i < 6; i++) { switch(i) { case 1: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEX; break; case 2: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_POSITIVEY; break; case 3: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEY; break; case 4: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_POSITIVEZ; break; case 5: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEZ; break; } ddscaps.dwCaps2 |= DDSCAPS2_CUBEMAP; pDDSNextLcl = NULL; hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pDDSNextLcl); if ((hr != D3D_OK) && (hr != DDERR_NOTFOUND)) { return hr; } if (hr == DDERR_NOTFOUND) { m_pDDSLcl[i] = NULL; return hr; } else { m_pDDSLcl[i] = pDDSNextLcl; } m_pBits[i] = (BYTE *)SURFACE_MEMORY(m_pDDSLcl[i]); m_iPitch[i] = ComputePitch( m_pDDSLcl[i] ); m_iSlicePitch[i] = 0; m_cTexels[i][0] = DDSurf_Width(m_pDDSLcl[i]); m_cTexels[i][1] = DDSurf_Height(m_pDDSLcl[i]); m_fTexels[i][0] = (float)m_cTexels[i][0]; m_fTexels[i][1] = (float)m_cTexels[i][1]; } for (i = 0; i < 6; i++) { pLcl = m_pDDSLcl[i]; m_cLOD = 0; if (pLcl) { // Check for mipmap if any. LPDDRAWI_DDRAWSURFACE_LCL pTmpSLcl; // iPreSizeU and iPreSizeV store the size(u and v) of the // previous level mipmap. They are init'ed with the first // texture size. INT16 iPreSizeU = (INT16)m_iWidth; INT16 iPreSizeV = (INT16)m_iHeight; for (;;) { ddscaps.dwCaps = DDSCAPS_TEXTURE; ddscaps.dwCaps2 = DDSCAPS2_MIPMAPSUBLEVEL; pTmpSLcl = NULL; hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pTmpSLcl); if (hr != D3D_OK && hr != DDERR_NOTFOUND) { return hr; } if (hr == DDERR_NOTFOUND) { break; } pLcl = pTmpSLcl; pGbl = pLcl->lpGbl; pMore = pLcl->lpSurfMore; m_cLOD ++; INT iMap = m_cLOD*6+i; m_pDDSLcl[iMap] = pLcl; m_pBits[iMap] = (BYTE *)SURFACE_MEMORY(pLcl); m_iPitch[iMap] = ComputePitch( pLcl, m_SurfFormat, m_iWidth>>m_cLOD, m_iHeight>>m_cLOD ); if (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_VOLUME) { // set slice pitch // (volume texture only). m_iSlicePitch[iMap] = pGbl->lSlicePitch; } else { m_iSlicePitch[iMap] = 0; } m_cTexels[iMap][0] = DDSurf_Width(pLcl); m_cTexels[iMap][1] = DDSurf_Height(pLcl); m_fTexels[iMap][0] = (float)m_cTexels[iMap][0]; m_fTexels[iMap][1] = (float)m_cTexels[iMap][1]; // Check for invalid mipmap texture size if (!ValidMipmapSize(iPreSizeU, (INT16)DDSurf_Width(pLcl)) || !ValidMipmapSize(iPreSizeV, (INT16)DDSurf_Height(pLcl))) { return DDERR_INVALIDPARAMS; } iPreSizeU = (INT16)DDSurf_Width(pLcl); iPreSizeV = (INT16)DDSurf_Height(pLcl); } } } } else if ((0 == (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_MIPMAPSUBLEVEL) && (0 == (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP))) ) { //This surface is not a top-level cubemap. //Maybe it's a top-level mipmap. Go find its sublevels. m_pDDSLcl[0] = pLcl; // Check for mipmap if any. LPDDRAWI_DDRAWSURFACE_LCL pTmpSLcl; // iPreSizeU and iPreSizeV store the size(u and v) of the previous // level mipmap. They are init'ed with the first texture size. INT16 iPreSizeU = (INT16)m_iWidth; INT16 iPreSizeV = (INT16)m_iHeight; for (;;) { ddscaps.dwCaps = DDSCAPS_TEXTURE; ddscaps.dwCaps2 = DDSCAPS2_MIPMAPSUBLEVEL; pTmpSLcl = NULL; hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pTmpSLcl); if (hr != D3D_OK && hr != DDERR_NOTFOUND) { return hr; } if (hr == DDERR_NOTFOUND) { break; } pLcl = pTmpSLcl; pGbl = pLcl->lpGbl; pMore = pLcl->lpSurfMore; m_cLOD ++; m_pDDSLcl[m_cLOD] = pLcl; // Save the pointer to the real bits and the pitch. m_pBits[m_cLOD] = (BYTE *)SURFACE_MEMORY(pLcl); m_iPitch[m_cLOD] = ComputePitch( pLcl, m_SurfFormat, m_iWidth>>m_cLOD, m_iHeight>>m_cLOD ); if (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_VOLUME) { // set slice pitch (volume texture only). m_iSlicePitch[m_cLOD] = pGbl->lSlicePitch; } else { m_iSlicePitch[m_cLOD] = 0; } // Check for invalid mipmap texture size if (!ValidMipmapSize(iPreSizeU, (INT16)DDSurf_Width(pLcl)) || !ValidMipmapSize(iPreSizeV, (INT16)DDSurf_Height(pLcl))) { return DDERR_INVALIDPARAMS; } iPreSizeU = (INT16)DDSurf_Width(pLcl); iPreSizeV = (INT16)DDSurf_Height(pLcl); m_cTexels[m_cLOD][0] = DDSurf_Width(pLcl); m_cTexels[m_cLOD][1] = DDSurf_Height(pLcl); if (pMore->ddsCapsEx.dwCaps2 & DDSCAPS2_VOLUME) m_cTexels[m_cLOD][2] = LOWORD(pMore->ddsCapsEx.dwCaps4); else m_cTexels[m_cLOD][2] = 0; m_fTexels[m_cLOD][0] = (float)m_cTexels[m_cLOD][0]; m_fTexels[m_cLOD][1] = (float)m_cTexels[m_cLOD][1]; m_fTexels[m_cLOD][2] = (float)m_cTexels[m_cLOD][2]; } } // Copy the palette // UpdatePalette(); m_cLODDDS = m_cLOD; if ( !(Validate()) ) { return DDERR_GENERIC; } SetInitialized(); return D3D_OK; } //---------------------------------------------------------------------------- // // UpdatePalette // //---------------------------------------------------------------------------- void RDSurface2D::UpdatePalette() { // Update palette if (m_SurfFormat == RD_SF_PALETTE8 || m_SurfFormat == RD_SF_PALETTE4 || m_SurfFormat == RD_SF_P8A8 ) { #if 0 // This code needs to be revived in case the DX6 DDI // emulation is ever implemented in RefDev. if (m_pDDSLcl[0]->lpDDPalette) { LPDDRAWI_DDRAWPALETTE_GBL pPal = m_pDDSLcl[0]->lpDDPalette->lpLcl->lpGbl; m_pPalette = (DWORD*)pPal->lpColorTable; if (pPal->dwFlags & DDRAWIPAL_ALPHA) { m_uFlags |= RR_TEXTURE_ALPHAINPALETTE; } else { m_uFlags &= ~RR_TEXTURE_ALPHAINPALETTE; } } #endif _ASSERT( m_pPalObj, "No Palette set for this paletted texture\n" ); m_pPalette = m_pPalObj->GetEntries(); if( m_SurfFormat == RD_SF_PALETTE8 || m_SurfFormat == RD_SF_PALETTE4 ) { if( m_pPalObj->HasAlpha() ) { m_uFlags |= RR_TEXTURE_ALPHAINPALETTE; } else { m_uFlags &= ~RR_TEXTURE_ALPHAINPALETTE; } } } } //----------------------------------------------------------------------------- // // Validate - Updates private data. Must be called anytime public data is // altered. // //----------------------------------------------------------------------------- BOOL RDSurface2D::Validate( void ) { // validate inputs if ( m_cLOD >= RD_MAX_CLOD ) // too many LODs { DPFRR(1,"RDSurface2D::Validate failed. Too many LODs"); return FALSE; } // compute the 'has alpha' flag m_bHasAlpha = FALSE; switch ( m_SurfFormat ) { case RD_SF_A8: case RD_SF_P8A8: case RD_SF_B8G8R8A8: case RD_SF_B5G5R5A1: case RD_SF_B4G4R4A4: case RD_SF_L8A8: case RD_SF_L4A4: case RD_SF_B2G3R3A8: case RD_SF_DXT1: case RD_SF_DXT2: case RD_SF_DXT3: case RD_SF_DXT4: case RD_SF_DXT5: m_bHasAlpha = TRUE; break; case RD_SF_PALETTE4: case RD_SF_PALETTE8: m_bHasAlpha = ( m_uFlags & RR_TEXTURE_ALPHAINPALETTE ) ? TRUE : FALSE; break; } return TRUE; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- inline UINT8 CLAMP_BYTE(double f) { if (f > 255.0) return 255; if (f < 0.0) return 0; return (BYTE) f; } //----------------------------------------------------------------------------- // TexelFromBlock - decompress a color block and obtain texel color //----------------------------------------------------------------------------- UINT32 TexelFromBlock(RDSurfaceFormat surfType, char *pblockSrc, int x, int y) { UINT32 index = ((y & 0x3)<<2) + (x & 0x3); DDRGBA colorDst[DXT_BLOCK_PIXELS]; switch(surfType) { case RD_SF_DXT1: DecodeBlockRGB((DXTBlockRGB *)pblockSrc, (DXT_COLOR *)colorDst); break; case RD_SF_DXT2: case RD_SF_DXT3: DecodeBlockAlpha4((DXTBlockAlpha4 *)pblockSrc, (DXT_COLOR *)colorDst); break; case RD_SF_DXT4: case RD_SF_DXT5: DecodeBlockAlpha3((DXTBlockAlpha3 *)pblockSrc, (DXT_COLOR *)colorDst); break; } return RGBA_MAKE(colorDst[index].red, colorDst[index].green, colorDst[index].blue, colorDst[index].alpha); } //----------------------------------------------------------------------------- // // ReadTexelColor - Reads texel from texture map at given LOD; converts to // RDColor format, applying palette if necessary; also performs colorkey by // returning match information // //----------------------------------------------------------------------------- void RDSurface2D::ReadColor( INT32 iX, INT32 iY, INT32 iZ, INT32 iLOD, RDColor& Texel, BOOL &bColorKeyKill ) { if ( (iLOD > m_cLOD) && !(m_uFlags & RR_TEXTURE_CUBEMAP) ) { return; } if ( NULL == m_pBits[iLOD] ) { return; } char* pSurfaceBits = PixelAddress( iX, iY, iZ, m_pBits[iLOD], m_iPitch[iLOD], m_iSlicePitch[iLOD], m_SurfFormat ); switch ( m_SurfFormat ) { default: Texel.ConvertFrom( m_SurfFormat, pSurfaceBits ); break; case RD_SF_P8A8: { UINT8 uIndex = *((UINT8*)pSurfaceBits); Texel.ConvertFrom( RD_SF_B8G8R8A8, (char*)((UINT32*)m_pPalette + uIndex) ); Texel.A = *((UINT8*)pSurfaceBits+1)/255.f; } break; case RD_SF_PALETTE8: { UINT8 uIndex = *((UINT8*)pSurfaceBits); Texel.ConvertFrom( RD_SF_B8G8R8A8, (char*)((UINT32*)m_pPalette + uIndex) ); if ( !( m_uFlags & RR_TEXTURE_ALPHAINPALETTE ) ) Texel.A = 1.f; } break; case RD_SF_PALETTE4: { UINT8 uIndex = *((INT8*)pSurfaceBits); if ((iX & 1) == 0) { uIndex &= 0xf; } else { uIndex >>= 4; } Texel.ConvertFrom( RD_SF_B8G8R8A8, (char*)((UINT32*)m_pPalette + uIndex) ); if ( !( m_uFlags & RR_TEXTURE_ALPHAINPALETTE ) ) Texel.A = 1.f; } break; case RD_SF_UYVY: case RD_SF_YUY2: // Converts a given YUV (8bits each) to RGB scaled between 0 and 255 // These are using the YCrCb to RGB algorithms given on page 30 // in "VIDEO DEMYSTIFIED" by Keith Jack // ISBN#: 1-878707-09-4 // IN PC graphics, even though they call it YUV, it is really YCrCb // formats that are used by most framegrabbers etc. Hence the pixel // data we will obtain in these YUV surfaces will most likely be this // and not the original YUV which is actually used in PAL broadcast // only (NTSC uses YIQ). So really, U should be called Cb (Blue color // difference) and V should be called Cr (Red color difference) // // These equations are meant to handle the following ranges // (from the same book): // Y (16 to 235), U and V (16 to 240, 128 = zero) // ----------- // Y U V // ----------- // White : 180 128 128 // Black : 16 128 128 // Red : 65 100 212 // Green : 112 72 58 // Blue : 35 212 114 // Yellow : 162 44 142 // Cyan : 131 156 44 // Magenta: 84 184 198 // ----------- // It is assumed that the gamma corrected RGB range is (0 - 255) // // UYVY: U0Y0 V0Y1 U2Y2 V2Y3 (low byte always has current Y) // If iX is even, hight-byte has current U (Cb) // If iX is odd, hight-byte has previous V (Cr) // // YUY2: Y0U0 Y1V0 Y2U2 Y3V2 (high byte always has current Y) // (UYVY bytes flipped) // // In this algorithm, we use U and V values from two neighboring // pixels { UINT8 Y, U, V; UINT16 u16Curr = *((UINT16*)pSurfaceBits); UINT16 u16ForU = 0; // Extract U from this UINT16 u16ForV = 0; // Extract V from this // By default we assume YUY2. Change it later if it is UYVY int uvShift = 8; int yShift = 0; if (m_SurfFormat == RD_SF_UYVY) { uvShift = 0; yShift = 8; } if ((iX & 1) == 0) { // For even numbered pixels: // Current U is available. // Current V is available in the next pixel. u16ForU = u16Curr; // Obtain V from the next pixel u16ForV = *((UINT16*)PixelAddress( iX+1, iY, iZ, m_pBits[iLOD], m_iPitch[iLOD], m_iSlicePitch[iLOD], m_SurfFormat )); U = (u16ForU >> uvShift) & 0xff; V = (u16ForV >> uvShift) & 0xff; } else { UINT16 u16ForU1 = 0, u16ForU2 = 0; UINT16 u16ForV1 = 0, u16ForV2 = 0; // For odd numbered pixels. Neither current U nor V are // available. // Obtain U by interpolating U from i-1 and i+1 pixels. _ASSERT( iX > 0, "iX is negative" ); u16ForU1 = *((UINT16*)PixelAddress( iX-1, iY, iZ, m_pBits[iLOD], m_iPitch[iLOD], m_iSlicePitch[iLOD], m_SurfFormat )); if( (iX+1) < (m_iWidth >> iLOD) ) { u16ForU2 = *((UINT16*)PixelAddress( iX+1, iY, iZ, m_pBits[iLOD], m_iPitch[iLOD], m_iSlicePitch[iLOD], m_SurfFormat )); U = (((u16ForU1 >> uvShift) & 0xff) + ((u16ForU2 >> uvShift) & 0xff)) >> 1; } else { U = (u16ForU1 >> uvShift) & 0xff; } // Obtain V by interpolating V from i and i+2 pixels. u16ForV1 = u16Curr; if( (iX+2) < (m_iWidth >> iLOD) ) { u16ForV2 = *((UINT16*)PixelAddress( iX+2, iY, iZ, m_pBits[iLOD], m_iPitch[iLOD], m_iSlicePitch[iLOD], m_SurfFormat )); V = (((u16ForV1 >> uvShift) & 0xff) + ((u16ForV2 >> uvShift) & 0xff)) >> 1; } else { V = (u16ForV1 >> uvShift) & 0xff; } } Y = (u16Curr >> yShift) & 0xff; Texel = RGB_MAKE( CLAMP_BYTE(1.164*(Y-16) + 1.596*(V-128)), CLAMP_BYTE(1.164*(Y-16) - 0.813*(V-128) - 0.391*(U-128)), CLAMP_BYTE(1.164*(Y-16) + 2.018*(U-128)) ); Texel.A = 1.f; } break; // DXTn compressed formats: // We have the address to the block, now extract the actual color case RD_SF_DXT1: case RD_SF_DXT2: case RD_SF_DXT3: case RD_SF_DXT4: case RD_SF_DXT5: Texel = TexelFromBlock(m_SurfFormat, pSurfaceBits, iX, iY); break; } // colorkey if ( m_pRefDev->ColorKeyEnabled() ) { DWORD dwBits; switch ( m_SurfFormat ) { default: case RD_SF_NULL: return; // don't colorkey unknown or null surfaces case RD_SF_PALETTE4: { UINT8 uIndex = *((INT8*)pSurfaceBits); if ((iX & 1) == 0) { uIndex &= 0xf; } else { uIndex >>= 4; } dwBits = (DWORD)uIndex; } break; case RD_SF_L8: case RD_SF_A8: case RD_SF_PALETTE8: case RD_SF_B2G3R3: case RD_SF_L4A4: { UINT8 uBits = *((UINT8*)pSurfaceBits); dwBits = (DWORD)uBits; } break; case RD_SF_B5G6R5: case RD_SF_B5G5R5X1: case RD_SF_B5G5R5A1: case RD_SF_B4G4R4A4: case RD_SF_B4G4R4X4: case RD_SF_L8A8: case RD_SF_P8A8: case RD_SF_B2G3R3A8: { UINT16 uBits = *((UINT16*)pSurfaceBits); dwBits = (DWORD)uBits; } break; case RD_SF_B8G8R8: { UINT32 uBits = 0; uBits |= ( *((UINT8*)pSurfaceBits+0) ) << 0; uBits |= ( *((UINT8*)pSurfaceBits+1) ) << 8; uBits |= ( *((UINT8*)pSurfaceBits+2) ) << 16; dwBits = (DWORD)uBits; } break; case RD_SF_B8G8R8A8: case RD_SF_B8G8R8X8: { UINT32 uBits = *((UINT32*)pSurfaceBits); dwBits = (DWORD)uBits; } break; } DWORD ColorKey = m_dwColorKey; if ( dwBits == ColorKey ) { if (m_pRefDev->GetRS()[D3DRENDERSTATE_COLORKEYENABLE]) { bColorKeyKill = TRUE; } if (m_pRefDev->GetRS()[D3DRENDERSTATE_COLORKEYBLENDENABLE]) { Texel.R = 0.F; Texel.G = 0.F; Texel.B = 0.F; Texel.A = 0.F; } } } } /////////////////////////////////////////////////////////////////////////////// // end