X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Frice_gles%2Fsrc%2FTextureFilters.cpp;fp=source%2Frice_gles%2Fsrc%2FTextureFilters.cpp;h=5f36816249b029051c61a5b8e0a642bac01fff9b;hb=d07c171fa694cae985ad7045f9ce2b2f1a5699b4;hp=0000000000000000000000000000000000000000;hpb=ca22e7b76883b946060a6b40bb8709c1981e1cf6;p=mupen64plus-pandora.git diff --git a/source/rice_gles/src/TextureFilters.cpp b/source/rice_gles/src/TextureFilters.cpp new file mode 100644 index 0000000..5f36816 --- /dev/null +++ b/source/rice_gles/src/TextureFilters.cpp @@ -0,0 +1,2235 @@ +/* +Copyright (C) 2003 Rice1964 + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "osal_files.h" + +#define M64P_PLUGIN_PROTOTYPES 1 +#include "m64p_plugin.h" +#include "typedefs.h" +#include "ConvertImage.h" +#include "DeviceBuilder.h" +#include "TextureFilters.h" +#include "Render.h" +#include "Video.h" + +#include "liblinux/BMGLibPNG.h" +#include "liblinux/BMGDLL.h" +#include +#include + +#ifdef min +#undef min +#endif + +/************************************************************************/ +/* 2X filters */ +/************************************************************************/ +// Basic 2x R8G8B8A8 filter with interpolation + +void Texture2x_32( DrawInfo &srcInfo, DrawInfo &destInfo) +{ + uint32 *pDst1, *pDst2; + uint32 *pSrc, *pSrc2; + uint32 nWidth = srcInfo.dwWidth; + uint32 nHeight = srcInfo.dwHeight; + + uint32 b1; + uint32 g1; + uint32 r1; + uint32 a1; + uint32 b2 = 0; + uint32 g2 = 0; + uint32 r2 = 0; + uint32 a2 = 0; + uint32 b3 = 0; + uint32 g3 = 0; + uint32 r3 = 0; + uint32 a3 = 0; + uint32 b4 = 0; + uint32 g4 = 0; + uint32 r4 = 0; + uint32 a4 = 0; + + + for (uint32 ySrc = 0; ySrc < nHeight; ySrc++) + { + pSrc = (uint32*)(((uint8*)srcInfo.lpSurface)+ySrc*srcInfo.lPitch); + pSrc2 = (uint32*)(((uint8*)srcInfo.lpSurface)+(ySrc+1)*srcInfo.lPitch); + pDst1 = (uint32*)(((uint8*)destInfo.lpSurface)+(ySrc*2)*destInfo.lPitch); + pDst2 = (uint32*)(((uint8*)destInfo.lpSurface)+(ySrc*2+1)*destInfo.lPitch); + + for (uint32 xSrc = 0; xSrc < nWidth; xSrc++) + { + b1 = (pSrc[xSrc]>>0)&0xFF; + g1 = (pSrc[xSrc]>>8)&0xFF; + r1 = (pSrc[xSrc]>>16)&0xFF; + a1 = (pSrc[xSrc]>>24)&0xFF; + + if( xSrc>0)&0xFF; + g2 = (pSrc[xSrc+1]>>8)&0xFF; + r2 = (pSrc[xSrc+1]>>16)&0xFF; + a2 = (pSrc[xSrc+1]>>24)&0xFF; + } + + if( ySrc>0)&0xFF; + g3 = (pSrc2[xSrc]>>8)&0xFF; + r3 = (pSrc2[xSrc]>>16)&0xFF; + a3 = (pSrc2[xSrc]>>24)&0xFF; + if( xSrc>0)&0xFF; + g4 = (pSrc2[xSrc+1]>>8)&0xFF; + r4 = (pSrc2[xSrc+1]>>16)&0xFF; + a4 = (pSrc2[xSrc+1]>>24)&0xFF; + } + } + + + // Pixel 1 + pDst1[xSrc*2] = pSrc[xSrc]; + + // Pixel 2 + if( xSrc> 0)&0xF; + g1 = (pSrc[xSrc]>> 4)&0xF; + r1 = (pSrc[xSrc]>> 8)&0xF; + a1 = (pSrc[xSrc]>>12)&0xF; + + if( xSrc> 0)&0xF; + g2 = (pSrc[xSrc+1]>> 4)&0xF; + r2 = (pSrc[xSrc+1]>> 8)&0xF; + a2 = (pSrc[xSrc+1]>>12)&0xF; + } + + if( ySrc> 0)&0xF; + g3 = (pSrc2[xSrc]>> 4)&0xF; + r3 = (pSrc2[xSrc]>> 8)&0xF; + a3 = (pSrc2[xSrc]>>12)&0xF; + if( xSrc> 0)&0xF; + g4 = (pSrc2[xSrc+1]>> 4)&0xF; + r4 = (pSrc2[xSrc+1]>> 8)&0xF; + a4 = (pSrc2[xSrc+1]>>12)&0xF; + } + } + + // Pixel 1 + pDst1[xSrc*2] = pSrc[xSrc]; + + // Pixel 2 + if( xSrc (t1+t3+t7+t9+t2+t4+t6+t8)*mul1 ) + { + val[z]= std::min((((t5*mul3) - (t1+t3+t7+t9+t2+t4+t6+t8)*mul1)>>shift4),0xFFU); + } + } + dest[x] = val[0]|(val[1]<<8)|(val[2]<<16)|(val[3]<<24); + } + } + delete [] pcopy; +} + +void SharpenFilter_16(uint16 *pdata, uint32 width, uint32 height, uint32 pitch, uint32 filter) +{ + //return; // Sharpen does not make sense for 16 bits + + uint32 len=height*pitch; + uint16 *pcopy = new uint16[len]; + + if( !pcopy ) return; + + memcpy(pcopy, pdata, len<<1); + + uint16 mul1, mul2, mul3, shift4; + switch( filter ) + { + case TEXTURE_SHARPEN_MORE_ENHANCEMENT: + mul1=1; + mul2=8; + mul3=12; + shift4=2; + break; + case TEXTURE_SHARPEN_ENHANCEMENT: + default: + mul1=1; + mul2=8; + mul3=16; + shift4=3; + break; + } + + uint32 x,y,z; + uint16 *src1, *src2, *src3, *dest; + uint16 val[4]; + uint16 t1,t2,t3,t4,t5,t6,t7,t8,t9; + + for( y=1; y>1)))>>shift; + t2 = (*((uint8*)(src1+x )+(z>>1)))>>shift; + t3 = (*((uint8*)(src1+x+1)+(z>>1)))>>shift; + t4 = (*((uint8*)(src2+x-1)+(z>>1)))>>shift; + t5 = (*((uint8*)(src2+x )+(z>>1)))>>shift; + t6 = (*((uint8*)(src2+x+1)+(z>>1)))>>shift; + t7 = (*((uint8*)(src3+x-1)+(z>>1)))>>shift; + t8 = (*((uint8*)(src3+x )+(z>>1)))>>shift; + t9 = (*((uint8*)(src3+x+1)+(z>>1)))>>shift; + val[z]=t5; + if( (t5*mul2) > (t1+t3+t7+t9+t2+t4+t6+t8)*mul1 ) + { + val[z] = (((t5*mul3) - (t1+t3+t7+t9+t2+t4+t6+t8)*mul1)>>shift4); + val[z]= std::min(val[z],(unsigned short)0xFU); + } + } + dest[x] = val[0]|(val[1]<<4)|(val[2]<<8)|(val[3]<<12); + } + } + delete [] pcopy; +} + +/************************************************************************/ +/* Smooth filters */ +/************************************************************************/ +void SmoothFilter_32(uint32 *pdata, uint32 width, uint32 height, uint32 pitch, uint32 filter) +{ + uint32 len=height*pitch; + uint32 *pcopy = new uint32[len]; + + if( !pcopy ) return; + + memcpy(pcopy, pdata, len<<2); + + uint32 mul1, mul2, mul3, shift4; + switch( filter ) + { + case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_1: + mul1=1; + mul2=2; + mul3=4; + shift4=4; + break; + case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_2: + mul1=1; + mul2=1; + mul3=8; + shift4=4; + break; + case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_3: + mul1=1; + mul2=1; + mul3=2; + shift4=2; + break; + case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_4: + default: + mul1=1; + mul2=1; + mul3=6; + shift4=3; + break; + } + + uint32 x,y,z; + uint32 *src1, *src2, *src3, *dest; + uint32 val[4]; + uint32 t1,t2,t3,t4,t5,t6,t7,t8,t9; + + if( filter == TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_3 || filter == TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_4 ) + { + for( y=1; y>shift4; + } + dest[x] = val[0]|(val[1]<<8)|(val[2]<<16)|(val[3]<<24); + } + } + } + else + { + for( y=0; y0 ) + { + src1 = pcopy+(y-1)*pitch; + src2 = src1 + pitch; + } + else + { + src1 = src2 = pcopy; + } + + src3 = src2; + if( y>shift4; + } + dest[x] = val[0]|(val[1]<<8)|(val[2]<<16)|(val[3]<<24); + } + } + } + delete [] pcopy; +} + +void SmoothFilter_16(uint16 *pdata, uint32 width, uint32 height, uint32 pitch, uint32 filter) +{ + uint32 len=height*pitch; + uint16 *pcopy = new uint16[len]; + + if( !pcopy ) + return; + + memcpy(pcopy, pdata, len<<1); + + uint16 mul1, mul2, mul3, shift4; + switch( filter ) + { + case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_1: + mul1=1; + mul2=2; + mul3=4; + shift4=4; + break; + case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_2: + mul1=1; + mul2=1; + mul3=8; + shift4=4; + break; + case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_3: + mul1=1; + mul2=1; + mul3=2; + shift4=2; + break; + case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_4: + default: + mul1=1; + mul2=1; + mul3=6; + shift4=3; + break; + } + + uint32 x,y,z; + uint16 *src1, *src2, *src3, *dest; + uint16 val[4]; + uint16 t1,t2,t3,t4,t5,t6,t7,t8,t9; + + if( filter == TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_3 || filter == TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_4 ) + { + for( y=1; y>1)))>>shift; + t5 = (*((uint8*)(src2+x )+(z>>1)))>>shift; + t8 = (*((uint8*)(src3+x )+(z>>1)))>>shift; + val[z] = ((t2+t8)*mul2+(t5*mul3))>>shift4; + } + dest[x] = val[0]|(val[1]<<4)|(val[2]<<8)|(val[3]<<12); + } + } + } + else + { + for( y=0; y0 ) + { + src1 = pcopy+(y-1)*pitch; + src2 = src1 + pitch; + } + else + { + src1 = src2 = pcopy; + } + + src3 = src2; + if( y>1)))>>shift; + t2 = (*((uint8*)(src1+x )+(z>>1)))>>shift; + t3 = (*((uint8*)(src1+x+1)+(z>>1)))>>shift; + t4 = (*((uint8*)(src2+x-1)+(z>>1)))>>shift; + t5 = (*((uint8*)(src2+x )+(z>>1)))>>shift; + t6 = (*((uint8*)(src2+x+1)+(z>>1)))>>shift; + t7 = (*((uint8*)(src3+x-1)+(z>>1)))>>shift; + t8 = (*((uint8*)(src3+x )+(z>>1)))>>shift; + t9 = (*((uint8*)(src3+x+1)+(z>>1)))>>shift; + val[z] = ((t1+t3+t7+t9)*mul1+((t2+t4+t6+t8)*mul2)+(t5*mul3))>>shift4; + } + dest[x] = val[0]|(val[1]<<4)|(val[2]<<8)|(val[3]<<12); + } + } + } + delete [] pcopy; +} + + +void EnhanceTexture(TxtrCacheEntry *pEntry) +{ + if( pEntry->dwEnhancementFlag == options.textureEnhancement ) + { + // The texture has already been enhanced + return; + } + else if( options.textureEnhancement == TEXTURE_NO_ENHANCEMENT ) + { + //Texture enhancement has being turned off + //Delete any allocated memory for the enhanced texture + SAFE_DELETE(pEntry->pEnhancedTexture); + //Set the enhancement flag so the texture wont be processed again + pEntry->dwEnhancementFlag = TEXTURE_NO_ENHANCEMENT; + return; + } + + if( status.primitiveType != PRIM_TEXTRECT && options.bTexRectOnly ) + { + return; + } + + DrawInfo srcInfo; + //Start the draw update + if( pEntry->pTexture->StartUpdate(&srcInfo) == false ) + { + //If we get here we were unable to start the draw update + //Delete any allocated memory for the enhanced texture + SAFE_DELETE(pEntry->pEnhancedTexture); + return; + } + + uint32 realwidth = srcInfo.dwWidth; + uint32 realheight = srcInfo.dwHeight; + uint32 nWidth = srcInfo.dwCreatedWidth; + uint32 nHeight = srcInfo.dwCreatedHeight; + + //Sharpen option is enabled, sharpen the texture + if( options.textureEnhancement == TEXTURE_SHARPEN_ENHANCEMENT || options.textureEnhancement == TEXTURE_SHARPEN_MORE_ENHANCEMENT ) + { + if( pEntry->pTexture->GetPixelSize() == 4 ) + SharpenFilter_32((uint32*)srcInfo.lpSurface, nWidth, nHeight, nWidth, options.textureEnhancement); + else + SharpenFilter_16((uint16*)srcInfo.lpSurface, nWidth, nHeight, nWidth, options.textureEnhancement); + pEntry->dwEnhancementFlag = options.textureEnhancement; + //End the draw update + pEntry->pTexture->EndUpdate(&srcInfo); + //Delete any allocated memory for the enhanced texture + SAFE_DELETE(pEntry->pEnhancedTexture); + return; + } + + pEntry->dwEnhancementFlag = options.textureEnhancement; + if( options.bSmallTextureOnly ) + { + if( nWidth + nHeight > 256 ) + { + //End the draw update + pEntry->pTexture->EndUpdate(&srcInfo); + //Delete any data allocated for the enhanced texture + SAFE_DELETE(pEntry->pEnhancedTexture); + //Set the enhancement flag so the texture wont be processed again + pEntry->dwEnhancementFlag = TEXTURE_NO_ENHANCEMENT; + return; + } + } + + + CTexture* pSurfaceHandler = NULL; + if( options.textureEnhancement == TEXTURE_HQ4X_ENHANCEMENT ) + { + if( nWidth + nHeight > 1024/4 ) + { + // Don't enhance for large textures + pEntry->pTexture->EndUpdate(&srcInfo); + SAFE_DELETE(pEntry->pEnhancedTexture); + pEntry->dwEnhancementFlag = TEXTURE_NO_ENHANCEMENT; + return; + } + pSurfaceHandler = CDeviceBuilder::GetBuilder()->CreateTexture(nWidth*4, nHeight*4); + } + else + { + if( nWidth + nHeight > 1024/2 ) + { + // Don't enhance for large textures + pEntry->pTexture->EndUpdate(&srcInfo); + SAFE_DELETE(pEntry->pEnhancedTexture); + pEntry->dwEnhancementFlag = TEXTURE_NO_ENHANCEMENT; + return; + } + pSurfaceHandler = CDeviceBuilder::GetBuilder()->CreateTexture(nWidth*2, nHeight*2); + } + DrawInfo destInfo; + if(pSurfaceHandler) + { + if(pSurfaceHandler->StartUpdate(&destInfo)) + { + if( options.textureEnhancement == TEXTURE_2XSAI_ENHANCEMENT ) + { + if( pEntry->pTexture->GetPixelSize() == 4 ) + Super2xSaI_32((uint32*)(srcInfo.lpSurface),(uint32*)(destInfo.lpSurface), nWidth, realheight, nWidth); + else + Super2xSaI_16((uint16*)(srcInfo.lpSurface),(uint16*)(destInfo.lpSurface), nWidth, realheight, nWidth); + } + else if( options.textureEnhancement == TEXTURE_HQ2X_ENHANCEMENT ) + { + if( pEntry->pTexture->GetPixelSize() == 4 ) + { + hq2x_init(32); + hq2x_32((uint8*)(srcInfo.lpSurface), srcInfo.lPitch, (uint8*)(destInfo.lpSurface), destInfo.lPitch, nWidth, realheight); + } + else + { + hq2x_init(16); + hq2x_16((uint8*)(srcInfo.lpSurface), srcInfo.lPitch, (uint8*)(destInfo.lpSurface), destInfo.lPitch, nWidth, realheight); + } + } + else if( options.textureEnhancement == TEXTURE_LQ2X_ENHANCEMENT ) + { + if( pEntry->pTexture->GetPixelSize() == 4 ) + { + hq2x_init(32); + lq2x_32((uint8*)(srcInfo.lpSurface), srcInfo.lPitch, (uint8*)(destInfo.lpSurface), destInfo.lPitch, nWidth, realheight); + } + else + { + hq2x_init(16); + lq2x_16((uint8*)(srcInfo.lpSurface), srcInfo.lPitch, (uint8*)(destInfo.lpSurface), destInfo.lPitch, nWidth, realheight); + } + } + else if( options.textureEnhancement == TEXTURE_HQ4X_ENHANCEMENT ) + { + if( pEntry->pTexture->GetPixelSize() == 4 ) + { + hq4x_InitLUTs(); + hq4x_32((uint8*)(srcInfo.lpSurface), (uint8*)(destInfo.lpSurface), realwidth, realheight, nWidth, destInfo.lPitch); + } + else + { + hq4x_InitLUTs(); + hq4x_16((uint8*)(srcInfo.lpSurface), (uint8*)(destInfo.lpSurface), realwidth, realheight, nWidth, destInfo.lPitch); + } + } + else + { + if( pEntry->pTexture->GetPixelSize() == 4 ) + Texture2x_32( srcInfo, destInfo); + else + Texture2x_16( srcInfo, destInfo); + } + + if( options.textureEnhancementControl >= TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_1 ) + { + if( options.textureEnhancement != TEXTURE_HQ4X_ENHANCEMENT ) + { + if( pEntry->pTexture->GetPixelSize() == 4 ) + SmoothFilter_32((uint32*)destInfo.lpSurface, realwidth<<1, realheight<<1, nWidth<<1, options.textureEnhancementControl); + else + SmoothFilter_16((uint16*)destInfo.lpSurface, realwidth<<1, realheight<<1, nWidth<<1, options.textureEnhancementControl); + } + else + { + if( pEntry->pTexture->GetPixelSize() == 4 ) + SmoothFilter_32((uint32*)destInfo.lpSurface, realwidth<<2, realheight<<2, nWidth<<2, options.textureEnhancementControl); + else + SmoothFilter_16((uint16*)destInfo.lpSurface, realwidth<<2, realheight<<2, nWidth<<2, options.textureEnhancementControl); + } + } + + pSurfaceHandler->EndUpdate(&destInfo); + } + + pSurfaceHandler->SetOthersVariables(); + pSurfaceHandler->m_bIsEnhancedTexture = true; + } + + pEntry->pTexture->EndUpdate(&srcInfo); + + pEntry->pEnhancedTexture = pSurfaceHandler; +} + + +/************************************************************************/ +/* */ +/************************************************************************/ +void MirrorEmulator_DrawLine(DrawInfo& destInfo, DrawInfo& srcInfo, uint32 *pSource, uint32 *pDest, uint32 nWidth, BOOL bFlipLeftRight) +{ + if(!bFlipLeftRight) + { + memcpy(pDest, pSource, nWidth * 4); + } + else + { + uint32 *pMaxDest = pDest + nWidth; + pSource += nWidth - 1; + for(; pDest < pMaxDest; pDest++, pSource--) + { + *pDest = *pSource; + } + } +} + + +void MirrorEmulator_Draw(DrawInfo& destInfo, DrawInfo& srcInfo, uint32 nDestX, uint32 nDestY, BOOL bFlipLeftRight, BOOL bFlipUpDown) +{ + uint8 *pDest = (uint8 *) destInfo.lpSurface + (destInfo.lPitch * nDestY) + (4 * nDestX); + uint8 *pMaxDest = pDest + (destInfo.lPitch * srcInfo.dwHeight); + uint8 *pSource = (uint8 *)(srcInfo.lpSurface); + if(!bFlipUpDown) + { + for(; pDest < pMaxDest; pDest += destInfo.lPitch, pSource += srcInfo.lPitch) + { + MirrorEmulator_DrawLine(destInfo, srcInfo, (uint32*)pSource, (uint32*)pDest, srcInfo.dwWidth, bFlipLeftRight); + } + } + else + { + pSource += (srcInfo.lPitch * (srcInfo.dwHeight - 1)); + for(; pDest < pMaxDest; pDest += destInfo.lPitch, pSource -= srcInfo.lPitch) + { + MirrorEmulator_DrawLine(destInfo, srcInfo, (uint32*)pSource, (uint32*)pDest, srcInfo.dwWidth, bFlipLeftRight); + } + } +} + +void MirrorTexture(uint32 dwTile, TxtrCacheEntry *pEntry) +{ + if( ((gRDP.tiles[dwTile].bMirrorS) || (gRDP.tiles[dwTile].bMirrorT)) && CGraphicsContext::Get()->m_supportTextureMirror == false ) + { + if(pEntry->pEnhancedTexture) + { + return; + } + else + { + CTexture* pSurfaceHandler = NULL; + + // FIXME: Compute the correct values. 2/2 seems to always work correctly in Mario64 + uint32 nXTimes = gRDP.tiles[dwTile].bMirrorS ? 2 : 1; + uint32 nYTimes = gRDP.tiles[dwTile].bMirrorT ? 2 : 1; + + // For any texture need to use mirror, we should not need to rescale it + // because texture need to be mirrored must with MaskS and MaskT + + // But again, check me + + //if( pEntry->pTexture->m_bScaledS == false || pEntry->pTexture->m_bScaledT == false) + //{ + // pEntry->pTexture->ScaleImageToSurface(); + //} + + DrawInfo srcInfo; + if( pEntry->pTexture->StartUpdate(&srcInfo) ) + { + uint32 nWidth = srcInfo.dwWidth; + uint32 nHeight = srcInfo.dwHeight; + + pSurfaceHandler = CDeviceBuilder::GetBuilder()->CreateTexture(nWidth * nXTimes, nHeight * nYTimes); + if( pSurfaceHandler ) + { + DrawInfo destInfo; + if( pSurfaceHandler->StartUpdate(&destInfo) ) + { + for(uint32 nY = 0; nY < nYTimes; nY++) + { + for(uint32 nX = 0; nX < nXTimes; nX++) + { + MirrorEmulator_Draw(destInfo, srcInfo, nWidth * nX, nHeight * nY, nX & 0x1, nY & 0x1); + } + } + + pSurfaceHandler->EndUpdate(&destInfo); + } + + // FIXME: There should be a flag to tell that it is a mirrored texture handler + // not the original texture handlers, so all texture coordinate should be divided by 2 + pSurfaceHandler->SetOthersVariables(); + } + + pEntry->pTexture->EndUpdate(&srcInfo); + pEntry->dwEnhancementFlag = TEXTURE_MIRRORED; + } + + + pEntry->pEnhancedTexture = pSurfaceHandler; + } + } +} +/**** + All code bellow, CLEAN ME +****/ + +enum TextureType +{ + NO_TEXTURE, + RGB_PNG, + COLOR_INDEXED_BMP, + RGB_WITH_ALPHA_TOGETHER_PNG, + RGBA_PNG_FOR_CI, + RGBA_PNG_FOR_ALL_CI, +}; +typedef struct { + unsigned int width; + unsigned int height; + int fmt; + int siz; + int crc32; + int pal_crc32; + char *foldername; + char *filename; + char *filename_a; + //char name[40]; + TextureType type; + bool bSeparatedAlpha; +} ExtTxtrInfo; + +CSortedList gTxtrDumpInfos; +CSortedList gHiresTxtrInfos; + +extern char * right(const char * src, int nchars); + +#define SURFFMT_P8 41 + +int GetImageInfoFromFile(char* pSrcFile, IMAGE_INFO *pSrcInfo) +{ + unsigned char sig[8]; + FILE *f; + + f = fopen(pSrcFile, "rb"); + if (f == NULL) + { + DebugMessage(M64MSG_ERROR, "GetImageInfoFromFile() error: couldn't open file '%s'", pSrcFile); + return 1; + } + if (fread(sig, 1, 8, f) != 8) + { + DebugMessage(M64MSG_ERROR, "GetImageInfoFromFile() error: couldn't read first 8 bytes of file '%s'", pSrcFile); + fclose(f); + return 1; + } + fclose(f); + + if(sig[0] == 'B' && sig[1] == 'M') // BMP + { + struct BMGImageStruct img; + memset(&img, 0, sizeof(BMGImageStruct)); + BMG_Error code = ReadBMP(pSrcFile, &img); + if( code == BMG_OK ) + { + pSrcInfo->Width = img.width; + pSrcInfo->Height = img.height; + pSrcInfo->Depth = img.bits_per_pixel; + pSrcInfo->MipLevels = 1; + if(img.bits_per_pixel == 32) + pSrcInfo->Format = SURFFMT_A8R8G8B8; + else if(img.bits_per_pixel == 8) + pSrcInfo->Format = SURFFMT_P8; + // Resource and File Format ignored + FreeBMGImage(&img); + return 0; + } + DebugMessage(M64MSG_ERROR, "Couldn't read BMP file '%s'; error = %i", pSrcFile, code); + return 1; + } + else if(sig[0] == 137 && sig[1] == 'P' && sig[2] == 'N' && sig[3] == 'G' && sig[4] == '\r' && sig[5] == '\n' && + sig[6] == 26 && sig[7] == '\n') // PNG + { + struct BMGImageStruct img; + memset(&img, 0, sizeof(BMGImageStruct)); + BMG_Error code = ReadPNGInfo(pSrcFile, &img); + if( code == BMG_OK ) + { + pSrcInfo->Width = img.width; + pSrcInfo->Height = img.height; + pSrcInfo->Depth = img.bits_per_pixel; + pSrcInfo->MipLevels = 1; + if(img.bits_per_pixel == 32) + pSrcInfo->Format = SURFFMT_A8R8G8B8; + else if(img.bits_per_pixel == 8) + pSrcInfo->Format = SURFFMT_P8; + // Resource and File Format ignored + FreeBMGImage(&img); + return 0; + } + DebugMessage(M64MSG_ERROR, "Couldn't read PNG file '%s'; error = %i", pSrcFile, code); + return 1; + } + + DebugMessage(M64MSG_ERROR, "GetImageInfoFromFile : unknown file format (%s)", pSrcFile); + return 1; +} + +BOOL PathFileExists(char* pszPath) +{ + FILE *f; + f = fopen(pszPath, "rb"); + if(f != NULL) + { + fclose(f); + return TRUE; + } + return FALSE; +} + +/******************************************************************************************************************** + * Truncates the current list with information about hires textures and scans the hires folder for hires textures and + * creates a list with records of properties of the hires textures. + * parameter: + * foldername: the folder that should be scaned for valid hires textures. + * infos: a pointer that will point to the list containing the records with the infos about the found hires textures. + * In case of enabled caching, these records will also contain the actual textures. + * extraCheck: ? + * bRecursive: flag that indicates if also subfolders should be scanned for hires textures + * bCacheTextures: flag that indicates if the identified hires textures should also be cached + * bMainFolder: indicates if the folder is the main folder that will be scanned. That way, texture counting does not + * start at 1 each time a subfolder is accessed. (microdev: I know that is not important but it really + * bugged me ;-)) + * return: + * infos: the list with the records of the identified hires textures. Be aware that these records also contains the + * actual textures if caching is enabled. + ********************************************************************************************************************/ +void FindAllTexturesFromFolder(char *foldername, CSortedList &infos, bool extraCheck, bool bRecursive) +{ + // check if folder actually exists + if (!osal_is_directory(foldername)) + return; + + // the path of the texture + char texturefilename[PATH_MAX]; + // + IMAGE_INFO imgInfo; + // + IMAGE_INFO imgInfo2; + + void *dir; + dir = osal_search_dir_open(foldername); + const char *foundfilename; + + int crc, palcrc32; + unsigned int fmt, siz; + char crcstr[16], crcstr2[16]; + + do + { + foundfilename = osal_search_dir_read_next(dir); + + // The array is empty, break the current operation + if (foundfilename == NULL) + break; + // The current file is a hidden one + if (foundfilename[0] == '.' ) + // These files we don't need + continue; + + // Get the folder name + strcpy(texturefilename, foldername); + // And append the file name + strcat(texturefilename, foundfilename); + + // Check if the current file is a directory and if recursive scanning is enabled + if (osal_is_directory(texturefilename) && bRecursive ) + { + // Add file-separator + strcat(texturefilename, OSAL_DIR_SEPARATOR_STR); + // Scan detected folder for hires textures (recursive call) + FindAllTexturesFromFolder(texturefilename, infos, extraCheck, bRecursive); + continue; + } + // well, the current file is actually no file (probably a directory & recursive scanning is not enabled) + if( strstr(foundfilename,(const char*)g_curRomInfo.szGameName) == 0 ) + // go on with the next one + continue; + + TextureType type = NO_TEXTURE; + bool bSeparatedAlpha = false; + + // Detect the texture type by it's extention + // microdev: this is not the smartest way. Should be done by header analysis if possible + if( strcasecmp(right(foundfilename,7), "_ci.bmp") == 0 ) + { + // Identify type + if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0) + { + DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename); + continue; + } + + if( imgInfo.Format == SURFFMT_P8 ) + // and store it to the record + type = COLOR_INDEXED_BMP; + else + // Type is not supported, go on with the next one + continue; + } + // Detect the texture type by its extention + else if( strcasecmp(right(foundfilename,13), "_ciByRGBA.png") == 0 ) + { + // Identify type + if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0 ) + { + DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename); + continue; + } + + if( imgInfo.Format == SURFFMT_A8R8G8B8 ) + // and store it to the record + type = RGBA_PNG_FOR_CI; + else + // Type is not supported, go on with the next one + continue; + } + // Detect the texture type by its extention + else if( strcasecmp(right(foundfilename,16), "_allciByRGBA.png") == 0 ) + { + // Identify type + if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0 ) + { + DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename); + continue; + } + if( imgInfo.Format == SURFFMT_A8R8G8B8 ) + // and store it to the record + type = RGBA_PNG_FOR_ALL_CI; + else + // Type not supported, go on with next one + continue; + } + // Detect the texture type by its extention + else if( strcasecmp(right(foundfilename,8), "_rgb.png") == 0 ) + { + // Identify type + if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0 ) + { + DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename); + continue; + } + + // Store type to the record + type = RGB_PNG; + + char filename2[PATH_MAX]; + // Assemble the file name for the separate alpha channel file + strcpy(filename2,texturefilename); + strcpy(filename2+strlen(filename2)-8,"_a.png"); + // Check if the file actually exists + if( PathFileExists(filename2) ) + { + // Check if the file with this name is actually a texture (well an alpha channel one) + if( GetImageInfoFromFile(filename2, &imgInfo2) != 0 ) + { + // Nope, it isn't. => Go on with the next file + DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", filename2); + continue; + } + + // Yes it is a texture file. Check if the size of the alpha channel is the same as the one of the texture + if( extraCheck && (imgInfo2.Width != imgInfo.Width || imgInfo2.Height != imgInfo.Height) ) + { + // Nope, it isn't => go on with next file + DebugMessage(M64MSG_WARNING, "RGB and alpha texture size mismatch: %s", filename2); + continue; + } + + bSeparatedAlpha = true; + } + } + // Detect the texture type by its extention + else if( strcasecmp(right(foundfilename,8), "_all.png") == 0 ) + { + // Check if texture is of expected type + if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0 ) + { + DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename); + // Nope, continue with next file + continue; + } + + // Indicate file type + type = RGB_WITH_ALPHA_TOGETHER_PNG; + } + + // If a known texture format has been detected... + if( type != NO_TEXTURE ) + { + /* + Try to read image information here. + + (CASTLEVANIA2)#(58E2333F)#(2)#(0)#(D7A5C6D9)_ciByRGBA.png + (------1-----)#(---2----)#(3)#(4)#(---5----)_ciByRGBA.png + + 1. Internal ROM name + 2. The DRAM CRC + 3. The image pixel size (8b=0, 16b=1, 24b=2, 32b=3) + 4. The texture format (RGBA=0, YUV=1, CI=2, IA=3, I=4) + 5. The palette CRC + + ##<24bit>##_ciByRGBA.png + */ + + // Get the actual file name + strcpy(texturefilename, foundfilename); + // Place the pointer before the DRAM-CRC (first occurrence of '#') + char *ptr = strchr(texturefilename,'#'); + // Terminate the string ('0' means end of string - or in this case begin of string) + *ptr++ = 0; + if( type == RGBA_PNG_FOR_CI ) + { + // Extract the information from the file name; information is: + // + sscanf(ptr,"%8c#%d#%d#%8c", crcstr, &fmt, &siz,crcstr2); + // Terminate the ascii represntation of the palette crc + crcstr2[8] = 0; + // Transform the ascii presentation of the hex value to an unsigned integer + palcrc32 = strtoul(crcstr2,NULL,16); + } + else + { + // Extract the information from the file name - this file does not have a palette crc; information is: + // + // o gosh, commenting source code is really boring - but necessary!! Thus do it! (and don't use drugs ;-)) + sscanf(ptr,"%8c#%d#%d", crcstr, &fmt, &siz); + // Use dummy for palette crc - that way each texture can be handled in a heterogeneous way + palcrc32 = 0xFFFFFFFF; + } + // Terminate the ascii represntation of the texture crc + crcstr[8]=0; + // Transform the ascii presentation of the hex value to an unsigned integer + crc = strtoul(crcstr,NULL,16); + // For the detection of an existing item + + int foundIdx = -1; + for( int k=0; k + uint64 crc64 = newinfo.crc32; + crc64 <<= 32; + if (options.bLoadHiResCRCOnly) + crc64 |= newinfo.pal_crc32&0xFFFFFFFF; + else + crc64 |= (newinfo.pal_crc32&0xFFFFFF00)|(newinfo.fmt<<4)|newinfo.siz; + // Add the new record to the list + infos.add(crc64,newinfo); + } + } + } while(foundfilename != NULL); + + osal_search_dir_close(dir); +} +/******************************************************************************************************************** + * Checks if a folder is actually existant. If not, it tries to create this folder + * parameter: + * pathname: the name of the folder that should be checked or created if not existant + * return: + * return value: flag that indicates true if the folder is existant or could be created. If none was the case, + * false will be returned + ********************************************************************************************************************/ + +bool CheckAndCreateFolder(const char* pathname) +{ + // Check if provided folder already exists + if( !PathFileExists((char*)pathname) ) + { + // It didn't. Try creating it. + if (osal_mkdirp(pathname, 0700) != 0) + { + // It didn't work (probably insufficient permissions or read-only media) ==> return false + DebugMessage(M64MSG_WARNING, "Can not create new folder: %s", pathname); + return false; + } + } + // success + + return true; +} +// microdev: THIS HAS TO BE CLEANED UP... + + +// Texture dumping filenaming +// GameName_FrameCount_CRC_Fmt_Siz.bmp +// File format: BMP +// GameName: N64 game internal name +// CRC: 32 bit, 8 hex digits +// Fmt: 0 - 4 +// Siz: 0 - 3 + +const char *subfolders[] = { + "png_all", + "png_by_rgb_a", + "ci_bmp", + "ci_bmp_with_pal_crc", + "ci_by_png", +}; + +void FindAllDumpedTextures(void) +{ + char foldername[PATH_MAX + 64]; + strncpy(foldername, ConfigGetUserDataPath(), PATH_MAX); + foldername[PATH_MAX] = 0; + + if (foldername[strlen(foldername) - 1] != OSAL_DIR_SEPARATOR_CHAR) + strcat(foldername, OSAL_DIR_SEPARATOR_STR); + strcat(foldername,"texture_dump" OSAL_DIR_SEPARATOR_STR); + + CheckAndCreateFolder(foldername); + + strcat(foldername,(const char*)g_curRomInfo.szGameName); + strcat(foldername, OSAL_DIR_SEPARATOR_STR); + + gTxtrDumpInfos.clear(); + if( !PathFileExists(foldername) ) + { + CheckAndCreateFolder(foldername); + char foldername2[PATH_MAX]; + for( int i=0; i<5; i++) + { + strcpy(foldername2,foldername); + strcat(foldername2,subfolders[i]); + CheckAndCreateFolder(foldername2); + } + return; + } + else + { + gTxtrDumpInfos.clear(); + FindAllTexturesFromFolder(foldername,gTxtrDumpInfos, false, true); + + char foldername2[PATH_MAX]; + for( int i=0; i<5; i++) + { + strcpy(foldername2,foldername); + strcat(foldername2,subfolders[i]); + CheckAndCreateFolder(foldername2); + } + } +} + +/******************************************************************************************************************** + * Truncates the current list with information about hires textures and scans the hires folder for hires textures and + * creates a list with records of properties of the hires textures. + * parameter: + * none + * return: + * none + ********************************************************************************************************************/ +void FindAllHiResTextures(void) +{ + char foldername[PATH_MAX + 64]; + strncpy(foldername, ConfigGetUserDataPath(), PATH_MAX); + foldername[PATH_MAX] = 0; + + // Assure that a backslash exists at the end (should be handled by GetPluginDir()) + if(foldername[strlen(foldername) - 1] != OSAL_DIR_SEPARATOR_CHAR) + strcat(foldername, OSAL_DIR_SEPARATOR_STR); + // Add the relative path to the hires folder + strcat(foldername,"hires_texture" OSAL_DIR_SEPARATOR_STR); + // It does not exist? => Create it + CheckAndCreateFolder(foldername); + + // Add the path to a sub-folder corresponding to the rom name + // HOOK IN: PACK SELECT + strcat(foldername,(const char*)g_curRomInfo.szGameName); + strcat(foldername, OSAL_DIR_SEPARATOR_STR); + // Truncate the current list with the hires texture info + gHiresTxtrInfos.clear(); + if (!osal_is_directory(foldername)) + { + DebugMessage(M64MSG_WARNING, "Couldn't open hi-res texture directory: %s", foldername); + return; + } + else + { + // Find all hires textures and also cache them if configured to do so + FindAllTexturesFromFolder(foldername,gHiresTxtrInfos, true, true); + } +} + +void CloseHiresTextures(void) +{ + for( int i=0; i= entry.ti.HeightToLoad*(1<= entry.ti.WidthToLoad*(1< &infos, TxtrCacheEntry &entry, int &indexa, int &scaleShift, bool bForDump = false) +{ + if ((entry.ti.WidthToLoad != 0 && entry.ti.WidthToCreate / entry.ti.WidthToLoad > 2) || + (entry.ti.HeightToLoad != 0 && entry.ti.HeightToCreate / entry.ti.HeightToLoad > 2 )) + { + //DebugMessage(M64MSG_WARNING, "Hires texture does not support extreme texture replication"); + return -1; + } + // determine if texture is a color-indexed (CI) texture + + bool bCI = (gRDP.otherMode.text_tlut>=2 || entry.ti.Format == TXT_FMT_CI || entry.ti.Format == TXT_FMT_RGBA) && entry.ti.Size <= TXT_SIZE_8b; + // generate two alternative ids + + uint64 crc64a = entry.dwCRC; + crc64a <<= 32; + uint64 crc64b = crc64a; + if (options.bLoadHiResCRCOnly) { + crc64a |= (0xFFFFFFFF); + crc64b |= (entry.dwPalCRC&0xFFFFFFFF); + } else { + crc64a |= (0xFFFFFF00|(entry.ti.Format<<4)|entry.ti.Size); + crc64b |= ((entry.dwPalCRC&0xFFFFFF00)|(entry.ti.Format<<4)|entry.ti.Size); + } + + // infos is the list containing the references to the detected external textures + // get the number of items contained in this list + int infosize = infos.size(); + int indexb=-1; + // try to identify the external texture that + // corresponds to the original texture + indexa = infos.find(crc64a); // For CI without pal CRC, and for RGBA_PNG_FOR_ALL_CI + if( bCI ) + // and also for textures with separate alpha channel + indexb = infos.find(crc64b); // For CI or PNG with pal CRC + + // did not found the ext. text. + if( indexa >= infosize ) + indexa = -1; + // did not found the ext. text. w/ sep. alpha channel + if( indexb >= infosize ) + indexb = -1; + + scaleShift = -1; + + // found texture with sep. alpha channel + + if( indexb >= 0 ) + { + // determine the factor for scaling + scaleShift = FindScaleFactor(infos[indexb], entry); + // ok. the scale factor is supported. A valid replacement has been found + if( scaleShift >= 0 ) + return indexb; + } + // if texture is 4bit, should be dumped and there is no match in the list of external textures + + if( bForDump && bCI && indexb < 0) + // than return that there is no replacement & therefore texture can be dumped (microdev: not sure about that...) + return -1; + + // texture has no separate alpha channel, try to find it in the ext. text. list + if( indexa >= 0 ) + scaleShift = FindScaleFactor(infos[indexa], entry); + // ok. the scale factor is supported. A valid replacement has been found + // this is a texture without ext. alpha channel + + if( scaleShift >= 0 ) + return indexa; + // no luck at all. there is no valid replacement + else + return -1; +} + +bool SaveCITextureToFile(TxtrCacheEntry &entry, char *filename, bool bShow, bool bWhole); + +void DumpCachedTexture( TxtrCacheEntry &entry ) +{ + char cSep = '/'; + + CTexture *pSrcTexture = entry.pTexture; + if( pSrcTexture ) + { + // Check the vector table + int ciidx, scaleShift; + if( CheckTextureInfos(gTxtrDumpInfos,entry,ciidx,scaleShift,true) >= 0 ) + return; // This texture has been dumpped + + char filename1[PATH_MAX + 64]; + char filename2[PATH_MAX + 64]; + char filename3[PATH_MAX + 64]; + char gamefolder[PATH_MAX + 64]; + strncpy(gamefolder, ConfigGetUserDataPath(), PATH_MAX); + gamefolder[PATH_MAX] = 0; + + strcat(gamefolder,"texture_dump" OSAL_DIR_SEPARATOR_STR); + strcat(gamefolder,(const char*)g_curRomInfo.szGameName); + strcat(gamefolder, OSAL_DIR_SEPARATOR_STR); + + //sprintf(filename1+strlen(filename1), "%08X#%d#%d", entry.dwCRC, entry.ti.Format, entry.ti.Size); + sprintf(filename1, "%s%s#%08X#%d#%d", gamefolder, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size); + + if( (gRDP.otherMode.text_tlut>=2 || entry.ti.Format == TXT_FMT_CI || entry.ti.Format == TXT_FMT_RGBA) && entry.ti.Size <= TXT_SIZE_8b ) + { + if( ciidx < 0 ) + { + sprintf(filename1, "%sci_bmp%c%s#%08X#%d#%d_ci", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size); + SaveCITextureToFile(entry, filename1, false, false); + } + + sprintf(filename1, "%sci_bmp_with_pal_crc%c%s#%08X#%d#%d#%08X_ci", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size,entry.dwPalCRC); + SaveCITextureToFile(entry, filename1, false, false); + + sprintf(filename1, "%sci_by_png%c%s#%08X#%d#%d#%08X_ciByRGBA", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size,entry.dwPalCRC); + CRender::g_pRender->SaveTextureToFile(*pSrcTexture, filename1, TXT_RGBA, false, false, entry.ti.WidthToLoad, entry.ti.HeightToLoad); + } + else + { + sprintf(filename1, "%spng_by_rgb_a%c%s#%08X#%d#%d_rgb", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size); + sprintf(filename2, "%spng_by_rgb_a%c%s#%08X#%d#%d_a", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size); + sprintf(filename3, "%spng_all%c%s#%08X#%d#%d_all", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size); + + + CRender::g_pRender->SaveTextureToFile(*pSrcTexture, filename1, TXT_RGB, false, false, entry.ti.WidthToLoad, entry.ti.HeightToLoad); + CRender::g_pRender->SaveTextureToFile(*pSrcTexture, filename3, TXT_RGBA, false, false, entry.ti.WidthToLoad, entry.ti.HeightToLoad); + if( entry.ti.Format != TXT_FMT_I ) + { + DrawInfo srcInfo; + uint32 aFF = 0xFF; + if( pSrcTexture->StartUpdate(&srcInfo) ) + { + // Copy RGB to buffer + for( int i=entry.ti.HeightToLoad-1; i>=0; i--) + { + unsigned char *pSrc = (unsigned char*)srcInfo.lpSurface+srcInfo.lPitch * i; + for( uint32 j=0; jEndUpdate(&srcInfo); + } + + if( aFF != 0xFF) + CRender::g_pRender->SaveTextureToFile(*pSrcTexture, filename2, TXT_ALPHA, false, false); + } + } + + ExtTxtrInfo newinfo; + newinfo.width = entry.ti.WidthToLoad; + newinfo.height = entry.ti.HeightToLoad; + //strcpy(newinfo.name,g_curRomInfo.szGameName); + newinfo.fmt = entry.ti.Format; + newinfo.siz = entry.ti.Size; + newinfo.crc32 = entry.dwCRC; + + newinfo.pal_crc32 = entry.dwPalCRC; + newinfo.foldername = NULL; + newinfo.filename = NULL; + newinfo.filename_a = NULL; + newinfo.type = NO_TEXTURE; + newinfo.bSeparatedAlpha = false; + + uint64 crc64 = newinfo.crc32; + crc64 <<= 32; + if (options.bLoadHiResCRCOnly) + crc64 |= newinfo.pal_crc32&0xFFFFFFFF; + else + crc64 |= (newinfo.pal_crc32&0xFFFFFF00)|(newinfo.fmt<<4)|newinfo.siz; + gTxtrDumpInfos.add(crc64,newinfo); + + } +} + +bool LoadRGBBufferFromPNGFile(char *filename, unsigned char **pbuf, int &width, int &height, int bits_per_pixel = 24 ) +{ + struct BMGImageStruct img; + memset(&img, 0, sizeof(BMGImageStruct)); + if (!PathFileExists(filename)) + { + DebugMessage(M64MSG_ERROR, "File at '%s' doesn't exist in LoadRGBBufferFromPNGFile!", filename); + return false; + } + + BMG_Error code = ReadPNG( filename, &img ); + if( code == BMG_OK ) + { + *pbuf = NULL; + + *pbuf = new unsigned char[img.width*img.height*bits_per_pixel/8]; + if (*pbuf == NULL) + { + DebugMessage(M64MSG_ERROR, "new[] returned NULL for image width=%i height=%i bpp=%i", img.width, img.height, bits_per_pixel); + return false; + } + if (img.bits_per_pixel == bits_per_pixel) + { + memcpy(*pbuf, img.bits, img.width*img.height*bits_per_pixel/8); + } + else if (img.bits_per_pixel == 24 && bits_per_pixel == 32) + { + unsigned char *pSrc = img.bits; + unsigned char *pDst = *pbuf; + for (int i = 0; i < (int)(img.width*img.height); i++) + { + *pDst++ = *pSrc++; + *pDst++ = *pSrc++; + *pDst++ = *pSrc++; + *pDst++ = 0; + } + } + // loaded image has alpha, needed image has to be without alpha channel + else if (img.bits_per_pixel == 32 && bits_per_pixel == 24) + { + // pointer to source image data + unsigned char *pSrc = img.bits; + // buffer for destination image + unsigned char *pDst = *pbuf; + // copy data of the loaded image to the buffer by skipping the alpha byte + for (int i = 0; i < (int)(img.width*img.height); i++) + { + // copy R + *pDst++ = *pSrc++; + // copy G + *pDst++ = *pSrc++; + // copy B + *pDst++ = *pSrc++; + // skip the alpha byte of the loaded image + pSrc++; + } + } + else if (img.bits_per_pixel == 8 && (bits_per_pixel == 24 || bits_per_pixel == 32)) + { + // do palette lookup and convert 8bpp to 24/32bpp + int destBytePP = bits_per_pixel / 8; + int paletteBytePP = img.bytes_per_palette_entry; + unsigned char *pSrc = img.bits; + unsigned char *pDst = *pbuf; + // clear the destination image data (just to clear alpha if bpp=32) + memset(*pbuf, 0, img.width*img.height*destBytePP); + for (int i = 0; i < (int)(img.width*img.height); i++) + { + unsigned char clridx = *pSrc++; + unsigned char *palcolor = img.palette + clridx * paletteBytePP; + pDst[0] = palcolor[2]; // red + pDst[1] = palcolor[1]; // green + pDst[2] = palcolor[0]; // blue + pDst += destBytePP; + } + } + else + { + DebugMessage(M64MSG_ERROR, "PNG file '%s' is %i bpp but texture is %i bpp.", filename, img.bits_per_pixel, bits_per_pixel); + delete [] *pbuf; + *pbuf = NULL; + } + + width = img.width; + height = img.height; + FreeBMGImage(&img); + + return true; + } + else + { + DebugMessage(M64MSG_ERROR, "ReadPNG() returned error for '%s' in LoadRGBBufferFromPNGFile!", filename); + *pbuf = NULL; + return false; + } +} + +bool LoadRGBABufferFromColorIndexedFile(char *filename, TxtrCacheEntry &entry, unsigned char **pbuf, int &width, int &height) +{ + BITMAPFILEHEADER fileHeader; + BITMAPINFOHEADER infoHeader; + + FILE *f; + f = fopen(filename, "rb"); + if(f != NULL) + { + if (fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f) != 1 || + fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f) != 1) + { + DebugMessage(M64MSG_ERROR, "Couldn't read BMP headers in file '%s'", filename); + return false; + } + + if( infoHeader.biBitCount != 4 && infoHeader.biBitCount != 8 ) + { + fclose(f); + DebugMessage(M64MSG_ERROR, "Unsupported BMP file format: %s", filename); + *pbuf = NULL; + return false; + } + + int tablesize = infoHeader.biBitCount == 4 ? 16 : 256; + uint32 *pTable = new uint32[tablesize]; + if (fread(pTable, tablesize*4, 1, f) != 1) + { + DebugMessage(M64MSG_ERROR, "Couldn't read BMP palette in file '%s'", filename); + delete [] pTable; + return false; + } + + // Create the pallette table + uint16 * pPal = (uint16 *)entry.ti.PalAddress; + if( entry.ti.Size == TXT_SIZE_4b ) + { + // 4-bit table + for( int i=0; i<16; i++ ) + { + pTable[i] = entry.ti.TLutFmt == TLUT_FMT_RGBA16 ? Convert555ToRGBA(pPal[i^1]) : ConvertIA16ToRGBA(pPal[i^1]); + } + } + else + { + // 8-bit table + for( int i=0; i<256; i++ ) + { + pTable[i] = entry.ti.TLutFmt == TLUT_FMT_RGBA16 ? Convert555ToRGBA(pPal[i^1]) : ConvertIA16ToRGBA(pPal[i^1]); + } + } + + *pbuf = new unsigned char[infoHeader.biWidth*infoHeader.biHeight*4]; + if( *pbuf ) + { + unsigned char *colorIdxBuf = new unsigned char[infoHeader.biSizeImage]; + if( colorIdxBuf ) + { + if (fread(colorIdxBuf, infoHeader.biSizeImage, 1, f) != 1) + { + DebugMessage(M64MSG_ERROR, "Couldn't read BMP image data in file '%s'", filename); + } + + width = infoHeader.biWidth; + height = infoHeader.biHeight; + + // Converting pallette texture to RGBA texture + int idx = 0; + uint32 *pbuf2 = (uint32*) *pbuf; + + for( int i=height-1; i>=0; i--) + { + for( int j=0; j>1]&0xF]; + } + else + { + // 0 + *pbuf2++ = pTable[(colorIdxBuf[(idx++)>>1]>>4)&0xF]; + } + } + else + { + // 8 bits + *pbuf2++ = pTable[colorIdxBuf[idx++]]; + } + } + if( entry.ti.Size == TXT_SIZE_4b ) + { + if( idx%8 ) idx = (idx/8+1)*8; + } + else + { + if( idx%4 ) idx = (idx/4+1)*4; + } + } + + delete [] colorIdxBuf; + } + else + { + TRACE0("Out of memory"); + } + + delete [] pTable; + return true; + } + else + { + fclose(f); + delete [] pTable; + return false; + } + } + else + { + // Do something + TRACE1("Fail to open file %s", filename); + *pbuf = NULL; + return false; + } +} + +bool LoadRGBBufferFromBMPFile(char *filename, unsigned char **pbuf, int &width, int &height) +{ + BITMAPFILEHEADER fileHeader; + BITMAPINFOHEADER infoHeader; + + FILE *f; + f = fopen(filename, "rb"); + if(f != NULL) + { + if (fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f) != 1 || + fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f) != 1) + { + DebugMessage(M64MSG_ERROR, "Couldn't read BMP headers in file '%s'", filename); + return false; + } + + if( infoHeader.biBitCount != 24 ) + { + fclose(f); + DebugMessage(M64MSG_ERROR, "Unsupported BMP file 16 bits format: %s", filename); + *pbuf = NULL; + return false; + } + + *pbuf = new unsigned char[infoHeader.biWidth*infoHeader.biHeight*3]; + if( *pbuf ) + { + if (fread(*pbuf, infoHeader.biWidth*infoHeader.biHeight*3, 1, f) != 1) + DebugMessage(M64MSG_ERROR, "Couldn't read RGB BMP image data in file '%s'", filename); + fclose(f); + width = infoHeader.biWidth; + height = infoHeader.biHeight; + return true; + } + else + { + fclose(f); + return false; + } + } + else + { + // Do something + DebugMessage(M64MSG_WARNING, "Fail to open file %s", filename); + *pbuf = NULL; + return false; + } +} + +/******************************************************* + * Loads the hires equivaltent of a texture + * parameter: + * TxtrCacheEntry: The original texture in the texture cache + * return: + * none + *******************************************************/ +void LoadHiresTexture( TxtrCacheEntry &entry ) +{ + // check if the external texture has already been loaded + if( entry.bExternalTxtrChecked ) + return; + // there is already an enhanced texture (e.g. a filtered one) + + if( entry.pEnhancedTexture ) + { + // delete it from memory before loading the external one + SAFE_DELETE(entry.pEnhancedTexture); + } + + int ciidx, scaleShift; + // search the index of the appropriate hires replacement texture + // in the list containing the infos of the external textures + // ciidx is not needed here (just needed for dumping) + int idx = CheckTextureInfos(gHiresTxtrInfos,entry,ciidx,scaleShift,false); + if( idx < 0 ) + { + // there is no hires replacement => indicate that + entry.bExternalTxtrChecked = true; + return; + } + + // Load the bitmap file + char filename_rgb[PATH_MAX]; + char filename_a[PATH_MAX]; + + strcpy(filename_rgb, gHiresTxtrInfos[idx].foldername); + strcat(filename_rgb, gHiresTxtrInfos[idx].filename); + + if (gHiresTxtrInfos[idx].filename_a) { + strcpy(filename_a, gHiresTxtrInfos[idx].foldername); + strcat(filename_a, gHiresTxtrInfos[idx].filename_a); + } else { + strcpy(filename_a, ""); + } + + // Load BMP image to buffer_rbg + unsigned char *buf_rgba = NULL; + unsigned char *buf_a = NULL; + int width, height; + + bool bResRGBA=false, bResA=false; + bool bCI = ((gRDP.otherMode.text_tlut>=2 || entry.ti.Format == TXT_FMT_CI || entry.ti.Format == TXT_FMT_RGBA) && entry.ti.Size <= TXT_SIZE_8b ); + + switch( gHiresTxtrInfos[idx].type ) + { + case RGB_PNG: + if( bCI ) + return; + else + { + bResRGBA = LoadRGBBufferFromPNGFile(filename_rgb, &buf_rgba, width, height); + if( bResRGBA && gHiresTxtrInfos[idx].bSeparatedAlpha ) + bResA = LoadRGBBufferFromPNGFile(filename_a, &buf_a, width, height); + } + break; + case COLOR_INDEXED_BMP: + if( bCI ) + bResRGBA = LoadRGBABufferFromColorIndexedFile(filename_rgb, entry, &buf_rgba, width, height); + else + return; + break; + case RGBA_PNG_FOR_CI: + case RGBA_PNG_FOR_ALL_CI: + if( bCI ) + bResRGBA = LoadRGBBufferFromPNGFile(filename_rgb, &buf_rgba, width, height, 32); + else + return; + break; + case RGB_WITH_ALPHA_TOGETHER_PNG: + if( bCI ) + return; + else + bResRGBA = LoadRGBBufferFromPNGFile(filename_rgb, &buf_rgba, width, height, 32); + break; + default: + return; + } + + if( !bResRGBA || !buf_rgba ) + { + DebugMessage(M64MSG_ERROR, "RGBBuffer creation failed for file '%s'.", filename_rgb); + return; + } + // check if the alpha channel has been loaded if the texture has a separate alpha channel + else if( gHiresTxtrInfos[idx].bSeparatedAlpha && !bResA ) + { + DebugMessage(M64MSG_ERROR, "Alpha buffer creation failed for file '%s'.", filename_a); + delete [] buf_rgba; + return; + } + + // calculate the texture size magnification by comparing the N64 texture size and the hi-res texture size + int scale = 1 << scaleShift; + int mirrorx = 1; + int mirrory = 1; + int input_height_shift = height - entry.ti.HeightToLoad * scale; + int input_pitch_a = width; + int input_pitch_rgb = width; + width = entry.ti.WidthToLoad * scale; + height = entry.ti.HeightToLoad * scale; + if (entry.ti.WidthToCreate/entry.ti.WidthToLoad == 2) mirrorx = 2; + if (entry.ti.HeightToCreate/entry.ti.HeightToLoad == 2) mirrory = 2; + entry.pEnhancedTexture = CDeviceBuilder::GetBuilder()->CreateTexture(entry.ti.WidthToCreate*scale, entry.ti.HeightToCreate*scale); + DrawInfo info; + + if( entry.pEnhancedTexture && entry.pEnhancedTexture->StartUpdate(&info) ) + { + + if( gHiresTxtrInfos[idx].type == RGB_PNG ) + { + input_pitch_rgb *= 3; + input_pitch_a *= 3; + + if (info.lPitch < width * 4) + DebugMessage(M64MSG_ERROR, "Texture pitch %i less than width %i times 4", info.lPitch, width); + if (height > info.dwHeight) + DebugMessage(M64MSG_ERROR, "Texture source height %i greater than destination height %i", height, info.dwHeight); + + // Update the texture by using the buffer + for( int i=0; i=0; i--) + { + uint32 *pRGB = (uint32*)(buf_rgba + (input_height_shift + i) * input_pitch_rgb); + uint32 *pdst = (uint32*)((unsigned char*)info.lpSurface + (height - i - 1)*info.lPitch); + for( int j=0; jm_dwCreatedTextureWidth, height, T_FLAG, 4 ); + } + + if( entry.ti.WidthToCreate*scale < entry.pEnhancedTexture->m_dwCreatedTextureWidth ) + { + // Clamp + gTextureManager.Clamp(info.lpSurface, width, entry.pEnhancedTexture->m_dwCreatedTextureWidth, entry.pEnhancedTexture->m_dwCreatedTextureWidth, height, S_FLAG, 4 ); + } + if( entry.ti.HeightToCreate*scale < entry.pEnhancedTexture->m_dwCreatedTextureHeight ) + { + // Clamp + gTextureManager.Clamp(info.lpSurface, height, entry.pEnhancedTexture->m_dwCreatedTextureHeight, entry.pEnhancedTexture->m_dwCreatedTextureWidth, height, T_FLAG, 4 ); + } + + entry.pEnhancedTexture->EndUpdate(&info); + entry.pEnhancedTexture->SetOthersVariables(); + entry.pEnhancedTexture->m_bIsEnhancedTexture = true; + entry.dwEnhancementFlag = TEXTURE_EXTERNAL; + + DebugMessage(M64MSG_VERBOSE, "Loaded hi-res texture: %s", filename_rgb); + } + else + { + DebugMessage(M64MSG_ERROR, "New texture creation failed."); + TRACE0("Cannot create a new texture"); + } + + if( buf_rgba ) + { + delete [] buf_rgba; + } + + if( buf_a ) + { + delete [] buf_a; + } +} +