raylib/src/textures.c

721 lines
25 KiB
C
Raw Normal View History

/**********************************************************************************************
*
* raylib.textures
*
* Basic functions to load and draw Textures (2d)
2014-09-03 18:51:28 +04:00
*
* Uses external lib:
* stb_image - Multiple formats image loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
* NOTE: stb_image has been slightly modified, original library: https://github.com/nothings/stb
*
* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com)
2014-09-03 18:51:28 +04:00
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
*
2014-09-03 18:51:28 +04:00
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
2014-09-03 18:51:28 +04:00
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
#include "raylib.h"
#include <stdlib.h> // Declares malloc() and free() for memory management
#include <string.h> // Required for strcmp(), strrchr(), strncmp()
2014-03-25 15:40:35 +04:00
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
#include "utils.h" // rRES data decompression utility function
// NOTE: Includes Android fopen function map
2014-03-25 15:40:35 +04:00
2014-11-23 23:58:17 +03:00
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" // Used to read image data (multiple formats support)
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// Nop...
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct {
unsigned char *data; // Image raw data
int width; // Image base width
int height; // Image base height
//int bpp; // bytes per pixel
//int components; // num color components
int mipmaps; // Mipmap levels, 1 by default
int compFormat; // Compressed data format, 0 if no compression
} ImageEx;
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
// It's lonely here...
2014-03-25 15:40:35 +04:00
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by text)
//----------------------------------------------------------------------------------
//...
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static ImageEx LoadDDS(const char *fileName); // Load DDS file
static ImageEx LoadPKM(const char *fileName); // Load PKM file
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Load an image into CPU memory (RAM)
Image LoadImage(const char *fileName)
{
Image image;
2014-09-03 18:51:28 +04:00
// Initial values
image.pixels = NULL;
image.width = 0;
image.height = 0;
2014-09-03 18:51:28 +04:00
if ((strcmp(GetExtension(fileName),"png") == 0) ||
(strcmp(GetExtension(fileName),"bmp") == 0) ||
(strcmp(GetExtension(fileName),"tga") == 0) ||
(strcmp(GetExtension(fileName),"jpg") == 0) ||
(strcmp(GetExtension(fileName),"gif") == 0) ||
(strcmp(GetExtension(fileName),"psd") == 0) ||
(strcmp(GetExtension(fileName),"pic") == 0))
2014-09-03 18:51:28 +04:00
{
int imgWidth;
int imgHeight;
int imgBpp;
2014-09-03 18:51:28 +04:00
// NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...)
// Force loading to 4 components (RGBA)
2014-09-03 18:51:28 +04:00
byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4);
2014-11-23 23:58:17 +03:00
// TODO: Check if file could be loaded! (imgData == NULL)?
if (imgData != NULL)
{
// Convert array to pixel array for working convenience
image.pixels = (Color *)malloc(imgWidth * imgHeight * sizeof(Color));
2014-09-03 18:51:28 +04:00
int pix = 0;
2014-09-03 18:51:28 +04:00
for (int i = 0; i < (imgWidth * imgHeight * 4); i += 4)
{
image.pixels[pix].r = imgData[i];
image.pixels[pix].g = imgData[i+1];
image.pixels[pix].b = imgData[i+2];
image.pixels[pix].a = imgData[i+3];
pix++;
}
2014-09-03 18:51:28 +04:00
stbi_image_free(imgData);
2014-09-03 18:51:28 +04:00
image.width = imgWidth;
image.height = imgHeight;
2014-09-03 18:51:28 +04:00
TraceLog(INFO, "[%s] Image loaded successfully", fileName);
}
2014-11-23 23:58:17 +03:00
else TraceLog(WARNING, "[%s] Image could not be loaded, file format not recognized", fileName);
}
else if (strcmp(GetExtension(fileName),"dds") == 0)
{
// NOTE: DDS uncompressed images can also be loaded (discarding mipmaps...)
2014-09-03 18:51:28 +04:00
ImageEx imageDDS = LoadDDS(fileName);
2014-09-03 18:51:28 +04:00
if (imageDDS.compFormat == 0)
{
image.pixels = (Color *)malloc(imageDDS.width * imageDDS.height * sizeof(Color));
image.width = imageDDS.width;
image.height = imageDDS.height;
2014-09-03 18:51:28 +04:00
int pix = 0;
2014-09-03 18:51:28 +04:00
for (int i = 0; i < (image.width * image.height * 4); i += 4)
{
image.pixels[pix].r = imageDDS.data[i];
image.pixels[pix].g = imageDDS.data[i+1];
image.pixels[pix].b = imageDDS.data[i+2];
image.pixels[pix].a = imageDDS.data[i+3];
pix++;
}
2014-09-03 18:51:28 +04:00
free(imageDDS.data);
2014-09-03 18:51:28 +04:00
TraceLog(INFO, "[%s] DDS Image loaded successfully (uncompressed, no mipmaps)", fileName);
}
else TraceLog(WARNING, "[%s] DDS Compressed image data could not be loaded", fileName);
}
else if (strcmp(GetExtension(fileName),"pkm") == 0)
{
TraceLog(INFO, "[%s] PKM Compressed image data could not be loaded", fileName);
}
else TraceLog(WARNING, "[%s] Image extension not recognized, it can't be loaded", fileName);
2014-09-03 18:51:28 +04:00
// ALTERNATIVE: We can load pixel data directly into Color struct pixels array,
// to do that, struct data alignment should be the right one (4 byte); it is.
//image.pixels = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4);
return image;
}
// Load an image from rRES file (raylib Resource)
Image LoadImageFromRES(const char *rresName, int resId)
{
// TODO: rresName could be directly a char array with all the data! --> support it! :P
2014-09-03 18:51:28 +04:00
Image image;
bool found = false;
char id[4]; // rRES file identifier
unsigned char version; // rRES file version and subversion
char useless; // rRES header reserved data
short numRes;
2014-09-03 18:51:28 +04:00
ResInfoHeader infoHeader;
2014-09-03 18:51:28 +04:00
FILE *rresFile = fopen(rresName, "rb");
if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName);
else
{
// Read rres file (basic file check - id)
fread(&id[0], sizeof(char), 1, rresFile);
fread(&id[1], sizeof(char), 1, rresFile);
fread(&id[2], sizeof(char), 1, rresFile);
fread(&id[3], sizeof(char), 1, rresFile);
fread(&version, sizeof(char), 1, rresFile);
fread(&useless, sizeof(char), 1, rresFile);
2014-09-03 18:51:28 +04:00
if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
{
TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName);
}
else
{
// Read number of resources embedded
fread(&numRes, sizeof(short), 1, rresFile);
2014-09-03 18:51:28 +04:00
for (int i = 0; i < numRes; i++)
{
fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile);
2014-09-03 18:51:28 +04:00
if (infoHeader.id == resId)
{
found = true;
2014-09-03 18:51:28 +04:00
// Check data is of valid IMAGE type
if (infoHeader.type == 0) // IMAGE data type
{
// TODO: Check data compression type
// NOTE: We suppose compression type 2 (DEFLATE - default)
short imgWidth, imgHeight;
char colorFormat, mipmaps;
2014-09-03 18:51:28 +04:00
fread(&imgWidth, sizeof(short), 1, rresFile); // Image width
fread(&imgHeight, sizeof(short), 1, rresFile); // Image height
fread(&colorFormat, 1, 1, rresFile); // Image data color format (default: RGBA 32 bit)
fread(&mipmaps, 1, 1, rresFile); // Mipmap images included (default: 0)
2014-09-03 18:51:28 +04:00
image.width = (int)imgWidth;
image.height = (int)imgHeight;
2014-09-03 18:51:28 +04:00
unsigned char *data = malloc(infoHeader.size);
fread(data, infoHeader.size, 1, rresFile);
2014-09-03 18:51:28 +04:00
unsigned char *imgData = DecompressData(data, infoHeader.size, infoHeader.srcSize);
2014-09-03 18:51:28 +04:00
image.pixels = (Color *)malloc(sizeof(Color)*imgWidth*imgHeight);
2014-09-03 18:51:28 +04:00
int pix = 0;
2014-09-03 18:51:28 +04:00
for (int i = 0; i < (imgWidth*imgHeight*4); i += 4)
{
image.pixels[pix].r = imgData[i];
image.pixels[pix].g = imgData[i+1];
image.pixels[pix].b = imgData[i+2];
image.pixels[pix].a = imgData[i+3];
pix++;
}
2014-09-03 18:51:28 +04:00
free(imgData);
2014-09-03 18:51:28 +04:00
free(data);
2014-09-03 18:51:28 +04:00
TraceLog(INFO, "[%s] Image loaded successfully from resource, size: %ix%i", rresName, image.width, image.height);
}
else
{
TraceLog(WARNING, "[%s] Required resource do not seem to be a valid IMAGE resource", rresName);
}
}
else
{
// Depending on type, skip the right amount of parameters
switch (infoHeader.type)
{
case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters
case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters
case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review)
case 3: break; // TEXT: No parameters
case 4: break; // RAW: No parameters
default: break;
}
2014-09-03 18:51:28 +04:00
// Jump DATA to read next infoHeader
fseek(rresFile, infoHeader.size, SEEK_CUR);
}
}
}
2014-09-03 18:51:28 +04:00
fclose(rresFile);
}
2014-09-03 18:51:28 +04:00
if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId);
2014-09-03 18:51:28 +04:00
return image;
}
// Load an image as texture into GPU memory
Texture2D LoadTexture(const char *fileName)
{
Texture2D texture;
if (strcmp(GetExtension(fileName),"dds") == 0)
{
ImageEx image = LoadDDS(fileName);
2014-09-03 18:51:28 +04:00
if (image.compFormat == 0)
{
texture.id = rlglLoadTexture(image.data, image.width, image.height, false);
}
else
{
texture.id = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.compFormat);
}
2014-09-03 18:51:28 +04:00
texture.width = image.width;
texture.height = image.height;
2014-09-03 18:51:28 +04:00
if (texture.id == 0) TraceLog(WARNING, "[%s] DDS texture could not be loaded", fileName);
2014-06-09 18:33:53 +04:00
else TraceLog(INFO, "[%s] DDS texture loaded successfully", fileName);
2014-09-03 18:51:28 +04:00
free(image.data);
}
else if (strcmp(GetExtension(fileName),"pkm") == 0)
{
ImageEx image = LoadPKM(fileName);
texture.id = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.compFormat);
texture.width = image.width;
texture.height = image.height;
if (texture.id == 0) TraceLog(WARNING, "[%s] PKM texture could not be loaded", fileName);
else TraceLog(INFO, "[%s] PKM texture loaded successfully", fileName);
free(image.data);
}
else
{
Image image = LoadImage(fileName);
2014-09-03 18:51:28 +04:00
if (image.pixels != NULL)
{
texture = CreateTexture(image, false);
UnloadImage(image);
}
}
2014-09-03 18:51:28 +04:00
return texture;
}
// Load an image as texture from rRES file (raylib Resource)
Texture2D LoadTextureFromRES(const char *rresName, int resId)
{
Texture2D texture;
Image image = LoadImageFromRES(rresName, resId);
texture = CreateTexture(image, false);
UnloadImage(image);
2014-09-03 18:51:28 +04:00
return texture;
}
// Unload image from CPU memory (RAM)
void UnloadImage(Image image)
{
free(image.pixels);
}
// Unload texture from GPU memory
void UnloadTexture(Texture2D texture)
{
rlDeleteTextures(texture.id);
}
// Draw a Texture2D
void DrawTexture(Texture2D texture, int posX, int posY, Color tint)
{
DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0, 1.0f, tint);
}
// Draw a Texture2D with position defined as Vector2
void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
{
DrawTextureEx(texture, position, 0, 1.0f, tint);
}
// Draw a Texture2D with extended parameters
void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
{
Rectangle sourceRec = { 0, 0, texture.width, texture.height };
Rectangle destRec = { (int)position.x, (int)position.y, texture.width*scale, texture.height*scale };
Vector2 origin = { 0, 0 };
2014-09-03 18:51:28 +04:00
DrawTexturePro(texture, sourceRec, destRec, origin, rotation, tint);
}
// Draw a part of a texture (defined by a rectangle)
void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint)
{
Rectangle destRec = { (int)position.x, (int)position.y, sourceRec.width, sourceRec.height };
Vector2 origin = { 0, 0 };
2014-09-03 18:51:28 +04:00
DrawTexturePro(texture, sourceRec, destRec, origin, 0, tint);
}
// Draw a part of a texture (defined by a rectangle) with 'pro' parameters
// NOTE: origin is relative to destination rectangle size
void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint)
{
rlEnableTexture(texture.id);
2014-09-03 18:51:28 +04:00
rlPushMatrix();
rlTranslatef(destRec.x, destRec.y, 0);
rlRotatef(rotation, 0, 0, 1);
rlTranslatef(-origin.x, -origin.y, 0);
2014-09-03 18:51:28 +04:00
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
2014-09-03 18:51:28 +04:00
// Bottom-left corner for texture and quad
2014-09-03 18:51:28 +04:00
rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height);
rlVertex2f(0.0f, 0.0f);
2014-09-03 18:51:28 +04:00
// Bottom-right corner for texture and quad
rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex2f(0.0f, destRec.height);
2014-09-03 18:51:28 +04:00
// Top-right corner for texture and quad
2014-09-03 18:51:28 +04:00
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
rlVertex2f(destRec.width, destRec.height);
2014-09-03 18:51:28 +04:00
// Top-left corner for texture and quad
rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
rlVertex2f(destRec.width, 0.0f);
rlEnd();
rlPopMatrix();
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
rlDisableTexture();
}
// Create a texture from an image
// NOTE: image is not unloaded, iot must be done manually
Texture2D CreateTexture(Image image, bool genMipmaps)
2014-03-25 15:40:35 +04:00
{
Texture2D texture;
2014-09-03 18:51:28 +04:00
// Init texture to default values
texture.id = 0;
texture.width = 0;
texture.height = 0;
2014-09-03 18:51:28 +04:00
if (image.pixels != NULL)
2014-03-25 15:40:35 +04:00
{
unsigned char *imgData = malloc(image.width * image.height * 4);
2014-09-03 18:51:28 +04:00
int j = 0;
2014-09-03 18:51:28 +04:00
for (int i = 0; i < image.width * image.height * 4; i += 4)
{
imgData[i] = image.pixels[j].r;
imgData[i+1] = image.pixels[j].g;
imgData[i+2] = image.pixels[j].b;
imgData[i+3] = image.pixels[j].a;
2014-09-03 18:51:28 +04:00
j++;
}
2014-03-25 15:40:35 +04:00
// NOTE: rlglLoadTexture() can generate mipmaps (POT image required)
texture.id = rlglLoadTexture(imgData, image.width, image.height, genMipmaps);
2014-03-25 15:40:35 +04:00
texture.width = image.width;
texture.height = image.height;
2014-09-03 18:51:28 +04:00
free(imgData);
}
else TraceLog(WARNING, "Texture could not be created, image data is not valid");
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
return texture;
}
// Loading DDS image data (compressed or uncompressed)
// NOTE: Compressed data loading not supported on OpenGL 1.1
ImageEx LoadDDS(const char *fileName)
2014-09-03 18:51:28 +04:00
{
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
2014-09-03 18:51:28 +04:00
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#endif
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#endif
#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
2014-09-03 18:51:28 +04:00
// DDS Pixel Format
typedef struct {
unsigned int size;
unsigned int flags;
unsigned int fourCC;
unsigned int rgbBitCount;
unsigned int rBitMask;
unsigned int gBitMask;
unsigned int bitMask;
unsigned int aBitMask;
} ddsPixelFormat;
2014-09-03 18:51:28 +04:00
// DDS Header (124 bytes)
typedef struct {
unsigned int size;
unsigned int flags;
unsigned int height;
unsigned int width;
unsigned int pitchOrLinearSize;
unsigned int depth;
unsigned int mipMapCount;
unsigned int reserved1[11];
ddsPixelFormat ddspf;
unsigned int caps;
unsigned int caps2;
unsigned int caps3;
unsigned int caps4;
unsigned int reserved2;
} ddsHeader;
2014-09-03 18:51:28 +04:00
ImageEx image;
ddsHeader header;
FILE *ddsFile = fopen(fileName, "rb");
2014-09-03 18:51:28 +04:00
if (ddsFile == NULL)
{
TraceLog(WARNING, "DDS File could not be opened");
}
else
{
// Verify the type of file
char filecode[4];
2014-09-03 18:51:28 +04:00
fread(filecode, 1, 4, ddsFile);
2014-09-03 18:51:28 +04:00
if (strncmp(filecode, "DDS ", 4) != 0)
{
TraceLog(WARNING, "DDS File does not seem to be valid");
fclose(ddsFile);
}
else
2014-09-03 18:51:28 +04:00
{
// Get the surface descriptor
fread(&header, sizeof(ddsHeader), 1, ddsFile);
TraceLog(DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(ddsHeader));
TraceLog(DEBUG, "[%s] DDS file pixel format size: %i", fileName, header.ddspf.size);
TraceLog(DEBUG, "[%s] DDS file pixel format flags: 0x%x", fileName, header.ddspf.flags);
TraceLog(DEBUG, "[%s] DDS file format: 0x%x", fileName, header.ddspf.fourCC);
2014-09-03 18:51:28 +04:00
image.width = header.width;
image.height = header.height;
image.mipmaps = 1;
image.compFormat = 0;
2014-09-03 18:51:28 +04:00
if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed
{
image.data = (unsigned char *)malloc(header.width * header.height * 4);
unsigned char *buffer = (unsigned char *)malloc(header.width * header.height * 3);
2014-09-03 18:51:28 +04:00
fread(buffer, image.width*image.height*3, 1, ddsFile);
2014-09-03 18:51:28 +04:00
unsigned char *src = buffer;
unsigned char *dest = image.data;
2014-09-03 18:51:28 +04:00
for(int y = 0; y < image.height; y++)
{
2014-09-03 18:51:28 +04:00
for(int x = 0; x < image.width; x++)
{
*dest++ = *src++;
*dest++ = *src++;
*dest++ = *src++;
*dest++ = 255;
}
}
2014-09-03 18:51:28 +04:00
free(buffer);
}
else if (header.ddspf.flags == 0x41 && header.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed
{
image.data = (unsigned char *)malloc(header.width * header.height * 4);
2014-09-03 18:51:28 +04:00
fread(image.data, image.width*image.height*4, 1, ddsFile);
2014-09-03 18:51:28 +04:00
image.mipmaps = 1;
image.compFormat = 0;
}
else if ((header.ddspf.flags == 0x04) && (header.ddspf.fourCC > 0))
{
TraceLog(WARNING, "[%s] DDS image uses compression, not supported on OpenGL 1.1", fileName);
TraceLog(WARNING, "[%s] DDS compressed files require OpenGL 3.2+ or ES 2.0", fileName);
int bufsize;
2014-09-03 18:51:28 +04:00
// Calculate data size, including all mipmaps
if (header.mipMapCount > 1) bufsize = header.pitchOrLinearSize * 2;
2014-09-03 18:51:28 +04:00
else bufsize = header.pitchOrLinearSize;
image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
fread(image.data, 1, bufsize, ddsFile);
// Close file pointer
fclose(ddsFile);
2014-09-03 18:51:28 +04:00
image.mipmaps = header.mipMapCount;
image.compFormat = 0;
2014-09-03 18:51:28 +04:00
switch(header.ddspf.fourCC)
2014-09-03 18:51:28 +04:00
{
case FOURCC_DXT1: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
case FOURCC_DXT3: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
case FOURCC_DXT5: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
default: break;
}
2014-09-03 18:51:28 +04:00
// NOTE: Image num color components not required... for now...
//if (fourCC == FOURCC_DXT1) image.components = 3;
//else image.components = 4;
}
}
}
2014-09-03 18:51:28 +04:00
return image;
}
// Loading PKM image data (ETC1/ETC2 compression)
// NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
// PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
ImageEx LoadPKM(const char *fileName)
{
// If OpenGL ES 2.0. the following format could be supported (ETC1):
//GL_ETC1_RGB8_OES
#ifndef GL_ETC1_RGB8_OES
#define GL_ETC1_RGB8_OES 0x8D64
#endif
// If OpenGL ES 3.0, the following formats are supported (ETC2/EAC):
//GL_COMPRESSED_RGB8_ETC2
//GL_COMPRESSED_RGBA8_ETC2
//GL_COMPRESSED_RG11_EAC
//...
// PKM file (ETC1) Header (16 bytes)
typedef struct {
char id[4]; // "PKM "
char version[2]; // "10"
unsigned short format; // Format = number of mipmaps = 0 (ETC1_RGB_NO_MIPMAPS)
unsigned short extWidth; // Texture width (big-endian)
unsigned short extHeight; // Texture height (big-endian)
unsigned short origWidth; // Original width (big-endian)
unsigned short origHeight; // Original height (big-endian)
} pkmHeader;
// NOTE: The extended width and height are the widths rounded up to a multiple of 4.
// NOTE: ETC is always 4bit per pixel (64 bits for each 4x4 block of pixels)
// Bytes Swap (little-endian <-> big-endian)
//unsigned short data;
//unsigned short swap = ((data & 0x00FF) << 8) | ((data & 0xFF00) >> 8);
ImageEx image;
unsigned short width;
unsigned short height;
unsigned short useless;
FILE *pkmFile = fopen(fileName, "rb");
if (pkmFile == NULL)
{
TraceLog(WARNING, "[%s] PKM File could not be opened", fileName);
}
else
{
// Verify the type of file
char filecode[4];
fread(filecode, 1, 4, pkmFile);
if (strncmp(filecode, "PKM ", 4) != 0)
{
TraceLog(WARNING, "[%s] PKM File does not seem to be valid", fileName);
fclose(pkmFile);
}
else
{
// Get the surface descriptor
fread(&useless, sizeof(unsigned short), 1, pkmFile); // Discard version
fread(&useless, sizeof(unsigned short), 1, pkmFile); // Discard format
fread(&width, sizeof(unsigned short), 1, pkmFile); // Read extended width
fread(&height, sizeof(unsigned short), 1, pkmFile); // Read extended height
int size = (width/4)*(height/4)*8; // Total data size in bytes
image.data = (unsigned char*)malloc(size * sizeof(unsigned char));
fread(image.data, 1, size, pkmFile);
fclose(pkmFile); // Close file pointer
image.width = width;
image.height = height;
image.mipmaps = 1;
image.compFormat = GL_ETC1_RGB8_OES;
}
}
return image;
}