void Image_DXTC::Decompress() { VERIFY( m_pCompBytes ); AllocateDecompBytes(); VERIFY( m_pDecompBytes ); // must already have allocated memory switch( m_CompFormat ) { case PF_DXT1 : //TRACE( "Decompressing image format: DXT1\n" ); DecompressDXT1(); break; case PF_DXT2 : //TRACE( "Decompressing image format: DXT2\n" ); DecompressDXT2(); break; case PF_DXT3 : //TRACE( "Decompressing image format: DXT3\n" ); DecompressDXT3(); break; case PF_DXT4 : //TRACE( "Decompressing image format: DXT4\n" ); DecompressDXT4(); break; case PF_DXT5 : //TRACE( "Decompressing image format: DXT5\n" ); DecompressDXT5(); break; case PF_UNKNOWN : break; } //. swap R<->B channels for (int y=0; y<m_nHeight; y++) { for (int x=0; x<m_nWidth; x++) { BYTE* ptr = m_pDecompBytes + (y*m_nWidth+x)*4; swap (ptr[0],ptr[2]); } } }
// Internal function used to load the BLP. ILboolean iLoadBlpInternal(void) { BLP2HEAD Header; ILubyte *CompData; ILimage *Image; ILuint Mip, j, x, CompSize, AlphaSize, AlphaOff; ILint y; ILboolean BaseCreated = IL_FALSE; ILubyte *DataAndAlpha = NULL, *Palette = NULL, AlphaMask; //, *JpegHeader, *JpegData; if (iCurImage == NULL) { ilSetError(IL_ILLEGAL_OPERATION); return IL_FALSE; } if (!iGetBlp2Head(&Header)) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } if (!iCheckBlp2(&Header)) { goto check_blp1; } //@TODO: Remove this! if (Header.Type != BLP_TYPE_DXTC_RAW) return IL_FALSE; switch (Header.Compression) { case BLP_RAW: for (Mip = 0; Mip < 16; Mip++) { // Possible maximum of 16 mipmaps if (BaseCreated) { if (Header.HasMips == 0) // Does not have mipmaps, so we are done. break; if (Image->Width == 1 && Image->Height == 1) // Already at the smallest mipmap (1x1), so we are done. break; if (Header.MipOffsets[Mip] == 0 || Header.MipLengths == 0) // No more mipmaps in the file. break; } switch (Header.AlphaBits) { case 0: if (!BaseCreated) { // Have not created the base image yet, so use ilTexImage. if (!ilTexImage(Header.Width, Header.Height, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL)) return IL_FALSE; Image = iCurImage; BaseCreated = IL_TRUE; Image->Pal.Palette = (ILubyte*)ialloc(256 * 4); // 256 entries of ARGB8888 values (1024). if (Image->Pal.Palette == NULL) return IL_FALSE; Image->Pal.PalSize = 1024; Image->Pal.PalType = IL_PAL_BGRA32; //@TODO: Find out if this is really BGRA data. if (iread(Image->Pal.Palette, 1, 1024) != 1024) // Read in the palette. return IL_FALSE; } else { Image->Mipmaps = ilNewImageFull(Image->Width >> 1, Image->Height >> 1, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL); if (Image->Mipmaps == NULL) return IL_FALSE; // Copy the palette from the first image before we change our Image pointer. iCopyPalette(&Image->Mipmaps->Pal, &Image->Pal); // Move to the next mipmap in the linked list. Image = Image->Mipmaps; } // The origin should be in the upper left. Image->Origin = IL_ORIGIN_UPPER_LEFT; // These two should be the same (tells us how much data is in the file for this mipmap level). if (Header.MipLengths[Mip] != Image->SizeOfData) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } // Finally read in the image data. iseek(Header.MipOffsets[Mip], IL_SEEK_SET); if (iread(Image->Data, 1, Image->SizeOfData) != Image->SizeOfData) return IL_FALSE; break; case 1: if (!BaseCreated) { // Have not created the base image yet, so use ilTexImage. if (!ilTexImage(Header.Width, Header.Height, 1, 4, IL_BGRA, IL_UNSIGNED_BYTE, NULL)) return IL_FALSE; Image = iCurImage; BaseCreated = IL_TRUE; Palette = (ILubyte*)ialloc(256 * 4); if (Palette == NULL) return IL_FALSE; // Read in the palette. if (iread(Palette, 1, 1024) != 1024) { ifree(Palette); return IL_FALSE; } // We only allocate this once and reuse this buffer with every mipmap (since successive ones are smaller). DataAndAlpha = (ILubyte*)ialloc(Image->Width * Image->Height); if (DataAndAlpha == NULL) { ifree(DataAndAlpha); ifree(Palette); return IL_FALSE; } } else { Image->Mipmaps = ilNewImageFull(Image->Width >> 1, Image->Height >> 1, 1, 4, IL_BGRA, IL_UNSIGNED_BYTE, NULL); if (Image->Mipmaps == NULL) return IL_FALSE; // Move to the next mipmap in the linked list. Image = Image->Mipmaps; } // The origin should be in the upper left. Image->Origin = IL_ORIGIN_UPPER_LEFT; // Determine the size of the alpha data following the color indices. AlphaSize = Image->Width * Image->Height / 8; if (AlphaSize == 0) AlphaSize = 1; // Should never be 0. // These two should be the same (tells us how much data is in the file for this mipmap level). if (Header.MipLengths[Mip] != Image->SizeOfData / 4 + AlphaSize) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } // Seek to the data and read it. iseek(Header.MipOffsets[Mip], IL_SEEK_SET); if (iread(DataAndAlpha, Image->Width * Image->Height, 1) != 1) { ifree(DataAndAlpha); ifree(Palette); return IL_FALSE; } // Convert the color-indexed data to BGRX. for (j = 0; j < Image->Width * Image->Height; j++) { Image->Data[j*4] = Palette[DataAndAlpha[j]*4]; Image->Data[j*4+1] = Palette[DataAndAlpha[j]*4+1]; Image->Data[j*4+2] = Palette[DataAndAlpha[j]*4+2]; } // Read in the alpha list. if (iread(DataAndAlpha, AlphaSize, 1) != 1) { ifree(DataAndAlpha); ifree(Palette); return IL_FALSE; } AlphaMask = 0x01; // Lowest bit AlphaOff = 0; // The really strange thing about this alpha data is that it is upside-down when compared to the // regular color-indexed data, so we have to flip it. for (y = Image->Height - 1; y >= 0; y--) { for (x = 0; x < Image->Width; x++) { if (AlphaMask == 0) { // Shifting it past the highest bit makes it 0, since we only have 1 byte. AlphaOff++; // Move along the alpha buffer. AlphaMask = 0x01; // Reset the alpha mask. } // This is just 1-bit alpha, so it is either on or off. Image->Data[Image->Bps * y + x * 4 + 3] = DataAndAlpha[AlphaOff] & AlphaMask ? 0xFF : 0x00; AlphaMask <<= 1; } } break; default: //@TODO: Accept any other alpha values? ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } } // Done, so we can finally free these two. ifree(DataAndAlpha); ifree(Palette); break; case BLP_DXTC: for (Mip = 0; Mip < 16; Mip++) { // Possible maximum of 16 mipmaps //@TODO: Other formats //if (Header.AlphaBits == 0) // if (!ilTexImage(Header.Width, Header.Height, 1, 3, IL_RGB, IL_UNSIGNED_BYTE, NULL)) // return IL_FALSE; if (!BaseCreated) { // Have not created the base image yet, so use ilTexImage. if (!ilTexImage(Header.Width, Header.Height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL)) return IL_FALSE; Image = iCurImage; BaseCreated = IL_TRUE; } else { if (Header.HasMips == 0) // Does not have mipmaps, so we are done. break; if (Image->Width == 1 && Image->Height == 1) // Already at the smallest mipmap (1x1), so we are done. break; if (Header.MipOffsets[Mip] == 0 || Header.MipLengths[Mip] == 0) // No more mipmaps in the file. break; //@TODO: Other formats // ilNewImageFull automatically changes widths and heights of 0 to 1, so we do not have to worry about it. Image->Mipmaps = ilNewImageFull(Image->Width >> 1, Image->Height >> 1, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL); if (Image->Mipmaps == NULL) return IL_FALSE; Image = Image->Mipmaps; } // The origin should be in the upper left. Image->Origin = IL_ORIGIN_UPPER_LEFT; //@TODO: Only do the allocation once. CompData = (ILubyte*)ialloc(Header.MipLengths[Mip]); if (CompData == NULL) return IL_FALSE; // Read in the compressed mipmap data. iseek(Header.MipOffsets[Mip], IL_SEEK_SET); if (iread(CompData, 1, Header.MipLengths[Mip]) != Header.MipLengths[Mip]) { ifree(CompData); return IL_FALSE; } switch (Header.AlphaBits) { case 0: // DXT1 without alpha case 1: // DXT1 with alpha // Check to make sure that the MipLength reported is the size needed, so that // DecompressDXT1 does not crash. CompSize = ((Image->Width + 3) / 4) * ((Image->Height + 3) / 4) * 8; if (CompSize != Header.MipLengths[Mip]) { ilSetError(IL_INVALID_FILE_HEADER); ifree(CompData); return IL_FALSE; } if (!DecompressDXT1(Image, CompData)) { ifree(CompData); return IL_FALSE; } break; case 8: // Check to make sure that the MipLength reported is the size needed, so that // DecompressDXT3/5 do not crash. CompSize = ((Image->Width + 3) / 4) * ((Image->Height + 3) / 4) * 16; if (CompSize != Header.MipLengths[Mip]) { ifree(CompData); ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } switch (Header.AlphaType) { case 0: // All three of case 1: // these refer to case 8: // DXT3... if (!DecompressDXT3(Image, CompData)) { ifree(CompData); return IL_FALSE; } break; case 7: // DXT5 compression if (!DecompressDXT5(Image, CompData)) { ifree(CompData); return IL_FALSE; } break; //default: // Should already be checked by iCheckBlp2. } break; //default: // Should already be checked by iCheckBlp2. } //@TODO: Save DXTC data. ifree(CompData); } break; //default: } return ilFixImage(); check_blp1: iseek(-148, IL_SEEK_CUR); // Go back the size of the BLP2 header, since we tried reading it. return iLoadBlp1(); }
// Internal function used to load the ROT. ILboolean iLoadRotInternal(void) { ILubyte Form[4], FormName[4]; ILuint FormLen, Width, Height, Format, Channels, CompSize; ILuint MipSize, MipLevel, MipWidth, MipHeight; ILenum FormatIL; ILimage *Image; ILboolean BaseCreated = IL_FALSE; ILubyte *CompData = NULL; if (iCurImage == NULL) { ilSetError(IL_ILLEGAL_OPERATION); return IL_FALSE; } // The first entry in the file must be 'FORM', 0x20 in a big endian integer and then 'HEAD'. iread(Form, 1, 4); FormLen = GetBigUInt(); iread(FormName, 1, 4); if (strncmp(Form, "FORM", 4) || FormLen != 0x14 || strncmp(FormName, "HEAD", 4)) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } // Next follows the width, height and format in the header. Width = GetLittleUInt(); Height = GetLittleUInt(); Format = GetLittleUInt(); //@TODO: More formats. switch (Format) { case ROT_RGBA32: // 32-bit RGBA format Channels = 4; FormatIL = IL_RGBA; break; case ROT_DXT1: // DXT1 (no alpha) Channels = 4; FormatIL = IL_RGBA; break; case ROT_DXT3: // DXT3 case ROT_DXT5: // DXT5 Channels = 4; FormatIL = IL_RGBA; // Allocates the maximum needed (the first width/height given in the file). CompSize = ((Width + 3) / 4) * ((Height + 3) / 4) * 16; CompData = ialloc(CompSize); if (CompData == NULL) return IL_FALSE; break; default: ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } if (Width == 0 || Height == 0) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } //@TODO: Find out what this is. GetLittleUInt(); // Skip this for the moment. This appears to be the number of channels. // Next comes 'FORM', a length and 'MIPS'. iread(Form, 1, 4); FormLen = GetBigUInt(); iread(FormName, 1, 4); //@TODO: Not sure if the FormLen has to be anything specific here. if (strncmp(Form, "FORM", 4) || strncmp(FormName, "MIPS", 4)) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } //@TODO: Can these mipmap levels be in any order? Some things may be easier if the answer is no. Image = iCurImage; do { // Then we have 'FORM' again. iread(Form, 1, 4); // This is the size of the mipmap data. MipSize = GetBigUInt(); iread(FormName, 1, 4); if (strncmp(Form, "FORM", 4)) { if (!BaseCreated) { // Our file is malformed. ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } // We have reached the end of the mipmap data. break; } if (strncmp(FormName, "MLVL", 4)) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } // Next is the mipmap attributes (level number, width, height and length) MipLevel = GetLittleUInt(); MipWidth = GetLittleUInt(); MipHeight = GetLittleUInt(); MipSize = GetLittleUInt(); // This is the same as the previous size listed -20 (for attributes). // Lower level mipmaps cannot be larger than the main image. if (MipWidth > Width || MipHeight > Height || MipSize > CompSize) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } // Just create our images here. if (!BaseCreated) { if (!ilTexImage(MipWidth, MipHeight, 1, Channels, FormatIL, IL_UNSIGNED_BYTE, NULL)) return IL_FALSE; BaseCreated = IL_TRUE; } else { Image->Mipmaps = ilNewImageFull(MipWidth, MipHeight, 1, Channels, FormatIL, IL_UNSIGNED_BYTE, NULL); Image = Image->Mipmaps; } switch (Format) { case ROT_RGBA32: // 32-bit RGBA format if (iread(Image->Data, Image->SizeOfData, 1) != 1) return IL_FALSE; break; case ROT_DXT1: // Allocates the size of the compressed data. CompSize = ((MipWidth + 3) / 4) * ((MipHeight + 3) / 4) * 8; if (CompSize != MipSize) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } CompData = ialloc(CompSize); if (CompData == NULL) return IL_FALSE; // Read in the DXT1 data... if (iread(CompData, CompSize, 1) != 1) return IL_FALSE; // ...and decompress it. if (!DecompressDXT1(Image, CompData)) { ifree(CompData); return IL_FALSE; } if (ilGetInteger(IL_KEEP_DXTC_DATA) == IL_TRUE) { Image->DxtcSize = CompSize; Image->DxtcData = CompData; Image->DxtcFormat = IL_DXT1; CompData = NULL; } break; case ROT_DXT3: // Allocates the size of the compressed data. CompSize = ((MipWidth + 3) / 4) * ((MipHeight + 3) / 4) * 16; if (CompSize != MipSize) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } CompData = ialloc(CompSize); if (CompData == NULL) return IL_FALSE; // Read in the DXT3 data... if (iread(CompData, MipSize, 1) != 1) return IL_FALSE; // ...and decompress it. if (!DecompressDXT3(Image, CompData)) { ifree(CompData); return IL_FALSE; } if (ilGetInteger(IL_KEEP_DXTC_DATA) == IL_TRUE) { Image->DxtcSize = CompSize; Image->DxtcData = CompData; Image->DxtcFormat = IL_DXT3; CompData = NULL; } break; case ROT_DXT5: // Allocates the size of the compressed data. CompSize = ((MipWidth + 3) / 4) * ((MipHeight + 3) / 4) * 16; if (CompSize != MipSize) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } CompData = ialloc(CompSize); if (CompData == NULL) return IL_FALSE; // Read in the DXT5 data... if (iread(CompData, MipSize, 1) != 1) return IL_FALSE; // ...and decompress it. if (!DecompressDXT5(Image, CompData)) { ifree(CompData); return IL_FALSE; } // Keeps a copy if (ilGetInteger(IL_KEEP_DXTC_DATA) == IL_TRUE) { Image->DxtcSize = CompSize; Image->DxtcData = CompData; Image->DxtcFormat = IL_DXT5; CompData = NULL; } break; } ifree(CompData); // Free it if it was not saved. } while (!ieof()); //@TODO: Is there any other condition that should end this? return ilFixImage(); }