From efa286a550115ebcb8dfb4e84ea6a719d110fd27 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 9 Oct 2016 13:09:08 +0200 Subject: Allow no default font loading Useful if text module is not required... --- src/text.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'src/text.c') diff --git a/src/text.c b/src/text.c index c538ea56..d1d7602f 100644 --- a/src/text.c +++ b/src/text.c @@ -293,13 +293,17 @@ void UnloadSpriteFont(SpriteFont spriteFont) // NOTE: chars spacing is proportional to fontSize void DrawText(const char *text, int posX, int posY, int fontSize, Color color) { - Vector2 position = { (float)posX, (float)posY }; + // Check if default font has been loaded + if (defaultFont.texture.id != 0) + { + Vector2 position = { (float)posX, (float)posY }; - int defaultFontSize = 10; // Default Font chars height in pixel - if (fontSize < defaultFontSize) fontSize = defaultFontSize; - int spacing = fontSize/defaultFontSize; + int defaultFontSize = 10; // Default Font chars height in pixel + if (fontSize < defaultFontSize) fontSize = defaultFontSize; + int spacing = fontSize/defaultFontSize; - DrawTextEx(defaultFont, text, position, (float)fontSize, spacing, color); + DrawTextEx(GetDefaultFont(), text, position, (float)fontSize, spacing, color); + } } // Draw text using SpriteFont @@ -404,13 +408,17 @@ const char *SubText(const char *text, int position, int length) // Measure string width for default font int MeasureText(const char *text, int fontSize) { - Vector2 vec; + Vector2 vec = { 0.0f, 0.0f }; - int defaultFontSize = 10; // Default Font chars height in pixel - if (fontSize < defaultFontSize) fontSize = defaultFontSize; - int spacing = fontSize/defaultFontSize; + // Check if default font has been loaded + if (defaultFont.texture.id != 0) + { + int defaultFontSize = 10; // Default Font chars height in pixel + if (fontSize < defaultFontSize) fontSize = defaultFontSize; + int spacing = fontSize/defaultFontSize; - vec = MeasureTextEx(defaultFont, text, fontSize, spacing); + vec = MeasureTextEx(GetDefaultFont(), text, fontSize, spacing); + } return (int)vec.x; } -- cgit v1.2.3 From 6d34adbd60631001c124ebe48d8d059a5ee8d3d8 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 24 Oct 2016 19:11:29 +0200 Subject: Improving sprite fonts support... Support grayscale (8 bit) textures for fonts Load unordered chars data above char 126 --- src/text.c | 31 +++++++++++++++++++++++-------- src/textures.c | 37 +++++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 18 deletions(-) (limited to 'src/text.c') diff --git a/src/text.c b/src/text.c index d1d7602f..02bc1fe6 100644 --- a/src/text.c +++ b/src/text.c @@ -355,7 +355,7 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float else rec = spriteFont.charRecs[(int)text[i] - FONT_FIRST_CHAR]; } - if (rec.x > 0) + if (rec.x >= 0) { DrawTexturePro(spriteFont.texture, rec, (Rectangle){ position.x + textOffsetX + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].x*scaleFactor, position.y + textOffsetY + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].y*scaleFactor, @@ -811,8 +811,12 @@ static SpriteFont LoadBMFont(const char *fileName) strncat(texPath, texFileName, strlen(texFileName)); TraceLog(DEBUG, "[%s] Font texture loading path: %s", fileName, texPath); + + Image imFont = LoadImage(texPath); + + if (imFont.format == UNCOMPRESSED_GRAYSCALE) ImageAlphaMask(&imFont, imFont); - font.texture = LoadTexture(texPath); + font.texture = LoadTextureFromImage(imFont); font.size = fontSize; font.numChars = numChars; font.charValues = (int *)malloc(numChars*sizeof(int)); @@ -820,12 +824,14 @@ static SpriteFont LoadBMFont(const char *fileName) font.charOffsets = (Vector2 *)malloc(numChars*sizeof(Vector2)); font.charAdvanceX = (int *)malloc(numChars*sizeof(int)); + UnloadImage(imFont); + free(texPath); int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX; bool unorderedChars = false; - int firstChar = 0; + int firstChar = 32; for (int i = 0; i < numChars; i++) { @@ -833,8 +839,20 @@ static SpriteFont LoadBMFont(const char *fileName) sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); - if (i == 0) firstChar = charId; - else if (i != (charId - firstChar)) unorderedChars = true; + if ((i == 0) && (charId != FONT_FIRST_CHAR)) + { + TraceLog(WARNING, "BMFont not supported: expected SPACE(32) as first character, falling back to default font"); + firstChar = charId; + break; + } + else if ((i < (126 - FONT_FIRST_CHAR)) && (i != (charId - FONT_FIRST_CHAR))) + { + // NOTE: We expect the first 95 chars (32..126) to be ordered for quick drawing access, + // characters above are stored and we look for them (search algorythm) when drawing + TraceLog(WARNING, "BMFont not supported: unordered chars data, falling back to default font"); + unorderedChars = true; + break; + } // Save data properly in sprite font font.charValues[i] = charId; @@ -845,9 +863,6 @@ static SpriteFont LoadBMFont(const char *fileName) fclose(fntFile); - if (firstChar != FONT_FIRST_CHAR) TraceLog(WARNING, "BMFont not supported: expected SPACE(32) as first character, falling back to default font"); - else if (unorderedChars) TraceLog(WARNING, "BMFont not supported: unordered chars data, falling back to default font"); - // NOTE: Font data could be not ordered by charId: 32,33,34,35... raylib does not support unordered BMFonts if ((firstChar != FONT_FIRST_CHAR) || (unorderedChars) || (font.texture.id == 0)) { diff --git a/src/textures.c b/src/textures.c index 323c0a8a..eab443b6 100644 --- a/src/textures.c +++ b/src/textures.c @@ -694,28 +694,45 @@ void ImageFormat(Image *image, int newFormat) } // Apply alpha mask to image -// NOTE 1: Returned image is RGBA - 32bit +// NOTE 1: Returned image is GRAY_ALPHA (16bit) or RGBA (32bit) // NOTE 2: alphaMask should be same size as image void ImageAlphaMask(Image *image, Image alphaMask) { - if (image->format >= COMPRESSED_DXT1_RGB) + if ((image->width != alphaMask.width) || (image->height != alphaMask.height)) + { + TraceLog(WARNING, "Alpha mask must be same size as image"); + } + else if (image->format >= COMPRESSED_DXT1_RGB) { TraceLog(WARNING, "Alpha mask can not be applied to compressed data formats"); - return; } else { // Force mask to be Grayscale Image mask = ImageCopy(alphaMask); - ImageFormat(&mask, UNCOMPRESSED_GRAYSCALE); + if (mask.format != UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, UNCOMPRESSED_GRAYSCALE); - // Convert image to RGBA - if (image->format != UNCOMPRESSED_R8G8B8A8) ImageFormat(image, UNCOMPRESSED_R8G8B8A8); - - // Apply alpha mask to alpha channel - for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4) + // In case image is only grayscale, we just add alpha channel + if (image->format == UNCOMPRESSED_GRAYSCALE) + { + ImageFormat(image, UNCOMPRESSED_GRAY_ALPHA); + + // Apply alpha mask to alpha channel + for (int i = 0, k = 1; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2) + { + ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i]; + } + } + else { - ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i]; + // Convert image to RGBA + if (image->format != UNCOMPRESSED_R8G8B8A8) ImageFormat(image, UNCOMPRESSED_R8G8B8A8); + + // Apply alpha mask to alpha channel + for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4) + { + ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i]; + } } UnloadImage(mask); -- cgit v1.2.3 From 137057f499f0c6a15ad7e306f27fc4d5c02933be Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 27 Oct 2016 13:39:47 +0200 Subject: Function added: GenSpriteFont() --- src/text.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/text.c') diff --git a/src/text.c b/src/text.c index 02bc1fe6..5e978823 100644 --- a/src/text.c +++ b/src/text.c @@ -272,6 +272,27 @@ SpriteFont LoadSpriteFont(const char *fileName) return spriteFont; } +// Generate SpriteFont from TTF file +// 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 +SpriteFont GenSpriteFont(const char *fileName, int fontSize, int *fontChars) +{ + SpriteFont spriteFont = { 0 }; + + if (strcmp(GetExtension(fileName),"ttf") == 0) + { + spriteFont = LoadTTF(fileName, fontSize, FONT_FIRST_CHAR, DEFAULT_TTF_NUMCHARS); + } + + if (spriteFont.texture.id == 0) + { + TraceLog(WARNING, "[%s] SpriteFont could not be generated, using default font", fileName); + spriteFont = GetDefaultFont(); + } + + return spriteFont; +} + // Unload SpriteFont from GPU memory void UnloadSpriteFont(SpriteFont spriteFont) { @@ -288,6 +309,7 @@ void UnloadSpriteFont(SpriteFont spriteFont) } } + // Draw text (using default font) // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used // NOTE: chars spacing is proportional to fontSize -- cgit v1.2.3 From cc917fbac6e7007fdb744afaf8f0879c984d95af Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 31 Oct 2016 15:38:15 +0100 Subject: Improve SpriteFont support LoadSpriteFontTTF() - TTF font loading with custom parameters --- src/raylib.h | 3 +-- src/text.c | 74 +++++++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 54 insertions(+), 23 deletions(-) (limited to 'src/text.c') diff --git a/src/raylib.h b/src/raylib.h index b0ee96bb..4996bb2b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -686,7 +686,6 @@ RLAPI bool IsKeyUp(int key); // Detect if a key RLAPI int GetKeyPressed(void); // Get latest key pressed 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 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 @@ -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 IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being 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 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 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 DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) diff --git a/src/text.c b/src/text.c index 5e978823..7707db7c 100644 --- a/src/text.c +++ b/src/text.c @@ -33,6 +33,7 @@ #include "utils.h" // Required for: GetExtension() // Following libs are used on LoadTTF() +//#define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #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); spriteFont = GetDefaultFont(); } + else SetTextureFilter(spriteFont.texture, FILTER_BILINEAR); 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 // 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 }; 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) @@ -522,7 +538,7 @@ void DrawFPS(int posX, int posY) // 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) { #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a)) @@ -595,15 +611,24 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar) xPosToRead = charSpacing; } - free(pixels); - 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 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; + + UnloadImage(fontClear); // Unload processed image once converted to texture // We got tempCharValues and tempCharsRecs populated with chars data // Now we move temp data to sized charValues and charRecs arrays @@ -900,12 +925,15 @@ static SpriteFont LoadBMFont(const char *fileName) // TODO: Review texture packing method and generation (use oversampling) static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int numChars) { - // NOTE: Generated font uses some hardcoded values - #define FONT_TEXTURE_WIDTH 512 // Font texture width - #define FONT_TEXTURE_HEIGHT 512 // Font texture height + // NOTE: Font texture size is predicted (being as much conservative as possible) + // Predictive method consist of supposing same number of chars by line-column (sqrtf) + // 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 *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); SpriteFont font = { 0 }; @@ -914,40 +942,44 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int 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; } fread(ttfBuffer, 1, 1<<25, ttfFile); // 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); // 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 - int k = 0; + unsigned char *dataGrayAlpha = (unsigned char *)malloc(textureSize*textureSize*sizeof(unsigned char)*2); // Two channels - 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 + 1] = dataBitmap[i]; - - k += 2; } free(dataBitmap); // Sprite font generation from TTF extracted data Image image; - image.width = FONT_TEXTURE_WIDTH; - image.height = FONT_TEXTURE_HEIGHT; + image.width = textureSize; + image.height = textureSize; image.mipmaps = 1; image.format = UNCOMPRESSED_GRAY_ALPHA; image.data = dataGrayAlpha; - + font.texture = LoadTextureFromImage(image); + + //WritePNG("generated_ttf_image.png", (unsigned char *)image.data, image.width, image.height, 2); + UnloadImage(image); // Unloads dataGrayAlpha font.size = fontSize; -- cgit v1.2.3 From 3393fda384479b237519c70a1cedeb0e5dad369c Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 31 Oct 2016 20:39:30 +0100 Subject: Improve TTF loading --- src/text.c | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'src/text.c') diff --git a/src/text.c b/src/text.c index 7707db7c..f56f588e 100644 --- a/src/text.c +++ b/src/text.c @@ -4,7 +4,7 @@ * * Basic functions to load SpriteFonts and draw Text * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * * 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. @@ -30,7 +30,7 @@ #include // Required for: va_list, va_start(), vfprintf(), va_end() #include // Required for: FILE, fopen(), fclose(), fscanf(), feof(), rewind(), fgets() -#include "utils.h" // Required for: GetExtension() +#include "utils.h" // Required for: GetExtension(), GetNextPOT() // Following libs are used on LoadTTF() //#define STBTT_STATIC @@ -72,9 +72,7 @@ static SpriteFont defaultFont; // Default font provided by raylib static SpriteFont LoadImageFont(Image image, Color key, int firstChar); // Load a Image font file (XNA style) static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font) static SpriteFont LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) - -// Generate a sprite font image from TTF data -static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int numChars); +static SpriteFont LoadTTF(const char *fileName, int fontSize, int numChars, int *fontChars); // Load spritefont from TTF data extern void LoadDefaultFont(void); extern void UnloadDefaultFont(void); @@ -255,7 +253,7 @@ SpriteFont LoadSpriteFont(const char *fileName) // Check file extension if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName); - else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadTTF(fileName, DEFAULT_TTF_FONTSIZE, FONT_FIRST_CHAR, DEFAULT_TTF_NUMCHARS); + else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadSpriteFontTTF(fileName, DEFAULT_TTF_FONTSIZE, 0, NULL); else if (strcmp(GetExtension(fileName),"fnt") == 0) spriteFont = LoadBMFont(fileName); else { @@ -283,21 +281,17 @@ SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, i if (strcmp(GetExtension(fileName),"ttf") == 0) { - 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; + int totalChars = 95; // Default charset [32..126] + + int *defaultFontChars = (int *)malloc(totalChars*sizeof(int)); + + for (int i = 0; i < totalChars; i++) defaultFontChars[i] = i + 32; // Default first character: SPACE[32] + + spriteFont = LoadTTF(fileName, fontSize, totalChars, defaultFontChars); } - - spriteFont = LoadTTF(fileName, fontSize, firstChar, totalChars); + else spriteFont = LoadTTF(fileName, fontSize, numChars, fontChars); } if (spriteFont.texture.id == 0) @@ -325,7 +319,6 @@ void UnloadSpriteFont(SpriteFont spriteFont) } } - // Draw text (using default font) // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used // NOTE: chars spacing is proportional to fontSize @@ -549,8 +542,8 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar) int x = 0; int y = 0; - // Default number of characters expected supported - #define MAX_FONTCHARS 128 + // Default number of characters supported + #define MAX_FONTCHARS 256 // We allocate a temporal arrays for chars data measures, // once we get the actual number of chars, we copy data to a sized arrays @@ -923,7 +916,7 @@ static SpriteFont LoadBMFont(const char *fileName) // Generate a sprite font from TTF file data (font size required) // 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 numChars, int *fontChars) { // NOTE: Font texture size is predicted (being as much conservative as possible) // Predictive method consist of supposing same number of chars by line-column (sqrtf) @@ -947,10 +940,13 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int } fread(ttfBuffer, 1, 1<<25, ttfFile); + + if (fontChars[0] != 32) TraceLog(WARNING, "TTF spritefont loading: first character is not SPACE(32) character"); // NOTE: Using stb_truetype crappy packing method, no guarante the font fits the image... - // 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); + // TODO: Replace this function by a proper packing method and support random chars order, + // we already receive a list (fontChars) with the ordered expected characters + int result = stbtt_BakeFontBitmap(ttfBuffer, 0, fontSize, dataBitmap, textureSize, textureSize, fontChars[0], 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"); @@ -991,7 +987,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int for (int i = 0; i < font.numChars; i++) { - font.charValues[i] = i + firstChar; + font.charValues[i] = fontChars[i]; font.charRecs[i].x = (int)charData[i].x0; font.charRecs[i].y = (int)charData[i].y0; -- cgit v1.2.3 From 6d3b11ef9124c9d938d9ecd2b341837bd5ed5dc1 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 1 Nov 2016 00:58:21 +0100 Subject: Support unordered charset, neither fixed first char Still requires some testing... --- src/text.c | 79 +++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 37 deletions(-) (limited to 'src/text.c') diff --git a/src/text.c b/src/text.c index f56f588e..23041603 100644 --- a/src/text.c +++ b/src/text.c @@ -44,7 +44,6 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define FONT_FIRST_CHAR 32 // NOTE: Expected first char for a sprite font #define MAX_FORMATTEXT_LENGTH 64 #define MAX_SUBTEXT_LENGTH 64 @@ -69,6 +68,8 @@ static SpriteFont defaultFont; // Default font provided by raylib //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- +static int GetCharIndex(SpriteFont font, int letter); + static SpriteFont LoadImageFont(Image image, Color key, int firstChar); // Load a Image font file (XNA style) static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font) static SpriteFont LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) @@ -197,7 +198,7 @@ extern void LoadDefaultFont(void) for (int i = 0; i < defaultFont.numChars; i++) { - defaultFont.charValues[i] = FONT_FIRST_CHAR + i; // First char is 32 + defaultFont.charValues[i] = 32 + i; // First char is 32 defaultFont.charRecs[i].x = currentPosX; defaultFont.charRecs[i].y = charsDivisor + currentLine*(charsHeight + charsDivisor); @@ -248,6 +249,7 @@ SpriteFont LoadSpriteFont(const char *fileName) // Default hardcoded values for ttf file loading #define DEFAULT_TTF_FONTSIZE 32 // Font first character (32 - space) #define DEFAULT_TTF_NUMCHARS 95 // ASCII 32..126 is 95 glyphs + #define DEFAULT_FIRST_CHAR 32 // Expected first char for image spritefont SpriteFont spriteFont = { 0 }; @@ -258,7 +260,7 @@ SpriteFont LoadSpriteFont(const char *fileName) else { Image image = LoadImage(fileName); - if (image.data != NULL) spriteFont = LoadImageFont(image, MAGENTA, FONT_FIRST_CHAR); + if (image.data != NULL) spriteFont = LoadImageFont(image, MAGENTA, DEFAULT_FIRST_CHAR); UnloadImage(image); } @@ -267,7 +269,7 @@ SpriteFont LoadSpriteFont(const char *fileName) TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName); spriteFont = GetDefaultFont(); } - else SetTextureFilter(spriteFont.texture, FILTER_BILINEAR); + else SetTextureFilter(spriteFont.texture, FILTER_POINT); // By default we set point filter (best performance) return spriteFont; } @@ -356,22 +358,18 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float for (int i = 0; i < length; i++) { - // TODO: Right now we are supposing characters that follow a continous order and start at FONT_FIRST_CHAR, - // this sytem can be improved to support any characters order and init value... - // An intermediate table could be created to link char values with predefined char position index in chars rectangle array - if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification HACK! { // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿) letter = (unsigned char)text[i + 1]; - rec = spriteFont.charRecs[letter - FONT_FIRST_CHAR]; + rec = spriteFont.charRecs[GetCharIndex(spriteFont, (int)letter)]; i++; } else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification HACK! { // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) letter = (unsigned char)text[i + 1]; - rec = spriteFont.charRecs[letter - FONT_FIRST_CHAR + 64]; + rec = spriteFont.charRecs[GetCharIndex(spriteFont, (int)letter + 64)]; i++; } else @@ -383,17 +381,19 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float textOffsetX = 0; rec.x = -1; } - else rec = spriteFont.charRecs[(int)text[i] - FONT_FIRST_CHAR]; + else rec = spriteFont.charRecs[GetCharIndex(spriteFont, (int)text[i])]; } if (rec.x >= 0) { - DrawTexturePro(spriteFont.texture, rec, (Rectangle){ position.x + textOffsetX + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].x*scaleFactor, - position.y + textOffsetY + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].y*scaleFactor, + int index = GetCharIndex(spriteFont, (int)text[i]); + + DrawTexturePro(spriteFont.texture, rec, (Rectangle){ position.x + textOffsetX + spriteFont.charOffsets[index].x*scaleFactor, + position.y + textOffsetY + spriteFont.charOffsets[index].y*scaleFactor, rec.width*scaleFactor, rec.height*scaleFactor} , (Vector2){ 0, 0 }, 0.0f, tint); - if (spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR] == 0) textOffsetX += (rec.width*scaleFactor + spacing); - else textOffsetX += (spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR]*scaleFactor + spacing); + if (spriteFont.charAdvanceX[index] == 0) textOffsetX += (rec.width*scaleFactor + spacing); + else textOffsetX += (spriteFont.charAdvanceX[index]*scaleFactor + spacing); } } } @@ -473,8 +473,10 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int if (text[i] != '\n') { - if (spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR] != 0) textWidth += spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR]; - else textWidth += (spriteFont.charRecs[(int)text[i] - FONT_FIRST_CHAR].width + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].x); + int index = GetCharIndex(spriteFont, (int)text[i]); + + if (spriteFont.charAdvanceX[index] != 0) textWidth += spriteFont.charAdvanceX[index]; + else textWidth += (spriteFont.charRecs[index].width + spriteFont.charOffsets[index].x); } else { @@ -531,6 +533,27 @@ void DrawFPS(int posX, int posY) // Module specific Functions Definition //---------------------------------------------------------------------------------- +static int GetCharIndex(SpriteFont font, int letter) +{ +//#define UNORDERED_CHARSET +#if defined(UNORDERED_CHARSET) + int index = 0; + + for (int i = 0; i < font.numChars; i++) + { + if (font.charValues[i] == letter) + { + index = i; + break; + } + } + + return index; +#else + return (letter - 32); +#endif +} + // Load an Image font file (XNA style) static SpriteFont LoadImageFont(Image image, Color key, int firstChar) { @@ -857,6 +880,7 @@ static SpriteFont LoadBMFont(const char *fileName) if (imFont.format == UNCOMPRESSED_GRAYSCALE) ImageAlphaMask(&imFont, imFont); font.texture = LoadTextureFromImage(imFont); + font.size = fontSize; font.numChars = numChars; font.charValues = (int *)malloc(numChars*sizeof(int)); @@ -870,30 +894,12 @@ static SpriteFont LoadBMFont(const char *fileName) int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX; - bool unorderedChars = false; - int firstChar = 32; - for (int i = 0; i < numChars; i++) { fgets(buffer, MAX_BUFFER_SIZE, fntFile); sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); - if ((i == 0) && (charId != FONT_FIRST_CHAR)) - { - TraceLog(WARNING, "BMFont not supported: expected SPACE(32) as first character, falling back to default font"); - firstChar = charId; - break; - } - else if ((i < (126 - FONT_FIRST_CHAR)) && (i != (charId - FONT_FIRST_CHAR))) - { - // NOTE: We expect the first 95 chars (32..126) to be ordered for quick drawing access, - // characters above are stored and we look for them (search algorythm) when drawing - TraceLog(WARNING, "BMFont not supported: unordered chars data, falling back to default font"); - unorderedChars = true; - break; - } - // Save data properly in sprite font font.charValues[i] = charId; font.charRecs[i] = (Rectangle){ charX, charY, charWidth, charHeight }; @@ -903,8 +909,7 @@ static SpriteFont LoadBMFont(const char *fileName) fclose(fntFile); - // NOTE: Font data could be not ordered by charId: 32,33,34,35... raylib does not support unordered BMFonts - if ((firstChar != FONT_FIRST_CHAR) || (unorderedChars) || (font.texture.id == 0)) + if (font.texture.id == 0) { UnloadSpriteFont(font); font = GetDefaultFont(); -- cgit v1.2.3