7 Commits

Author SHA1 Message Date
Ray
d2d6fe6ef9 Update rtext.c 2026-09-30 22:41:55 +02:00
47dea9b452 [rtext] Fix BDF loading writing glyphs through a NULL-based pointer (#6198)
LoadFontDataBDF() used to track the glyph being filled in a separate
charGlyphInfo pointer. Since that pointer was folded into `glyphs`, the
loader resets the glyph array itself to NULL at every STARTCHAR and
BITMAP line, then computes the current glyph as &glyphs[index] from NULL.
Any BDF font with a glyph past the first requested codepoint crashes, and
the glyph array leaks.

Restore the separate per-glyph pointer, and allocate requiredCodepoints
after the NULL check and the default codepoint count are applied.
2026-09-30 22:37:42 +02:00
Ray
002f0310f3 Update rtextures.c 2026-09-30 22:30:44 +02:00
Ray
2ca7349a0c Update rtextures.c 2026-09-30 22:29:56 +02:00
Ray
3b028f0a17 ADDED: Implemented ImageDrawRectanglePro() 2026-09-30 22:26:19 +02:00
Ray
83b9c17366 ADDED: Implemented ImageDrawImageEx() 2026-09-30 22:24:14 +02:00
Ray
5496945d8b Update rtextures.c 2026-09-30 22:23:17 +02:00
2 changed files with 194 additions and 24 deletions

View File

@ -2811,6 +2811,7 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
char buffer[MAX_BUFFER_SIZE] = { 0 }; char buffer[MAX_BUFFER_SIZE] = { 0 };
GlyphInfo *glyphs = NULL; GlyphInfo *glyphs = NULL;
GlyphInfo *outGlyphPtr = NULL; // Pointer to output glyph info (NULL if not set)
bool internalCodepoints = false; bool internalCodepoints = false;
int totalReadBytes = 0; // Data bytes read (total) int totalReadBytes = 0; // Data bytes read (total)
@ -2839,12 +2840,11 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
int charDWidthX = 0; // Character advance X int charDWidthX = 0; // Character advance X
int charDWidthY = 0; // Character advance Y (unused) int charDWidthY = 0; // Character advance Y (unused)
int *requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
if (fileData == NULL) return glyphs; if (fileData == NULL) return glyphs;
// In case no chars count provided, default to 95 // In case no chars count provided, default to 95
codepointCount = (codepointCount > 0)? codepointCount : 95; codepointCount = (codepointCount > 0)? codepointCount : 95;
int *requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
if (codepoints == NULL) if (codepoints == NULL)
{ {
@ -2880,11 +2880,11 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
if (charBitmapStarted) if (charBitmapStarted)
{ {
if (glyphs != NULL) if (outGlyphPtr != NULL)
{ {
int pixelY = charBitmapNextRow++; int pixelY = charBitmapNextRow++;
if (pixelY >= glyphs->image.height) break; if (pixelY >= outGlyphPtr->image.height) break;
for (int x = 0; x < readBytes; x++) for (int x = 0; x < readBytes; x++)
{ {
@ -2894,9 +2894,9 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
{ {
int pixelX = ((x*4) + bitX); int pixelX = ((x*4) + bitX);
if (pixelX >= glyphs->image.width) break; if (pixelX >= outGlyphPtr->image.width) break;
if ((byte & (8 >> bitX)) > 0) ((unsigned char *)glyphs->image.data)[(pixelY*glyphs->image.width) + pixelX] = 255; if ((byte & (8 >> bitX)) > 0) ((unsigned char *)outGlyphPtr->image.data)[(pixelY*outGlyphPtr->image.width) + pixelX] = 255;
} }
} }
} }
@ -2928,30 +2928,30 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
if (strstr(buffer, "BITMAP") != NULL) if (strstr(buffer, "BITMAP") != NULL)
{ {
// Search for glyph index in codepoints // Search for glyph index in codepoints
glyphs = NULL; outGlyphPtr = NULL;
for (int index = 0; index < codepointCount; index++) for (int index = 0; index < codepointCount; index++)
{ {
if (requiredCodepoints[index] == charEncoding) if (requiredCodepoints[index] == charEncoding)
{ {
glyphs = &glyphs[index]; outGlyphPtr = &glyphs[index];
break; break;
} }
} }
// Init glyph info // Init glyph info
if (glyphs != NULL) if (outGlyphPtr != NULL)
{ {
glyphs->value = charEncoding; outGlyphPtr->value = charEncoding;
glyphs->offsetX = charBBxoff0 + fontBByoff0; outGlyphPtr->offsetX = charBBxoff0 + fontBByoff0;
glyphs->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent); outGlyphPtr->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent);
glyphs->advanceX = charDWidthX; outGlyphPtr->advanceX = charDWidthX;
glyphs->image.data = RL_CALLOC(charBBw*charBBh, 1); outGlyphPtr->image.data = RL_CALLOC(charBBw*charBBh, 1);
glyphs->image.width = charBBw; outGlyphPtr->image.width = charBBw;
glyphs->image.height = charBBh; outGlyphPtr->image.height = charBBh;
glyphs->image.mipmaps = 1; outGlyphPtr->image.mipmaps = 1;
glyphs->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; outGlyphPtr->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
} }
charBitmapStarted = true; charBitmapStarted = true;
@ -3002,14 +3002,14 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
{ {
charStarted = true; charStarted = true;
charEncoding = -1; charEncoding = -1;
glyphs = NULL; outGlyphPtr = NULL;
charBBw = 0; charBBw = 0;
charBBh = 0; charBBh = 0;
charBBxoff0 = 0; charBBxoff0 = 0;
charBByoff0 = 0; charBByoff0 = 0;
charDWidthX = 0; charDWidthX = 0;
charDWidthY = 0; charDWidthY = 0;
glyphs = NULL; outGlyphPtr = NULL;
charBitmapStarted = false; charBitmapStarted = false;
charBitmapNextRow = 0; charBitmapNextRow = 0;
continue; continue;

View File

@ -1592,7 +1592,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
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 // Using nearest-neighbor scaling algorithm for default font
// TODO: Support selection of preferred scaling mechanism, use flag? // TODO: Support other scaling mechanism, use flag?
if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
} }
@ -3902,7 +3902,82 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
// Draw a color-filled rectangle with pro parameters within and image // Draw a color-filled rectangle with pro parameters within and image
void ImageDrawRectanglePro(Image *dst, Rectangle rec, Vector2 origin, float rotation, Color color) void ImageDrawRectanglePro(Image *dst, Rectangle rec, Vector2 origin, float rotation, Color color)
{ {
// TODO: NEW: Implement ImageDrawRectanglePro() // Security checks to avoid program crash
if ((dst == NULL) || (dst->data == NULL) || (rec.width <= 0) || (rec.height <= 0)) return;
float cosAngle = cosf(rotation*DEG2RAD);
float sinAngle = sinf(rotation*DEG2RAD);
// Rotation origin in world/image coordinates
float ox = rec.x + origin.x;
float oy = rec.y + origin.y;
// Rectangle corners relative to rotation origin
float x1 = -origin.x;
float y1 = -origin.y;
float x2 = rec.width - origin.x;
float y2 = rec.height - origin.y;
// Rotate the four corners to calculate the bounding box
float cornersX[4] = { x1, x2, x2, x1 };
float cornersY[4] = { y1, y1, y2, y2 };
float minX = 65536;
float minY = 65536;
float maxX = -65536;
float maxY = -65536;
for (int i = 0; i < 4; i++)
{
float rx = cornersX[i]*cosAngle - cornersY[i]*sinAngle;
float ry = cornersX[i]*sinAngle + cornersY[i]*cosAngle;
rx += ox;
ry += oy;
if (rx < minX) minX = rx;
if (ry < minY) minY = ry;
if (rx > maxX) maxX = rx;
if (ry > maxY) maxY = ry;
}
// Convert bounding box to integer pixel bounds
int x0 = (int)floorf(minX);
int y0 = (int)floorf(minY);
int xEnd = (int)ceilf(maxX);
int yEnd = (int)ceilf(maxY);
// Limit drawing to image bounds
if (x0 < 0) x0 = 0;
if (y0 < 0) y0 = 0;
if (xEnd > dst->width) xEnd = dst->width;
if (yEnd > dst->height) yEnd = dst->height;
// Safety check
if ((x0 >= xEnd) || (y0 >= yEnd)) return;
for (int y = y0; y < yEnd; y++)
{
for (int x = x0; x < xEnd; x++)
{
// Pixel center in world coordinates
float px = x + 0.5f - ox;
float py = y + 0.5f - oy;
// Inverse-rotate pixel into rectangle local space
float localX = px*cosAngle + py*sinAngle;
float localY = -px*sinAngle + py*cosAngle;
// Check whether pixel lies inside the rectangle
if ((localX >= -origin.x) && (localX < (rec.width - origin.x)) &&
(localY >= -origin.y) && (localY < (rec.height - origin.y)))
{
// NOTE: Pixel format conversion managed by function
ImageDrawPixel(dst, x, y, color);
}
}
}
} }
// Draw rectangle lines within an image // Draw rectangle lines within an image
@ -4080,7 +4155,7 @@ void ImageDrawCircleGradient(Image *dst, Vector2 center, float radius, Color inn
(unsigned char)(inner.a + (outer.a - inner.a)*t) (unsigned char)(inner.a + (outer.a - inner.a)*t)
}; };
// NOTE: Pixel format conversion managed by ImageDrawPixel() // NOTE: Pixel format conversion managed by function
ImageDrawPixel(dst, x, y, color); ImageDrawPixel(dst, x, y, color);
} }
} }
@ -4095,9 +4170,103 @@ void ImageDrawImage(Image *dst, Image src, int posX, int posY, Color tint)
} }
// Draw an image with scaling and rotation within an image // Draw an image with scaling and rotation within an image
// NOTE: Rotation applied from top-left corner origin
void ImageDrawImageEx(Image *dst, Image src, Vector2 position, float rotation, float scale, Color tint) void ImageDrawImageEx(Image *dst, Image src, Vector2 position, float rotation, float scale, Color tint)
{ {
// TODO: NEW: Implement ImageDrawImageEx() // Security checks to avoid program crash
if ((dst == NULL) || (dst->data == NULL) || (dst->width <= 0) || (dst->height <= 0) || (src.width <= 0) || (src.height <= 0)) return;
float cosA = cosf(rotation*DEG2RAD);
float sinA = sinf(rotation*DEG2RAD);
// Rotate around the source image top-left corner
float cornersX[4] = { 0.0f, src.width*scale, src.width*scale, 0.0f };
float cornersY[4] = { 0.0f, 0.0f, src.height*scale, src.height*scale };
float minX = 65536;
float minY = 65536;
float maxX = -65536;
float maxY = -65536;
// Calculate the rotated bounding box
for (int i = 0; i < 4; i++)
{
float rx = cornersX[i]*cosA - cornersY[i]*sinA;
float ry = cornersX[i]*sinA + cornersY[i]*cosA;
rx += position.x;
ry += position.y;
if (rx < minX) minX = rx;
if (ry < minY) minY = ry;
if (rx > maxX) maxX = rx;
if (ry > maxY) maxY = ry;
}
int x0 = (int)floorf(minX);
int y0 = (int)floorf(minY);
int x1 = (int)ceilf(maxX);
int y1 = (int)ceilf(maxY);
// Limit drawing to destination bounds
if (x0 < 0) x0 = 0;
if (y0 < 0) y0 = 0;
if (x1 > dst->width) x1 = dst->width;
if (y1 > dst->height) y1 = dst->height;
// Safety check
if ((x0 >= x1) || (y0 >= y1)) return;
for (int y = y0; y < y1; y++)
{
for (int x = x0; x < x1; x++)
{
// Destination pixel center relative to image position
float dx = (x + 0.5f) - position.x;
float dy = (y + 0.5f) - position.y;
// Inverse-rotate into the scaled source space
float sx = dx*cosA + dy*sinA;
float sy = -dx*sinA + dy*cosA;
// Revert scaling
sx /= scale;
sy /= scale;
// Skip pixels outside the source image
if ((sx < 0.0f) || (sy < 0.0f) || (sx >= src.width) || (sy >= src.height)) continue;
// Nearest-neighbor source sampling
int srcX = (int)sx;
int srcY = (int)sy;
Color srcColor = GetImageColor(src, srcX, srcY);
// Apply tint
srcColor.r = (unsigned char)(srcColor.r*tint.r/255.0f);
srcColor.g = (unsigned char)(srcColor.g*tint.g/255.0f);
srcColor.b = (unsigned char)(srcColor.b*tint.b/255.0f);
srcColor.a = (unsigned char)(srcColor.a*tint.a/255.0f);
// Skip fully transparent pixels
if (srcColor.a == 0) continue;
// Read destination pixel for alpha blending
Color dstColor = GetImageColor(*dst, x, y);
unsigned int alpha = srcColor.a;
unsigned int invA = 255 - alpha;
Color out = {
(unsigned char)((srcColor.r*alpha + dstColor.r*invA)/255), // red
(unsigned char)((srcColor.g*alpha + dstColor.g*invA)/255), // green
(unsigned char)((srcColor.b*alpha + dstColor.b*invA)/255), // blue
(unsigned char)(alpha + (dstColor.a*invA)/255) // alpha
};
ImageDrawPixel(dst, x, y, out);
}
}
} }
// Draw a part of an image defined by a rectangle within an image // Draw a part of an image defined by a rectangle within an image
@ -4682,6 +4851,7 @@ void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
} }
// Draw a texture with rotation and scale // Draw a texture with rotation and scale
// NOTE: Rotation applied from top-left corner origin
void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint) void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
{ {
Rectangle srcrec = { 0.0f, 0.0f, (float)texture.width, (float)texture.height }; Rectangle srcrec = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };