//===================================================================== // Copyright 2016 (c), Advanced Micro Devices, Inc. All rights reserved. //===================================================================== // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // Windows Header Files: #ifdef _WIN32 #include #endif #include "cKTX.h" #include "TC_PluginAPI.h" #include "TC_PluginInternal.h" #include "Common.h" #include "softfloat.h" #include #include #include #pragma comment(lib, "opengl32.lib") // Open GL #pragma comment(lib, "Glu32.lib") // Glu #pragma comment(lib, "glew32.lib") // glew 1.13.0 CMIPS *KTX_CMips; #ifdef BUILD_AS_PLUGIN_DLL DECLARE_PLUGIN(Plugin_KTX) SET_PLUGIN_TYPE("IMAGE") SET_PLUGIN_NAME("KTX") #else void *make_Plugin_KTX() { return new Plugin_KTX; } #endif uint32_t Endian_Conversion(uint32_t dword) { return (((dword>>24)&0x000000FF) | ((dword>>8)&0x0000FF00) | ((dword<<8)&0x00FF0000) | ((dword<<24)&0xFF000000)); } Plugin_KTX::Plugin_KTX() { } Plugin_KTX::~Plugin_KTX() { } int Plugin_KTX::TC_PluginSetSharedIO(void* Shared) { if (Shared) { KTX_CMips = static_cast(Shared); return 0; } return 1; } int Plugin_KTX::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion) { #ifdef _WIN32 pPluginVersion->guid = g_GUID; #endif pPluginVersion->dwAPIVersionMajor = TC_API_VERSION_MAJOR; pPluginVersion->dwAPIVersionMinor = TC_API_VERSION_MINOR; pPluginVersion->dwPluginVersionMajor = TC_PLUGIN_VERSION_MAJOR; pPluginVersion->dwPluginVersionMinor = TC_PLUGIN_VERSION_MINOR; return 0; } int Plugin_KTX::TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture *srcTexture) { return -1; } int Plugin_KTX::TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture *srcTexture) { return -1; } uint32_t ktx_u32_byterev(uint32_t v) { return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24); } // perform endianness switch on raw data static void switch_endianness2(void *dataptr, int bytes) { int i; uint8_t *data = (uint8_t *) dataptr; for (i = 0; i < bytes / 2; i++) { uint8_t d0 = data[0]; uint8_t d1 = data[1]; data[0] = d1; data[1] = d0; data += 2; } } static void switch_endianness4(void *dataptr, int bytes) { int i; uint8_t *data = (uint8_t *) dataptr; for (i = 0; i < bytes / 4; i++) { uint8_t d0 = data[0]; uint8_t d1 = data[1]; uint8_t d2 = data[2]; uint8_t d3 = data[3]; data[0] = d3; data[1] = d2; data[2] = d1; data[3] = d0; data += 4; } } static void copy_scanline(void *dst, const void *src, int pixels, int method) { #define id(x) (x) #define u16_sf16(x) float_to_sf16( x * (1.0f/65535.0f), SF_NEARESTEVEN ) #define f32_sf16(x) sf32_to_sf16( x, SF_NEARESTEVEN ) #define COPY_R( dsttype, srctype, convfunc, oneval ) \ do { \ srctype *s = (srctype *)src; \ dsttype *d = (dsttype *)dst; \ for(i=0;iPrintError(("Error(%d): KTX Plugin ID(%d) opening file = %s \n"), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); return -1; } //using libktx KTX_header fheader; KTX_texinfo texinfo; if (fread(&fheader, sizeof(KTX_header), 1, pFile) != 1) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) invalid KTX header. Filename = %s \n"), EL_Error, IDS_ERROR_NOT_KTX, pszFilename); fclose(pFile); return -1; } if (_ktxCheckHeader(&fheader, &texinfo) != KTX_SUCCESS) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) invalid KTX header. Filename = %s \n"), EL_Error, IDS_ERROR_NOT_KTX, pszFilename); fclose(pFile); return -1; } if (texinfo.compressed) { pMipSet->m_compressed = true; pMipSet->m_nBlockHeight = 4; pMipSet->m_nBlockWidth = 4; pMipSet->m_nBlockDepth = 1; pMipSet->m_ChannelFormat = CF_Compressed; switch (fheader.glInternalFormat) { case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: pMipSet->m_format = CMP_FORMAT_BC1; pMipSet->m_TextureDataType = TDT_XRGB; break; case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: pMipSet->m_format = CMP_FORMAT_BC1; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: pMipSet->m_format = CMP_FORMAT_BC2; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: pMipSet->m_format = CMP_FORMAT_BC3; pMipSet->m_TextureDataType = TDT_ARGB; break; case RGB_BP_UNorm: pMipSet->m_format = CMP_FORMAT_BC7; pMipSet->m_TextureDataType = TDT_ARGB; break; case R_ATI1N_UNorm: pMipSet->m_format = CMP_FORMAT_ATI1N; pMipSet->m_TextureDataType = TDT_ARGB; break; case R_ATI1N_SNorm: pMipSet->m_format = CMP_FORMAT_ATI2N; pMipSet->m_TextureDataType = TDT_ARGB; break; case RG_ATI2N_UNorm: pMipSet->m_format = CMP_FORMAT_ATI2N_XY; pMipSet->m_TextureDataType = TDT_ARGB; break; case RG_ATI2N_SNorm: pMipSet->m_format = CMP_FORMAT_ATI2N_DXT5; pMipSet->m_TextureDataType = TDT_ARGB; break; case RGB_BP_UNSIGNED_FLOAT: pMipSet->m_format = CMP_FORMAT_BC6H; pMipSet->m_TextureDataType = TDT_ARGB; break; case RGB_BP_SIGNED_FLOAT: pMipSet->m_format = CMP_FORMAT_BC6H_SF; pMipSet->m_TextureDataType = TDT_ARGB; break; case ATC_RGB_AMD: pMipSet->m_format = CMP_FORMAT_ATC_RGB; pMipSet->m_TextureDataType = TDT_ARGB; break; case ATC_RGBA_EXPLICIT_ALPHA_AMD: pMipSet->m_format = CMP_FORMAT_ATC_RGBA_Explicit; pMipSet->m_TextureDataType = TDT_ARGB; break; case ATC_RGBA_INTERPOLATED_ALPHA_AMD: pMipSet->m_format = CMP_FORMAT_ATC_RGBA_Interpolated; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 4; pMipSet->m_nBlockHeight = 4; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 5; pMipSet->m_nBlockHeight = 4; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 5; pMipSet->m_nBlockHeight = 5; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 6; pMipSet->m_nBlockHeight = 5; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 6; pMipSet->m_nBlockHeight = 6; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 8; pMipSet->m_nBlockHeight = 5; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 8; pMipSet->m_nBlockHeight = 6; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 10; pMipSet->m_nBlockHeight = 5; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 10; pMipSet->m_nBlockHeight = 6; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 8; pMipSet->m_nBlockHeight = 8; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 10; pMipSet->m_nBlockHeight = 8; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 10; pMipSet->m_nBlockHeight = 10; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 12; pMipSet->m_nBlockHeight = 10; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: pMipSet->m_format = CMP_FORMAT_ASTC; pMipSet->m_nBlockWidth = 12; pMipSet->m_nBlockHeight = 12; pMipSet->m_TextureDataType = TDT_ARGB; break; case ETC1_RGB8_OES: pMipSet->m_format = CMP_FORMAT_ETC_RGB; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGB8_ETC2: pMipSet->m_format = CMP_FORMAT_ETC2_RGB; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_SRGB8_ETC2: pMipSet->m_format = CMP_FORMAT_ETC2_SRGB; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGBA8_ETC2_EAC: pMipSet->m_format = CMP_FORMAT_ETC2_RGBA; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: pMipSet->m_format = CMP_FORMAT_ETC2_RGBA1; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: pMipSet->m_format = CMP_FORMAT_ETC2_SRGBA; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: pMipSet->m_format = CMP_FORMAT_ETC2_SRGBA1; pMipSet->m_TextureDataType = TDT_ARGB; break; case COMPRESSED_FORMAT_DXT5_RxBG : pMipSet->m_format = CMP_FORMAT_DXT5_RxBG; pMipSet->m_TextureDataType = TDT_ARGB; break; case COMPRESSED_FORMAT_DXT5_RBxG : pMipSet->m_format = CMP_FORMAT_DXT5_RBxG; pMipSet->m_TextureDataType = TDT_ARGB; break; case COMPRESSED_FORMAT_DXT5_xRBG : pMipSet->m_format = CMP_FORMAT_DXT5_xRBG; pMipSet->m_TextureDataType = TDT_ARGB; break; case COMPRESSED_FORMAT_DXT5_RGxB : pMipSet->m_format = CMP_FORMAT_DXT5_RGxB; pMipSet->m_TextureDataType = TDT_ARGB; break; case COMPRESSED_FORMAT_DXT5_xGxR : pMipSet->m_format = CMP_FORMAT_DXT5_xGxR; pMipSet->m_TextureDataType = TDT_ARGB; break; default: if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) unsupported GL format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); fclose(pFile); return -1; } } else { pMipSet->m_compressed = false; switch (fheader.glType) { case GL_UNSIGNED_BYTE: pMipSet->m_ChannelFormat = CF_8bit; switch (fheader.glFormat) { case GL_RED: pMipSet->m_format = CMP_FORMAT_R_8; pMipSet->m_TextureDataType = TDT_R; break; case GL_RG: pMipSet->m_format = CMP_FORMAT_RG_8; pMipSet->m_TextureDataType = TDT_RG; break; case GL_RGB: pMipSet->m_format = CMP_FORMAT_RGB_888; pMipSet->m_TextureDataType = TDT_XRGB; break; case GL_RGBA: case GL_RGBA8: pMipSet->m_format = CMP_FORMAT_ARGB_8888; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_BGR: pMipSet->m_swizzle = true; pMipSet->m_format = CMP_FORMAT_RGB_888; pMipSet->m_TextureDataType = TDT_XRGB; break; case GL_BGRA: pMipSet->m_swizzle = true; pMipSet->m_format = CMP_FORMAT_ARGB_8888; pMipSet->m_TextureDataType = TDT_ARGB; break; } break; case GL_UNSIGNED_SHORT: pMipSet->m_ChannelFormat = CF_16bit; switch (fheader.glFormat) { case GL_RED: pMipSet->m_format = CMP_FORMAT_R_16; pMipSet->m_TextureDataType = TDT_R; break; case GL_RG: pMipSet->m_format = CMP_FORMAT_RG_16; pMipSet->m_TextureDataType = TDT_RG; break; case GL_RGBA: pMipSet->m_format = CMP_FORMAT_ARGB_16; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_BGRA: pMipSet->m_swizzle = true; pMipSet->m_format = CMP_FORMAT_ARGB_16; pMipSet->m_TextureDataType = TDT_ARGB; break; } break; case GL_HALF_FLOAT: pMipSet->m_ChannelFormat = CF_Float16; switch (fheader.glFormat) { case GL_RED: pMipSet->m_format = CMP_FORMAT_R_16F; pMipSet->m_TextureDataType = TDT_R; break; case GL_RG: pMipSet->m_format = CMP_FORMAT_RG_16F; pMipSet->m_TextureDataType = TDT_RG; break; case GL_RGBA: pMipSet->m_format = CMP_FORMAT_ARGB_16F; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_BGRA: pMipSet->m_swizzle = true; pMipSet->m_format = CMP_FORMAT_ARGB_16F; pMipSet->m_TextureDataType = TDT_ARGB; break; } break; case GL_UNSIGNED_INT_2_10_10_10_REV: pMipSet->m_format = CMP_FORMAT_ARGB_2101010; pMipSet->m_TextureDataType = TDT_ARGB; pMipSet->m_ChannelFormat = CF_2101010; break; case GL_FLOAT: pMipSet->m_ChannelFormat = CF_Float32; switch (fheader.glFormat) { case GL_RED: pMipSet->m_format = CMP_FORMAT_R_32F; pMipSet->m_TextureDataType = TDT_R; break; case GL_RG: pMipSet->m_format = CMP_FORMAT_RG_32F; pMipSet->m_TextureDataType = TDT_RG; break; case GL_RGBA: pMipSet->m_format = CMP_FORMAT_ARGB_32F; pMipSet->m_TextureDataType = TDT_ARGB; break; case GL_BGRA: pMipSet->m_swizzle = true; pMipSet->m_format = CMP_FORMAT_ARGB_32F; pMipSet->m_TextureDataType = TDT_ARGB; break; } break; break; default: if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) unsupported GL format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); fclose(pFile); return -1; } } switch (texinfo.glTarget) { case GL_TEXTURE_2D: pMipSet->m_TextureType = TT_2D; break; case GL_TEXTURE_3D: pMipSet->m_TextureType = TT_VolumeTexture; break; case GL_TEXTURE_CUBE_MAP: pMipSet->m_TextureType = TT_CubeMap; break; default: if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) unsupported texture format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, texinfo.glTarget); fclose(pFile); return -1; } if (fheader.numberOfMipmapLevels == 0) fheader.numberOfMipmapLevels = 1; if (fheader.numberOfArrayElements != 0) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) array textures not supported %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.numberOfArrayElements); fclose(pFile); return -1; } pMipSet->m_nMipLevels = fheader.numberOfMipmapLevels; if (fheader.numberOfFaces < 1) fheader.numberOfFaces = 1; // depthsupport pMipSet->m_nDepth = fheader.numberOfFaces; pMipSet->dwWidth = fheader.pixelWidth; pMipSet->dwHeight = fheader.pixelHeight; // Allocate MipSet header KTX_CMips->AllocateMipSet(pMipSet, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType, pMipSet->m_TextureType, fheader.pixelWidth, fheader.pixelHeight, pMipSet->m_nDepth // depthsupport ); if (pMipSet->m_nMipLevels > pMipSet->m_nMaxMipLevels) { pMipSet->m_nMipLevels = pMipSet->m_nMaxMipLevels; } fclose(pFile); pFile = NULL; pFile = fopen(pszFilename, "rb"); if (pFile == NULL) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) loading file = %s "), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); return -1; } //skip key value data int imageSizeOffset = sizeof(KTX_header) + fheader.bytesOfKeyValueData; if (fseek(pFile, imageSizeOffset, SEEK_SET)) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) Seek past key/vals in KTX compressed bitmap file failed. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); fclose(pFile); return -1; } int w = pMipSet->m_nWidth; int h = pMipSet->m_nHeight; unsigned int totalByteRead=0; unsigned int faceSize = 0; unsigned int faceSizeRounded = 0; unsigned int numArrayElement = fheader.numberOfArrayElements; for (int nMipLevel = 0; nMipLevel < fheader.numberOfMipmapLevels; nMipLevel++) { if ((w <= 0) || (h <= 0)) break; totalByteRead = fread(&faceSize, 1, sizeof(khronos_uint32_t), pFile); if (totalByteRead == 0) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) Read image data size failed. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); fclose(pFile); return -1; } if (fheader.endianness == KTX_ENDIAN_REF_REV) { _ktxSwapEndian32(&faceSize, 1); } faceSizeRounded = (faceSize + 3) & ~(khronos_uint32_t)3; for (int face = 0; face < fheader.numberOfFaces; ++face) { // Determine buffer size and set Mip Set Levels MipLevel *pMipLevel = KTX_CMips->GetMipLevel(pMipSet, nMipLevel, face); if (pMipSet->m_compressed) KTX_CMips->AllocateCompressedMipLevelData(pMipLevel, w, h, faceSizeRounded); else KTX_CMips->AllocateMipLevelData(pMipLevel, w, h, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType); // handle padded KTX data // store size to be read at this miplevel to check with face size in KTX file int sizeTobeRead = 0; int numChannel = 0; int bytesPerChannel = 0; if (pMipSet->m_compressed) { sizeTobeRead = faceSizeRounded; } else { switch (pMipSet->m_TextureDataType) { case TDT_XRGB: numChannel = 3; break; case TDT_ARGB: case TDT_NORMAL_MAP: numChannel = 4; break; case TDT_R: numChannel = 1; break; case TDT_RG: numChannel = 2; break; default: return -1; } switch (pMipSet->m_ChannelFormat) { case CF_8bit: case CF_2101010: case CF_Float9995E: bytesPerChannel = 1; break; case CF_16bit: case CF_Float16: bytesPerChannel = 2; break; case CF_32bit: case CF_Float32: bytesPerChannel = 4; break; default: return -1; } sizeTobeRead = w * h * numChannel * bytesPerChannel; } CMP_BYTE* pData = (CMP_BYTE*)(pMipLevel->m_pbData); if (!pData) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) Read image data failed, Out of Memory. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); fclose(pFile); return -1; } //read image data unsigned int bytesRead = 0; //size to be read has to be same as face size, else padding is done in the KTX file if (sizeTobeRead == faceSizeRounded) { bytesRead = fread(pData, 1, faceSizeRounded, pFile); } else if(faceSizeRounded > sizeTobeRead) // padding in KTX file { std::vector pTempData; pTempData.resize(faceSizeRounded); bytesRead = fread(pTempData.data(), 1, faceSizeRounded, pFile); int paddedBytes = faceSizeRounded - sizeTobeRead; int n = 0; for (int i = 0; i < pTempData.size(); i++) { for (int w_i = 0; w_i < w; w_i++) { for (int j = 0; j < numChannel*bytesPerChannel; j++) { pData[n++] = pTempData[i++]; } } //skip padded bytes i += (paddedBytes / h)-1; } } if (bytesRead != faceSizeRounded) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) Read image data failed. Unexpectec EOF. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); fclose(pFile); return -1; } } // next miplevel width and height w = ((std::max))(0, (w >> 1)); h = ((std::max))(0, (h >> 1)); } return 0; } int Plugin_KTX::TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet) { assert(pszFilename); assert(pMipSet); FILE* pFile = NULL; pFile = fopen(pszFilename, "wb"); if (pFile == NULL) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); return -1; } //using libktx KTX_texture_info textureinfo; KTX_image_info* inputMip = new KTX_image_info[pMipSet->m_nMipLevels]; unsigned pDataLen = 0; CMP_BYTE* pData = NULL; bool isCompressed = false; if (pMipSet->m_TextureType == TT_CubeMap) textureinfo.numberOfFaces = 6; else textureinfo.numberOfFaces = 1; //todo: handle array textures textureinfo.numberOfArrayElements = 0; if (pMipSet->m_pMipLevelTable == NULL) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename); return -1; } if (KTX_CMips->GetMipLevel(pMipSet, 0) == NULL) { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename); return -1; } int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); for (int nSlice = 0; nSlice < nSlices; nSlice++) { for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) { inputMip[nMipLevel].size = KTX_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize; inputMip[nMipLevel].data = KTX_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData; } } switch (pMipSet->m_format) { //uncompressed format case case CMP_FORMAT_ARGB_8888 : case CMP_FORMAT_RGB_888 : case CMP_FORMAT_RG_8 : case CMP_FORMAT_R_8 : isCompressed = false; textureinfo.glType = GL_UNSIGNED_BYTE; textureinfo.glTypeSize = 1; break; case CMP_FORMAT_ARGB_2101010 : textureinfo.glType = GL_UNSIGNED_INT_2_10_10_10_REV; textureinfo.glTypeSize = 1; break; case CMP_FORMAT_ARGB_16 : textureinfo.glType = GL_UNSIGNED_SHORT; textureinfo.glTypeSize = 2; break; case CMP_FORMAT_RG_16 : textureinfo.glType = GL_UNSIGNED_SHORT; textureinfo.glTypeSize = 2; break; case CMP_FORMAT_R_16 : textureinfo.glType = GL_UNSIGNED_SHORT; textureinfo.glTypeSize = 2; break; case CMP_FORMAT_ARGB_16F : case CMP_FORMAT_RG_16F : case CMP_FORMAT_R_16F : textureinfo.glType = GL_HALF_FLOAT; textureinfo.glTypeSize = 2; break; case CMP_FORMAT_ARGB_32F : case CMP_FORMAT_RGB_32F : case CMP_FORMAT_RG_32F : case CMP_FORMAT_R_32F : textureinfo.glType = GL_FLOAT; textureinfo.glTypeSize = 4; break; //compressed format case case CMP_FORMAT_ATI1N : case CMP_FORMAT_ATI2N : case CMP_FORMAT_ATI2N_XY : case CMP_FORMAT_ATI2N_DXT5 : case CMP_FORMAT_ATC_RGB : case CMP_FORMAT_ATC_RGBA_Explicit : case CMP_FORMAT_ATC_RGBA_Interpolated : case CMP_FORMAT_BC1 : case CMP_FORMAT_BC2 : case CMP_FORMAT_BC3 : case CMP_FORMAT_BC4 : case CMP_FORMAT_BC5 : case CMP_FORMAT_BC6H : case CMP_FORMAT_BC6H_SF: case CMP_FORMAT_BC7 : case CMP_FORMAT_DXT1 : case CMP_FORMAT_DXT3 : case CMP_FORMAT_DXT5 : case CMP_FORMAT_DXT5_xGBR : case CMP_FORMAT_DXT5_RxBG : case CMP_FORMAT_DXT5_RBxG : case CMP_FORMAT_DXT5_xRBG : case CMP_FORMAT_DXT5_RGxB : case CMP_FORMAT_DXT5_xGxR : case CMP_FORMAT_ETC_RGB : case CMP_FORMAT_ETC2_RGB: case CMP_FORMAT_ETC2_SRGB: case CMP_FORMAT_ETC2_RGBA: case CMP_FORMAT_ETC2_RGBA1: case CMP_FORMAT_ETC2_SRGBA: case CMP_FORMAT_ETC2_SRGBA1: case CMP_FORMAT_ASTC : #ifdef USE_GTC case CMP_FORMAT_GTC: #endif #ifdef USE_BASIS case CMP_FORMAT_BASIS: #endif isCompressed = true; textureinfo.glType = 0; textureinfo.glTypeSize = 1; textureinfo.glFormat = 0; break; //default case default: isCompressed = false; textureinfo.glType = GL_UNSIGNED_BYTE; textureinfo.glTypeSize = 1; break; } switch (pMipSet->m_TextureDataType) { case TDT_R: //single component-- can be Luminance and Alpha case, here only cover R { if (!isCompressed) { textureinfo.glFormat = textureinfo.glBaseInternalFormat = GL_RED; textureinfo.glInternalFormat = GL_RED; } else { textureinfo.glBaseInternalFormat = GL_RED; textureinfo.glInternalFormat = GL_RED; } } break; case TDT_RG: //two component { if (!isCompressed) { textureinfo.glFormat = textureinfo.glBaseInternalFormat = GL_RG; textureinfo.glInternalFormat = GL_RG; } else { textureinfo.glBaseInternalFormat = GL_RG; textureinfo.glInternalFormat = GL_COMPRESSED_RG; } } break; case TDT_XRGB: //normally 3 component { if (!isCompressed) { textureinfo.glFormat = textureinfo.glBaseInternalFormat = GL_RGB; textureinfo.glInternalFormat = GL_RGB; } else { switch (pMipSet->m_format) { case CMP_FORMAT_BC1 : case CMP_FORMAT_DXT1: textureinfo.glBaseInternalFormat = GL_RGB; textureinfo.glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case CMP_FORMAT_ETC_RGB: textureinfo.glBaseInternalFormat = GL_RGB; textureinfo.glInternalFormat = ETC1_RGB8_OES; break; case CMP_FORMAT_ETC2_RGB: textureinfo.glBaseInternalFormat = GL_RGB; textureinfo.glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; case CMP_FORMAT_ETC2_SRGB: textureinfo.glBaseInternalFormat = GL_SRGB; textureinfo.glInternalFormat = GL_COMPRESSED_SRGB8_ETC2; break; default: textureinfo.glBaseInternalFormat = GL_RGB; textureinfo.glInternalFormat = GL_RGB; //other compressed format not found in qtangle header break; } } } break; case TDT_ARGB: //4 component { if (!isCompressed) { textureinfo.glFormat = textureinfo.glBaseInternalFormat = GL_RGBA; textureinfo.glInternalFormat = GL_RGBA8; } else { textureinfo.glBaseInternalFormat = GL_RGBA; switch (pMipSet->m_format) { case CMP_FORMAT_BC1: case CMP_FORMAT_DXT1: textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; case CMP_FORMAT_BC2: case CMP_FORMAT_DXT3: textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; case CMP_FORMAT_BC3: case CMP_FORMAT_DXT5: textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; case CMP_FORMAT_BC7: textureinfo.glInternalFormat = RGB_BP_UNorm; break; case CMP_FORMAT_ATI1N: textureinfo.glInternalFormat = R_ATI1N_UNorm; break; case CMP_FORMAT_ATI2N: textureinfo.glInternalFormat = R_ATI1N_SNorm; break; case CMP_FORMAT_ATI2N_XY: textureinfo.glInternalFormat = RG_ATI2N_UNorm; break; case CMP_FORMAT_ATI2N_DXT5: textureinfo.glInternalFormat = RG_ATI2N_SNorm; break; case CMP_FORMAT_ATC_RGB: textureinfo.glInternalFormat = ATC_RGB_AMD; break; case CMP_FORMAT_ATC_RGBA_Explicit: textureinfo.glInternalFormat = ATC_RGBA_EXPLICIT_ALPHA_AMD; break; case CMP_FORMAT_ATC_RGBA_Interpolated: textureinfo.glInternalFormat = ATC_RGBA_INTERPOLATED_ALPHA_AMD; break; case CMP_FORMAT_BC4: textureinfo.glInternalFormat = GL_COMPRESSED_RED_RGTC1; break; case CMP_FORMAT_BC5: textureinfo.glInternalFormat = GL_COMPRESSED_RG_RGTC2; break; case CMP_FORMAT_BC6H: textureinfo.glInternalFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; break; case CMP_FORMAT_BC6H_SF: textureinfo.glInternalFormat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; break; case CMP_FORMAT_ASTC: if ((pMipSet->m_nBlockWidth == 4) && (pMipSet->m_nBlockHeight == 4)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 4)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_5x4_KHR; else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 5)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_5x5_KHR; else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 5)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_6x5_KHR; else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 6)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_6x6_KHR; else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 5)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x5_KHR; else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 6)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x6_KHR; else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 8)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 5)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_10x5_KHR; else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 6)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_10x6_KHR; else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 8)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_10x8_KHR; else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 10)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_10x10_KHR; else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 10)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_12x10_KHR; else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 12)) textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_12x12_KHR; else textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; case CMP_FORMAT_ETC_RGB: textureinfo.glInternalFormat = ETC1_RGB8_OES; break; case CMP_FORMAT_ETC2_RGB: textureinfo.glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; case CMP_FORMAT_ETC2_SRGB: textureinfo.glInternalFormat = GL_COMPRESSED_SRGB8_ETC2; break; case CMP_FORMAT_ETC2_RGBA: textureinfo.glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; case CMP_FORMAT_ETC2_RGBA1: textureinfo.glInternalFormat = GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; break; case CMP_FORMAT_ETC2_SRGBA: textureinfo.glBaseInternalFormat = GL_SRGB8_ALPHA8; textureinfo.glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; break; case CMP_FORMAT_ETC2_SRGBA1: textureinfo.glBaseInternalFormat = GL_SRGB8_ALPHA8; textureinfo.glInternalFormat = GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; break; case CMP_FORMAT_DXT5_xGBR: textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_xGBR; break; case CMP_FORMAT_DXT5_RxBG: textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_RxBG; break; case CMP_FORMAT_DXT5_RBxG: textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_RBxG; break; case CMP_FORMAT_DXT5_xRBG: textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_xRBG; break; case CMP_FORMAT_DXT5_RGxB: textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_RGxB; break; case CMP_FORMAT_DXT5_xGxR: textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_xGxR; break; } } } break; default: break; } textureinfo.pixelWidth = pMipSet->m_nWidth; textureinfo.pixelHeight = pMipSet->m_nHeight; textureinfo.pixelDepth = 0; //for 1D, 2D and cube texture , depth =0; //1D if (pMipSet->m_nHeight == 1 && pData != NULL) { delete(pData); pData = NULL; pDataLen = 0; } textureinfo.numberOfMipmapLevels = pMipSet->m_nMipLevels; KTX_error_code save = ktxWriteKTXF(pFile, &textureinfo, pDataLen, pData, pMipSet->m_nMipLevels, inputMip); if (save == KTX_SUCCESS) { fclose(pFile); } else { if (KTX_CMips) KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); fclose(pFile); return -1; } return 0; }