Improve SpriteFont support
LoadSpriteFontTTF() - TTF font loading with custom parameters
This commit is contained in:
parent
16101ce3d8
commit
cc917fbac6
@ -686,7 +686,6 @@ RLAPI bool IsKeyUp(int key); // Detect if a key
|
|||||||
RLAPI int GetKeyPressed(void); // Get latest key pressed
|
RLAPI int GetKeyPressed(void); // Get latest key pressed
|
||||||
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
|
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
|
||||||
|
|
||||||
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
|
|
||||||
RLAPI bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available
|
RLAPI bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available
|
||||||
RLAPI const char *GetGamepadName(int gamepad); // Return gamepad internal name id
|
RLAPI const char *GetGamepadName(int gamepad); // Return gamepad internal name id
|
||||||
RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis
|
RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis
|
||||||
@ -695,7 +694,6 @@ RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Detect if a gam
|
|||||||
RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once
|
RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once
|
||||||
RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed
|
RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed
|
||||||
RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed
|
RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed
|
||||||
#endif
|
|
||||||
|
|
||||||
RLAPI bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once
|
RLAPI bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once
|
||||||
RLAPI bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed
|
RLAPI bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed
|
||||||
@ -821,6 +819,7 @@ RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle dest
|
|||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
RLAPI SpriteFont GetDefaultFont(void); // Get the default SpriteFont
|
RLAPI SpriteFont GetDefaultFont(void); // Get the default SpriteFont
|
||||||
RLAPI SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory
|
RLAPI SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory
|
||||||
|
RLAPI SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, int *fontChars); // Load a SpriteFont from TTF font with parameters
|
||||||
RLAPI void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory
|
RLAPI void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory
|
||||||
|
|
||||||
RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
|
RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
|
||||||
|
72
src/text.c
72
src/text.c
@ -33,6 +33,7 @@
|
|||||||
#include "utils.h" // Required for: GetExtension()
|
#include "utils.h" // Required for: GetExtension()
|
||||||
|
|
||||||
// Following libs are used on LoadTTF()
|
// Following libs are used on LoadTTF()
|
||||||
|
//#define STBTT_STATIC
|
||||||
#define STB_TRUETYPE_IMPLEMENTATION
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
#include "external/stb_truetype.h" // Required for: stbtt_BakeFontBitmap()
|
#include "external/stb_truetype.h" // Required for: stbtt_BakeFontBitmap()
|
||||||
|
|
||||||
@ -268,20 +269,35 @@ SpriteFont LoadSpriteFont(const char *fileName)
|
|||||||
TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName);
|
TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName);
|
||||||
spriteFont = GetDefaultFont();
|
spriteFont = GetDefaultFont();
|
||||||
}
|
}
|
||||||
|
else SetTextureFilter(spriteFont.texture, FILTER_BILINEAR);
|
||||||
|
|
||||||
return spriteFont;
|
return spriteFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate SpriteFont from TTF file
|
// Load SpriteFont from TTF file with custom parameters
|
||||||
// NOTE: You can pass an array with desired characters, those characters should be available in the font
|
// NOTE: You can pass an array with desired characters, those characters should be available in the font
|
||||||
// if array is NULL, default char set is selected 32..126
|
// if array is NULL, default char set is selected 32..126
|
||||||
SpriteFont GenSpriteFont(const char *fileName, int fontSize, int *fontChars)
|
SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, int *fontChars)
|
||||||
{
|
{
|
||||||
SpriteFont spriteFont = { 0 };
|
SpriteFont spriteFont = { 0 };
|
||||||
|
|
||||||
if (strcmp(GetExtension(fileName),"ttf") == 0)
|
if (strcmp(GetExtension(fileName),"ttf") == 0)
|
||||||
{
|
{
|
||||||
spriteFont = LoadTTF(fileName, fontSize, FONT_FIRST_CHAR, DEFAULT_TTF_NUMCHARS);
|
int firstChar = 0;
|
||||||
|
int totalChars = 0;
|
||||||
|
|
||||||
|
if ((fontChars == NULL) || (numChars == 0))
|
||||||
|
{
|
||||||
|
firstChar = 32; // Default first character: SPACE[32]
|
||||||
|
totalChars = 95; // Default charset [32..126]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firstChar = fontChars[0];
|
||||||
|
totalChars = numChars;
|
||||||
|
}
|
||||||
|
|
||||||
|
spriteFont = LoadTTF(fileName, fontSize, firstChar, totalChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spriteFont.texture.id == 0)
|
if (spriteFont.texture.id == 0)
|
||||||
@ -522,7 +538,7 @@ void DrawFPS(int posX, int posY)
|
|||||||
// Module specific Functions Definition
|
// Module specific Functions Definition
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Load a Image font file (XNA style)
|
// Load an Image font file (XNA style)
|
||||||
static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
|
static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
|
||||||
{
|
{
|
||||||
#define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
|
#define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
|
||||||
@ -595,16 +611,25 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
|
|||||||
xPosToRead = charSpacing;
|
xPosToRead = charSpacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(pixels);
|
|
||||||
|
|
||||||
TraceLog(DEBUG, "SpriteFont data parsed correctly from image");
|
TraceLog(DEBUG, "SpriteFont data parsed correctly from image");
|
||||||
|
|
||||||
|
// NOTE: We need to remove key color borders from image to avoid weird
|
||||||
|
// artifacts on texture scaling when using FILTER_BILINEAR or FILTER_TRILINEAR
|
||||||
|
for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK;
|
||||||
|
|
||||||
|
// Create a new image with the processed color data (key color replaced by BLANK)
|
||||||
|
Image fontClear = LoadImageEx(pixels, image.width, image.height);
|
||||||
|
|
||||||
|
free(pixels); // Free pixels array memory
|
||||||
|
|
||||||
// Create spritefont with all data parsed from image
|
// Create spritefont with all data parsed from image
|
||||||
SpriteFont spriteFont = { 0 };
|
SpriteFont spriteFont = { 0 };
|
||||||
|
|
||||||
spriteFont.texture = LoadTextureFromImage(image); // Convert loaded image to OpenGL texture
|
spriteFont.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
|
||||||
spriteFont.numChars = index;
|
spriteFont.numChars = index;
|
||||||
|
|
||||||
|
UnloadImage(fontClear); // Unload processed image once converted to texture
|
||||||
|
|
||||||
// We got tempCharValues and tempCharsRecs populated with chars data
|
// We got tempCharValues and tempCharsRecs populated with chars data
|
||||||
// Now we move temp data to sized charValues and charRecs arrays
|
// Now we move temp data to sized charValues and charRecs arrays
|
||||||
spriteFont.charRecs = (Rectangle *)malloc(spriteFont.numChars*sizeof(Rectangle));
|
spriteFont.charRecs = (Rectangle *)malloc(spriteFont.numChars*sizeof(Rectangle));
|
||||||
@ -900,12 +925,15 @@ static SpriteFont LoadBMFont(const char *fileName)
|
|||||||
// TODO: Review texture packing method and generation (use oversampling)
|
// TODO: Review texture packing method and generation (use oversampling)
|
||||||
static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int numChars)
|
static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int numChars)
|
||||||
{
|
{
|
||||||
// NOTE: Generated font uses some hardcoded values
|
// NOTE: Font texture size is predicted (being as much conservative as possible)
|
||||||
#define FONT_TEXTURE_WIDTH 512 // Font texture width
|
// Predictive method consist of supposing same number of chars by line-column (sqrtf)
|
||||||
#define FONT_TEXTURE_HEIGHT 512 // Font texture height
|
// and a maximum character width of 3/4 of fontSize... it worked ok with all my tests...
|
||||||
|
int textureSize = GetNextPOT(ceil((float)fontSize*3/4)*ceil(sqrtf((float)numChars)));
|
||||||
|
|
||||||
|
TraceLog(INFO, "TTF spritefont loading: Predicted texture size: %ix%i", textureSize, textureSize);
|
||||||
|
|
||||||
unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25);
|
unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25);
|
||||||
unsigned char *dataBitmap = (unsigned char *)malloc(FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT*sizeof(unsigned char)); // One channel bitmap returned!
|
unsigned char *dataBitmap = (unsigned char *)malloc(textureSize*textureSize*sizeof(unsigned char)); // One channel bitmap returned!
|
||||||
stbtt_bakedchar *charData = (stbtt_bakedchar *)malloc(sizeof(stbtt_bakedchar)*numChars);
|
stbtt_bakedchar *charData = (stbtt_bakedchar *)malloc(sizeof(stbtt_bakedchar)*numChars);
|
||||||
|
|
||||||
SpriteFont font = { 0 };
|
SpriteFont font = { 0 };
|
||||||
@ -914,40 +942,44 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int
|
|||||||
|
|
||||||
if (ttfFile == NULL)
|
if (ttfFile == NULL)
|
||||||
{
|
{
|
||||||
TraceLog(WARNING, "[%s] FNT file could not be opened", fileName);
|
TraceLog(WARNING, "[%s] TTF file could not be opened", fileName);
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
fread(ttfBuffer, 1, 1<<25, ttfFile);
|
fread(ttfBuffer, 1, 1<<25, ttfFile);
|
||||||
|
|
||||||
// NOTE: Using stb_truetype crappy packing method, no guarante the font fits the image...
|
// NOTE: Using stb_truetype crappy packing method, no guarante the font fits the image...
|
||||||
stbtt_BakeFontBitmap(ttfBuffer,0, fontSize, dataBitmap, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, firstChar, numChars, charData);
|
// TODO: Replace this function by a proper packing method and support random chars order
|
||||||
|
int result = stbtt_BakeFontBitmap(ttfBuffer, 0, fontSize, dataBitmap, textureSize, textureSize, firstChar, numChars, charData);
|
||||||
|
|
||||||
|
//if (result > 0) TraceLog(INFO, "TTF spritefont loading: first unused row of generated bitmap: %i", result);
|
||||||
|
if (result < 0) TraceLog(WARNING, "TTF spritefont loading: Not all the characters fit in the font");
|
||||||
|
|
||||||
free(ttfBuffer);
|
free(ttfBuffer);
|
||||||
|
|
||||||
// Convert image data from grayscale to to UNCOMPRESSED_GRAY_ALPHA
|
// Convert image data from grayscale to to UNCOMPRESSED_GRAY_ALPHA
|
||||||
unsigned char *dataGrayAlpha = (unsigned char *)malloc(FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT*sizeof(unsigned char)*2); // Two channels
|
unsigned char *dataGrayAlpha = (unsigned char *)malloc(textureSize*textureSize*sizeof(unsigned char)*2); // Two channels
|
||||||
int k = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT; i++)
|
for (int i = 0, k = 0; i < textureSize*textureSize; i++, k += 2)
|
||||||
{
|
{
|
||||||
dataGrayAlpha[k] = 255;
|
dataGrayAlpha[k] = 255;
|
||||||
dataGrayAlpha[k + 1] = dataBitmap[i];
|
dataGrayAlpha[k + 1] = dataBitmap[i];
|
||||||
|
|
||||||
k += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(dataBitmap);
|
free(dataBitmap);
|
||||||
|
|
||||||
// Sprite font generation from TTF extracted data
|
// Sprite font generation from TTF extracted data
|
||||||
Image image;
|
Image image;
|
||||||
image.width = FONT_TEXTURE_WIDTH;
|
image.width = textureSize;
|
||||||
image.height = FONT_TEXTURE_HEIGHT;
|
image.height = textureSize;
|
||||||
image.mipmaps = 1;
|
image.mipmaps = 1;
|
||||||
image.format = UNCOMPRESSED_GRAY_ALPHA;
|
image.format = UNCOMPRESSED_GRAY_ALPHA;
|
||||||
image.data = dataGrayAlpha;
|
image.data = dataGrayAlpha;
|
||||||
|
|
||||||
font.texture = LoadTextureFromImage(image);
|
font.texture = LoadTextureFromImage(image);
|
||||||
|
|
||||||
|
//WritePNG("generated_ttf_image.png", (unsigned char *)image.data, image.width, image.height, 2);
|
||||||
|
|
||||||
UnloadImage(image); // Unloads dataGrayAlpha
|
UnloadImage(image); // Unloads dataGrayAlpha
|
||||||
|
|
||||||
font.size = fontSize;
|
font.size = fontSize;
|
||||||
|
Loading…
Reference in New Issue
Block a user