aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/rlgl.h128
-rw-r--r--src/textures.c93
2 files changed, 154 insertions, 67 deletions
diff --git a/src/rlgl.h b/src/rlgl.h
index 75246cab..3f5c67a9 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -446,9 +446,11 @@ void rlLoadExtensions(void *loader); // Load OpenGL extensions
Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates
// Textures data management
-unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU
-void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data
-void rlUnloadTexture(unsigned int id);
+unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU
+void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data
+void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats
+void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory
+
void rlGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture
void *rlReadTexturePixels(Texture2D texture); // Read texture pixel data
unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer)
@@ -913,9 +915,6 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); /
#endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-// Get OpenGL internal formats and data type from raylib PixelFormat
-static void GetGlFormats(int format, int *glInternalFormat, int *glFormat, int *glType);
-
#if defined(GRAPHICS_API_OPENGL_11)
static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight);
static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight);
@@ -2015,8 +2014,8 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi
{
unsigned int mipSize = GetPixelDataSize(mipWidth, mipHeight, format);
- int glInternalFormat, glFormat, glType;
- GetGlFormats(format, &glInternalFormat, &glFormat, &glType);
+ unsigned int glInternalFormat, glFormat, glType;
+ rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
TraceLog(LOG_DEBUG, "Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset);
@@ -2105,8 +2104,8 @@ void rlUpdateTexture(unsigned int id, int width, int height, int format, const v
{
glBindTexture(GL_TEXTURE_2D, id);
- int glInternalFormat, glFormat, glType;
- GetGlFormats(format, &glInternalFormat, &glFormat, &glType);
+ unsigned int glInternalFormat, glFormat, glType;
+ rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
if ((glInternalFormat != -1) && (format < COMPRESSED_DXT1_RGB))
{
@@ -2115,13 +2114,64 @@ void rlUpdateTexture(unsigned int id, int width, int height, int format, const v
else TraceLog(LOG_WARNING, "Texture format updating not supported");
}
+// Get OpenGL internal formats and data type from raylib PixelFormat
+void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType)
+{
+ *glInternalFormat = -1;
+ *glFormat = -1;
+ *glType = -1;
+
+ switch (format)
+ {
+ #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
+ // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA
+ case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break;
+ case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break;
+ case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
+ case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
+ #if !defined(GRAPHICS_API_OPENGL_11)
+ case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
+ case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
+ case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
+ #endif
+ #elif defined(GRAPHICS_API_OPENGL_33)
+ case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break;
+ case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break;
+ case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
+ case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break;
+ case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break;
+ case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break;
+ #endif
+ #if !defined(GRAPHICS_API_OPENGL_11)
+ case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break;
+ case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
+ case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
+ case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
+ case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3
+ case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
+ case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
+ case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU
+ case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU
+ case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
+ case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
+ #endif
+ default: TraceLog(LOG_WARNING, "Texture format not supported"); break;
+ }
+}
+
// Unload texture from GPU memory
void rlUnloadTexture(unsigned int id)
{
if (id > 0) glDeleteTextures(1, &id);
}
-
// Load a texture to be used for rendering (fbo with color and depth attachments)
RenderTexture2D rlLoadRenderTexture(int width, int height)
{
@@ -2745,8 +2795,8 @@ void *rlReadTexturePixels(Texture2D texture)
// GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
- int glInternalFormat, glFormat, glType;
- GetGlFormats(texture.format, &glInternalFormat, &glFormat, &glType);
+ unsigned int glInternalFormat, glFormat, glType;
+ rlGetGlTextureFormats(texture.format, &glInternalFormat, &glFormat, &glType);
unsigned int size = GetPixelDataSize(texture.width, texture.height, texture.format);
if ((glInternalFormat != -1) && (texture.format < COMPRESSED_DXT1_RGB))
@@ -4591,58 +4641,6 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView)
#endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-// Get OpenGL internal formats and data type from raylib PixelFormat
-static void GetGlFormats(int format, int *glInternalFormat, int *glFormat, int *glType)
-{
- *glInternalFormat = -1;
- *glFormat = -1;
- *glType = -1;
-
- switch (format)
- {
- #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
- // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA
- case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break;
- case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break;
- case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
- case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
- #if !defined(GRAPHICS_API_OPENGL_11)
- case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
- case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
- case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
- #endif
- #elif defined(GRAPHICS_API_OPENGL_33)
- case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break;
- case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break;
- case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
- case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break;
- case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break;
- case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break;
- #endif
- #if !defined(GRAPHICS_API_OPENGL_11)
- case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break;
- case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
- case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
- case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
- case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3
- case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
- case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
- case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU
- case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU
- case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
- case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
- #endif
- default: TraceLog(LOG_WARNING, "Texture format not supported"); break;
- }
-}
-
#if defined(GRAPHICS_API_OPENGL_11)
// Mipmaps data is generated after image data
// NOTE: Only works with RGBA (4 bytes) data!
diff --git a/src/textures.c b/src/textures.c
index a7b9d3e2..bffe347a 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -148,6 +148,7 @@ static Image LoadPKM(const char *fileName); // Load PKM file
#endif
#if defined(SUPPORT_FILEFORMAT_KTX)
static Image LoadKTX(const char *fileName); // Load KTX file
+static void SaveKTX(Image image, const char *fileName); // Save image data as KTX file
#endif
#if defined(SUPPORT_FILEFORMAT_PVR)
static Image LoadPVR(const char *fileName); // Load PVR file
@@ -726,10 +727,11 @@ void ExportImage(Image image, const char *fileName)
if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4);
else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData);
else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData);
- else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // Between 1 and 100
+ else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100
+ else if (IsFileExtension(fileName, ".ktx")) SaveKTX(image, fileName);
else if (IsFileExtension(fileName, ".raw"))
{
- // Export raw pixel data
+ // Export raw pixel data (without header)
// NOTE: It's up to the user to track image parameters
FILE *rawFile = fopen(fileName, "wb");
fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile);
@@ -2932,6 +2934,93 @@ static Image LoadKTX(const char *fileName)
return image;
}
+
+// Save image data as KTX file
+// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
+static void SaveKTX(Image image, const char *fileName)
+{
+ // KTX file Header (64 bytes)
+ // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+ // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation
+
+ typedef struct {
+ char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n"
+ unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
+ unsigned int glType; // For compressed textures, glType must equal 0
+ unsigned int glTypeSize; // For compressed texture data, usually 1
+ unsigned int glFormat; // For compressed textures is 0
+ unsigned int glInternalFormat; // Compressed internal format
+ unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat
+ unsigned int width; // Texture image width in pixels
+ unsigned int height; // Texture image height in pixels
+ unsigned int depth; // For 2D textures is 0
+ unsigned int elements; // Number of array elements, usually 0
+ unsigned int faces; // Cubemap faces, for no-cubemap = 1
+ unsigned int mipmapLevels; // Non-mipmapped textures = 1
+ unsigned int keyValueDataSize; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0
+ // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)...
+ // KTX 2.0 defines additional header elements...
+ } KTXHeader;
+
+ // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize
+
+ FILE *ktxFile = fopen(fileName, "wb");
+
+ if (ktxFile == NULL) TraceLog(LOG_WARNING, "[%s] KTX image file could not be created", fileName);
+ else
+ {
+ KTXHeader ktxHeader;
+
+ // KTX identifier (v2.2)
+ //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
+ //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+
+ // Get the image header
+ strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature
+ ktxHeader.endianness = 0;
+ ktxHeader.glType = 0; // Obtained from image.format
+ ktxHeader.glTypeSize = 1;
+ ktxHeader.glFormat = 0; // Obtained from image.format
+ ktxHeader.glInternalFormat = 0; // Obtained from image.format
+ ktxHeader.glBaseInternalFormat = 0;
+ ktxHeader.width = image.width;
+ ktxHeader.height = image.height;
+ ktxHeader.depth = 0;
+ ktxHeader.elements = 0;
+ ktxHeader.faces = 1;
+ ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
+ ktxHeader.keyValueDataSize = 0; // No extra data after the header
+
+ rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function
+ ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only
+
+ // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
+
+ if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export.");
+ else
+ {
+ fwrite(&ktxHeader, 1, sizeof(KTXHeader), ktxFile);
+
+ int width = image.width;
+ int height = image.height;
+ int dataOffset = 0;
+
+ // Save all mipmaps data
+ for (int i = 0; i < image.mipmaps; i++)
+ {
+ unsigned int dataSize = GetPixelDataSize(width, height, image.format);
+ fwrite(&dataSize, 1, sizeof(unsigned int), ktxFile);
+ fwrite(image.data + dataOffset, 1, dataSize, ktxFile);
+
+ width /= 2;
+ height /= 2;
+ dataOffset += dataSize;
+ }
+ }
+
+ fclose(ktxFile); // Close file pointer
+ }
+}
#endif
#if defined(SUPPORT_FILEFORMAT_PVR)