//----------------------------------------------------------------------------- // // ImageLib Sources // Copyright (C) 2000-2002 by Denton Woods // Last modified: 06/20/2002 <--Y2K Compliant! =] // // Filename: src-IL/src/il_dds-save.c // // Description: Saves a DirectDraw Surface (.dds) file. // //----------------------------------------------------------------------------- #include "il_internal.h" #include "il_dds.h" #include "il_manip.h" #include #ifndef IL_NO_DDS //! Writes a Dds file ILboolean ilSaveDds(ILconst_string FileName) { ILHANDLE DdsFile; ILboolean bDds = IL_FALSE; if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) { if (iFileExists(FileName)) { ilSetError(IL_FILE_ALREADY_EXISTS); return IL_FALSE; } } DdsFile = iopenw(FileName); if (DdsFile == NULL) { ilSetError(IL_COULD_NOT_OPEN_FILE); return bDds; } bDds = ilSaveDdsF(DdsFile); iclosew(DdsFile); return bDds; } //! Writes a Dds to an already-opened file ILboolean ilSaveDdsF(ILHANDLE File) { iSetOutputFile(File); return iSaveDdsInternal(); } //! Writes a Dds to a memory "lump" ILboolean ilSaveDdsL(ILvoid *Lump, ILuint Size) { iSetOutputLump(Lump, Size); return iSaveDdsInternal(); } //! Checks if an image is a cubemap ILuint GetCubemapInfo(ILimage* image, ILint* faces) { ILint indices[] = { -1, -1, -1, -1, -1, -1 }, i; ILimage* img; ILuint ret = 0, srcMipmapCount, srcImagesCount, mipmapCount; if (image == NULL) return 0; iGetIntegervImage(image, IL_NUM_IMAGES, (ILint*) &srcImagesCount ); if (srcImagesCount != 5) //write only complete cubemaps (TODO?) return 0; img = image; iGetIntegervImage(image, IL_NUM_MIPMAPS, (ILint*) &srcMipmapCount ); mipmapCount = srcMipmapCount; for (i = 0; i < 6; ++i) { switch(img->CubeFlags) { case DDS_CUBEMAP_POSITIVEX: indices[i] = 0; break; case DDS_CUBEMAP_NEGATIVEX: indices[i] = 1; break; case DDS_CUBEMAP_POSITIVEY: indices[i] = 2; break; case DDS_CUBEMAP_NEGATIVEY: indices[i] = 3; break; case DDS_CUBEMAP_POSITIVEZ: indices[i] = 4; break; case DDS_CUBEMAP_NEGATIVEZ: indices[i] = 5; break; } iGetIntegervImage(img, IL_NUM_MIPMAPS, (ILint*) &srcMipmapCount ); if (srcMipmapCount != mipmapCount) return 0; //equal # of mipmaps required ret |= img->CubeFlags; img = img->Next; } for (i = 0; i < 6; ++i) if (indices[i] == -1) return 0; //one face not found if (ret != 0) //should always be true ret |= DDS_CUBEMAP; for (i =0; i < 6; ++i) faces[indices[i]] = i; return ret; } // Internal function used to save the Dds. ILboolean iSaveDdsInternal() { ILenum DXTCFormat; ILuint counter, numMipMaps, image, numFaces, i; ILubyte *CurData = NULL; ILint CubeTable[6] = { 0 }; ILuint CubeFlags; CubeFlags = GetCubemapInfo(iCurImage, CubeTable); image = ilGetInteger(IL_CUR_IMAGE); DXTCFormat = iGetInt(IL_DXTC_FORMAT); WriteHeader(iCurImage, DXTCFormat, CubeFlags); if (CubeFlags != 0) numFaces = ilGetInteger(IL_NUM_IMAGES); //should always be 5 for now else numFaces = 0; numMipMaps = ilGetInteger(IL_NUM_MIPMAPS); //this assumes all faces have same # of mipmaps for (i = 0; i <= numFaces; ++i) { for (counter = 0; counter <= numMipMaps; counter++) { ilBindImage(image); ilActiveImage(CubeTable[i]); ilActiveMipmap(counter); if (iCurImage->Origin != IL_ORIGIN_UPPER_LEFT) { CurData = iCurImage->Data; iCurImage->Data = iGetFlipped(iCurImage); if (iCurImage->Data == NULL) { iCurImage->Data = CurData; return IL_FALSE; } } if (!Compress(iCurImage, DXTCFormat)) return IL_FALSE; if (iCurImage->Origin != IL_ORIGIN_UPPER_LEFT) { ifree(iCurImage->Data); iCurImage->Data = CurData; } } } return IL_TRUE; } // @TODO: Finish this, as it is incomplete. ILboolean WriteHeader(ILimage *Image, ILenum DXTCFormat, ILuint CubeFlags) { ILuint i, FourCC, Flags1 = 0, Flags2 = 0, ddsCaps1 = 0, LinearSize, BlockSize, ddsCaps2 = 0; Flags1 |= DDS_LINEARSIZE | DDS_MIPMAPCOUNT | DDS_WIDTH | DDS_HEIGHT | DDS_CAPS | DDS_PIXELFORMAT; Flags2 |= DDS_FOURCC; if (Image->Depth > 1) Flags1 |= DDS_DEPTH; // @TODO: Fix the pre-multiplied alpha problem. if (DXTCFormat == IL_DXT2) DXTCFormat = IL_DXT3; else if (DXTCFormat == IL_DXT4) DXTCFormat = IL_DXT5; switch (DXTCFormat) { case IL_DXT1: FourCC = IL_MAKEFOURCC('D','X','T','1'); break; case IL_DXT2: FourCC = IL_MAKEFOURCC('D','X','T','2'); break; case IL_DXT3: FourCC = IL_MAKEFOURCC('D','X','T','3'); break; case IL_DXT4: FourCC = IL_MAKEFOURCC('D','X','T','4'); break; case IL_DXT5: FourCC = IL_MAKEFOURCC('D','X','T','5'); break; case IL_ATI1N: FourCC = IL_MAKEFOURCC('A', 'T', 'I', '1'); break; case IL_3DC: FourCC = IL_MAKEFOURCC('A','T','I','2'); break; case IL_RXGB: FourCC = IL_MAKEFOURCC('R','X','G','B'); break; default: // Error! ilSetError(IL_INTERNAL_ERROR); // Should never happen, though. return IL_FALSE; } iwrite("DDS ", 1, 4); SaveLittleUInt(124); // Size1 SaveLittleUInt(Flags1); // Flags1 SaveLittleUInt(Image->Height); SaveLittleUInt(Image->Width); if (DXTCFormat == IL_DXT1 || DXTCFormat == IL_ATI1N) { BlockSize = 8; } else { BlockSize = 16; } LinearSize = (((Image->Width + 3)/4) * ((Image->Height + 3)/4)) * BlockSize * Image->Depth; /* // doing this is actually wrong, linear size is only size of one cube face if (CubeFlags != 0) { ILint numFaces = 0; for (i = 0; i < 6; ++i) if (CubeFlags & CubemapDirections[i]) ++numFaces; LinearSize *= numFaces; } */ SaveLittleUInt(LinearSize); // LinearSize (TODO: change this when uncompressed formats are supported) if (Image->Depth > 1) { SaveLittleUInt(Image->Depth); // Depth ddsCaps2 |= DDS_VOLUME; } else SaveLittleUInt(0); // Depth SaveLittleUInt(ilGetInteger(IL_NUM_MIPMAPS) + 1); // MipMapCount SaveLittleUInt(0); // AlphaBitDepth for (i = 0; i < 10; i++) SaveLittleUInt(0); // Not used SaveLittleUInt(32); // Size2 SaveLittleUInt(Flags2); // Flags2 SaveLittleUInt(FourCC); // FourCC SaveLittleUInt(0); // RGBBitCount SaveLittleUInt(0); // RBitMask SaveLittleUInt(0); // GBitMask SaveLittleUInt(0); // BBitMask SaveLittleUInt(0); // RGBAlphaBitMask ddsCaps1 |= DDS_TEXTURE; //changed 20040516: set mipmap flag on mipmap images //(non-compressed .dds files still not supported, //though) if (ilGetInteger(IL_NUM_MIPMAPS) > 0) ddsCaps1 |= DDS_MIPMAP | DDS_COMPLEX; if (CubeFlags != 0) { ddsCaps1 |= DDS_COMPLEX; ddsCaps2 |= CubeFlags; } SaveLittleUInt(ddsCaps1); // ddsCaps1 SaveLittleUInt(ddsCaps2); // ddsCaps2 SaveLittleUInt(0); // ddsCaps3 SaveLittleUInt(0); // ddsCaps4 SaveLittleUInt(0); // TextureStage return IL_TRUE; } #endif//IL_NO_DDS ILuint ILAPIENTRY ilGetDXTCData(ILvoid *Buffer, ILuint BufferSize, ILenum DXTCFormat) { ILubyte *CurData = NULL; ILuint retVal; ILint BlockNum; if (Buffer == NULL) { // Return the number that will be written with a subsequent call. BlockNum = ((iCurImage->Width + 3)/4) * ((iCurImage->Height + 3)/4) * iCurImage->Depth; switch (DXTCFormat) { case IL_DXT1: case IL_ATI1N: return BlockNum * 8; case IL_DXT3: case IL_DXT5: case IL_3DC: case IL_RXGB: return BlockNum * 16; default: ilSetError(IL_FORMAT_NOT_SUPPORTED); return 0; } } if (DXTCFormat == iCurImage->DxtcFormat && iCurImage->DxtcSize && iCurImage->DxtcData) { memcpy(Buffer, iCurImage->DxtcData, IL_MIN(BufferSize, iCurImage->DxtcSize)); return IL_MIN(BufferSize, iCurImage->DxtcSize); } if (iCurImage->Origin != IL_ORIGIN_UPPER_LEFT) { CurData = iCurImage->Data; iCurImage->Data = iGetFlipped(iCurImage); if (iCurImage->Data == NULL) { iCurImage->Data = CurData; return 0; } ifree(CurData); } iSetOutputLump(Buffer, BufferSize); retVal = Compress(iCurImage, DXTCFormat); if (iCurImage->Origin != IL_ORIGIN_UPPER_LEFT) { ifree(iCurImage->Data); iCurImage->Data = CurData; } return retVal; } ILushort *CompressTo565(ILimage *Image) { ILimage *TempImage; ILushort *Data; ILuint i, j; if ((Image->Type != IL_UNSIGNED_BYTE && Image->Type != IL_BYTE) || Image->Format == IL_COLOUR_INDEX) { TempImage = iConvertImage(iCurImage, IL_BGR, IL_UNSIGNED_BYTE); // @TODO: Needs to be BGRA. if (TempImage == NULL) return NULL; } else { TempImage = Image; } Data = (ILushort*)ialloc(iCurImage->Width * iCurImage->Height * 2 * iCurImage->Depth); if (Data == NULL) { if (TempImage != Image) ilCloseImage(TempImage); return NULL; } //changed 20040623: Use TempImages format :) switch (TempImage->Format) { case IL_RGB: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 3, j++) { Data[j] = (TempImage->Data[i ] >> 3) << 11; Data[j] |= (TempImage->Data[i+1] >> 2) << 5; Data[j] |= TempImage->Data[i+2] >> 3; } break; case IL_RGBA: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 4, j++) { Data[j] = (TempImage->Data[i ] >> 3) << 11; Data[j] |= (TempImage->Data[i+1] >> 2) << 5; Data[j] |= TempImage->Data[i+2] >> 3; } break; case IL_BGR: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 3, j++) { Data[j] = (TempImage->Data[i+2] >> 3) << 11; Data[j] |= (TempImage->Data[i+1] >> 2) << 5; Data[j] |= TempImage->Data[i ] >> 3; } break; case IL_BGRA: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 4, j++) { Data[j] = (TempImage->Data[i+2] >> 3) << 11; Data[j] |= (TempImage->Data[i+1] >> 2) << 5; Data[j] |= TempImage->Data[i ] >> 3; } break; case IL_LUMINANCE: for (i = 0, j = 0; i < TempImage->SizeOfData; i++, j++) { Data[j] = (TempImage->Data[i] >> 3) << 11; Data[j] |= (TempImage->Data[i] >> 2) << 5; Data[j] |= TempImage->Data[i] >> 3; } break; case IL_LUMINANCE_ALPHA: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 2, j++) { Data[j] = (TempImage->Data[i] >> 3) << 11; Data[j] |= (TempImage->Data[i] >> 2) << 5; Data[j] |= TempImage->Data[i] >> 3; } break; } if (TempImage != Image) ilCloseImage(TempImage); return Data; } ILubyte *CompressTo88(ILimage *Image) { ILimage *TempImage; ILubyte *Data; ILuint i, j; if ((Image->Type != IL_UNSIGNED_BYTE && Image->Type != IL_BYTE) || Image->Format == IL_COLOUR_INDEX) { TempImage = iConvertImage(iCurImage, IL_BGR, IL_UNSIGNED_BYTE); // @TODO: Needs to be BGRA. if (TempImage == NULL) return NULL; } else { TempImage = Image; } Data = (ILubyte*)ialloc(iCurImage->Width * iCurImage->Height * 2 * iCurImage->Depth); if (Data == NULL) { if (TempImage != Image) ilCloseImage(TempImage); return NULL; } //changed 20040623: Use TempImages format :) switch (TempImage->Format) { case IL_RGB: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 3, j += 2) { Data[j ] = TempImage->Data[i+1]; Data[j+1] = TempImage->Data[i ]; } break; case IL_RGBA: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 4, j += 2) { Data[j ] = TempImage->Data[i+1]; Data[j+1] = TempImage->Data[i ]; } break; case IL_BGR: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 3, j += 2) { Data[j ] = TempImage->Data[i+1]; Data[j+1] = TempImage->Data[i+2]; } break; case IL_BGRA: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 4, j += 2) { Data[j ] = TempImage->Data[i+1]; Data[j+1] = TempImage->Data[i+2]; } break; case IL_LUMINANCE: case IL_LUMINANCE_ALPHA: for (i = 0, j = 0; i < TempImage->SizeOfData; i++, j += 2) { Data[j ] = Data[j+1] = 0; //??? Luminance is no normal map format... } break; } if (TempImage != Image) ilCloseImage(TempImage); return Data; } void CompressToRXGB(ILimage *Image, ILushort** xgb, ILubyte** r) { ILimage *TempImage; ILuint i, j; ILushort *Data; ILubyte *Alpha; *xgb = NULL; *r = NULL; if ((Image->Type != IL_UNSIGNED_BYTE && Image->Type != IL_BYTE) || Image->Format == IL_COLOUR_INDEX) { TempImage = iConvertImage(iCurImage, IL_BGR, IL_UNSIGNED_BYTE); // @TODO: Needs to be BGRA. if (TempImage == NULL) return; } else { TempImage = Image; } *xgb = (ILushort*)ialloc(iCurImage->Width * iCurImage->Height * 2 * iCurImage->Depth); *r = (ILubyte*)ialloc(iCurImage->Width * iCurImage->Height * iCurImage->Depth); if (*xgb == NULL || *r == NULL) { if (TempImage != Image) ilCloseImage(TempImage); return; } //Alias pointers to be able to use copy'n'pasted code :) Data = *xgb; Alpha = *r; switch (TempImage->Format) { case IL_RGB: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 3, j++) { Alpha[j] = TempImage->Data[i]; Data[j] = (TempImage->Data[i+1] >> 2) << 5; Data[j] |= TempImage->Data[i+2] >> 3; } break; case IL_RGBA: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 4, j++) { Alpha[j] = TempImage->Data[i]; Data[j] = (TempImage->Data[i+1] >> 2) << 5; Data[j] |= TempImage->Data[i+2] >> 3; } break; case IL_BGR: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 3, j++) { Alpha[j] = TempImage->Data[i+2]; Data[j] = (TempImage->Data[i+1] >> 2) << 5; Data[j] |= TempImage->Data[i ] >> 3; } break; case IL_BGRA: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 4, j++) { Alpha[j] = TempImage->Data[i+2]; Data[j] = (TempImage->Data[i+1] >> 2) << 5; Data[j] |= TempImage->Data[i ] >> 3; } break; case IL_LUMINANCE: for (i = 0, j = 0; i < TempImage->SizeOfData; i++, j++) { Alpha[j] = TempImage->Data[i]; Data[j] = (TempImage->Data[i] >> 2) << 5; Data[j] |= TempImage->Data[i] >> 3; } break; case IL_LUMINANCE_ALPHA: for (i = 0, j = 0; i < TempImage->SizeOfData; i += 2, j++) { Alpha[j] = TempImage->Data[i]; Data[j] = (TempImage->Data[i] >> 2) << 5; Data[j] |= TempImage->Data[i] >> 3; } break; } if (TempImage != Image) ilCloseImage(TempImage); } ILuint Compress(ILimage *Image, ILenum DXTCFormat) { ILushort *Data, Block[16], ex0, ex1, *Runner16, t0, t1; ILuint x, y, z, i, BitMask;//, Rms1, Rms2; ILubyte *Alpha, AlphaBlock[16], AlphaBitMask[6], /*AlphaOut[16],*/ a0, a1; ILboolean HasAlpha; ILuint Count = 0; ILubyte *Data3Dc, *Runner8; if (DXTCFormat == IL_3DC) { Data3Dc = CompressTo88(Image); if (Data3Dc == NULL) return 0; Runner8 = Data3Dc; for (z = 0; z < Image->Depth; z++) { for (y = 0; y < Image->Height; y += 4) { for (x = 0; x < Image->Width; x += 4) { Get3DcBlock(AlphaBlock, Runner8, Image, x, y, 0); ChooseAlphaEndpoints(AlphaBlock, &a0, &a1); GenAlphaBitMask(a0, a1, AlphaBlock, AlphaBitMask, NULL); iputc(a0); iputc(a1); iwrite(AlphaBitMask, 1, 6); Get3DcBlock(AlphaBlock, Runner8, Image, x, y, 1); ChooseAlphaEndpoints(AlphaBlock, &a0, &a1); GenAlphaBitMask(a0, a1, AlphaBlock, AlphaBitMask, NULL); iputc(a0); iputc(a1); iwrite(AlphaBitMask, 1, 6); Count += 16; } } Runner8 += Image->Width * Image->Height * 2; } ifree(Data3Dc); } else if(DXTCFormat == IL_ATI1N) { ILimage *TempImage; if (Image->Bpp != 1) { TempImage = iConvertImage(iCurImage, IL_LUMINANCE, IL_UNSIGNED_BYTE); if (TempImage == NULL) return 0; } else { TempImage = Image; } Runner8 = TempImage->Data; for (z = 0; z < Image->Depth; z++) { for (y = 0; y < Image->Height; y += 4) { for (x = 0; x < Image->Width; x += 4) { GetAlphaBlock(AlphaBlock, Runner8, Image, x, y); ChooseAlphaEndpoints(AlphaBlock, &a0, &a1); GenAlphaBitMask(a0, a1, AlphaBlock, AlphaBitMask, NULL); iputc(a0); iputc(a1); iwrite(AlphaBitMask, 1, 6); Count += 8; } } Runner8 += Image->Width * Image->Height; } if (TempImage != Image) ilCloseImage(TempImage); } else { if (DXTCFormat != IL_RXGB) { Data = CompressTo565(Image); if (Data == NULL) return 0; Alpha = ilGetAlpha(IL_UNSIGNED_BYTE); if (Alpha == NULL) { ifree(Data); return 0; } } else { CompressToRXGB(Image, &Data, &Alpha); if (Data == NULL || Alpha == NULL) { if (Data != NULL) ifree(Data); if (Alpha != NULL) ifree(Alpha); return 0; } } Runner8 = Alpha; Runner16 = Data; switch (DXTCFormat) { case IL_DXT1: for (z = 0; z < Image->Depth; z++) { for (y = 0; y < Image->Height; y += 4) { for (x = 0; x < Image->Width; x += 4) { GetAlphaBlock(AlphaBlock, Runner8, Image, x, y); HasAlpha = IL_FALSE; for (i = 0 ; i < 16; i++) { if (AlphaBlock[i] < 128) { HasAlpha = IL_TRUE; break; } } GetBlock(Block, Runner16, Image, x, y); ChooseEndpoints(Block, &ex0, &ex1); CorrectEndDXT1(&ex0, &ex1, HasAlpha); SaveLittleUShort(ex0); SaveLittleUShort(ex1); if (HasAlpha) BitMask = GenBitMask(ex0, ex1, 3, Block, AlphaBlock, NULL); else BitMask = GenBitMask(ex0, ex1, 4, Block, NULL, NULL); SaveLittleUInt(BitMask); Count += 8; } } Runner16 += Image->Width * Image->Height; Runner8 += Image->Width * Image->Height; } break; /*case IL_DXT2: for (y = 0; y < Image->Height; y += 4) { for (x = 0; x < Image->Width; x += 4) { GetAlphaBlock(AlphaBlock, Alpha, Image, x, y); for (i = 0; i < 16; i += 2) { iputc((ILubyte)(((AlphaBlock[i] >> 4) << 4) | (AlphaBlock[i+1] >> 4))); } GetBlock(Block, Data, Image, x, y); PreMult(Block, AlphaBlock); ChooseEndpoints(Block, &ex0, &ex1); SaveLittleUShort(ex0); SaveLittleUShort(ex1); BitMask = GenBitMask(ex0, ex1, 4, Block, NULL, NULL); SaveLittleUInt(BitMask); } } break;*/ case IL_DXT3: for (z = 0; z < Image->Depth; z++) { for (y = 0; y < Image->Height; y += 4) { for (x = 0; x < Image->Width; x += 4) { GetAlphaBlock(AlphaBlock, Runner8, Image, x, y); for (i = 0; i < 16; i += 2) { iputc((ILubyte)(((AlphaBlock[i+1] >> 4) << 4) | (AlphaBlock[i] >> 4))); } GetBlock(Block, Runner16, Image, x, y); ChooseEndpoints(Block, &t0, &t1); ex0 = IL_MAX(t0, t1); ex1 = IL_MIN(t0, t1); CorrectEndDXT1(&ex0, &ex1, 0); SaveLittleUShort(ex0); SaveLittleUShort(ex1); BitMask = GenBitMask(ex0, ex1, 4, Block, NULL, NULL); SaveLittleUInt(BitMask); Count += 16; } } Runner16 += Image->Width * Image->Height; Runner8 += Image->Width * Image->Height; } break; case IL_RXGB: case IL_DXT5: for (z = 0; z < Image->Depth; z++) { for (y = 0; y < Image->Height; y += 4) { for (x = 0; x < Image->Width; x += 4) { GetAlphaBlock(AlphaBlock, Runner8, Image, x, y); ChooseAlphaEndpoints(AlphaBlock, &a0, &a1); GenAlphaBitMask(a0, a1, AlphaBlock, AlphaBitMask, NULL/*AlphaOut*/); /*Rms2 = RMSAlpha(AlphaBlock, AlphaOut); GenAlphaBitMask(a0, a1, 8, AlphaBlock, AlphaBitMask, AlphaOut); Rms1 = RMSAlpha(AlphaBlock, AlphaOut); if (Rms2 <= Rms1) { // Yeah, we have to regenerate... GenAlphaBitMask(a0, a1, 6, AlphaBlock, AlphaBitMask, AlphaOut); Rms2 = a1; // Just reuse Rms2 as a temporary variable... a1 = a0; a0 = Rms2; }*/ iputc(a0); iputc(a1); iwrite(AlphaBitMask, 1, 6); GetBlock(Block, Runner16, Image, x, y); ChooseEndpoints(Block, &t0, &t1); ex0 = IL_MAX(t0, t1); ex1 = IL_MIN(t0, t1); CorrectEndDXT1(&ex0, &ex1, 0); SaveLittleUShort(ex0); SaveLittleUShort(ex1); BitMask = GenBitMask(ex0, ex1, 4, Block, NULL, NULL); SaveLittleUInt(BitMask); Count += 16; } } Runner16 += Image->Width * Image->Height; Runner8 += Image->Width * Image->Height; } break; } ifree(Data); ifree(Alpha); } //else no 3dc return Count; } // Assumed to be 16-bit (5:6:5). ILboolean GetBlock(ILushort *Block, ILushort *Data, ILimage *Image, ILuint XPos, ILuint YPos) { ILuint x, y, i = 0, Offset = YPos * Image->Width + XPos; for (y = 0; y < 4; y++) { for (x = 0; x < 4; x++) { if (x < Image->Width && y < Image->Height) Block[i++] = Data[Offset + x]; else Block[i++] = Data[Offset]; } Offset += Image->Width; } return IL_TRUE; } ILboolean GetAlphaBlock(ILubyte *Block, ILubyte *Data, ILimage *Image, ILuint XPos, ILuint YPos) { ILuint x, y, i = 0, Offset = YPos * Image->Width + XPos; for (y = 0; y < 4; y++) { for (x = 0; x < 4; x++) { if (x < Image->Width && y < Image->Height) Block[i++] = Data[Offset + x]; else Block[i++] = Data[Offset]; } Offset += Image->Width; } return IL_TRUE; } ILboolean Get3DcBlock(ILubyte *Block, ILubyte *Data, ILimage *Image, ILuint XPos, ILuint YPos, int channel) { ILuint x, y, i = 0, Offset = 2*(YPos * Image->Width + XPos) + channel; for (y = 0; y < 4; y++) { for (x = 0; x < 4; x++) { if (x < Image->Width && y < Image->Height) Block[i++] = Data[Offset + 2*x]; else Block[i++] = Data[Offset]; } Offset += 2*Image->Width; } return IL_TRUE; } ILvoid ShortToColor565(ILushort Pixel, Color565 *Colour) { Colour->nRed = (Pixel & 0xF800) >> 11; Colour->nGreen = (Pixel & 0x07E0) >> 5; Colour->nBlue = (Pixel & 0x001F); return; } ILvoid ShortToColor888(ILushort Pixel, Color888 *Colour) { Colour->r = ((Pixel & 0xF800) >> 11) << 3; Colour->g = ((Pixel & 0x07E0) >> 5) << 2; Colour->b = ((Pixel & 0x001F)) << 3; return; } ILushort Color565ToShort(Color565 *Colour) { return (Colour->nRed << 11) | (Colour->nGreen << 5) | (Colour->nBlue); } ILushort Color888ToShort(Color888 *Colour) { return ((Colour->r >> 3) << 11) | ((Colour->g >> 2) << 5) | (Colour->b >> 3); } ILuint GenBitMask(ILushort ex0, ILushort ex1, ILuint NumCols, ILushort *In, ILubyte *Alpha, Color888 *OutCol) { ILuint i, j, Closest, Dist, BitMask = 0; ILubyte Mask[16]; Color888 c, Colours[4]; ShortToColor888(ex0, &Colours[0]); ShortToColor888(ex1, &Colours[1]); if (NumCols == 3) { Colours[2].r = (Colours[0].r + Colours[1].r) / 2; Colours[2].g = (Colours[0].g + Colours[1].g) / 2; Colours[2].b = (Colours[0].b + Colours[1].b) / 2; Colours[3].r = (Colours[0].r + Colours[1].r) / 2; Colours[3].g = (Colours[0].g + Colours[1].g) / 2; Colours[3].b = (Colours[0].b + Colours[1].b) / 2; } else { // NumCols == 4 Colours[2].r = (2 * Colours[0].r + Colours[1].r + 1) / 3; Colours[2].g = (2 * Colours[0].g + Colours[1].g + 1) / 3; Colours[2].b = (2 * Colours[0].b + Colours[1].b + 1) / 3; Colours[3].r = (Colours[0].r + 2 * Colours[1].r + 1) / 3; Colours[3].g = (Colours[0].g + 2 * Colours[1].g + 1) / 3; Colours[3].b = (Colours[0].b + 2 * Colours[1].b + 1) / 3; } for (i = 0; i < 16; i++) { if (Alpha) { // Test to see if we have 1-bit transparency if (Alpha[i] < 128) { Mask[i] = 3; // Transparent if (OutCol) { OutCol[i].r = Colours[3].r; OutCol[i].g = Colours[3].g; OutCol[i].b = Colours[3].b; } continue; } } // If no transparency, try to find which colour is the closest. Closest = UINT_MAX; ShortToColor888(In[i], &c); for (j = 0; j < NumCols; j++) { Dist = Distance(&c, &Colours[j]); if (Dist < Closest) { Closest = Dist; Mask[i] = j; if (OutCol) { OutCol[i].r = Colours[j].r; OutCol[i].g = Colours[j].g; OutCol[i].b = Colours[j].b; } } } } for (i = 0; i < 16; i++) { BitMask |= (Mask[i] << (i*2)); } return BitMask; } ILvoid GenAlphaBitMask(ILubyte a0, ILubyte a1, ILubyte *In, ILubyte *Mask, ILubyte *Out) { ILubyte Alphas[8], M[16]; ILuint i, j, Closest, Dist; Alphas[0] = a0; Alphas[1] = a1; // 8-alpha or 6-alpha block? if (a0 > a1) { // 8-alpha block: derive the other six alphas. // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. 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 { // 6-alpha block. // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. 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] = 0x00; // Bit code 110 Alphas[7] = 0xFF; // Bit code 111 } for (i = 0; i < 16; i++) { Closest = UINT_MAX; for (j = 0; j < 8; j++) { Dist = abs((ILint)In[i] - (ILint)Alphas[j]); if (Dist < Closest) { Closest = Dist; M[i] = j; } } } if (Out) { for (i = 0; i < 16; i++) { Out[i] = Alphas[M[i]]; } } //this was changed 20040623. There was a shift bug in here. Now the code //produces much higher quality images. // First three bytes. Mask[0] = (M[0]) | (M[1] << 3) | ((M[2] & 0x03) << 6); Mask[1] = ((M[2] & 0x04) >> 2) | (M[3] << 1) | (M[4] << 4) | ((M[5] & 0x01) << 7); Mask[2] = ((M[5] & 0x06) >> 1) | (M[6] << 2) | (M[7] << 5); // Second three bytes. Mask[3] = (M[8]) | (M[9] << 3) | ((M[10] & 0x03) << 6); Mask[4] = ((M[10] & 0x04) >> 2) | (M[11] << 1) | (M[12] << 4) | ((M[13] & 0x01) << 7); Mask[5] = ((M[13] & 0x06) >> 1) | (M[14] << 2) | (M[15] << 5); return; } ILuint RMSAlpha(ILubyte *Orig, ILubyte *Test) { ILuint RMS = 0, i; ILint d; for (i = 0; i < 16; i++) { d = Orig[i] - Test[i]; RMS += d*d; } //RMS /= 16; return RMS; } ILuint Distance(Color888 *c1, Color888 *c2) { return (c1->r - c2->r) * (c1->r - c2->r) + (c1->g - c2->g) * (c1->g - c2->g) + (c1->b - c2->b) * (c1->b - c2->b); } #define Sum(c) ((c)->r + (c)->g + (c)->b) ILvoid ChooseEndpoints(ILushort *Block, ILushort *ex0, ILushort *ex1) { ILuint i; Color888 Colours[16]; ILint Lowest=0, Highest=0; for (i = 0; i < 16; i++) { ShortToColor888(Block[i], &Colours[i]); if (Sum(&Colours[i]) < Sum(&Colours[Lowest])) Lowest = i; if (Sum(&Colours[i]) > Sum(&Colours[Highest])) Highest = i; } *ex0 = Block[Highest]; *ex1 = Block[Lowest]; } #undef Sum ILvoid ChooseAlphaEndpoints(ILubyte *Block, ILubyte *a0, ILubyte *a1) { ILuint i, Lowest = 0xFF, Highest = 0; for (i = 0; i < 16; i++) { if( Block[i] < Lowest) Lowest = Block[i]; if (Block[i] > Highest) Highest = Block[i]; } *a0 = Lowest; *a1 = Highest; } ILvoid CorrectEndDXT1(ILushort *ex0, ILushort *ex1, ILboolean HasAlpha) { ILushort Temp; if (HasAlpha) { if (*ex0 > *ex1) { Temp = *ex0; *ex0 = *ex1; *ex1 = Temp; } } else { if (*ex0 < *ex1) { Temp = *ex0; *ex0 = *ex1; *ex1 = Temp; } } return; } ILvoid PreMult(ILushort *Data, ILubyte *Alpha) { Color888 Colour; ILuint i; for (i = 0; i < 16; i++) { ShortToColor888(Data[i], &Colour); Colour.r = (ILubyte)(((ILuint)Colour.r * Alpha[i]) >> 8); Colour.g = (ILubyte)(((ILuint)Colour.g * Alpha[i]) >> 8); Colour.b = (ILubyte)(((ILuint)Colour.b * Alpha[i]) >> 8); /*Colour.r = (ILubyte)(Colour.r * (Alpha[i] / 255.0)); Colour.g = (ILubyte)(Colour.g * (Alpha[i] / 255.0)); Colour.b = (ILubyte)(Colour.b * (Alpha[i] / 255.0));*/ Data[i] = Color888ToShort(&Colour); ShortToColor888(Data[i], &Colour); } return; }