summaryrefslogtreecommitdiff
path: root/libs/raylib/src/textures.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/raylib/src/textures.c')
-rw-r--r--libs/raylib/src/textures.c459
1 files changed, 263 insertions, 196 deletions
diff --git a/libs/raylib/src/textures.c b/libs/raylib/src/textures.c
index 5b0c397..1bf086b 100644
--- a/libs/raylib/src/textures.c
+++ b/libs/raylib/src/textures.c
@@ -65,14 +65,15 @@
#endif
#include <stdlib.h> // Required for: malloc(), free()
-#include <string.h> // Required for: strlen()
#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
+#include <string.h> // Required for: strlen() [Used in ImageTextEx()]
+#include <math.h> // Required for: fabsf()
#include "utils.h" // Required for: fopen() Android mapping
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
// Required for: rlLoadTexture() rlDeleteTextures(),
- // rlGenerateMipmaps(), some funcs for DrawTexturePro()
+ // rlGenerateMipmaps(), some funcs for DrawTexturePro()
// Support only desired texture formats on stb_image
#if !defined(SUPPORT_FILEFORMAT_BMP)
@@ -123,14 +124,20 @@
#endif
#if defined(SUPPORT_IMAGE_EXPORT)
+ #define STBIW_MALLOC RL_MALLOC
+ #define STBIW_FREE RL_FREE
+ #define STBIW_REALLOC RL_REALLOC
+
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "external/stb_image_write.h" // Required for: stbi_write_*()
#endif
#if defined(SUPPORT_IMAGE_MANIPULATION)
+ #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
+ #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
+
#define STB_IMAGE_RESIZE_IMPLEMENTATION
- #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8()
- // NOTE: Used for image scaling on ImageResize()
+ #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()]
#endif
#if defined(SUPPORT_IMAGE_GENERATION)
@@ -195,6 +202,7 @@ Image LoadImage(const char *fileName)
defined(SUPPORT_FILEFORMAT_TGA) || \
defined(SUPPORT_FILEFORMAT_GIF) || \
defined(SUPPORT_FILEFORMAT_PIC) || \
+ defined(SUPPORT_FILEFORMAT_HDR) || \
defined(SUPPORT_FILEFORMAT_PSD)
#define STBI_REQUIRED
#endif
@@ -225,53 +233,53 @@ Image LoadImage(const char *fileName)
)
{
#if defined(STBI_REQUIRED)
- int imgWidth = 0;
- int imgHeight = 0;
- int imgBpp = 0;
+ // NOTE: Using stb_image to load images (Supports multiple image formats)
- FILE *imFile = fopen(fileName, "rb");
+ unsigned int dataSize = 0;
+ unsigned char *fileData = LoadFileData(fileName, &dataSize);
- if (imFile != NULL)
+ if (fileData != NULL)
{
- // NOTE: Using stb_image to load images (Supports multiple image formats)
- image.data = stbi_load_from_file(imFile, &imgWidth, &imgHeight, &imgBpp, 0);
-
- fclose(imFile);
+ int comp = 0;
+ image.data = stbi_load_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
- image.width = imgWidth;
- image.height = imgHeight;
image.mipmaps = 1;
- if (imgBpp == 1) image.format = UNCOMPRESSED_GRAYSCALE;
- else if (imgBpp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA;
- else if (imgBpp == 3) image.format = UNCOMPRESSED_R8G8B8;
- else if (imgBpp == 4) image.format = UNCOMPRESSED_R8G8B8A8;
+ if (comp == 1) image.format = UNCOMPRESSED_GRAYSCALE;
+ else if (comp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA;
+ else if (comp == 3) image.format = UNCOMPRESSED_R8G8B8;
+ else if (comp == 4) image.format = UNCOMPRESSED_R8G8B8A8;
+
+ RL_FREE(fileData);
}
#endif
}
#if defined(SUPPORT_FILEFORMAT_HDR)
else if (IsFileExtension(fileName, ".hdr"))
{
- int imgBpp = 0;
-
- FILE *imFile = fopen(fileName, "rb");
+#if defined(STBI_REQUIRED)
+ unsigned int dataSize = 0;
+ unsigned char *fileData = LoadFileData(fileName, &dataSize);
- // Load 32 bit per channel floats data
- //stbi_set_flip_vertically_on_load(true);
- image.data = stbi_loadf_from_file(imFile, &image.width, &image.height, &imgBpp, 0);
+ if (fileData != NULL)
+ {
+ int comp = 0;
+ image.data = stbi_loadf_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
- fclose(imFile);
+ image.mipmaps = 1;
- image.mipmaps = 1;
+ if (comp == 1) image.format = UNCOMPRESSED_R32;
+ else if (comp == 3) image.format = UNCOMPRESSED_R32G32B32;
+ else if (comp == 4) image.format = UNCOMPRESSED_R32G32B32A32;
+ else
+ {
+ TRACELOG(LOG_WARNING, "IMAGE: [%s] HDR fileformat not supported", fileName);
+ UnloadImage(image);
+ }
- if (imgBpp == 1) image.format = UNCOMPRESSED_R32;
- else if (imgBpp == 3) image.format = UNCOMPRESSED_R32G32B32;
- else if (imgBpp == 4) image.format = UNCOMPRESSED_R32G32B32A32;
- else
- {
- TraceLog(LOG_WARNING, "[%s] Image fileformat not supported", fileName);
- UnloadImage(image);
+ RL_FREE(fileData);
}
+#endif
}
#endif
#if defined(SUPPORT_FILEFORMAT_DDS)
@@ -289,10 +297,10 @@ Image LoadImage(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_ASTC)
else if (IsFileExtension(fileName, ".astc")) image = LoadASTC(fileName);
#endif
- else TraceLog(LOG_WARNING, "[%s] Image fileformat not supported", fileName);
+ else TRACELOG(LOG_WARNING, "IMAGE: [%s] Fileformat not supported", fileName);
- if (image.data != NULL) TraceLog(LOG_INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height);
- else TraceLog(LOG_WARNING, "[%s] Image could not be loaded", fileName);
+ if (image.data != NULL) TRACELOG(LOG_INFO, "IMAGE: [%s] Data loaded successfully (%ix%i)", fileName, image.width, image.height);
+ else TRACELOG(LOG_WARNING, "IMAGE: [%s] Failed to load data", fileName);
return image;
}
@@ -346,40 +354,24 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
{
Image image = { 0 };
- FILE *rawFile = fopen(fileName, "rb");
+ unsigned int dataSize = 0;
+ unsigned char *fileData = LoadFileData(fileName, &dataSize);
- if (rawFile == NULL)
- {
- TraceLog(LOG_WARNING, "[%s] RAW image file could not be opened", fileName);
- }
- else
+ if (fileData != NULL)
{
- if (headerSize > 0) fseek(rawFile, headerSize, SEEK_SET);
-
+ unsigned char *dataPtr = fileData;
unsigned int size = GetPixelDataSize(width, height, format);
- image.data = RL_MALLOC(size); // Allocate required memory in bytes
-
- // NOTE: fread() returns num read elements instead of bytes,
- // to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element)
- int bytes = fread(image.data, 1, size, rawFile);
-
- // Check if data has been read successfully
- if (bytes < size)
- {
- TraceLog(LOG_WARNING, "[%s] RAW image data can not be read, wrong requested format or size", fileName);
+ if (headerSize > 0) dataPtr += headerSize;
- RL_FREE(image.data);
- }
- else
- {
- image.width = width;
- image.height = height;
- image.mipmaps = 1;
- image.format = format;
- }
+ image.data = RL_MALLOC(size); // Allocate required memory in bytes
+ memcpy(image.data, dataPtr, size); // Copy required data to image
+ image.width = width;
+ image.height = height;
+ image.mipmaps = 1;
+ image.format = format;
- fclose(rawFile);
+ RL_FREE(fileData);
}
return image;
@@ -397,7 +389,6 @@ Texture2D LoadTexture(const char *fileName)
texture = LoadTextureFromImage(image);
UnloadImage(image);
}
- else TraceLog(LOG_WARNING, "Texture could not be created");
return texture;
}
@@ -412,7 +403,7 @@ Texture2D LoadTextureFromImage(Image image)
{
texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps);
}
- else TraceLog(LOG_WARNING, "Texture could not be loaded from Image");
+ else TRACELOG(LOG_WARNING, "IMAGE: Data is not valid to load texture");
texture.width = image.width;
texture.height = image.height;
@@ -444,7 +435,7 @@ void UnloadTexture(Texture2D texture)
{
rlDeleteTextures(texture.id);
- TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data from VRAM (GPU)", texture.id);
+ TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Unloaded texture data from VRAM (GPU)", texture.id);
}
}
@@ -458,15 +449,15 @@ void UnloadRenderTexture(RenderTexture2D target)
Color *GetImageData(Image image)
{
if ((image.width == 0) || (image.height == 0)) return NULL;
-
+
Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color));
-
- if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats");
+
+ if (image.format >= COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
else
{
if ((image.format == UNCOMPRESSED_R32) ||
(image.format == UNCOMPRESSED_R32G32B32) ||
- (image.format == UNCOMPRESSED_R32G32B32A32)) TraceLog(LOG_WARNING, "32bit pixel format converted to 8bit per channel");
+ (image.format == UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 32bit to 8bit per channel");
for (int i = 0, k = 0; i < image.width*image.height; i++)
{
@@ -576,7 +567,7 @@ Vector4 *GetImageDataNormalized(Image image)
{
Vector4 *pixels = (Vector4 *)RL_MALLOC(image.width*image.height*sizeof(Vector4));
- if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats");
+ if (image.format >= COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
else
{
for (int i = 0, k = 0; i < image.width*image.height; i++)
@@ -686,9 +677,9 @@ Vector4 *GetImageDataNormalized(Image image)
Rectangle GetImageAlphaBorder(Image image, float threshold)
{
Rectangle crop = { 0 };
-
+
Color *pixels = GetImageData(image);
-
+
if (pixels != NULL)
{
int xMin = 65536; // Define a big enough number
@@ -783,11 +774,11 @@ Image GetTextureData(Texture2D texture)
// original texture format is retrieved on RPI...
image.format = UNCOMPRESSED_R8G8B8A8;
#endif
- TraceLog(LOG_INFO, "Texture pixel data obtained successfully");
+ TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Pixel data retrieved successfully", texture.id);
}
- else TraceLog(LOG_WARNING, "Texture pixel data could not be obtained");
+ else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve pixel data", texture.id);
}
- else TraceLog(LOG_WARNING, "Compressed texture data could not be obtained");
+ else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve compressed pixel data", texture.id);
return image;
}
@@ -844,16 +835,15 @@ void ExportImage(Image image, const char *fileName)
{
// Export raw pixel data (without header)
// NOTE: It's up to the user to track image parameters
- FILE *rawFile = fopen(fileName, "wb");
- success = fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile);
- fclose(rawFile);
+ SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format));
+ success = true;
}
RL_FREE(imgData);
#endif
- if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName);
- else TraceLog(LOG_WARNING, "Image could not be exported.");
+ if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName);
+ else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName);
}
// Export image as code file (.h) defining an array of bytes
@@ -953,8 +943,6 @@ void ImageToPOT(Image *image, Color fillColor)
// Security check to avoid program crash
if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
- Color *pixels = GetImageData(*image); // Get pixels data
-
// Calculate next power-of-two values
// NOTE: Just add the required amount of pixels at the right and bottom sides of image...
int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2)));
@@ -963,6 +951,7 @@ void ImageToPOT(Image *image, Color fillColor)
// Check if POT texture generation is required (if texture is not already POT)
if ((potWidth != image->width) || (potHeight != image->height))
{
+ Color *pixels = GetImageData(*image); // Get pixels data
Color *pixelsPOT = NULL;
// Generate POT array from NPOT data
@@ -977,19 +966,20 @@ void ImageToPOT(Image *image, Color fillColor)
}
}
- TraceLog(LOG_WARNING, "Image converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight);
-
- RL_FREE(pixels); // Free pixels data
- RL_FREE(image->data); // Free old image data
+ RL_FREE(pixels); // Free pixels data
+ RL_FREE(image->data); // Free old image data
int format = image->format; // Store image data format to reconvert later
// NOTE: Image size changes, new width and height
*image = LoadImageEx(pixelsPOT, potWidth, potHeight);
- RL_FREE(pixelsPOT); // Free POT pixels data
+ RL_FREE(pixelsPOT); // Free POT pixels data
- ImageFormat(image, format); // Reconvert image to previous format
+ ImageFormat(image, format); // Reconvert image to previous format
+
+ // TODO: Verification required for log
+ TRACELOG(LOG_WARNING, "IMAGE: Converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight);
}
}
@@ -1167,7 +1157,7 @@ void ImageFormat(Image *image, int newFormat)
#endif
}
}
- else TraceLog(LOG_WARNING, "Image data format is compressed, can not be converted");
+ else TRACELOG(LOG_WARNING, "IMAGE: Data format is compressed, can not be converted");
}
}
@@ -1178,11 +1168,11 @@ void ImageAlphaMask(Image *image, Image alphaMask)
{
if ((image->width != alphaMask.width) || (image->height != alphaMask.height))
{
- TraceLog(LOG_WARNING, "Alpha mask must be same size as image");
+ TRACELOG(LOG_WARNING, "IMAGE: Alpha mask must be same size as image");
}
else if (image->format >= COMPRESSED_DXT1_RGB)
{
- TraceLog(LOG_WARNING, "Alpha mask can not be applied to compressed data formats");
+ TRACELOG(LOG_WARNING, "IMAGE: Alpha mask can not be applied to compressed data formats");
}
else
{
@@ -1239,6 +1229,7 @@ void ImageAlphaClear(Image *image, Color color, float threshold)
*image = LoadImageEx(pixels, image->width, image->height);
ImageFormat(image, prevFormat);
+ RL_FREE(pixels);
}
// Premultiply alpha channel
@@ -1264,6 +1255,7 @@ void ImageAlphaPremultiply(Image *image)
*image = LoadImageEx(pixels, image->width, image->height);
ImageFormat(image, prevFormat);
+ RL_FREE(pixels);
}
#if defined(SUPPORT_IMAGE_MANIPULATION)
@@ -1340,11 +1332,11 @@ TextureCubemap LoadTextureCubemap(Image image, int layoutType)
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, size*i, size, size }, WHITE);
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
- if (cubemap.id == 0) TraceLog(LOG_WARNING, "Cubemap image could not be loaded.");
+ if (cubemap.id == 0) TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
UnloadImage(faces);
}
- else TraceLog(LOG_WARNING, "Cubemap image layout can not be detected.");
+ else TRACELOG(LOG_WARNING, "IMAGE: Failed to detect cubemap image layout");
return cubemap;
}
@@ -1389,7 +1381,7 @@ void ImageCrop(Image *image, Rectangle crop)
// Reformat 32bit RGBA image to original format
ImageFormat(image, format);
}
- else TraceLog(LOG_WARNING, "Image can not be cropped, crop rectangle out of bounds");
+ else TRACELOG(LOG_WARNING, "IMAGE: Failed to crop, rectangle out of bounds");
}
// Crop image depending on alpha value
@@ -1592,26 +1584,18 @@ void ImageMipmaps(Image *image)
if (mipWidth < 1) mipWidth = 1;
if (mipHeight < 1) mipHeight = 1;
- TraceLog(LOG_DEBUG, "Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize);
+ TRACELOGD("IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize);
mipCount++;
mipSize += GetPixelDataSize(mipWidth, mipHeight, image->format); // Add mipmap size (in bytes)
}
- TraceLog(LOG_DEBUG, "Mipmaps available: %i - Mipmaps required: %i", image->mipmaps, mipCount);
- TraceLog(LOG_DEBUG, "Mipmaps total size required: %i", mipSize);
- TraceLog(LOG_DEBUG, "Image data memory start address: 0x%x", image->data);
-
if (image->mipmaps < mipCount)
{
void *temp = RL_REALLOC(image->data, mipSize);
- if (temp != NULL)
- {
- image->data = temp; // Assign new pointer (new size) to store mipmaps data
- TraceLog(LOG_DEBUG, "Image data memory point reallocated: 0x%x", temp);
- }
- else TraceLog(LOG_WARNING, "Mipmaps required memory could not be allocated");
+ if (temp != NULL) image->data = temp; // Assign new pointer (new size) to store mipmaps data
+ else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated");
// Pointer to allocated memory point where store next mipmap level data
unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format);
@@ -1623,7 +1607,7 @@ void ImageMipmaps(Image *image)
for (int i = 1; i < mipCount; i++)
{
- TraceLog(LOG_DEBUG, "Gen mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
+ TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
@@ -1643,7 +1627,7 @@ void ImageMipmaps(Image *image)
UnloadImage(imCopy);
}
- else TraceLog(LOG_WARNING, "Image mipmaps already available");
+ else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available");
}
// Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
@@ -1656,13 +1640,13 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
if (image->format >= COMPRESSED_DXT1_RGB)
{
- TraceLog(LOG_WARNING, "Compressed data formats can not be dithered");
+ TRACELOG(LOG_WARNING, "IMAGE: Compressed data formats can not be dithered");
return;
}
if ((rBpp + gBpp + bBpp + aBpp) > 16)
{
- TraceLog(LOG_WARNING, "Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp));
+ TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp));
}
else
{
@@ -1672,7 +1656,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
if ((image->format != UNCOMPRESSED_R8G8B8) && (image->format != UNCOMPRESSED_R8G8B8A8))
{
- TraceLog(LOG_WARNING, "Image format is already 16bpp or lower, dithering could have no effect");
+ TRACELOG(LOG_WARNING, "IMAGE: Format is already 16bpp or lower, dithering could have no effect");
}
// Define new image format, check if desired bpp match internal known format
@@ -1682,7 +1666,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
else
{
image->format = 0;
- TraceLog(LOG_WARNING, "Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp);
+ TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp);
}
// NOTE: We will store the dithered data as unsigned short (16bpp)
@@ -1796,7 +1780,7 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
if (palCount >= maxPaletteSize)
{
i = image.width*image.height; // Finish palette get
- TraceLog(LOG_WARNING, "Image palette is greater than %i colors!", maxPaletteSize);
+ TRACELOG(LOG_WARNING, "IMAGE: Palette is greater than %i colors", maxPaletteSize);
}
}
}
@@ -1825,13 +1809,13 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
if ((srcRec.x + srcRec.width) > src.width)
{
srcRec.width = src.width - srcRec.x;
- TraceLog(LOG_WARNING, "Source rectangle width out of bounds, rescaled width: %i", srcRec.width);
+ TRACELOG(LOG_WARNING, "IMAGE: Source rectangle width out of bounds, rescaled width: %i", srcRec.width);
}
if ((srcRec.y + srcRec.height) > src.height)
{
srcRec.height = src.height - srcRec.y;
- TraceLog(LOG_WARNING, "Source rectangle height out of bounds, rescaled height: %i", srcRec.height);
+ TRACELOG(LOG_WARNING, "IMAGE: Source rectangle height out of bounds, rescaled height: %i", srcRec.height);
}
Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it
@@ -1946,9 +1930,8 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
{
int length = strlen(text);
- int index; // Index position in sprite font
- int letter = 0; // Current character
- int positionX = 0; // Image drawing position
+ int textOffsetX = 0; // Image drawing position X
+ int textOffsetY = 0; // Offset between lines (on line break '\n')
// NOTE: Text image is generated at font base size, later scaled to desired font size
Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing);
@@ -1958,36 +1941,42 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
for (int i = 0; i < length; i++)
{
- int next = 0;
- letter = GetNextCodepoint(&text[i], &next);
- index = GetGlyphIndex(font, letter);
+ // Get next codepoint from byte string and glyph index in font
+ int codepointByteCount = 0;
+ int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
+ int index = GetGlyphIndex(font, codepoint);
- if (letter == 0x3f) next = 1;
- i += (next - 1);
+ // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+ // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+ if (codepoint == 0x3f) codepointByteCount = 1;
- if (letter == '\n')
+ if (codepoint == '\n')
{
- // TODO: Support line break
+ // NOTE: Fixed line spacing of 1.5 line-height
+ // TODO: Support custom line spacing defined by user
+ textOffsetY += (font.baseSize + font.baseSize/2);
+ textOffsetX = 0.0f;
}
else
{
- if (letter != ' ')
+ if ((codepoint != ' ') && (codepoint != '\t'))
{
- ImageDraw(&imText, font.chars[index].image, (Rectangle){ 0, 0, font.chars[index].image.width, font.chars[index].image.height },
- (Rectangle){ (float)(positionX + font.chars[index].offsetX),(float)font.chars[index].offsetY,
- font.chars[index].image.width, font.chars[index].image.height }, tint);
+ Rectangle rec = { textOffsetX + font.chars[index].offsetX, textOffsetY + font.chars[index].offsetY, font.recs[index].width, font.recs[index].height };
+ ImageDraw(&imText, font.chars[index].image, (Rectangle){ 0, 0, font.chars[index].image.width, font.chars[index].image.height }, rec, tint);
}
- if (font.chars[index].advanceX == 0) positionX += (int)(font.recs[index].width + spacing);
- else positionX += font.chars[index].advanceX + (int)spacing;
+ if (font.chars[index].advanceX == 0) textOffsetX += (int)(font.recs[index].width + spacing);
+ else textOffsetX += font.chars[index].advanceX + (int)spacing;
}
+
+ i += (codepointByteCount - 1); // Move text bytes counter to next codepoint
}
// Scale image depending on text size
if (fontSize > imSize.y)
{
float scaleFactor = fontSize/imSize.y;
- TraceLog(LOG_INFO, "Image text scaled by factor: %f", scaleFactor);
+ TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor);
// Using nearest-neighbor scaling algorithm for default font
if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
@@ -1998,7 +1987,19 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
}
// Draw rectangle within an image
-void ImageDrawRectangle(Image *dst, Rectangle rec, Color color)
+void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color)
+{
+ ImageDrawRectangleRec(dst, (Rectangle){ posX, posY, width, height }, color);
+}
+
+// Draw rectangle within an image (Vector version)
+void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color)
+{
+ ImageDrawRectangle(dst, position.x, position.y, size.x, size.y, color);
+}
+
+// Draw rectangle within an image
+void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
{
// Security check to avoid program crash
if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
@@ -2011,10 +2012,86 @@ void ImageDrawRectangle(Image *dst, Rectangle rec, Color color)
// Draw rectangle lines within an image
void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
{
- ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y, rec.width, thick }, color);
- ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + thick, thick, rec.height - thick*2 }, color);
- ImageDrawRectangle(dst, (Rectangle){ rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2 }, color);
- ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + rec.height - thick, rec.width, thick }, color);
+ ImageDrawRectangle(dst, rec.x, rec.y, rec.width, thick, color);
+ ImageDrawRectangle(dst, rec.x, rec.y + thick, thick, rec.height - thick*2, color);
+ ImageDrawRectangle(dst, rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2, color);
+ ImageDrawRectangle(dst, rec.x, rec.y + rec.height - thick, rec.width, thick, color);
+}
+
+// Clear image background with given color
+void ImageClearBackground(Image *dst, Color color)
+{
+ ImageDrawRectangle(dst, 0, 0, dst->width, dst->height, color);
+}
+
+// Draw pixel within an image
+void ImageDrawPixel(Image *dst, int x, int y, Color color)
+{
+ ImageDrawRectangle(dst, x, y, 1, 1, color);
+}
+
+// Draw pixel within an image (Vector version)
+void ImageDrawPixelV(Image *dst, Vector2 position, Color color)
+{
+ ImageDrawRectangle(dst, (int)position.x, (int)position.y, 1, 1, color);
+}
+
+// Draw circle within an image
+void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color)
+{
+ int x = 0, y = radius;
+ int decesionParameter = 3 - 2*radius;
+
+ while (y >= x)
+ {
+ ImageDrawPixel(dst, centerX + x, centerY + y, color);
+ ImageDrawPixel(dst, centerX - x, centerY + y, color);
+ ImageDrawPixel(dst, centerX + x, centerY - y, color);
+ ImageDrawPixel(dst, centerX - x, centerY - y, color);
+ ImageDrawPixel(dst, centerX + y, centerY + x, color);
+ ImageDrawPixel(dst, centerX - y, centerY + x, color);
+ ImageDrawPixel(dst, centerX + y, centerY - x, color);
+ ImageDrawPixel(dst, centerX - y, centerY - x, color);
+ x++;
+
+ if (decesionParameter > 0)
+ {
+ y--;
+ decesionParameter = decesionParameter + 4*(x - y) + 10;
+ }
+ else decesionParameter = decesionParameter + 4*x + 6;
+ }
+}
+
+// Draw circle within an image (Vector version)
+void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color)
+{
+ ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color);
+}
+
+// Draw line within an image
+void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color)
+{
+ int m = 2*(endPosY - startPosY);
+ int slopeError = m - (startPosY - startPosX);
+
+ for (int x = startPosX, y = startPosY; x <= startPosY; x++)
+ {
+ ImageDrawPixel(dst, x, y, color);
+ slopeError += m;
+
+ if (slopeError >= 0)
+ {
+ y++;
+ slopeError -= 2*(startPosY - startPosX);
+ }
+ }
+}
+
+// Draw line within an image (Vector version)
+void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color)
+{
+ ImageDrawLine(dst, (int)start.x, (int)start.y, (int)end.x, (int)end.y, color);
}
// Draw text (default font) within an image (destination)
@@ -2635,7 +2712,7 @@ void SetTextureFilter(Texture2D texture, int filterMode)
}
else
{
- TraceLog(LOG_WARNING, "[TEX ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id);
+ TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id);
// RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR);
@@ -2703,7 +2780,7 @@ void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float sc
// Draw a part of a texture (defined by a rectangle)
void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint)
{
- Rectangle destRec = { position.x, position.y, (float)fabs(sourceRec.width), (float)fabs(sourceRec.height) };
+ Rectangle destRec = { position.x, position.y, fabsf(sourceRec.width), fabsf(sourceRec.height) };
Vector2 origin = { 0.0f, 0.0f };
DrawTexturePro(texture, sourceRec, destRec, origin, 0.0f, tint);
@@ -2982,30 +3059,18 @@ static Image LoadAnimatedGIF(const char *fileName, int *frames, int **delays)
{
Image image = { 0 };
- FILE *gifFile = fopen(fileName, "rb");
+ unsigned int dataSize = 0;
+ unsigned char *fileData = LoadFileData(fileName, &dataSize);
- if (gifFile == NULL)
+ if (fileData != NULL)
{
- TraceLog(LOG_WARNING, "[%s] Animated GIF file could not be opened", fileName);
- }
- else
- {
- fseek(gifFile, 0L, SEEK_END);
- int size = ftell(gifFile);
- fseek(gifFile, 0L, SEEK_SET);
-
- unsigned char *buffer = (unsigned char *)RL_CALLOC(size, sizeof(char));
- fread(buffer, sizeof(char), size, gifFile);
-
- fclose(gifFile); // Close file pointer
-
int comp = 0;
- image.data = stbi_load_gif_from_memory(buffer, size, delays, &image.width, &image.height, frames, &comp, 4);
+ image.data = stbi_load_gif_from_memory(fileData, dataSize, delays, &image.width, &image.height, frames, &comp, 4);
image.mipmaps = 1;
image.format = UNCOMPRESSED_R8G8B8A8;
- free(buffer);
+ RL_FREE(fileData);
}
return image;
@@ -3065,31 +3130,32 @@ static Image LoadDDS(const char *fileName)
if (ddsFile == NULL)
{
- TraceLog(LOG_WARNING, "[%s] DDS file could not be opened", fileName);
+ TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open DDS file", fileName);
}
else
{
// Verify the type of file
- char ddsHeaderId[4];
+ char ddsHeaderId[4] = { 0 };
fread(ddsHeaderId, 4, 1, ddsFile);
if ((ddsHeaderId[0] != 'D') || (ddsHeaderId[1] != 'D') || (ddsHeaderId[2] != 'S') || (ddsHeaderId[3] != ' '))
{
- TraceLog(LOG_WARNING, "[%s] DDS file does not seem to be a valid image", fileName);
+ TRACELOG(LOG_WARNING, "IMAGE: [%s] DDS file not a valid image", fileName);
}
else
{
- DDSHeader ddsHeader;
+ DDSHeader ddsHeader = { 0 };
// Get the image header
fread(&ddsHeader, sizeof(DDSHeader), 1, ddsFile);
- TraceLog(LOG_DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(DDSHeader));
- TraceLog(LOG_DEBUG, "[%s] DDS file pixel format size: %i", fileName, ddsHeader.ddspf.size);
- TraceLog(LOG_DEBUG, "[%s] DDS file pixel format flags: 0x%x", fileName, ddsHeader.ddspf.flags);
- TraceLog(LOG_DEBUG, "[%s] DDS file format: 0x%x", fileName, ddsHeader.ddspf.fourCC);
- TraceLog(LOG_DEBUG, "[%s] DDS file bit count: 0x%x", fileName, ddsHeader.ddspf.rgbBitCount);
+ TRACELOGD("IMAGE: [%s] DDS file info:", fileName);
+ TRACELOGD(" > Header size: %i", fileName, sizeof(DDSHeader));
+ TRACELOGD(" > Pixel format size: %i", fileName, ddsHeader.ddspf.size);
+ TRACELOGD(" > Pixel format flags: 0x%x", fileName, ddsHeader.ddspf.flags);
+ TRACELOGD(" > File format: 0x%x", fileName, ddsHeader.ddspf.fourCC);
+ TRACELOGD(" > File bit count: 0x%x", fileName, ddsHeader.ddspf.rgbBitCount);
image.width = ddsHeader.width;
image.height = ddsHeader.height;
@@ -3144,7 +3210,7 @@ static Image LoadDDS(const char *fileName)
}
}
}
- if (ddsHeader.ddspf.flags == 0x40 && ddsHeader.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed
+ else if (ddsHeader.ddspf.flags == 0x40 && ddsHeader.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed
{
// NOTE: not sure if this case exists...
image.data = (unsigned char *)RL_MALLOC(image.width*image.height*3*sizeof(unsigned char));
@@ -3179,8 +3245,6 @@ static Image LoadDDS(const char *fileName)
if (ddsHeader.mipmapCount > 1) size = ddsHeader.pitchOrLinearSize*2;
else size = ddsHeader.pitchOrLinearSize;
- TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize);
-
image.data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char));
fread(image.data, size, 1, ddsFile);
@@ -3245,18 +3309,18 @@ static Image LoadPKM(const char *fileName)
if (pkmFile == NULL)
{
- TraceLog(LOG_WARNING, "[%s] PKM file could not be opened", fileName);
+ TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open PKM file", fileName);
}
else
{
- PKMHeader pkmHeader;
+ PKMHeader pkmHeader = { 0 };
// Get the image header
fread(&pkmHeader, sizeof(PKMHeader), 1, pkmFile);
if ((pkmHeader.id[0] != 'P') || (pkmHeader.id[1] != 'K') || (pkmHeader.id[2] != 'M') || (pkmHeader.id[3] != ' '))
{
- TraceLog(LOG_WARNING, "[%s] PKM file does not seem to be a valid image", fileName);
+ TRACELOG(LOG_WARNING, "IMAGE: [%s] PKM file not a valid image", fileName);
}
else
{
@@ -3265,9 +3329,10 @@ static Image LoadPKM(const char *fileName)
pkmHeader.width = ((pkmHeader.width & 0x00FF) << 8) | ((pkmHeader.width & 0xFF00) >> 8);
pkmHeader.height = ((pkmHeader.height & 0x00FF) << 8) | ((pkmHeader.height & 0xFF00) >> 8);
- TraceLog(LOG_DEBUG, "PKM (ETC) image width: %i", pkmHeader.width);
- TraceLog(LOG_DEBUG, "PKM (ETC) image height: %i", pkmHeader.height);
- TraceLog(LOG_DEBUG, "PKM (ETC) image format: %i", pkmHeader.format);
+ TRACELOGD("IMAGE: [%s] PKM file info:", fileName);
+ TRACELOGD(" > Image width: %i", pkmHeader.width);
+ TRACELOGD(" > Image height: %i", pkmHeader.height);
+ TRACELOGD(" > Image format: %i", pkmHeader.format);
image.width = pkmHeader.width;
image.height = pkmHeader.height;
@@ -3338,11 +3403,11 @@ static Image LoadKTX(const char *fileName)
if (ktxFile == NULL)
{
- TraceLog(LOG_WARNING, "[%s] KTX image file could not be opened", fileName);
+ TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load KTX file", fileName);
}
else
{
- KTXHeader ktxHeader;
+ KTXHeader ktxHeader = { 0 };
// Get the image header
fread(&ktxHeader, sizeof(KTXHeader), 1, ktxFile);
@@ -3350,7 +3415,7 @@ static Image LoadKTX(const char *fileName)
if ((ktxHeader.id[1] != 'K') || (ktxHeader.id[2] != 'T') || (ktxHeader.id[3] != 'X') ||
(ktxHeader.id[4] != ' ') || (ktxHeader.id[5] != '1') || (ktxHeader.id[6] != '1'))
{
- TraceLog(LOG_WARNING, "[%s] KTX file does not seem to be a valid file", fileName);
+ TRACELOG(LOG_WARNING, "IMAGE: [%s] KTX file not a valid image", fileName);
}
else
{
@@ -3358,9 +3423,10 @@ static Image LoadKTX(const char *fileName)
image.height = ktxHeader.height;
image.mipmaps = ktxHeader.mipmapLevels;
- TraceLog(LOG_DEBUG, "KTX (ETC) image width: %i", ktxHeader.width);
- TraceLog(LOG_DEBUG, "KTX (ETC) image height: %i", ktxHeader.height);
- TraceLog(LOG_DEBUG, "KTX (ETC) image format: 0x%x", ktxHeader.glInternalFormat);
+ TRACELOGD("IMAGE: [%s] KTX file info:", fileName);
+ TRACELOGD(" > Image width: %i", ktxHeader.width);
+ TRACELOGD(" > Image height: %i", ktxHeader.height);
+ TRACELOGD(" > Image format: 0x%x", ktxHeader.glInternalFormat);
unsigned char unused;
@@ -3420,10 +3486,10 @@ static int SaveKTX(Image image, const char *fileName)
FILE *ktxFile = fopen(fileName, "wb");
- if (ktxFile == NULL) TraceLog(LOG_WARNING, "[%s] KTX image file could not be created", fileName);
+ if (ktxFile == NULL) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open KTX file", fileName);
else
{
- KTXHeader ktxHeader;
+ KTXHeader ktxHeader = { 0 };
// KTX identifier (v1.1)
//unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
@@ -3452,7 +3518,7 @@ static int SaveKTX(Image image, const char *fileName)
// 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.");
+ if (ktxHeader.glFormat == -1) TRACELOG(LOG_WARNING, "IMAGE: GL format not supported for KTX export (%i)", ktxHeader.glFormat);
else
{
success = fwrite(&ktxHeader, sizeof(KTXHeader), 1, ktxFile);
@@ -3547,7 +3613,7 @@ static Image LoadPVR(const char *fileName)
if (pvrFile == NULL)
{
- TraceLog(LOG_WARNING, "[%s] PVR file could not be opened", fileName);
+ TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load PVR file", fileName);
}
else
{
@@ -3559,14 +3625,14 @@ static Image LoadPVR(const char *fileName)
// Load different PVR data formats
if (pvrVersion == 0x50)
{
- PVRHeaderV3 pvrHeader;
+ PVRHeaderV3 pvrHeader = { 0 };
// Get PVR image header
fread(&pvrHeader, sizeof(PVRHeaderV3), 1, pvrFile);
if ((pvrHeader.id[0] != 'P') || (pvrHeader.id[1] != 'V') || (pvrHeader.id[2] != 'R') || (pvrHeader.id[3] != 3))
{
- TraceLog(LOG_WARNING, "[%s] PVR file does not seem to be a valid image", fileName);
+ TRACELOG(LOG_WARNING, "IMAGE: [%s] PVR file not a valid image", fileName);
}
else
{
@@ -3627,7 +3693,7 @@ static Image LoadPVR(const char *fileName)
fread(image.data, dataSize, 1, pvrFile);
}
}
- else if (pvrVersion == 52) TraceLog(LOG_INFO, "PVR v2 not supported, update your files to PVR v3");
+ else if (pvrVersion == 52) TRACELOG(LOG_INFO, "IMAGE: [%s] PVRv2 format not supported, update your files to PVRv3", fileName);
fclose(pvrFile); // Close file pointer
}
@@ -3665,18 +3731,18 @@ static Image LoadASTC(const char *fileName)
if (astcFile == NULL)
{
- TraceLog(LOG_WARNING, "[%s] ASTC file could not be opened", fileName);
+ TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load ASTC file", fileName);
}
else
{
- ASTCHeader astcHeader;
+ ASTCHeader astcHeader = { 0 };
// Get ASTC image header
fread(&astcHeader, sizeof(ASTCHeader), 1, astcFile);
if ((astcHeader.id[3] != 0x5c) || (astcHeader.id[2] != 0xa1) || (astcHeader.id[1] != 0xab) || (astcHeader.id[0] != 0x13))
{
- TraceLog(LOG_WARNING, "[%s] ASTC file does not seem to be a valid image", fileName);
+ TRACELOG(LOG_WARNING, "IMAGE: [%s] ASTC file not a valid image", fileName);
}
else
{
@@ -3684,9 +3750,10 @@ static Image LoadASTC(const char *fileName)
image.width = 0x00000000 | ((int)astcHeader.width[2] << 16) | ((int)astcHeader.width[1] << 8) | ((int)astcHeader.width[0]);
image.height = 0x00000000 | ((int)astcHeader.height[2] << 16) | ((int)astcHeader.height[1] << 8) | ((int)astcHeader.height[0]);
- TraceLog(LOG_DEBUG, "ASTC image width: %i", image.width);
- TraceLog(LOG_DEBUG, "ASTC image height: %i", image.height);
- TraceLog(LOG_DEBUG, "ASTC image blocks: %ix%i", astcHeader.blockX, astcHeader.blockY);
+ TRACELOGD("IMAGE: [%s] ASTC file info:", fileName);
+ TRACELOGD(" > Image width: %i", image.width);
+ TRACELOGD(" > Image height: %i", image.height);
+ TRACELOGD(" > Image blocks: %ix%i", astcHeader.blockX, astcHeader.blockY);
image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level
@@ -3704,7 +3771,7 @@ static Image LoadASTC(const char *fileName)
if (bpp == 8) image.format = COMPRESSED_ASTC_4x4_RGBA;
else if (bpp == 2) image.format = COMPRESSED_ASTC_8x8_RGBA;
}
- else TraceLog(LOG_WARNING, "[%s] ASTC block size configuration not supported", fileName);
+ else TRACELOG(LOG_WARNING, "IMAGE: [%s] ASTC block size configuration not supported", fileName);
}
fclose(astcFile);