// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdmisc.h" #include "nel/misc/bitmap.h" #include "nel/misc/stream.h" #include "nel/misc/file.h" #include "nel/misc/system_info.h" // Define this to force all bitmap white (debug) // #define NEL_ALL_BITMAP_WHITE using namespace std; #ifdef DEBUG_NEW #define new DEBUG_NEW #endif namespace NLMISC { struct EDDSBadHeader : public NLMISC::EStream { EDDSBadHeader() : EStream( "Bad or unrecognized DDS file header" ) {} }; struct ESeekFailed : public NLMISC::EStream { ESeekFailed() : EStream( "Seek failed" ) {} }; struct EAllocationFailure : public Exception { EAllocationFailure() : Exception( "Can't allocate memory" ) {} }; void blendFromui(NLMISC::CRGBA &c0, NLMISC::CRGBA &c1, uint coef); uint32 blend(uint32 &n0, uint32 &n1, uint32 coef0); const uint32 CBitmap::bitPerPixels[ModeCount]= { 32, // RGBA 8, // Luminance 8, // Alpha 16, // AlphaLuminance 4, // DXTC1 4, // DXTC1Alpha 8, // DXTC3 8, // DXTC5 16 // DsDt }; const uint32 CBitmap::DXTC1HEADER = NL_MAKEFOURCC('D', 'X', 'T', '1'); const uint32 CBitmap::DXTC3HEADER = NL_MAKEFOURCC('D', 'X', 'T', '3'); const uint32 CBitmap::DXTC5HEADER = NL_MAKEFOURCC('D', 'X', 'T', '5'); #ifdef NEL_ALL_BITMAP_WHITE // Make all the textures white void MakeWhite(CBitmap &bitmaps) { for (uint i=0; i= endData) { // return the uniform value if (alpha) *alpha = value; return true; } return false; } /*-------------------------------------------------------------------*\ isGrayscale \*-------------------------------------------------------------------*/ bool CBitmap::isGrayscale() const { // all grayscale formats or, al least, without color information switch(PixelFormat) { case Luminance: case AlphaLuminance: case Alpha: return true; case RGBA: break; default: // DXTC formats won't be managed at the moment return false; } uint32 size = _Data[0].size(); if (size == 0) return false; // get a pointer on original data uint32 *data = (uint32*)_Data[0].getPtr(); uint32 *endData = (uint32*)((uint8*)data + size); NLMISC::CRGBA *color = NULL; // check if all alphas have the same value while(data < endData) { color = (NLMISC::CRGBA*)data; if (!color->isGray()) return false; ++data; } return true; } /*-------------------------------------------------------------------*\ readDDS \*-------------------------------------------------------------------*/ uint8 CBitmap::readDDS(NLMISC::IStream &f, uint mipMapSkip) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ //------------------ Reading Header ------------------------ //-------------- reading entire header uint32 size = 0; f.serial(size); // size in Bytes of header(without "DDS") uint32 * _DDSSurfaceDesc = new uint32[size]; _DDSSurfaceDesc[0]= size; #ifdef NL_LITTLE_ENDIAN f.serialBuffer((uint8*)(_DDSSurfaceDesc+1), size-4); #else for(uint i= 0; i0) //AlphaBitDepth { PixelFormat = DXTC1Alpha; } //#endif if(PixelFormat!= DXTC1 && PixelFormat!= DXTC1Alpha && PixelFormat!= DXTC3 && PixelFormat!= DXTC5) { delete [] _DDSSurfaceDesc; throw EDDSBadHeader(); } // compute the min power of 2 between width and height uint minSizeLevel= min(_Width, _Height); minSizeLevel= getPowerOf2(minSizeLevel); //------------- manage mipMapSkip if(_MipMapCount>1 && mipMapSkip>0 && minSizeLevel>2) { // Keep at least the level where width and height are at least 4. mipMapSkip= min(mipMapSkip, minSizeLevel-2); // skip any mipmap uint seekSize= 0; while(mipMapSkip>0 && _MipMapCount>1) { // raise to next multiple of 4 uint32 wtmp= (_Width+3)&(~3); uint32 htmp= (_Height+3)&(~3); wtmp= max(wtmp, uint32(4)); htmp= max(htmp, uint32(4)); uint32 mipMapSz; if(PixelFormat==DXTC1 || PixelFormat==DXTC1Alpha) mipMapSz = wtmp*htmp/2; else mipMapSz = wtmp*htmp; // add to how many to skip seekSize+= mipMapSz; // Size of final bitmap is reduced. _Width>>=1; _Height>>=1; _MipMapCount--; mipMapSkip--; } // skip data in file if(seekSize>0) { if(!f.seek(seekSize, IStream::current)) { delete [] _DDSSurfaceDesc; throw ESeekFailed(); } } } //------------- preload all the mipmaps (one serialBuffer() is faster) uint32 w = _Width; uint32 h = _Height; uint32 totalSize= 0; uint8 m; for(m= 0; m<_MipMapCount; m++) { // raise to next multiple of 4 uint32 wtmp= (w+3)&(~3); uint32 htmp= (h+3)&(~3); wtmp= max(wtmp, uint32(4)); htmp= max(htmp, uint32(4)); uint32 mipMapSz; if(PixelFormat==DXTC1 || PixelFormat==DXTC1Alpha) mipMapSz = wtmp*htmp/2; else mipMapSz = wtmp*htmp; _Data[m].resize(mipMapSz); totalSize+= mipMapSz; w = (w+1)/2; h = (h+1)/2; } // Read all the data in one block. vector pixData; pixData.resize(totalSize); f.serialBuffer(&(*pixData.begin()), totalSize); //------------- reading mipmap levels from pixData uint32 pixIndex= 0; for(m= 0; m<_MipMapCount; m++) { uint32 mipMapSz= _Data[m].size(); memcpy(_Data[m].getPtr(), &(pixData[pixIndex]), mipMapSz); pixIndex+= mipMapSz; } //------------- End delete [] _DDSSurfaceDesc; switch(PixelFormat) { case DXTC1 : return 24; case DXTC1Alpha : return 32; case DXTC3 : return 32; case DXTC5 : return 32; default : break; } return 0; } /*-------------------------------------------------------------------*\ convertToDXTC5 \*-------------------------------------------------------------------*/ bool CBitmap::convertToDXTC5() { /* Yoyo: RGB encoding for DXTC1 and DXTC5/3 are actually different!! DXTC3/5 don't rely on sign of color0>color1 to setup special encoding (ie use a special compression for Black) Since this can arise if the src is DXTC1 , we can't simply compress it into DXTC5 without doing a heavy compression... (the inverse is false: DXTC5 to DXTC1 is possible, with maybe swap color0/color1 and bits). */ return false; /* uint32 i,j; if(PixelFormat!=DXTC1) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(2*_Data[m].size()); uint dstId= 0; for(i=0; i<_Data[m].size(); i+=8) { //64 bits alpha for(j=0; j<8; j++) { dataTmp[dstId++]= 255; } //64 bits RGB for(j=0; j<8; j++) { dataTmp[dstId++]= _Data[m][i+j]; } } _Data[m] = dataTmp; } PixelFormat = DXTC5; return true; */ } /*-------------------------------------------------------------------*\ luminanceToRGBA() \*-------------------------------------------------------------------*/ bool CBitmap::luminanceToRGBA() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()*4); uint dstId= 0; for(i=0; i<_Data[m].size(); i++) { dataTmp[dstId++]= _Data[m][i]; dataTmp[dstId++]= _Data[m][i]; dataTmp[dstId++]= _Data[m][i]; dataTmp[dstId++]= 255; } _Data[m] = dataTmp; } PixelFormat = RGBA; return true; } /*-------------------------------------------------------------------*\ alphaToRGBA() \*-------------------------------------------------------------------*/ bool CBitmap::alphaToRGBA() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()*4); uint dstId= 0; for(i=0; i<_Data[m].size(); i++) { dataTmp[dstId++]= 255; dataTmp[dstId++]= 255; dataTmp[dstId++]= 255; dataTmp[dstId++]= _Data[m][i]; } _Data[m] = dataTmp; } PixelFormat = RGBA; return true; } /*-------------------------------------------------------------------*\ alphaLuminanceToRGBA() \*-------------------------------------------------------------------*/ bool CBitmap::alphaLuminanceToRGBA() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()*2); uint dstId= 0; for(i=0; i<_Data[m].size(); i+=2) { dataTmp[dstId++]= _Data[m][i]; dataTmp[dstId++]= _Data[m][i]; dataTmp[dstId++]= _Data[m][i]; dataTmp[dstId++]= _Data[m][i+1]; } _Data[m] = dataTmp; } PixelFormat = RGBA; return true; } /*-------------------------------------------------------------------*\ rgbaToAlphaLuminance \*-------------------------------------------------------------------*/ bool CBitmap::rgbaToAlphaLuminance() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()/2); uint dstId= 0; for(i=0; i<_Data[m].size(); i+=4) { dataTmp[dstId++]= (_Data[m][i]*77 + _Data[m][i+1]*150 + _Data[m][i+2]*28)/255; dataTmp[dstId++]= _Data[m][i+3]; } NLMISC::contReset(_Data[m]); _Data[m].resize(0); _Data[m] = dataTmp; } PixelFormat = AlphaLuminance; return true; } /*-------------------------------------------------------------------*\ luminanceToAlphaLuminance \*-------------------------------------------------------------------*/ bool CBitmap::luminanceToAlphaLuminance() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()*2); uint dstId= 0; for(i=0; i<_Data[m].size(); i++) { dataTmp[dstId++]= _Data[m][i]; dataTmp[dstId++]= 255; } _Data[m] = dataTmp; } PixelFormat = AlphaLuminance; return true; } /*-------------------------------------------------------------------*\ alphaToAlphaLuminance \*-------------------------------------------------------------------*/ bool CBitmap::alphaToAlphaLuminance() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()*2); uint dstId= 0; for(i=0; i<_Data[m].size(); i++) { dataTmp[dstId++]= 0; dataTmp[dstId++]= _Data[m][i]; } _Data[m] = dataTmp; } PixelFormat = AlphaLuminance; return true; } /*-------------------------------------------------------------------*\ rgbaToLuminance \*-------------------------------------------------------------------*/ bool CBitmap::rgbaToLuminance() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()/4); uint dstId= 0; for(i=0; i<_Data[m].size(); i+=4) { dataTmp[dstId++]= (_Data[m][i]*77 + _Data[m][i+1]*150 + _Data[m][i+2]*28)/255; } NLMISC::contReset(_Data[m]); _Data[m].resize(0); _Data[m] = dataTmp; } PixelFormat = Luminance; return true; } /*-------------------------------------------------------------------*\ alphaToLuminance \*-------------------------------------------------------------------*/ bool CBitmap::alphaToLuminance() { if(_Width*_Height == 0) return false; PixelFormat = Luminance; return true; } /*-------------------------------------------------------------------*\ alphaLuminanceToLuminance \*-------------------------------------------------------------------*/ bool CBitmap::alphaLuminanceToLuminance() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()/2); uint dstId= 0; for(i=0; i<_Data[m].size(); i+=2) { dataTmp[dstId++]= 0; dataTmp[dstId++]= 0; dataTmp[dstId++]= 0; dataTmp[dstId++]= _Data[m][i]; } NLMISC::contReset(_Data[m]); _Data[m].resize(0); _Data[m] = dataTmp; } PixelFormat = Luminance; return true; } /*-------------------------------------------------------------------*\ rgbaToAlpha \*-------------------------------------------------------------------*/ bool CBitmap::rgbaToAlpha() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()/4); uint dstId= 0; for(i=0; i<_Data[m].size(); i+=4) { dataTmp[dstId++]= _Data[m][i+3]; } NLMISC::contReset(_Data[m]); _Data[m].resize(0); _Data[m] = dataTmp; } PixelFormat = Alpha; return true; } /*-------------------------------------------------------------------*\ luminanceToAlpha \*-------------------------------------------------------------------*/ bool CBitmap::luminanceToAlpha() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()); uint dstId= 0; for(i=0; i<_Data[m].size(); i++) { dataTmp[dstId++]= _Data[m][i]; } _Data[m] = dataTmp; } PixelFormat = Alpha; return true; } /*-------------------------------------------------------------------*\ alphaLuminanceToAlpha \*-------------------------------------------------------------------*/ bool CBitmap::alphaLuminanceToAlpha() { uint32 i; if(_Width*_Height == 0) return false; for(uint8 m= 0; m<_MipMapCount; m++) { CObjectVector dataTmp; dataTmp.resize(_Data[m].size()/2); uint dstId= 0; for(i=0; i<_Data[m].size(); i+=2) { dataTmp[dstId++]= _Data[m][i+1]; } NLMISC::contReset(_Data[m]); _Data[m].resize(0); _Data[m] = dataTmp; } PixelFormat = Alpha; return true; } /*-------------------------------------------------------------------*\ convertToLuminance \*-------------------------------------------------------------------*/ bool CBitmap::convertToLuminance() { switch(PixelFormat) { case RGBA : return rgbaToLuminance(); break; case Luminance : return true; break; case Alpha : return alphaToLuminance(); break; case AlphaLuminance : return alphaLuminanceToLuminance(); break; default: break; } return false; } /*-------------------------------------------------------------------*\ convertToAlpha \*-------------------------------------------------------------------*/ bool CBitmap::convertToAlpha() { switch(PixelFormat) { case RGBA : return rgbaToAlpha(); break; case Luminance : return luminanceToAlpha(); break; case Alpha : return true; break; case AlphaLuminance : return alphaLuminanceToAlpha(); break; default: break; } return false; } /*-------------------------------------------------------------------*\ convertToAlphaLuminance \*-------------------------------------------------------------------*/ bool CBitmap::convertToAlphaLuminance() { switch(PixelFormat) { case RGBA : return rgbaToAlphaLuminance(); break; case Luminance : return luminanceToAlphaLuminance(); break; case Alpha : return alphaToAlphaLuminance(); break; case AlphaLuminance : return true; break; default: break; } return false; } /*-------------------------------------------------------------------*\ convertToRGBA \*-------------------------------------------------------------------*/ bool CBitmap::convertToRGBA() { switch(PixelFormat) { case DXTC1 : return decompressDXT1(false); break; case DXTC1Alpha : return decompressDXT1(true); break; case DXTC3 : return decompressDXT3(); break; case DXTC5 : return decompressDXT5(); break; case Luminance : return luminanceToRGBA(); break; case Alpha : return alphaToRGBA(); break; case AlphaLuminance : return alphaLuminanceToRGBA(); break; case RGBA: return true; break; default: break; } return false; } /*-------------------------------------------------------------------*\ convertToType \*-------------------------------------------------------------------*/ bool CBitmap::convertToType(CBitmap::TType type) { if(PixelFormat==type) return true; switch(type) { case RGBA : return convertToRGBA(); break; case DXTC5 : return convertToDXTC5(); break; case Luminance : return convertToLuminance(); break; case Alpha : return convertToAlpha(); break; case AlphaLuminance : return convertToAlphaLuminance(); break; default: break; } return false; } /*-------------------------------------------------------------------*\ decompressDXT1 \*-------------------------------------------------------------------*/ bool CBitmap::decompressDXT1(bool alpha) { uint32 i,j,k; NLMISC::CRGBA c[4]; CObjectVector dataTmp[MAX_MIPMAP]; uint32 width= _Width; uint32 height= _Height; for(uint8 m= 0; m<_MipMapCount; m++) { uint32 wtmp, htmp; if(width<4) wtmp = 4; else wtmp = width; if(height < 4) htmp = 4; else htmp = height; uint32 mipMapSz = wtmp*htmp*4; dataTmp[m].resize(mipMapSz); if(dataTmp[m].size()color1) { c[2].blendFromui(c[0],c[1],85); if(alpha) c[2].A= 255; c[3].blendFromui(c[0],c[1],171); if(alpha) c[3].A= 255; } else { c[2].blendFromui(c[0],c[1],128); if(alpha) c[2].A= 255; c[3].set(0,0,0,0); } // computing the 16 RGBA of the block uint32 blockNum= i/8; //(64 bits) // * 4 (rows) * _Width (columns) + 4pix*4rgba* uint32 pixelsCount= 4*(blockNum/wBlockCount)*wtmp*4 + 4*4*(blockNum%wBlockCount); for(j=0; j<4; j++) { for(k=0; k<4; k++) { dataTmp[m][pixelsCount + j*wtmp*4 + 4*k]= c[bits&3].R; dataTmp[m][pixelsCount + j*wtmp*4 + 4*k+1]= c[bits&3].G; dataTmp[m][pixelsCount + j*wtmp*4 + 4*k+2]= c[bits&3].B; dataTmp[m][pixelsCount + j*wtmp*4 + 4*k+3]= c[bits&3].A; bits>>=2; } } } // Copy result into the mipmap level. if(wtmp==width && htmp==height) { // For mipmaps level >4 pixels. _Data[m]= dataTmp[m]; } else { // For last mipmaps, level <4 pixels. _Data[m].resize(width*height*4); CRGBA *src= (CRGBA*)&dataTmp[m][0]; CRGBA *dst= (CRGBA*)&_Data[m][0]; uint x,y; for(y=0;y dataTmp[MAX_MIPMAP]; uint32 width= _Width; uint32 height= _Height; for(uint8 m= 0; m<_MipMapCount; m++) { uint32 wtmp, htmp; if(width<4) wtmp = 4; else wtmp = width; if(height < 4) htmp = 4; else htmp = height; uint32 mipMapSz = wtmp*htmp*4; dataTmp[m].resize(mipMapSz); if(dataTmp[m].size()>=4; } uint16 color0; uint16 color1; uint32 bits; memcpy(&color0,&_Data[m][i+8],2); memcpy(&color1,&_Data[m][i+10],2); memcpy(&bits,&_Data[m][i+12],4); uncompress(color0,c[0]); uncompress(color1,c[1]); // ignore color0>color1 for DXT3 and DXT5. c[2].blendFromui(c[0],c[1],85); c[3].blendFromui(c[0],c[1],171); // computing the 16 RGBA of the block uint32 blockNum= i/16; //(128 bits) // * 4 (rows) * wtmp (columns) + 4pix*4rgba* uint32 pixelsCount= 4*(blockNum/wBlockCount)*wtmp*4 + 4*4*(blockNum%wBlockCount); for(j=0; j<4; j++) { for(k=0; k<4; k++) { dataTmp[m][pixelsCount + j*wtmp*4 + 4*k]= c[bits&3].R; dataTmp[m][pixelsCount + j*wtmp*4 + 4*k+1]= c[bits&3].G; dataTmp[m][pixelsCount + j*wtmp*4 + 4*k+2]= c[bits&3].B; dataTmp[m][pixelsCount + j*wtmp*4 + 4*k+3]= alpha[4*j+k]; bits>>=2; } } } // Copy result into the mipmap level. if(wtmp==width && htmp==height) { // For mipmaps level >4 pixels. _Data[m]= dataTmp[m]; } else { // For last mipmaps, level <4 pixels. _Data[m].resize(width*height*4); CRGBA *src= (CRGBA*)&dataTmp[m][0]; CRGBA *dst= (CRGBA*)&_Data[m][0]; uint x,y; for(y=0;y dataTmp[MAX_MIPMAP]; uint32 width= _Width; uint32 height= _Height; for(uint8 m= 0; m<_MipMapCount; m++) { uint32 wtmp, htmp; if(width<4) wtmp = 4; else wtmp = width; if(height < 4) htmp = 4; else htmp = height; uint32 mipMapSz = wtmp*htmp*4; dataTmp[m].resize(mipMapSz); if(dataTmp[m].size()>= 16; uint32 alpha[8]; alpha[0]= _Data[m][i+0]; alpha[1]= _Data[m][i+1]; if(alpha[0]>alpha[1]) { alpha[2]= blend(alpha[0], alpha[1], 219); alpha[3]= blend(alpha[0], alpha[1], 183); alpha[4]= blend(alpha[0], alpha[1], 146); alpha[5]= blend(alpha[0], alpha[1], 110); alpha[6]= blend(alpha[0], alpha[1], 73); alpha[7]= blend(alpha[0], alpha[1], 37); } else { alpha[2]= blend(alpha[0], alpha[1], 204); alpha[3]= blend(alpha[0], alpha[1], 154); alpha[4]= blend(alpha[0], alpha[1], 102); alpha[5]= blend(alpha[0], alpha[1], 51); alpha[6]= 0; alpha[7]= 255; } uint8 codeAlpha[16]; for(j=0; j<16; j++) { codeAlpha[j] = (uint8)(bitsAlpha & 7); bitsAlpha>>=3; } uint16 color0; uint16 color1; uint32 bits; memcpy(&color0,&_Data[m][i+8],2); memcpy(&color1,&_Data[m][i+10],2); memcpy(&bits,&_Data[m][i+12],4); uncompress(color0,c[0]); uncompress(color1,c[1]); // ignore color0>color1 for DXT3 and DXT5. c[2].blendFromui(c[0],c[1],85); c[3].blendFromui(c[0],c[1],171); // computing the 16 RGBA of the block uint32 blockNum= i/16; //(128 bits) // * 4 (rows) * wtmp (columns) + 4pix* uint32 pixelsCount= (blockNum/wBlockCount)*wtmp*4 + 4*(blockNum%wBlockCount); // *sizeof(RGBA) pixelsCount*=4; for(j=0; j<4; j++) { for(k=0; k<4; k++) { dataTmp[m][pixelsCount + (j*wtmp+k)*4 +0]= c[bits&3].R; dataTmp[m][pixelsCount + (j*wtmp+k)*4 +1]= c[bits&3].G; dataTmp[m][pixelsCount + (j*wtmp+k)*4 +2]= c[bits&3].B; dataTmp[m][pixelsCount + (j*wtmp+k)*4 +3]= (uint8) alpha[codeAlpha[4*j+k]]; bits>>=2; } } } // Copy result into the mipmap level. if(wtmp==width && htmp==height) { // For mipmaps level >4 pixels. _Data[m]= dataTmp[m]; } else { // For last mipmaps, level <4 pixels. _Data[m].resize(width*height*4); CRGBA *src= (CRGBA*)&dataTmp[m][0]; CRGBA *dst= (CRGBA*)&_Data[m][0]; uint x,y; for(y=0;y>8); } /*-------------------------------------------------------------------*\ uncompress \*-------------------------------------------------------------------*/ inline void CBitmap::uncompress(uint16 color, NLMISC::CRGBA &r) { r.A= 0; r.R= ((color>>11)&31) << 3; r.R+= r.R>>5; r.G= ((color>>5)&63) << 2; r.G+= r.G>>6; r.B= ((color)&31) << 3; r.B+= r.B>>5; } /*-------------------------------------------------------------------*\ getWidth \*-------------------------------------------------------------------*/ uint32 CBitmap::getWidth(uint32 mipMap) const { if(mipMap==0) return _Width; uint32 w = _Width; uint32 h = _Height; uint32 m = 0; do { m++; w = (w+1)/2; h = (h+1)/2; if(m==mipMap) return w; } while(w!=1 || h!=1); return 0; } /*-------------------------------------------------------------------*\ getHeight \*-------------------------------------------------------------------*/ uint32 CBitmap::getHeight(uint32 mipMap) const { if(mipMap==0) return _Height; uint32 w = _Width; uint32 h = _Height; uint32 m = 0; do { m++; w = (w+1)/2; h = (h+1)/2; if(m==mipMap) return h; } while(w!=1 || h!=1); return 0; } /*-------------------------------------------------------------------*\ getSize \*-------------------------------------------------------------------*/ uint32 CBitmap::getSize(uint32 numMipMap) const { return getHeight(numMipMap)*getWidth(numMipMap); } /*-------------------------------------------------------------------*\ buildMipMaps \*-------------------------------------------------------------------*/ void CBitmap::buildMipMaps() { uint32 i,j; if(PixelFormat!=RGBA) return; if(_MipMapCount!=1) return; if(!NLMISC::isPowerOf2(_Width)) return; if(!NLMISC::isPowerOf2(_Height)) return; uint32 w = _Width; uint32 h = _Height; while(w>1 || h>1) { uint32 precw = w; uint32 prech = h; w = (w+1)/2; h = (h+1)/2; uint32 mulw= precw/w; uint32 mulh= prech/h; _Data[_MipMapCount].resize(w*h*4); NLMISC::CRGBA *pRgba = (NLMISC::CRGBA*)&_Data[_MipMapCount][0]; NLMISC::CRGBA *pRgbaPrev = (NLMISC::CRGBA*)&_Data[_MipMapCount-1][0]; for(i=0; i1 || h>1) { w = (w+1)/2; h = (h+1)/2; ++mipMapCount; } return mipMapCount; } /*-------------------------------------------------------------------*\ releaseMipMaps \*-------------------------------------------------------------------*/ void CBitmap::releaseMipMaps() { if(_MipMapCount<=1) return; _MipMapCount=1; for(sint i=1;i1) needRebuild = true; releaseMipMaps(); //logResample("Resample: 20"); if(nNewWidth==0 || nNewHeight==0) { _Width = _Height = 0; //logResample("Resample: 25"); return; } //logResample("Resample: 30"); CObjectVector pDestui; if (PixelFormat == RGBA) { pDestui.resize(nNewWidth*nNewHeight*4); //logResample("Resample: 40"); NLMISC::CRGBA *pDestRgba = (NLMISC::CRGBA*)&pDestui[0]; //logResample("Resample: 50"); resamplePicture32 ((NLMISC::CRGBA*)&_Data[0][0], pDestRgba, _Width, _Height, nNewWidth, nNewHeight); //logResample("Resample: 60"); } else if (PixelFormat == Luminance) { pDestui.resize(nNewWidth*nNewHeight); //logResample("Resample: 40"); uint8 *pDestGray = &pDestui[0]; //logResample("Resample: 50"); resamplePicture8 (&_Data[0][0], pDestGray, _Width, _Height, nNewWidth, nNewHeight); //logResample("Resample: 60"); } NLMISC::contReset(_Data[0]); // free memory //logResample("Resample: 70"); _Data[0] = pDestui; //logResample("Resample: 80"); _Width= nNewWidth; _Height= nNewHeight; // Rebuilding mipmaps //logResample("Resample: 90"); if(needRebuild) { buildMipMaps(); //logResample("Resample: 95"); } //logResample("Resample: 100"); } /*-------------------------------------------------------------------*\ resize \*-------------------------------------------------------------------*/ void CBitmap::resize (sint32 nNewWidth, sint32 nNewHeight, TType newType, bool resetTo0) { // Deleting mipmaps releaseMipMaps(); // Change type of bitmap ? if (newType!=DonTKnow) PixelFormat=newType; _Width = nNewWidth; _Height = nNewHeight; // resize the level 0 only. resizeMipMap(0, nNewWidth, nNewHeight, resetTo0); } /*-------------------------------------------------------------------*\ resizeMipMap \*-------------------------------------------------------------------*/ void CBitmap::resizeMipMap (uint32 numMipMap, sint32 nNewWidth, sint32 nNewHeight, bool resetTo0) { nlassert(numMipMap=nSrcWidth); bool bYMag=(nDestHeight>=nSrcHeight); bool bXEq=(nDestWidth==nSrcWidth); bool bYEq=(nDestHeight==nSrcHeight); std::vector pIterm (nDestWidth*nSrcHeight); if (bXMag) { float fXdelta=(float)(nSrcWidth)/(float)(nDestWidth); NLMISC::CRGBAF *pItermPtr=&*pIterm.begin(); sint32 nY; for (nY=0; nY=0.f); NLMISC::CRGBAF vColor; if (fVirgule>=0.5f) { if (fX<(float)(nSrcWidth-1)) { NLMISC::CRGBAF vColor1 (pSrcLine[(sint32)floor(fX)]); NLMISC::CRGBAF vColor2 (pSrcLine[(sint32)floor(fX)+1]); vColor=vColor1*(1.5f-fVirgule)+vColor2*(fVirgule-0.5f); } else vColor=NLMISC::CRGBAF (pSrcLine[(sint32)floor(fX)]); } else { if (fX>=1.f) { NLMISC::CRGBAF vColor1 (pSrcLine[(sint32)floor(fX)]); NLMISC::CRGBAF vColor2 (pSrcLine[(sint32)floor(fX)-1]); vColor=vColor1*(0.5f+fVirgule)+vColor2*(0.5f-fVirgule); } else vColor=NLMISC::CRGBAF (pSrcLine[(sint32)floor(fX)]); } *(pItermPtr++)=vColor; fX+=fXdelta; } pSrc+=nSrcWidth; } } else if (bXEq) { NLMISC::CRGBAF *pItermPtr=&*pIterm.begin(); for (sint32 nY=0; nY1.f); NLMISC::CRGBAF *pItermPtr=&*pIterm.begin(); sint32 nY; for (nY=0; nYfFinal) fNext=fFinal; vColor+=((float)(fNext-fX))*NLMISC::CRGBAF (pSrcLine[(sint32)floor(fX)]); fX=fNext; } fX = fFinal; // ensure fX == fFinal vColor/=(float)fXdelta; *(pItermPtr++)=vColor; } pSrc+=nSrcWidth; } } if (bYMag) { double fYdelta=(double)(nSrcHeight)/(double)(nDestHeight); sint32 nX; for (nX=0; nX=0.f); NLMISC::CRGBAF vColor; if (fVirgule>=0.5f) { if (fY<(double)(nSrcHeight-1)) { NLMISC::CRGBAF vColor1=pIterm[((sint32)floor(fY))*nDestWidth+nX]; NLMISC::CRGBAF vColor2=pIterm[(((sint32)floor(fY))+1)*nDestWidth+nX]; vColor=vColor1*(1.5f-(float)fVirgule)+vColor2*((float)fVirgule-0.5f); } else vColor=pIterm[((sint32)floor(fY))*nDestWidth+nX]; } else { if (fY>=1.f) { NLMISC::CRGBAF vColor1=pIterm[((sint32)floor(fY))*nDestWidth+nX]; NLMISC::CRGBAF vColor2=pIterm[(((sint32)floor(fY))-1)*nDestWidth+nX]; vColor=vColor1*(0.5f+(float)fVirgule)+vColor2*(0.5f-(float)fVirgule); } else vColor=pIterm[((sint32)floor(fY))*nDestWidth+nX]; } pDest[nX+nY*nDestWidth]=vColor; fY+=fYdelta; } } } else if (bYEq) { for (sint32 nX=0; nX1.f); sint32 nX; for (nX=0; nXfFinal) fNext=fFinal; vColor+=((float)(fNext-fY))*pIterm[((sint32)floor(fY))*nDestWidth+nX]; fY=fNext; } vColor/=(float)fYdelta; pDest[nX+nY*nDestWidth]=vColor; } } } } /*-------------------------------------------------------------------*\ resamplePicture32Fast \*-------------------------------------------------------------------*/ void CBitmap::resamplePicture32Fast (const NLMISC::CRGBA *pSrc, NLMISC::CRGBA *pDest, sint32 nSrcWidth, sint32 nSrcHeight, sint32 nDestWidth, sint32 nDestHeight) { // the image is divided by two : 1 pixel in dest = 4 pixels in src // the resulting pixel in dest is an average of the four pixels in src nlassert(nSrcWidth % 2 == 0); nlassert(nSrcHeight % 2 == 0); nlassert(nSrcWidth / 2 == nDestWidth); nlassert(nSrcHeight / 2 == nDestHeight); sint32 x, y, twoX, twoSrcWidthByY; for (y=0 ; y=nSrcWidth); bool bYMag=(nDestHeight>=nSrcHeight); bool bXEq=(nDestWidth==nSrcWidth); bool bYEq=(nDestHeight==nSrcHeight); std::vector pIterm (nDestWidth*nSrcHeight); if (bXMag) { float fXdelta=(float)(nSrcWidth)/(float)(nDestWidth); float *pItermPtr=&*pIterm.begin(); sint32 nY; for (nY=0; nY=0.f); float vColor; if (fVirgule>=0.5f) { if (fX<(float)(nSrcWidth-1)) { float vColor1 (pSrcLine[(sint32)floor(fX)]); float vColor2 (pSrcLine[(sint32)floor(fX)+1]); vColor=vColor1*(1.5f-fVirgule)+vColor2*(fVirgule-0.5f); } else vColor = float(pSrcLine[(sint32)floor(fX)]); } else { if (fX>=1.f) { float vColor1 (pSrcLine[(sint32)floor(fX)]); float vColor2 (pSrcLine[(sint32)floor(fX)-1]); vColor = vColor1*(0.5f+fVirgule)+vColor2*(0.5f-fVirgule); } else vColor = float (pSrcLine[(sint32)floor(fX)]); } *(pItermPtr++)=vColor; fX+=fXdelta; } pSrc+=nSrcWidth; } } else if (bXEq) { float *pItermPtr=&*pIterm.begin(); for (sint32 nY=0; nY1.f); float *pItermPtr=&*pIterm.begin(); sint32 nY; for (nY=0; nYfFinal) fNext=fFinal; vColor+=((float)(fNext-fX))* float (pSrcLine[(sint32)floor(fX)]); fX=fNext; } fX = fFinal; // ensure fX == fFinal vColor/=(float)fXdelta; *(pItermPtr++)=vColor; } pSrc+=nSrcWidth; } } if (bYMag) { double fYdelta=(double)(nSrcHeight)/(double)(nDestHeight); sint32 nX; for (nX=0; nX=0.f); float vColor; if (fVirgule>=0.5f) { if (fY<(double)(nSrcHeight-1)) { float vColor1=pIterm[((sint32)floor(fY))*nDestWidth+nX]; float vColor2=pIterm[(((sint32)floor(fY))+1)*nDestWidth+nX]; vColor=vColor1*(1.5f-(float)fVirgule)+vColor2*((float)fVirgule-0.5f); } else vColor=pIterm[((sint32)floor(fY))*nDestWidth+nX]; } else { if (fY>=1.f) { float vColor1=pIterm[((sint32)floor(fY))*nDestWidth+nX]; float vColor2=pIterm[(((sint32)floor(fY))-1)*nDestWidth+nX]; vColor=vColor1*(0.5f+(float)fVirgule)+vColor2*(0.5f-(float)fVirgule); } else vColor=pIterm[((sint32)floor(fY))*nDestWidth+nX]; } pDest[nX+nY*nDestWidth]=vColor; fY+=fYdelta; } } } else if (bYEq) { for (sint32 nX=0; nX1.f); sint32 nX; for (nX=0; nXfFinal) fNext=fFinal; vColor+=((float)(fNext-fY))*pIterm[((sint32)floor(fY))*nDestWidth+nX]; fY=fNext; } vColor/=(float)fYdelta; pDest[nX+nY*nDestWidth]=vColor; } } } } /*-------------------------------------------------------------------*\ resamplePicture8Fast \*-------------------------------------------------------------------*/ void CBitmap::resamplePicture8Fast (const uint8 *pSrc, uint8 *pDest, sint32 nSrcWidth, sint32 nSrcHeight, sint32 nDestWidth, sint32 nDestHeight) { // the image is divided by two : 1 pixel in dest = 4 pixels in src // the resulting pixel in dest is an average of the four pixels in src nlassert(nSrcWidth % 2 == 0); nlassert(nSrcHeight % 2 == 0); nlassert(nSrcWidth / 2 == nDestWidth); nlassert(nSrcHeight / 2 == nDestHeight); sint32 x, y, twoX, twoSrcWidthByY; for (y=0 ; y>2; } } } /*-------------------------------------------------------------------*\ readTGA \*-------------------------------------------------------------------*/ uint8 CBitmap::readTGA( NLMISC::IStream &f) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ if(!f.isReading()) return 0; uint32 size; uint32 x,y; sint32 slsize; uint8 *scanline; uint8 r,g,b; sint32 i,j,k; // TGA file header fields uint8 lengthID; uint8 cMapType; uint8 imageType; uint16 origin; uint16 length; uint8 depth; uint16 xOrg; uint16 yOrg; uint16 width; uint16 height; uint8 imageDepth; uint8 desc; // Determining whether file is in Original or New TGA format uint32 extAreaOffset; uint32 devDirectoryOffset; char signature[16]; f.seek (0, f.end); if (f.getPos() >= 26) { f.seek (-26, f.end); f.serial(extAreaOffset); f.serial(devDirectoryOffset); for(i=0; i<16; i++) { f.serial(signature[i]); } } // Reading TGA file header f.seek (0, f.begin); f.serial(lengthID); f.serial(cMapType); f.serial(imageType); f.serial(origin); f.serial(length); f.serial(depth); f.serial(xOrg); f.serial(yOrg); f.serial(width); f.serial(height); f.serial(imageDepth); f.serial(desc); if(cMapType!=0) { nlinfo("readTga : color-map not supported"); } if(lengthID>0) { uint8 dummy; for(i=0; i>10; uint _g = (toto>>5)&0x1f; uint _b = toto&0x1f; _Data[0][(height-y-1)*width*4 + 4*i] = uint8((_r<<3) | (_r>>2)); _Data[0][(height-y-1)*width*4 + 4*i + 1] = uint8((_g<<3) | (_g>>2)); _Data[0][(height-y-1)*width*4 + 4*i + 2] = uint8((_b<<3) | (_b>>2)); _Data[0][(height-y-1)*width*4 + 4*i + 3] = 255; } else { _Data[0][(height-y-1)*width*4 + 4*i] = scanline[k++]; _Data[0][(height-y-1)*width*4 + 4*i + 1] = scanline[k++]; _Data[0][(height-y-1)*width*4 + 4*i + 2] = scanline[k++]; if(imageDepth==32) _Data[0][(height-y-1)*width*4 + 4*i + 3] = scanline[k++]; else _Data[0][(height-y-1)*width*4 + 4*i + 3] = 255; } } else { if(imageDepth==16) { uint16 toto = (uint16)scanline[k++]; toto |= scanline[k++]<<8; int _r = toto>>10; int _g = toto&(0x3e0)>>5; int _b = toto&0x1f; _Data[0][y*width*4 + 4*i] = uint8((_r<<3) | (_r>>2)); _Data[0][y*width*4 + 4*i + 1] = uint8((_g<<3) | (_g>>2)); _Data[0][y*width*4 + 4*i + 2] = uint8((_b<<3) | (_b>>2)); _Data[0][y*width*4 + 4*i + 3] = 255; } else { _Data[0][y*width*4 + 4*i] = scanline[k++]; _Data[0][y*width*4 + 4*i + 1] = scanline[k++]; _Data[0][y*width*4 + 4*i + 2] = scanline[k++]; if(imageDepth==32) _Data[0][y*width*4 + 4*i + 3] = scanline[k++]; else _Data[0][y*width*4 + 4*i + 3] = 255; } } } } PixelFormat = RGBA; delete []scanline; }; break; // Uncompressed Grayscale bitmap case 3: { _Data[0].resize(_Width*_Height); uint8 upSideDown = ((desc & (1 << 5))==0); slsize = _Width; scanline = new uint8[slsize]; if(!scanline) { throw EAllocationFailure(); } for(y=0; y<_Height;y++) { // Serial buffer: more efficient way to load. f.serialBuffer (scanline, slsize); k=0; for(i=0; i 0) // packet RLE { for(i=0; i 0) // packet RLE { f.serial(pixel[0]); for (i=0; i < (packet & 0x7F) + 1; i++) { _Data[0][dstId++]= pixel[0]; } } else // packet Raw { for(i=0; i<((packet & 0x7F) + 1); i++) { f.serial(pixel[0]); _Data[0][dstId++]= pixel[0]; } } readSize += (packet & 0x7F) + 1; } PixelFormat = _LoadGrayscaleAsAlpha?Alpha:Luminance; }; break; default: return 0; } _MipMapCount = 1; return(imageDepth); } /*-------------------------------------------------------------------*\ writeTGA \*-------------------------------------------------------------------*/ bool CBitmap::writeTGA( NLMISC::IStream &f, uint32 d, bool upsideDown) { if(f.isReading()) return false; if (d==0) { switch (PixelFormat) { case RGBA: d = 32; break; case Luminance: d = 8; break; case Alpha: d = 8; break; default: ; } } if(d!=24 && d!=32 && d!=16 && d!=8) return false; if ((PixelFormat != RGBA)&&(PixelFormat != Alpha)&&(PixelFormat != Luminance)) return false; if ((PixelFormat == Alpha) && (d != 8)) return false; if ((PixelFormat == Luminance) && (d != 8)) return false; sint32 i,j,x,y; uint8 * scanline; uint8 r,g,b,a; uint8 lengthID = 0; uint8 cMapType = 0; uint8 imageType = 2; uint16 origin = 0; uint16 length = 0; uint8 depth = 0; uint16 xOrg = 0; uint16 yOrg = 0; uint16 width = (uint16)_Width; uint16 height = (uint16)_Height; uint8 imageDepth = (uint8)d; uint8 desc = 0; if (upsideDown) desc |= 1<<5; if ((PixelFormat == Alpha) || (PixelFormat == Luminance)) imageType = 3; // Uncompressed grayscale f.serial(lengthID); f.serial(cMapType); f.serial(imageType); f.serial(origin); f.serial(length); f.serial(depth); f.serial(xOrg); f.serial(yOrg); f.serial(width); f.serial(height); f.serial(imageDepth); f.serial(desc); if ((PixelFormat == Alpha)||(PixelFormat == Luminance)) scanline = new uint8[width]; else scanline = new uint8[width*4]; if(!scanline) { throw EAllocationFailure(); } for(y=0; y<(sint32)height; y++) { uint32 k=0; if (PixelFormat == Alpha) { for(i=0; i>3; int gg = g >>3; int bb = b >>3; uint16 c16 = uint16((rr<<10) | (gg<<5) | bb); scanline[x*2+0] = c16&0xff; scanline[x*2+1] = c16>>8; } } if(d==24) { for(x=0; x<(sint32)width; x++) { r = scanline[x*3+0]; g = scanline[x*3+1]; b = scanline[x*3+2]; scanline[x*3+0] = b; scanline[x*3+1] = g; scanline[x*3+2] = r; } } if(d==32) { for(x=0; x<(sint32)width; x++) { r = scanline[x*4+0]; g = scanline[x*4+1]; b = scanline[x*4+2]; a= scanline[x*4+3]; scanline[x*4+0] = b; scanline[x*4+1] = g; scanline[x*4+2] = r; scanline[x*4+3] = a; } } int finaleSize=width*d/8; for(i=0; i void rotateCCW (const T* src, T* dst, uint srcWidth, uint srcHeight) { for (uint y=0; y void rotateCCW (const vector& src, vector& dst, uint srcWidth, uint srcHeight) { for (uint y=0; y copy=_Data[0]; switch (PixelFormat) { case RGBA: NLMISC::rotateCCW ((uint32*)&(_Data[0][0]), (uint32*)&(copy[0]), _Width, _Height); break; case Luminance: case Alpha: NLMISC::rotateCCW (&_Data[0][0], ©[0], _Width, _Height); break; case AlphaLuminance: NLMISC::rotateCCW ((uint16*)&(_Data[0][0]), (uint16*)&(copy[0]), _Width, _Height);; break; default: break; } uint32 tmp=_Width; _Width=_Height; _Height=tmp; _Data[0]=copy; } void CBitmap::blit(const CBitmap &src, sint srcX, sint srcY, sint srcWidth, sint srcHeight, sint destX, sint destY) { nlassert(PixelFormat == RGBA); nlassert(src.PixelFormat == RGBA); // clip x if (srcX < 0) { srcWidth += srcX; if (srcWidth <= 0) return; destX -= srcX; srcX = 0; } if (srcX + srcWidth > (sint) src.getWidth()) { srcWidth = src.getWidth() - srcX; if (srcWidth <= 0) return; } if (destX < 0) { srcWidth += destX; if (srcWidth <= 0) return; srcX -= destX; destX = 0; } if (destX + srcWidth > (sint) getWidth()) { srcWidth = getWidth() - destX; if (srcWidth <= 0) return; } // clip y if (srcY < 0) { srcHeight += srcY; if (srcHeight <= 0) return; destY -= srcY; srcY = 0; } if (srcY + srcHeight > (sint) src.getHeight()) { srcHeight = src.getHeight() - srcY; if (srcHeight <= 0) return; } if (destY < 0) { srcHeight += destY; if (srcHeight <= 0) return; srcY -= destY; destY = 0; } if (destY + srcHeight > (sint) getHeight()) { srcHeight = getHeight() - destY; if (srcHeight <= 0) return; } uint32 *srcPixels = (uint32 *) &src.getPixels()[0]; uint32 *srcPtr = &(srcPixels[srcX + srcY * src.getWidth()]); uint32 *srcEndPtr = srcPtr + srcHeight * src.getWidth(); uint32 *destPixels = (uint32 *) &getPixels()[0]; uint32 *destPtr = &(destPixels[destX + destY * getWidth()]); while (srcPtr != srcEndPtr) { memcpy(destPtr, srcPtr, sizeof(uint32) * srcWidth); srcPtr += src.getWidth(); destPtr += getWidth(); } } bool CBitmap::blit(const CBitmap *src, sint32 x, sint32 y) { nlassert(this->PixelFormat == src->PixelFormat); if (this->PixelFormat != src->PixelFormat) { return false; } // check for dxtc use const bool useDXTC = PixelFormat == DXTC1 || PixelFormat == DXTC1Alpha || PixelFormat == DXTC3 || PixelFormat == DXTC5; // number of bits for a 4x4 pix block const uint dxtcNumBits = PixelFormat == DXTC1 || PixelFormat == DXTC1Alpha ? 64 : 128; if (useDXTC) { // blit pos must be multiple of 4 nlassert(! (x & 3 || y & 3) ); if (x & 3 || y & 3) return false; } nlassert(PixelFormat != DonTKnow); // the width to copy sint width = src->_Width; // the height to copy sint height = src->_Height; uint destStartX, destStartY; uint srcStartX, srcStartY; // clip against left if (x < 0) { width += x; if (width <= 0) return true; destStartX = 0; srcStartX = -x; } else { destStartX = x; srcStartX = 0; } // clip against top if (y < 0) { height += y; if (height <= 0) return true; srcStartY = -y; destStartY = 0; } else { destStartY = y; srcStartY = 0; } // clip against right if ((destStartX + width - 1) >= _Width) { width = _Width - destStartX; if (width <= 0) return true; } // clip against bottom if ((destStartY + height - 1) >= _Height) { height = _Height - destStartY; if (width <= 0) return true; } // divide all distance by 4 when using DXTC if (useDXTC) { destStartX >>= 2; destStartY >>= 2; srcStartX >>= 2; srcStartY >>= 2; width >>= 2; height >>= 2; } // bytes per pixs is for either one pixel or 16 (a 4x4 block in DXTC) const uint bytePerPixs = ( useDXTC ? dxtcNumBits : bitPerPixels[PixelFormat] ) >> 3 /* divide by 8 to get the number of bytes */; const uint destRealWidth = useDXTC ? (_Width >> 2) : _Width; const uint srcRealWidth = useDXTC ? (src->_Width >> 2) : src->_Width; // size to go to the next line in the destination const uint destStride = destRealWidth * bytePerPixs; // size to go to the next line in the source const uint srcStride = srcRealWidth * bytePerPixs; // length in bytes of a line to copy const uint lineLength = width * bytePerPixs; uint8 *destPos = &(_Data[0][0]) + destStride * destStartY + bytePerPixs * destStartX; const uint8 *srcPos = &(src->_Data[0][0]) + srcStride * srcStartY + bytePerPixs * srcStartX; // copy each hline for (sint k = 0; k < height; ++k) { ::memcpy(destPos, srcPos, lineLength); destPos += destStride; srcPos += srcStride; } return true; } // Private : float CBitmap::getColorInterp (float x, float y, float colorInXY00, float colorInXY10, float colorInXY01, float colorInXY11) const { float res = colorInXY00*(1.0f-x)*(1.0f-y) + colorInXY10*( x)*(1.0f-y) + colorInXY01*(1.0f-x)*( y) + colorInXY11*( x)*( y); clamp (res, 0.0f, 255.0f); return res; } // Public: CRGBAF CBitmap::getColor (float x, float y) const { if (x < 0.0f) x = 0.0f; if (x > 1.0f) x = 1.0f; if (y < 0.0f) y = 0.0f; if (y > 1.0f) y = 1.0f; sint32 nWidth = getWidth(0); sint32 nHeight = getHeight(0); if (nWidth == 0 || nHeight == 0) return CRGBAF(0, 0, 0, 0); const CObjectVector &rBitmap = getPixels(0); sint32 nX[4], nY[4]; x *= nWidth-1; y *= nHeight-1; // Integer part of (x,y) //nX[0] = ((sint32)floor(x-0.5f)); //nY[0] = ((sint32)floor(y-0.5f)); nX[0] = ((sint32)floor(x)); nY[0] = ((sint32)floor(y)); nX[1] = (nX[0] < (nWidth-1) ? nX[0]+1 : nX[0]); nY[1] = nY[0]; nX[2] = nX[0]; nY[2] = (nY[0] < (nHeight-1) ? nY[0]+1 : nY[0]); nX[3] = nX[1]; nY[3] = nY[2]; uint32 i; for (i = 0; i < 4; ++i) { nlassert (nX[i] >= 0); nlassert (nY[i] >= 0 ); nlassert (nX[i] < nWidth); nlassert (nY[i] < nHeight); } // Decimal part of (x,y) x = x - (float)nX[0]; y = y - (float)nY[0]; switch (this->PixelFormat) { case RGBA: case DXTC1: case DXTC1Alpha: case DXTC3: case DXTC5: { CRGBAF finalVal; CRGBA val[4]; if (this->PixelFormat == RGBA) { for (i = 0; i < 4; ++i) { val[i] = CRGBA (rBitmap[(nX[i]+nY[i]*nWidth)*4+0], rBitmap[(nX[i]+nY[i]*nWidth)*4+1], rBitmap[(nX[i]+nY[i]*nWidth)*4+2], rBitmap[(nX[i]+nY[i]*nWidth)*4+3]); } } else { // slower version : get from DXT for (i = 0; i < 4; ++i) { val[i] = getPixelColor(nX[i], nY[i]); } } finalVal.R = getColorInterp (x, y, val[0].R, val[1].R, val[2].R, val[3].R); finalVal.G = getColorInterp (x, y, val[0].G, val[1].G, val[2].G, val[3].G); finalVal.B = getColorInterp (x, y, val[0].B, val[1].B, val[2].B, val[3].B); finalVal.A = getColorInterp (x, y, val[0].A, val[1].A, val[2].A, val[3].A); finalVal /= 255.f; return finalVal; } break; case Alpha: case Luminance: { float finalVal; float val[4]; for (i = 0; i < 4; ++i) val[i] = rBitmap[(nX[i]+nY[i]*nWidth)]; finalVal = getColorInterp (x, y, val[0], val[1], val[2], val[3]); finalVal /= 255.f; if (this->PixelFormat == Alpha) return CRGBAF (1.f, 1.f, 1.f, finalVal); else // Luminance return CRGBAF (finalVal, finalVal, finalVal, 1.f); } break; default: break; } return CRGBAF (0.0f, 0.0f, 0.0f, 0.0f); } // wrap a value inside the given range (for positive value it is like a modulo) static inline uint32 wrap(sint32 value, uint32 range) { return value >= 0 ? (value % range) : range - 1 - (- value - 1) % range; } CRGBAF CBitmap::getColor(float x, float y, bool tileU, bool tileV) const { sint32 nWidth = getWidth(0); sint32 nHeight = getHeight(0); if (nWidth == 0 || nHeight == 0) return CRGBAF(0, 0, 0, 0); sint32 nX[4], nY[4]; if (!tileU) { if (x < 0.0f) x = 0.0f; if (x > 1.0f) x = 1.0f; x *= nWidth-1; nX[0] = ((sint32)floor(x)); nX[1] = (nX[0] < (nWidth-1) ? nX[0]+1 : nX[0]); nX[2] = nX[0]; nX[3] = nX[1]; uint32 i; for (i = 0; i < 4; ++i) { nlassert (nX[i] >= 0); nlassert (nX[i] < nWidth); } } else { x *= nWidth; nX[0] = wrap((sint32)floorf(x), nWidth); nX[1] = wrap(nX[0] + 1, nWidth); nX[2] = nX[0]; nX[3] = nX[1]; } // if (!tileV) { if (y < 0.0f) y = 0.0f; if (y > 1.0f) y = 1.0f; y *= nHeight-1; nY[0] = ((sint32)floor(y)); nY[1] = nY[0]; nY[2] = (nY[0] < (nHeight-1) ? nY[0]+1 : nY[0]); nY[3] = nY[2]; uint32 i; for (i = 0; i < 4; ++i) { nlassert (nY[i] >= 0 ); nlassert (nY[i] < nHeight); } } else { y *= nHeight; nY[0] = wrap((sint32)floorf(y), nHeight); nY[1] = nY[0]; nY[2] = wrap(nY[0] + 1, nHeight); nY[3] = nY[2]; } // Decimal part of (x,y) x = x - (float)nX[0]; y = y - (float)nY[0]; const CObjectVector &rBitmap = getPixels(0); switch (this->PixelFormat) { case RGBA: case DXTC1: case DXTC1Alpha: case DXTC3: case DXTC5: { CRGBAF finalVal; CRGBA val[4]; if (this->PixelFormat == RGBA) { for (uint32 i = 0; i < 4; ++i) { val[i] = CRGBA (rBitmap[(nX[i]+nY[i]*nWidth)*4+0], rBitmap[(nX[i]+nY[i]*nWidth)*4+1], rBitmap[(nX[i]+nY[i]*nWidth)*4+2], rBitmap[(nX[i]+nY[i]*nWidth)*4+3]); } } else { // slower version : get from DXT for (uint32 i = 0; i < 4; ++i) { val[i] = getPixelColor(nX[i], nY[i]); } } finalVal.R = getColorInterp (x, y, val[0].R, val[1].R, val[2].R, val[3].R); finalVal.G = getColorInterp (x, y, val[0].G, val[1].G, val[2].G, val[3].G); finalVal.B = getColorInterp (x, y, val[0].B, val[1].B, val[2].B, val[3].B); finalVal.A = getColorInterp (x, y, val[0].A, val[1].A, val[2].A, val[3].A); finalVal /= 255.f; return finalVal; } break; case Alpha: case Luminance: { float finalVal; float val[4]; for (uint32 i = 0; i < 4; ++i) val[i] = rBitmap[(nX[i]+nY[i]*nWidth)]; finalVal = getColorInterp (x, y, val[0], val[1], val[2], val[3]); finalVal /= 255.f; if (this->PixelFormat == Alpha) return CRGBAF (1.f, 1.f, 1.f, finalVal); else // Luminance return CRGBAF (finalVal, finalVal, finalVal, 1.f); } break; default: break; } return CRGBAF (0.0f, 0.0f, 0.0f, 0.0f); } void CBitmap::loadSize(NLMISC::IStream &f, uint32 &retWidth, uint32 &retHeight) { retWidth= 0; retHeight= 0; nlassert(f.isReading()); // testing if DDS uint32 fileType = 0; f.serial(fileType); if(fileType == DDS_HEADER) { // read entire DDS header. uint32 size = 0; f.serial(size); // size in Bytes of header(without "DDS") uint32 * _DDSSurfaceDesc = new uint32[size]; _DDSSurfaceDesc[0]= size; for(uint i= 0; i= 0xd0 && blockMarker2 <= 0xd8) { // no content } else { // size of a block f.serial(blockSize); NLMISC_BSWAP16(blockSize); // frame marker (which contains image width and height) if (blockMarker2 >= 0xc0 && blockMarker2 <= 0xc3) { uint8 imagePrecision = 0; // sample precision uint32 imageSize = 0; // width and height f.serial(imagePrecision); f.serial(imageSize); NLMISC_BSWAP32(imageSize); retWidth = imageSize & 0xffff; retHeight = (imageSize & 0xffff0000) >> 16; break; } // skip the block f.seek(blockSize - 2, IStream::current); } } } catch(...) { eof = true; } } while(!eof); } else if(fileType == GIF_HEADER) { // check second part of header ("7a" or "9a" in 'GIF89a') uint16 s; f.serial(s); if (s != 0x6137 && s != 0x6139) { nlwarning("Invalid GIF header, expected GIF87a or GIF89a"); return; } uint16 lsWidth; uint16 lsHeight; f.serial(lsWidth); f.serial(lsHeight); retWidth = lsWidth; retHeight = lsHeight; } // assuming it's TGA else { if(!f.seek (0, NLMISC::IStream::begin)) { throw ESeekFailed(); } // Reading header, // To make sure that the bitmap is TGA, we check imageType and imageDepth. uint8 lengthID; uint8 cMapType; uint8 imageType; uint16 tgaOrigin; uint16 length; uint8 depth; uint16 xOrg; uint16 yOrg; uint16 width; uint16 height; uint8 imageDepth; uint8 desc; f.serial(lengthID); f.serial(cMapType); f.serial(imageType); if(imageType!=2 && imageType!=3 && imageType!=10 && imageType!=11) { nlwarning("Invalid TGA format, type %u in not supported (must be 2,3,10 or 11)", imageType); return; } f.serial(tgaOrigin); f.serial(length); f.serial(depth); f.serial(xOrg); f.serial(yOrg); f.serial(width); f.serial(height); f.serial(imageDepth); if(imageDepth!=8 && imageDepth!=16 && imageDepth!=24 && imageDepth!=32) { nlwarning("Invalid TGA format, bit depth %u in not supported (must be 8,16,24 or 32)", imageDepth); return; } f.serial(desc); // Ok, we have width and height. retWidth= width; retHeight= height; } // reset stream. if(!f.seek (0, NLMISC::IStream::begin)) { throw ESeekFailed(); } } void CBitmap::loadSize(const std::string &path, uint32 &retWidth, uint32 &retHeight) { retWidth= 0; retHeight= 0; CIFile f(path); if(f.open(path)) loadSize(f, retWidth, retHeight); } // *************************************************************************** void CBitmap::flipHDXTCBlockColor(uint8 *bitColor, uint32 w) { // pack each line in a u32 (NB: the following works either in Little and Big Endian) uint32 bits= *(uint32*)bitColor; // swap in X for each line uint32 res; if(w!=2) { res = (bits & 0xC0C0C0C0) >> 6; res+= (bits & 0x30303030) >> 2; res+= (bits & 0x0C0C0C0C) << 2; res+= (bits & 0x03030303) << 6; } // special case where w==2 else { res = (bits & 0x0C0C0C0C) >> 2; res+= (bits & 0x03030303) << 2; } // copy *((uint32*)bitColor)= res; } // *************************************************************************** void CBitmap::flipVDXTCBlockColor(uint8 *bitColor, uint32 h) { // swap just bytes (work either in Little and Big Endian) if(h!=2) { std::swap(bitColor[0], bitColor[3]); std::swap(bitColor[1], bitColor[2]); } // special case where h==2) else { // whatever Little or Big endian, the first byte is the first line, and the second byte is the second line std::swap(bitColor[0], bitColor[1]); } } // *************************************************************************** void CBitmap::flipHDXTCBlockAlpha3(uint8 *blockAlpha, uint32 w) { #ifdef NL_LITTLE_ENDIAN uint64 bits= *(uint64*)blockAlpha; #else uint64 bits= (uint64)blockAlpha[0] + ((uint64)blockAlpha[1]<<8) + ((uint64)blockAlpha[2]<<16) + ((uint64)blockAlpha[3]<<24) + ((uint64)blockAlpha[4]<<32) + ((uint64)blockAlpha[5]<<40) + ((uint64)blockAlpha[6]<<48) + ((uint64)blockAlpha[7]<<56); #endif // swap in X for each line uint64 res; if(w!=2) { res = (bits & INT64_CONSTANT(0xF000F000F000F000)) >> 12; res+= (bits & INT64_CONSTANT(0x0F000F000F000F00)) >> 4; res+= (bits & INT64_CONSTANT(0x00F000F000F000F0)) << 4; res+= (bits & INT64_CONSTANT(0x000F000F000F000F)) << 12; } // special case where w==2 else { res = (bits & INT64_CONSTANT(0x00F000F000F000F0)) >> 4; res+= (bits & INT64_CONSTANT(0x000F000F000F000F)) << 4; } // copy #ifdef NL_LITTLE_ENDIAN *((uint64*)blockAlpha)= res; #else blockAlpha[0]= res & 255; blockAlpha[1]= (res>>8) & 255; blockAlpha[2]= (res>>16) & 255; blockAlpha[3]= (res>>24) & 255; blockAlpha[4]= (res>>32) & 255; blockAlpha[5]= (res>>40) & 255; blockAlpha[6]= (res>>48) & 255; blockAlpha[7]= (res>>56) & 255; #endif } // *************************************************************************** void CBitmap::flipVDXTCBlockAlpha3(uint8 *blockAlpha, uint32 h) { uint16 *wAlpha= (uint16*)blockAlpha; // swap just words (work either in Little and Big Endian) if(h!=2) { std::swap(wAlpha[0], wAlpha[3]); std::swap(wAlpha[1], wAlpha[2]); } // special case where h==2) else { // whatever Little or Big endian, the first byte is the first line, and the second byte is the second line std::swap(wAlpha[0], wAlpha[1]); } } // *************************************************************************** void CBitmap::flipHDXTCBlockAlpha5(uint8 *bitAlpha, uint32 w) { // pack into bits. Little Indian in all cases uint64 bits= (uint64)bitAlpha[0] + ((uint64)bitAlpha[1]<<8) + ((uint64)bitAlpha[2]<<16) + ((uint64)bitAlpha[3]<<24) + ((uint64)bitAlpha[4]<<32) + ((uint64)bitAlpha[5]<<40); // swap in X for each line uint64 res; if(w!=2) { res = (bits & INT64_CONSTANT(0xE00E00E00E00)) >> 9; res+= (bits & INT64_CONSTANT(0x1C01C01C01C0)) >> 3; res+= (bits & INT64_CONSTANT(0x038038038038)) << 3; res+= (bits & INT64_CONSTANT(0x007007007007)) << 9; } // special case where w==2 else { res = (bits & INT64_CONSTANT(0x038038038038)) >> 3; res+= (bits & INT64_CONSTANT(0x007007007007)) << 3; } // copy. Little Indian in all cases bitAlpha[0]= uint8(res & 255); bitAlpha[1]= uint8((res>>8) & 255); bitAlpha[2]= uint8((res>>16) & 255); bitAlpha[3]= uint8((res>>24) & 255); bitAlpha[4]= uint8((res>>32) & 255); bitAlpha[5]= uint8((res>>40) & 255); } // *************************************************************************** void CBitmap::flipVDXTCBlockAlpha5(uint8 *bitAlpha, uint32 h) { // pack into bits. Little Indian in all cases uint64 bits= (uint64)bitAlpha[0] + ((uint64)bitAlpha[1]<<8) + ((uint64)bitAlpha[2]<<16) + ((uint64)bitAlpha[3]<<24) + ((uint64)bitAlpha[4]<<32) + ((uint64)bitAlpha[5]<<40); // swap in Y uint64 res; if(h!=2) { res = (bits & INT64_CONSTANT(0xFFF000000000)) >> 36; res+= (bits & INT64_CONSTANT(0x000FFF000000)) >> 12; res+= (bits & INT64_CONSTANT(0x000000FFF000)) << 12; res+= (bits & INT64_CONSTANT(0x000000000FFF)) << 36; } // special case where h==2 else { res = (bits & INT64_CONSTANT(0x000000FFF000)) >> 12; res+= (bits & INT64_CONSTANT(0x000000000FFF)) << 12; } // copy. Little Indian in all cases bitAlpha[0]= uint8(res & 255); bitAlpha[1]= uint8((res>>8) & 255); bitAlpha[2]= uint8((res>>16) & 255); bitAlpha[3]= uint8((res>>24) & 255); bitAlpha[4]= uint8((res>>32) & 255); bitAlpha[5]= uint8((res>>40) & 255); } // *************************************************************************** void CBitmap::flipDXTCMipMap(bool vertical, uint mm, uint type) { nlassert(mm1) needRebuild = true; releaseMipMaps(); for( i = 0; i < nHeight; ++i ) for( j = 0; j < nWidth/2; ++j ) { temp = pBitmap[i*nWidth+j]; pBitmap[i*nWidth+j] = pBitmap[i*nWidth+nWidth-j-1]; pBitmap[i*nWidth+nWidth-j-1] = temp; } // Rebuilding mipmaps if(needRebuild) { buildMipMaps(); } } // *************************************************************************** void CBitmap::flipV() { if (PixelFormat != RGBA) { // try for DXTC flipDXTC(true); // then quit (whether it worked or not) return; } sint32 nWidth = getWidth(0); sint32 nHeight = getHeight(0); sint32 i, j; NLMISC::CRGBA *pBitmap = (NLMISC::CRGBA*)&_Data[0][0]; bool needRebuild = false; CRGBA temp; if(_MipMapCount>1) needRebuild = true; releaseMipMaps(); for( j = 0; j < nHeight/2; ++j ) for( i = 0; i < nWidth; ++i ) { temp = pBitmap[j*nWidth+i]; pBitmap[j*nWidth+i] = pBitmap[(nHeight-j-1)*nWidth+i]; pBitmap[(nHeight-j-1)*nWidth+i] = temp; } // Rebuilding mipmaps if(needRebuild) { buildMipMaps(); } } void CBitmap::rot90CW() { if (PixelFormat != RGBA) return; sint32 nWidth = getWidth(0); sint32 nHeight = getHeight(0); sint32 i, j; NLMISC::CRGBA *pSrcRgba = (NLMISC::CRGBA*)&_Data[0][0]; bool needRebuild = false; if(_MipMapCount>1) needRebuild = true; releaseMipMaps(); CObjectVector pDestui; pDestui.resize(nWidth*nHeight*4); NLMISC::CRGBA *pDestRgba = (NLMISC::CRGBA*)&pDestui[0]; for( j = 0; j < nHeight; ++j ) for( i = 0; i < nWidth; ++i ) pDestRgba[j+i*nHeight] = pSrcRgba[i+(nHeight-1-j)*nWidth]; uint32 nTemp = _Width; _Width = _Height; _Height = nTemp; NLMISC::contReset(_Data[0]); // free memory _Data[0] = pDestui; // Rebuilding mipmaps if(needRebuild) { buildMipMaps(); } } void CBitmap::rot90CCW() { if (PixelFormat != RGBA) return; sint32 nWidth = getWidth(0); sint32 nHeight = getHeight(0); sint32 i, j; NLMISC::CRGBA *pSrcRgba = (NLMISC::CRGBA*)&_Data[0][0]; bool needRebuild = false; if(_MipMapCount>1) needRebuild = true; releaseMipMaps(); CObjectVector pDestui; pDestui.resize(nWidth*nHeight*4); NLMISC::CRGBA *pDestRgba = (NLMISC::CRGBA*)&pDestui[0]; for( j = 0; j < nHeight; ++j ) for( i = 0; i < nWidth; ++i ) pDestRgba[j+i*nHeight] = pSrcRgba[nWidth-1-i+j*nWidth]; uint32 nTemp = _Width; _Width = _Height; _Height = nTemp; NLMISC::contReset(_Data[0]); // free memory _Data[0] = pDestui; // Rebuilding mipmaps if(needRebuild) { buildMipMaps(); } } //=========================================================================== void CBitmap::blend(CBitmap &Bm0, CBitmap &Bm1, uint16 factor, bool inputBitmapIsMutable /*= false*/) { nlassert(factor <= 256); nlassert(Bm0._Width != 0 && Bm0._Height != 0 && Bm1._Width != 0 && Bm1._Height != 0); nlassert(Bm0._Width == Bm1._Width); // the bitmap should have the same size nlassert(Bm0._Height == Bm1._Height); const CBitmap *nBm0, *nBm1; // pointer to the bitmap that is used for blending, or to a copy is a conversion wa required CBitmap cp0, cp1; // these bitmap are copies of Bm1 and Bm0 if a conversion was needed if (Bm0.PixelFormat != RGBA) { if (inputBitmapIsMutable) { Bm0.convertToRGBA(); nBm0 = &Bm0; } else { cp0 = Bm0; cp0.convertToRGBA(); nBm0 = &cp0; } } else { nBm0 = &Bm0; } if (Bm1.PixelFormat != RGBA) { if (inputBitmapIsMutable) { Bm1.convertToRGBA(); nBm1 = &Bm1; } else { cp1 = Bm1; cp1.convertToRGBA(); nBm1 = &cp1; } } else { nBm1 = &Bm1; } if (this != nBm0 && this != nBm1) { // if source is the same than the dets, don't resize because this clear the bitmap this->resize(Bm0._Width, Bm0._Height, RGBA); } uint numPix = _Width * _Height; // 4 component per pixels const uint8 *src0 = &(nBm0->_Data[0][0]); const uint8 *src1 = &(nBm1->_Data[0][0]); uint8 *dest = &(this->_Data[0][0]); #if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM) if (CSystemInfo::hasMMX()) { // On a P4 2GHz, with a 256x256 texture, I got the following results : // without mmx : 5.2 ms // with mmx : 1.7 ms // I'm sure this can be further optimized.. uint numPixLeft = numPix & 1; // process 2 pixels at once, so special case for odd number numPix = numPix & ~1; // do fast blend with mmx uint64 blendFactor0; uint64 blendFactor1; uint16 *bf0 = (uint16 *) &blendFactor0; uint16 *bf1 = (uint16 *) &blendFactor1; bf0[0] = bf0[1] = bf0[2] = bf0[3] = (1 << 6) * (factor); bf1[0] = bf1[1] = bf1[2] = bf1[3] = (1 << 6) * (256 - factor); __asm { mov esi, src0 mov eax, src1 mov edi, dest mov ebx, -8 mov ecx, numPix shr ecx, 1 // process pixels 2 by 2 movq mm1, blendFactor0 movq mm0, blendFactor1 myLoop: pxor mm6, mm6 lea ebx, [ebx + 8] // points next location pxor mm7, mm7 movq mm2, [esi + ebx] movq mm3, [eax + ebx] // do blend punpckhbw mm7, mm2 // mm7 contains src0 color 0 in high bytes punpckhbw mm6, mm3 // mm6 contains src1 color 0 in high bytes psrl mm7, 1 pxor mm4, mm4 // mm4 = 0 psrl mm6, 1 pmulhw mm7, mm0 // src0 = src0 * blendFactor pxor mm5, mm5 // mm5 = 0 pmulhw mm6, mm1 // src1 = src1 * (1 - blendfactor) punpcklbw mm4, mm2 // mm4 contains src0 color 1 in high bytes paddusw mm6, mm7 // mm6 = src0[0] blended with src1[0] psrl mm4, 1 psrlw mm6, 5 punpcklbw mm5, mm3 // mm4 contains src1 color 1 in high bytes psrl mm5, 1 pmulhw mm4, mm0 // src0 = src0 * blendFactor pmulhw mm5, mm1 // src1 = src1 * (1 - blendfactor) paddusw mm4, mm5 // mm6 = src0[1] blended with src1[1] psrlw mm4, 5 // pack result packuswb mm4, mm6 dec ecx movq [edi + ebx], mm4 // store result jne myLoop emms } if (numPixLeft) { // case of odd number of pixels src0 += 4 * numPix; src1 += 4 * numPix; dest += 4 * numPix; uint blendFact = (uint) factor; uint invblendFact = 256 - blendFact; *dest = (uint8) (((blendFact * *src1) + (invblendFact * *src0)) >> 8); *(dest + 1) = (uint8) (((blendFact * *(src1 + 1)) + (invblendFact * *(src0 + 1))) >> 8); *(dest + 2) = (uint8) (((blendFact * *(src1 + 2)) + (invblendFact * *(src0 + 2))) >> 8); *(dest + 3) = (uint8) (((blendFact * *(src1 + 3)) + (invblendFact * *(src0 + 3))) >> 8); } } else #endif //#ifdef NL_OS_WINDOWS { uint8 *endPix = dest + (numPix << 2); // no mmx version uint blendFact = (uint) factor; uint invblendFact = 256 - blendFact; do { /// blend 4 component at each pass *dest = (uint8) (((blendFact * *src1) + (invblendFact * *src0)) >> 8); *(dest + 1) = (uint8) (((blendFact * *(src1 + 1)) + (invblendFact * *(src0 + 1))) >> 8); *(dest + 2) = (uint8) (((blendFact * *(src1 + 2)) + (invblendFact * *(src0 + 2))) >> 8); *(dest + 3) = (uint8) (((blendFact * *(src1 + 3)) + (invblendFact * *(src0 + 3))) >> 8); src0 = src0 + 4; src1 = src1 + 4; dest = dest + 4; } while (dest != endPix); } } //----------------------------------------------- CRGBA CBitmap::getRGBAPixel(sint x, sint y, uint32 numMipMap /*=0*/) const { uint w = getWidth(numMipMap); uint h = getHeight(numMipMap); if (w == 0 || (uint) x >= w || (uint) y >= h) return CRGBA::Black; // include negative cases const uint8 *pix = &getPixels(numMipMap)[(x + y * w) << 2]; return CRGBA(pix[0], pix[1], pix[2], pix[3]); } //----------------------------------------------- CRGBA CBitmap::getDXTCColorFromBlock(const uint8 *block, sint x, sint y) { uint16 col0; uint16 col1; memcpy(&col0, block, sizeof(uint16)); memcpy(&col1, block + 2, sizeof(uint16)); uint colIndex = (block[4 + (y & 3)] >> ((x & 3) << 1)) & 3; CRGBA result, c0, c1; if (col0 > col1) { switch(colIndex) { case 0: uncompress(col0, result); break; case 1: uncompress(col1, result); break; case 2: uncompress(col0, c0); uncompress(col1, c1); result.blendFromui(c0, c1, 85); break; case 3: uncompress(col0, c0); uncompress(col1, c1); result.blendFromui(c0, c1, 171); break; default: ; } result.A = 255; } else { switch(colIndex) { case 0: uncompress(col0, result); result.A = 255; break; case 1: uncompress(col1, result); result.A = 255; break; case 2: uncompress(col0, c0); uncompress(col1, c1); result.blendFromui(c0, c1, 128); result.A = 255; break; case 3: result.set(0, 0, 0, 0); break; } } return result; } //----------------------------------------------- CRGBA CBitmap::getDXTC1Texel(sint x, sint y, uint32 numMipMap) const { uint w = getWidth(numMipMap); uint h = getHeight(numMipMap); if (w == 0 || h == 0 || (uint) x >= w || (uint) y >= h) return CRGBA::Black; // include negative cases uint numRowBlocks = std::max((w + 3) >> 2, 1u); const uint8 *pix = &getPixels(numMipMap)[0]; const uint8 *block = pix + ((y >> 2) * (numRowBlocks << 3) + ((x >> 2) << 3)); return getDXTCColorFromBlock(block, x, y); } //----------------------------------------------- CRGBA CBitmap::getDXTC3Texel(sint x, sint y, uint32 numMipMap) const { uint w = getWidth(numMipMap); uint h = getHeight(numMipMap); if (w == 0 || h == 0 || (uint) x >= w || (uint) y >= h) return CRGBA::Black; // include negative cases uint numRowBlocks = std::max((w + 3) >> 2, 1u); const uint8 *pix = &getPixels(numMipMap)[0]; const uint8 *block = pix + ((y >> 2) * (numRowBlocks << 4) + ((x >> 2) << 4)); CRGBA result = getDXTCColorFromBlock(block + 8, x, y); // get alpha part uint8 alphaByte = block[((y & 3) << 1) + ((x & 2) >> 1)]; result.A = (x & 1) ? (alphaByte & 0xf0) : ((alphaByte & 0x0f) << 4); return result; } //----------------------------------------------- CRGBA CBitmap::getDXTC5Texel(sint x, sint y, uint32 numMipMap) const { uint w = getWidth(numMipMap); uint h = getHeight(numMipMap); if (w == 0 || h == 0 || (uint) x >= w || (uint) y >= h) return CRGBA::Black; // include negative cases uint numRowBlocks = std::max((w + 3) >> 2, 1u); const uint8 *pix = &getPixels(numMipMap)[0]; const uint8 *block = pix + ((y >> 2) * (numRowBlocks << 4) + ((x >> 2) << 4)); CRGBA result = getDXTCColorFromBlock(block + 8, x, y); // get alpha part uint8 alpha0 = block[0]; uint8 alpha1 = block[1]; uint alphaIndex; uint tripletIndex = (x & 3) + ((y & 3) << 2); if (tripletIndex < 8) { alphaIndex = (((uint32 &) block[2]) >> (tripletIndex * 3)) & 7; } else { alphaIndex = (((uint32 &) block[5]) >> ((tripletIndex - 8) * 3)) & 7; // we can read a dword there because there are color datas following he alpha datas } if (alpha0 > alpha1) { switch (alphaIndex) { case 0: result.A = alpha0; break; case 1: result.A = alpha1; break; case 2: result.A = (uint8) ((6 * (uint) alpha0 + (uint) alpha1) / 7); break; case 3: result.A = (uint8) ((5 * (uint) alpha0 + 2 * (uint) alpha1) / 7); break; case 4: result.A = (uint8) ((4 * (uint) alpha0 + 3 * (uint) alpha1) / 7); break; case 5: result.A = (uint8) ((3 * (uint) alpha0 + 4 * (uint) alpha1) / 7); break; case 6: result.A = (uint8) ((2 * (uint) alpha0 + 5 * (uint) alpha1) / 7); break; case 7: result.A = (uint8) (((uint) alpha0 + (uint) 6 * alpha1) / 7); break; } } else { switch (alphaIndex) { case 0: result.A = alpha0; break; case 1: result.A = alpha1; break; case 2: result.A = (uint8) ((4 * (uint) alpha0 + (uint) alpha1) / 5); break; case 3: result.A = (uint8) ((3 * (uint) alpha0 + 2 * (uint) alpha1) / 5); break; case 4: result.A = (uint8) ((2 * (uint) alpha0 + 3 * (uint) alpha1) / 5); break; case 5: result.A = (uint8) (((uint) alpha0 + 4 * (uint) alpha1) / 5); break; case 6: result.A = 0; break; case 7: result.A = 255; break; } } return result; } //----------------------------------------------- CRGBA CBitmap::getPixelColor(sint x, sint y, uint32 numMipMap /*=0*/) const { switch (PixelFormat) { case RGBA: return getRGBAPixel(x, y, numMipMap); case DXTC1: case DXTC1Alpha: return getDXTC1Texel(x, y, numMipMap); case DXTC3: return getDXTC3Texel(x, y, numMipMap); case DXTC5: return getDXTC5Texel(x, y, numMipMap); default: nlstop; break; } return CRGBA::Black; } //----------------------------------------------- void CBitmap::swap(CBitmap &other) { std::swap(PixelFormat, other.PixelFormat); std::swap(_MipMapCount, other._MipMapCount); std::swap(_LoadGrayscaleAsAlpha, other._LoadGrayscaleAsAlpha); std::swap(_Width, other._Width); std::swap(_Height, other._Height); for(uint k = 0; k < MAX_MIPMAP; ++k) { _Data[k].swap(other._Data[k]); } } //----------------------------------------------- void CBitmap::unattachPixels(CObjectVector *mipmapDestArray, uint maxMipMapCount /*=MAX_MIPMAP*/) { if (!mipmapDestArray) return; uint k; for(k = 0; k < std::min((uint) _MipMapCount, maxMipMapCount); ++k) { mipmapDestArray[k].swap(_Data[k]); _Data[k].clear(); } for(; k < _MipMapCount; ++k) { _Data[k].clear(); } #ifdef NL_DEBUG // check that remaining mipmaps are empty for(; k < _MipMapCount; ++k) { nlassert(_Data[k].empty()); } #endif _MipMapCount = 1; _Width = 0; _Height = 0; PixelFormat = RGBA; _LoadGrayscaleAsAlpha = true; } void CBitmap::getData(uint8*& extractData) { uint32 size=0; if(PixelFormat==RGBA) size=_Width*_Height*4; else if(PixelFormat==Alpha||PixelFormat==Luminance) size=_Width*_Height; else { nlstop; } for(uint32 pix=0;pix=0;i--) { buf[_Height-1-i]=&_Data[0][i*lineSize]; } size=lineSize*_Height; for(uint32 line=0;line<_Height;line++) { for(uint32 pix=0;pix