This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/ddsfile.cpp

698 lines
22 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ddsfile.h"
#include "ffactory.h"
#include "bufffile.h"
#include "formconv.h"
#include "dx8wrapper.h"
#include "bitmaphandler.h"
#include <string.h>
// ----------------------------------------------------------------------------
DDSFileClass::DDSFileClass(const char* name,unsigned reduction_factor)
:
DDSMemory(NULL),
Width(0),
Height(0),
FullWidth(0),
FullHeight(0),
LevelSizes(NULL),
LevelOffsets(NULL),
MipLevels(0),
ReductionFactor(reduction_factor),
Format(WW3D_FORMAT_UNKNOWN),
DateTime(0)
{
strncpy(Name,name,sizeof(Name));
// The name could be given in .tga or .dds format, so ensure we're opening .dds...
int len=strlen(Name);
Name[len-3]='d';
Name[len-2]='d';
Name[len-1]='s';
file_auto_ptr file(_TheFileFactory,Name);
if (!file->Is_Available()) {
return;
}
file->Open();
DateTime=file->Get_Date_Time();
char header[4];
file->Read(header,4);
// Now, we read DDSURFACEDESC2 defining the compressed data
unsigned read_bytes=file->Read(&SurfaceDesc,sizeof(LegacyDDSURFACEDESC2));
// Verify the structure size matches the read size
if (read_bytes!=SurfaceDesc.Size) {
StringClass tmp(0,true);
tmp.Format("File %s loading failed.\nTried to read %d bytes, got %d. (SurfDesc.size=%d)\n",name,sizeof(LegacyDDSURFACEDESC2),read_bytes,SurfaceDesc.Size);
WWASSERT_PRINT(0,tmp);
return;
}
Format=D3DFormat_To_WW3DFormat((D3DFORMAT)SurfaceDesc.PixelFormat.FourCC);
WWASSERT(
Format==WW3D_FORMAT_DXT1 ||
Format==WW3D_FORMAT_DXT2 ||
Format==WW3D_FORMAT_DXT3 ||
Format==WW3D_FORMAT_DXT4 ||
Format==WW3D_FORMAT_DXT5);
MipLevels=SurfaceDesc.MipMapCount;
if (MipLevels==0) MipLevels=1;
if (MipLevels>ReductionFactor) MipLevels-=ReductionFactor;
else {
MipLevels=1;
ReductionFactor=ReductionFactor-MipLevels;
}
// Drop the two lowest miplevels!
if (MipLevels>2) MipLevels-=2;
else MipLevels=1;
FullWidth=SurfaceDesc.Width;
FullHeight=SurfaceDesc.Height;
Width=SurfaceDesc.Width>>ReductionFactor;
Height=SurfaceDesc.Height>>ReductionFactor;
unsigned level_size=Calculate_DXTC_Surface_Size(SurfaceDesc.Width,SurfaceDesc.Height,Format);
unsigned level_offset=0;
LevelSizes=new unsigned[MipLevels];
LevelOffsets=new unsigned[MipLevels];
for (unsigned level=0;level<ReductionFactor;++level) {
if (level_size>16) { // If surface is bigger than one block (8 or 16 bytes)...
level_size/=4;
}
}
for (level=0;level<MipLevels;++level) {
LevelSizes[level]=level_size;
LevelOffsets[level]=level_offset;
level_offset+=level_size;
if (level_size>16) { // If surface is bigger than one block (8 or 16 bytes)...
level_size/=4;
}
}
file->Close();
}
// ----------------------------------------------------------------------------
DDSFileClass::~DDSFileClass()
{
delete[] DDSMemory;
delete[] LevelSizes;
delete[] LevelOffsets;
}
unsigned DDSFileClass::Get_Width(unsigned level) const
{
WWASSERT(level<MipLevels);
unsigned width=Width>>level;
if (width<4) width=4;
return width;
}
unsigned DDSFileClass::Get_Height(unsigned level) const
{
WWASSERT(level<MipLevels);
unsigned height=Height>>level;
if (height<4) height=4;
return height;
}
const unsigned char* DDSFileClass::Get_Memory_Pointer(unsigned level) const
{
WWASSERT(level<MipLevels);
return DDSMemory+LevelOffsets[level];
}
unsigned DDSFileClass::Get_Level_Size(unsigned level) const
{
WWASSERT(level<MipLevels);
return LevelSizes[level];
}
// For some reason DX-Tex tool doesn't fill the surface size field, so we need to calculate it...
unsigned DDSFileClass::Calculate_DXTC_Surface_Size(unsigned width, unsigned height, WW3DFormat format)
{
unsigned level_size=(width/4)*(height/4);
switch (format) {
case WW3D_FORMAT_DXT1:
level_size*=8;
break;
case WW3D_FORMAT_DXT2:
case WW3D_FORMAT_DXT3:
case WW3D_FORMAT_DXT4:
case WW3D_FORMAT_DXT5:
level_size*=16;
break;
}
return level_size;
}
// ----------------------------------------------------------------------------
bool DDSFileClass::Load()
{
if (DDSMemory) return false;
if (!LevelSizes || !LevelOffsets) return false;
file_auto_ptr file(_TheFileFactory,Name);
if (!file->Is_Available()) {
return false;
}
file->Open();
// Data size is file size minus the header and info block
unsigned size=file->Size()-SurfaceDesc.Size-4;
// Skip mip levels if reduction factor is not zero
unsigned level_size=Calculate_DXTC_Surface_Size(SurfaceDesc.Width,SurfaceDesc.Height,Format);
unsigned skipped_offset=0;
for (unsigned i=0;i<ReductionFactor;++i) {
skipped_offset+=level_size;
size-=level_size;
if (level_size>16) { // If surface is bigger than one block (8 or 16 bytes)...
level_size/=4;
}
}
// Skip the header and info block and possible unused mip levels
unsigned seek_size=file->Seek(SurfaceDesc.Size+4+skipped_offset);
WWASSERT(seek_size==(SurfaceDesc.Size+4+skipped_offset));
if (size) {
// Allocate memory for the data excluding the headers
DDSMemory=new unsigned char[size];
// Read data
unsigned read_size=file->Read(DDSMemory,size);
// Verify we got all the data
WWASSERT(read_size==size);
}
file->Close();
return true;
}
// ----------------------------------------------------------------------------
//
// Copy mipmap level to D3D surface. The copying is performed using another
// Copy_Level_To_Surface function (see below).
//
// ----------------------------------------------------------------------------
void DDSFileClass::Copy_Level_To_Surface(unsigned level,IDirect3DSurface8* d3d_surface)
{
WWASSERT(d3d_surface);
// Verify that the destination surface size matches the source surface size
D3DSURFACE_DESC surface_desc;
DX8_ErrorCode(d3d_surface->GetDesc(&surface_desc));
// First lock the surface
D3DLOCKED_RECT locked_rect;
DX8_ErrorCode(d3d_surface->LockRect(&locked_rect,NULL,0));
Copy_Level_To_Surface(
level,
D3DFormat_To_WW3DFormat(surface_desc.Format),
surface_desc.Width,
surface_desc.Height,
reinterpret_cast<unsigned char*>(locked_rect.pBits),
locked_rect.Pitch);
// Finally, unlock the surface
DX8_ErrorCode(d3d_surface->UnlockRect());
}
// ----------------------------------------------------------------------------
//
// Copy one mipmap level of texture to a memory surface. Surface type conversion
// is performed if the destination is of different format. Scaling will be done
// one of these days as well. Conversions between different types of compressed
// surfaces are not performed and scaling of compressed surfaces is also not
// possible.
//
// ----------------------------------------------------------------------------
void DDSFileClass::Copy_Level_To_Surface(
unsigned level,
WW3DFormat dest_format,
unsigned dest_width,
unsigned dest_height,
unsigned char* dest_surface,
unsigned dest_pitch)
{
WWASSERT(DDSMemory);
WWASSERT(dest_surface);
// If the format and size is a match just copy the contents
if (dest_format==Format && dest_width==Get_Width(level) && dest_height==Get_Height(level)) {
unsigned compressed_size=Get_Level_Size(level);
memcpy(dest_surface,Get_Memory_Pointer(level),compressed_size);
}
else {
// If size matches, copy each pixel linearly with color space conversion
if (dest_width==Get_Width(level) && dest_height==Get_Height(level)) {
// An exception here - if the source format is DXT1 and the destination
// is DXT2, just copy the contents and create an empty alpha channel.
// This is needed on NVidia cards that have problems with DXT1 compression.
if (Format==WW3D_FORMAT_DXT1 && dest_format==WW3D_FORMAT_DXT2) {
const unsigned* src_ptr=reinterpret_cast<const unsigned*>(Get_Memory_Pointer(level));
unsigned* dest_ptr=reinterpret_cast<unsigned*>(dest_surface);
for (unsigned y=0;y<dest_height;y+=4) {
for (unsigned x=0;x<dest_width;x+=4) {
*dest_ptr++=0xffffffff; // Bytes 1-4 of alpha block
*dest_ptr++=0xffffffff; // Bytes 5-8 of alpha block
*dest_ptr++=*src_ptr++; // Bytes 1-4 of color block
*dest_ptr++=*src_ptr++; // Bytes 5-8 of color block
}
}
}
else {
unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_format);
// Copy 4x4 block at a time
bool contains_alpha=false;
for (unsigned y=0;y<dest_height;y+=4) {
unsigned char* dest_ptr=dest_surface;
dest_ptr+=y*dest_pitch;
for (unsigned x=0;x<dest_width;x+=4,dest_ptr+=dest_bpp*4) {
contains_alpha|=Get_4x4_Block(dest_ptr,dest_pitch,dest_format,level,x,y);
}
}
if (Format==WW3D_FORMAT_DXT1 && contains_alpha) {
WWDEBUG_SAY(("Warning: DXT1 format should not contain alpha information - file %s\n",Name));
}
}
}
}
}
// ----------------------------------------------------------------------------
WWINLINE static unsigned RGB565_To_ARGB8888(unsigned short rgb)
{
unsigned rgba=0;
rgba|=unsigned(rgb&0x001f)<<3;
rgba|=unsigned(rgb&0x07e0)<<5;
rgba|=unsigned(rgb&0xf800)<<8;
return rgba;
}
// ----------------------------------------------------------------------------
WWINLINE static unsigned Combine_Colors(unsigned col1, unsigned col2, unsigned rel)
{
const unsigned R_B_MASK=0x00ff00ff;
const unsigned G_MASK=0x0000ff00;
unsigned rel2=255-rel;
unsigned r_b_col1=col1&R_B_MASK;
r_b_col1*=rel;
unsigned r_b_col2=col2&R_B_MASK;
r_b_col2*=rel2;
r_b_col1+=r_b_col2;
r_b_col1>>=8;
r_b_col1&=R_B_MASK;
unsigned g_col1=col1&G_MASK;
g_col1*=rel;
unsigned g_col2=col2&G_MASK;
g_col2*=rel2;
g_col1+=g_col2;
g_col1>>=8;
g_col1&=G_MASK;
return r_b_col1|g_col1;
/* float f=float(rel)/256.0f;
unsigned new_col=0;
new_col|=int(float(int(col1&0x00ff0000))*f+float(int(col2&0x00ff0000))*(1.0f-f))&0x00ff0000;
new_col|=int(float(int(col1&0x0000ff00))*f+float(int(col2&0x0000ff00))*(1.0f-f))&0x0000ff00;
new_col|=int(float(int(col1&0x000000ff))*f+float(int(col2&0x000000ff))*(1.0f-f))&0x000000ff;
return new_col;
*/
}
// ----------------------------------------------------------------------------
//
// Note that this is NOT an efficient way of extracting pixels from compressed image - we should implement
// faster block-copy method for non-scaled copying.
//
// ----------------------------------------------------------------------------
unsigned DDSFileClass::Get_Pixel(unsigned level,unsigned x,unsigned y) const
{
WWASSERT(level<MipLevels);
WWASSERT(x<Get_Width(level));
WWASSERT(y<Get_Height(level));
switch (Format) {
// Note that we don't currently really support alpha on DXT1 - all alpha textures should use DXT5.
// The reason for this is that when converting from DXT1 to 16 bit uncompressed texture we want
// to be able to use RGB565 format instead of ARGB4444. As the alpha is encoded in DXT1 per-block
// basis there isn't really a way to tell if the surface has an alpha or not so either we use alpha
// or we don't.
case WW3D_FORMAT_DXT1:
{
const unsigned char* block_memory=Get_Memory_Pointer(level)+(x/4)*8+((y/4)*(Get_Width(level)/4))*8;
unsigned col0=RGB565_To_ARGB8888(*(unsigned short*)&block_memory[0]);
unsigned col1=RGB565_To_ARGB8888(*(unsigned short*)&block_memory[2]);
unsigned char line=block_memory[4+(y%4)];
line>>=(x%4)*2;
line=(line&3);
if (col0>col1) {
switch (line) {
case 0: return col0|0xff000000;
case 1: return col1|0xff000000;
case 2: return Combine_Colors(col1,col0,85)|0xff000000;
case 3: return Combine_Colors(col0,col1,85)|0xff000000;
}
}
else {
switch (line) {
case 0: return col0|0xff000000;
case 1: return col1|0xff000000;
case 2: return Combine_Colors(col1,col0,128)|0xff000000;
case 3: return 0x00000000;
}
}
}
break;
case WW3D_FORMAT_DXT2:
return 0xffffffff;
case WW3D_FORMAT_DXT3:
return 0xffffffff;
case WW3D_FORMAT_DXT4:
return 0xffffffff;
case WW3D_FORMAT_DXT5:
{
const unsigned char* alpha_block=Get_Memory_Pointer(level)+(x/4)*16+((y/4)*(Get_Width(level)/4))*16;
unsigned alpha0=alpha_block[0];
unsigned alpha1=alpha_block[1];
unsigned bit_idx=((x%4)+4*(y%4))*3;
unsigned byte_idx=bit_idx/8;
bit_idx%=8;
unsigned alpha_index=0;
for (int i=0;i<3;++i) {
WWASSERT(byte_idx<6);
unsigned alpha_bit=(alpha_block[2+byte_idx]>>(bit_idx))&1;
alpha_index|=alpha_bit<<(i);
bit_idx++;
if (bit_idx>=8) {
bit_idx=0;
byte_idx++;
}
}
WWASSERT(alpha_index<8);
// 8-alpha or 6-alpha block?
unsigned alpha_value=0;
if (alpha0>alpha1) {
// 8-alpha block: derive the other six alphas.
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
switch (alpha_index) {
case 0: alpha_value=alpha0; break;
case 1: alpha_value=alpha1; break;
case 2: alpha_value=(6*alpha0+1*alpha1+3) / 7; break; // bit code 010
case 3: alpha_value=(5*alpha0+2*alpha1+3) / 7; break; // bit code 011
case 4: alpha_value=(4*alpha0+3*alpha1+3) / 7; break; // bit code 100
case 5: alpha_value=(3*alpha0+4*alpha1+3) / 7; break; // bit code 101
case 6: alpha_value=(2*alpha0+5*alpha1+3) / 7; break; // bit code 110
case 7: alpha_value=(1*alpha0+6*alpha1+3) / 7; break; // bit code 111
}
}
else {
// 6-alpha block.
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
switch (alpha_index) {
case 0: alpha_value=alpha0; break;
case 1: alpha_value=alpha1; break;
case 2: alpha_value=(4*alpha0+1*alpha1+2) / 5; break; // Bit code 010
case 3: alpha_value=(3*alpha0+2*alpha1+2) / 5; break; // Bit code 011
case 4: alpha_value=(2*alpha0+3*alpha1+2) / 5; break; // Bit code 100
case 5: alpha_value=(1*alpha0+4*alpha1+2) / 5; break; // Bit code 101
case 6: alpha_value=0; break; // Bit code 110
case 7: alpha_value=255; break; // Bit code 111
}
}
alpha_value<<=24;
// Extract color
const unsigned char* color_block=alpha_block+8;
unsigned col0=RGB565_To_ARGB8888(*(unsigned short*)&color_block[0]);
unsigned col1=RGB565_To_ARGB8888(*(unsigned short*)&color_block[2]);
unsigned char line=color_block[4+(y%4)];
line>>=(x%4)*2;
line=(line&3);
switch (line) {
case 0: return col0|alpha_value;
case 1: return col1|alpha_value;
case 2: return Combine_Colors(col1,col0,85)|alpha_value;
case 3: return Combine_Colors(col0,col1,85)|alpha_value;
}
}
break;
}
return 0xffffffff;
}
// ----------------------------------------------------------------------------
//
// Uncompress one 4x4 block from the compressed image.
//
// Returns: true if block contained alpha, false is not
//
// Note: Destination can't be DXT or paletted surface!
//
// ----------------------------------------------------------------------------
bool DDSFileClass::Get_4x4_Block(
unsigned char* dest_ptr, // Destination surface pointer
unsigned dest_pitch, // Destination surface pitch, in bytes
WW3DFormat dest_format, // Destination surface format, A8R8G8B8 is fastest
unsigned level, // DDS mipmap level to copy from
unsigned source_x, // DDS x offset to copy from, must be aligned by 4!
unsigned source_y) const // DDS y offset to copy from, must be aligned by 4!
{
// Verify the block alignment
WWASSERT((source_x&3)==0);
WWASSERT((source_y&3)==0);
// Verify level
WWASSERT(level<MipLevels);
// Verify coordinate bounds
WWASSERT(source_x<Get_Width(level));
WWASSERT(source_y<Get_Height(level));
unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_format);
switch (Format) {
// Note that we don't currently really support alpha on DXT1 - all alpha textures should use DXT5.
// The reason for this is that when converting from DXT1 to 16 bit uncompressed texture we want
// to be able to use RGB565 format instead of ARGB4444. As the alpha is encoded in DXT1 per-block
// basis there isn't really a way to tell if the surface has an alpha or not so either we use alpha
// or we don't.
case WW3D_FORMAT_DXT1:
{
const unsigned char* block_memory=Get_Memory_Pointer(level)+(source_x/4)*8+((source_y/4)*(Get_Width(level)/4))*8;
unsigned col0=RGB565_To_ARGB8888(*(unsigned short*)&block_memory[0]);
unsigned col1=RGB565_To_ARGB8888(*(unsigned short*)&block_memory[2]);
// Even if we don't support alpha, decompression is different if source has alpha
unsigned dest_pixel=0;
if (col0>col1) {
for (int y=0;y<4;++y) {
unsigned char* tmp_dest_ptr=dest_ptr;
dest_ptr+=dest_pitch;
unsigned char line=block_memory[4+y];
for (int x=0;x<4;++x) {
switch (line&3) {
case 0: dest_pixel=col0|0xff000000; break;
case 1: dest_pixel=col1|0xff000000; break;
case 2: dest_pixel=Combine_Colors(col1,col0,85)|0xff000000; break;
case 3: dest_pixel=Combine_Colors(col0,col1,85)|0xff000000; break;
}
line>>=2;
BitmapHandlerClass::Write_B8G8R8A8(tmp_dest_ptr,dest_format,dest_pixel);
tmp_dest_ptr+=dest_bpp;
}
}
return false; // No alpha found in the block
}
else {
bool contains_alpha=false;
for (int y=0;y<4;++y) {
unsigned char* tmp_dest_ptr=dest_ptr;
dest_ptr+=dest_pitch;
unsigned char line=block_memory[4+y];
for (int x=0;x<4;++x) {
switch (line&3) {
case 0: dest_pixel=col0|0xff000000; break;
case 1: dest_pixel=col1|0xff000000; break;
case 2: dest_pixel=Combine_Colors(col1,col0,128)|0xff000000; break;
case 3: dest_pixel=0x00000000; contains_alpha=true; break;
}
line>>=2;
BitmapHandlerClass::Write_B8G8R8A8(tmp_dest_ptr,dest_format,dest_pixel);
tmp_dest_ptr+=dest_bpp;
}
}
return contains_alpha; // Alpha block...?
}
}
break;
case WW3D_FORMAT_DXT2:
return false;
case WW3D_FORMAT_DXT3:
return false;
case WW3D_FORMAT_DXT4:
return false;
case WW3D_FORMAT_DXT5:
{
// Init alphas
const unsigned char* alpha_block=Get_Memory_Pointer(level)+(source_x/4)*16+((source_y/4)*(Get_Width(level)/4))*16;
unsigned alphas[8];
alphas[0]=alpha_block[0];
alphas[1]=alpha_block[1];
// 8-alpha or 6-alpha block?
if (alphas[0]>alphas[1]) {
alphas[2]=(6*alphas[0]+1*alphas[1]+3) / 7; // bit code 010
alphas[3]=(5*alphas[0]+2*alphas[1]+3) / 7; // bit code 011
alphas[4]=(4*alphas[0]+3*alphas[1]+3) / 7; // bit code 100
alphas[5]=(3*alphas[0]+4*alphas[1]+3) / 7; // bit code 101
alphas[6]=(2*alphas[0]+5*alphas[1]+3) / 7; // bit code 110
alphas[7]=(1*alphas[0]+6*alphas[1]+3) / 7; // bit code 111
}
else {
alphas[2]=(4*alphas[0]+1*alphas[1]+2) / 5; // Bit code 010
alphas[3]=(3*alphas[0]+2*alphas[1]+2) / 5; // Bit code 011
alphas[4]=(2*alphas[0]+3*alphas[1]+2) / 5; // Bit code 100
alphas[5]=(1*alphas[0]+4*alphas[1]+2) / 5; // Bit code 101
alphas[6]=0; // Bit code 110
alphas[7]=255; // Bit code 111
}
// Init colors
const unsigned char* color_block=alpha_block+8;
unsigned col0=RGB565_To_ARGB8888(*(unsigned short*)&color_block[0]);
unsigned col1=RGB565_To_ARGB8888(*(unsigned short*)&color_block[2]);
unsigned dest_pixel=0;
unsigned bit_idx=0;
unsigned contains_alpha=0xff;
unsigned alpha_indices[16];
unsigned* ai_ptr=alpha_indices;
for (int a=0;a<2;++a) {
ai_ptr[0]=alpha_block[2]&0x7;
ai_ptr[1]=(alpha_block[2]>>3)&0x7;
ai_ptr[2]=(alpha_block[2]>>6)|((alpha_block[3]&1)<<2);
ai_ptr[3]=(alpha_block[3]>>1)&0x7;
ai_ptr[4]=(alpha_block[3]>>4)&0x7;
ai_ptr[5]=(alpha_block[3]>>7)|((alpha_block[4]&3)<<1);
ai_ptr[6]=(alpha_block[4]>>2)&0x7;
ai_ptr[7]=(alpha_block[4]>>5);
ai_ptr+=8;
alpha_block+=3;
}
unsigned aii=0;
for (int y=0;y<4;++y) {
unsigned char* tmp_dest_ptr=dest_ptr;
dest_ptr+=dest_pitch;
unsigned char line=color_block[4+y];
for (int x=0;x<4;++x,bit_idx+=3) {
unsigned alpha_value=alphas[alpha_indices[aii++]];
contains_alpha&=alpha_value;
alpha_value<<=24;
// Extract color
switch (line&3) {
case 0: dest_pixel=col0|alpha_value; break;
case 1: dest_pixel=col1|alpha_value; break;
case 2: dest_pixel=Combine_Colors(col1,col0,85)|alpha_value; break;
case 3: dest_pixel=Combine_Colors(col0,col1,85)|alpha_value; break;
}
line>>=2;
BitmapHandlerClass::Write_B8G8R8A8(tmp_dest_ptr,dest_format,dest_pixel);
tmp_dest_ptr+=dest_bpp;
}
}
/*
for (int y=0;y<4;++y) {
unsigned char* tmp_dest_ptr=dest_ptr;
dest_ptr+=dest_pitch;
unsigned char line=color_block[4+y];
for (int x=0;x<4;++x,bit_idx+=3) {
unsigned byte_idx=bit_idx/8;
unsigned tmp_bit_idx=bit_idx&7;
unsigned alpha_index=0;
for (int i=0;i<3;++i) {
WWASSERT(byte_idx<6);
unsigned alpha_bit=(alpha_block[2+byte_idx]>>(tmp_bit_idx))&1;
alpha_index|=alpha_bit<<(i);
tmp_bit_idx++;
if (tmp_bit_idx>=8) {
tmp_bit_idx=0;
byte_idx++;
}
}
WWASSERT(alpha_index<8);
unsigned alpha_value=alphas[alpha_index];
contains_alpha&=alpha_value;
alpha_value<<=24;
// Extract color
switch (line&3) {
case 0: dest_pixel=col0|alpha_value; break;
case 1: dest_pixel=col1|alpha_value; break;
case 2: dest_pixel=Combine_Colors(col1,col0,85)|alpha_value; break;
case 3: dest_pixel=Combine_Colors(col0,col1,85)|alpha_value; break;
}
line>>=2;
BitmapHandlerClass::Write_B8G8R8A8(tmp_dest_ptr,dest_format,dest_pixel);
tmp_dest_ptr+=dest_bpp;
}
}
*/
return contains_alpha!=0xff; // Alpha block... DXT5 should only be used when the image needs alpha
// but for now check anyway...
}
}
return false;
}