diff --git a/examples/text/text_styled.c b/examples/text/text_styled.c new file mode 100644 index 000000000000..c88edef9c2a2 --- /dev/null +++ b/examples/text/text_styled.c @@ -0,0 +1,70 @@ +/******************************************************************************************* +* +* raylib [text] example - Text styled +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Wagner Barongello (@SultansOfCode) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [text] example - text styled"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + Color colors[3] = { RED, GREEN, BLUE }; + int colorCount = sizeof(colors) / sizeof(Color); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // TODO: Update your variables here + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(SKYBLUE); + + DrawTextStyled("This changes the \0032foreground color", 200, 80, 20, colors, colorCount); + + DrawTextStyled("This changes the \0042background color", 200, 120, 20, colors, colorCount); + + DrawTextStyled("This changes the \0031\0042foreground and background colors", 200, 160, 20, colors, colorCount); + + DrawTextStyled("\0031\0042This \015restores the colors to the default ones", 200, 200, 20, colors, colorCount); + + DrawTextStyled("\0031\0042This \022inverts\022 the colors", 200, 240, 20, colors, colorCount); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/src/raylib.h b/src/raylib.h index 7919db77567a..77b9e5152621 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1479,16 +1479,20 @@ RLAPI void DrawFPS(int posX, int posY); RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation) +RLAPI void DrawTextStyled(const char *text, int posX, int posY, int fontSize, const Color *colors, int colorCount); // Draw styled text (using default font) +RLAPI void DrawTextStyledEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, const Color *colors, int colorCount); // Draw styled text using font and additional parameters RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) // Text font info functions -RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks -RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font -RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font -RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found -RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found -RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found +RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks +RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font +RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font +RLAPI int MeasureTextStyled(const char *text, int fontSize); // Measure styled string width for default font +RLAPI Vector2 MeasureTextStyledEx(Font font, const char *text, float fontSize, float spacing); // Measure styled string size for Font +RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found +RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found +RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found // Text codepoints management functions (unicode characters) RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array diff --git a/src/rtext.c b/src/rtext.c index 0d06caa9a9ac..907d5e8cdd69 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1202,6 +1202,131 @@ void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, rlPopMatrix(); } +// Draw styled text +void DrawTextStyled(const char *text, int posX, int posY, int fontSize, const Color *colors, int colorCount) +{ + if (GetFontDefault().texture.id != 0) + { + Vector2 position = { (float)posX, (float)posY }; + + int defaultFontSize = 10; + if (fontSize < defaultFontSize) fontSize = defaultFontSize; + int spacing = fontSize/defaultFontSize; + + DrawTextStyledEx(GetFontDefault(), text, position, (float)fontSize, (float)spacing, colors, colorCount); + } +} + +// Draw styled text using Font +void DrawTextStyledEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, const Color *colors, int colorCount) +{ + if (font.texture.id == 0) font = GetFontDefault(); + + int size = TextLength(text); + + Color defaultForeground = colorCount > 0 ? colors[0] : BLACK; + Color defaultBackground = { 0, 0, 0, 0 }; + + Color foreground = defaultForeground; + Color background = defaultBackground; + + float textOffsetY = 0.0f; + float textOffsetX = 0.0f; + + float scaleFactor = fontSize/font.baseSize; + + for (int i = 0; i < size;) + { + int codepointByteCount = 0; + int codepoint = GetCodepointNext(&text[i], &codepointByteCount); + + if (codepoint == '\n') + { + textOffsetY += (fontSize + textLineSpacing); + textOffsetX = 0.0f; + } + else + { + if (codepoint == '\003' || codepoint == '\004') + { + bool isForeground = codepoint == '\003'; + char colorBuffer[16] = { 0 }; + int colorBufferIndex = 0; + + i += codepointByteCount; + + codepoint = GetCodepointNext(&text[i], &codepointByteCount); + + while (codepoint >= '0' && codepoint <= '9' && i < size && colorBufferIndex < sizeof(colorBuffer) - 1) + { + colorBuffer[colorBufferIndex++] = codepoint; + + i += codepointByteCount; + + codepoint = GetCodepointNext(&text[i], &codepointByteCount); + } + + if (colorBufferIndex > 0) + { + int colorIndex = atoi(&colorBuffer); + + if (colorIndex >= 0) + { + if (isForeground) + { + foreground = colorIndex < colorCount ? colors[colorIndex] : defaultForeground; + } + else + { + background = colorIndex < colorCount ? colors[colorIndex] : defaultBackground; + } + } + } + + continue; + } + else if (codepoint == '\015') { + foreground = defaultForeground; + background = defaultBackground; + + i += codepointByteCount; + + continue; + } + else if (codepoint == '\022') { + Color temp = foreground; + + foreground = background; + background = temp; + + i += codepointByteCount; + + continue; + } + + int index = GetGlyphIndex(font, codepoint); + float increaseX = 0.0f; + + if (font.glyphs[index].advanceX == 0) increaseX = ((float)font.recs[index].width*scaleFactor + spacing); + else increaseX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing); + + if (background.a > 0) + { + DrawRectangle(position.x + textOffsetX, position.y + textOffsetY, increaseX, fontSize + textLineSpacing, background); + } + + if ((codepoint != ' ') && (codepoint != '\t')) + { + DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, foreground); + } + + textOffsetX += increaseX; + } + + i += codepointByteCount; + } +} + // Draw one character (codepoint) void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint) { @@ -1338,6 +1463,100 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing return textSize; } +// Measure styled string width for default font +int MeasureTextStyled(const char *text, int fontSize) +{ + Vector2 textSize = { 0.0f, 0.0f }; + + // Check if default font has been loaded + if (GetFontDefault().texture.id != 0) + { + int defaultFontSize = 10; // Default Font chars height in pixel + if (fontSize < defaultFontSize) fontSize = defaultFontSize; + int spacing = fontSize/defaultFontSize; + + textSize = MeasureTextStyledEx(GetFontDefault(), text, (float)fontSize, (float)spacing); + } + + return (int)textSize.x; +} + +// Measure styled string size for Font +Vector2 MeasureTextStyledEx(Font font, const char *text, float fontSize, float spacing) +{ + Vector2 textSize = { 0 }; + + if ((isGpuReady && (font.texture.id == 0)) || + (text == NULL) || (text[0] == '\0')) return textSize; // Security check + + int size = TextLength(text); // Get size in bytes of text + int tempByteCounter = 0; // Used to count longer text line num chars + int byteCounter = 0; + + float textWidth = 0.0f; + float tempTextWidth = 0.0f; // Used to count longer text line width + + float textHeight = fontSize; + float scaleFactor = fontSize/(float)font.baseSize; + + int letter = 0; // Current character + int index = 0; // Index position in sprite font + + for (int i = 0; i < size;) + { + int codepointByteCount = 0; + letter = GetCodepointNext(&text[i], &codepointByteCount); + + i += codepointByteCount; + + if (letter == '\015' || letter == '\022') + { + continue; + } + else if (letter == '\003' || letter == '\004') + { + letter = GetCodepointNext(&text[i], &codepointByteCount); + + while (letter >= '0' && letter <= '9' && i < size) + { + i += codepointByteCount; + + letter = GetCodepointNext(&text[i], &codepointByteCount); + } + + continue; + } + + byteCounter++; + + if (letter != '\n') + { + index = GetGlyphIndex(font, letter); + + if (font.glyphs[index].advanceX > 0) textWidth += font.glyphs[index].advanceX; + else textWidth += (font.recs[index].width + font.glyphs[index].offsetX); + } + else + { + if (tempTextWidth < textWidth) tempTextWidth = textWidth; + byteCounter = 0; + textWidth = 0; + + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup + textHeight += (fontSize + textLineSpacing); + } + + if (tempByteCounter < byteCounter) tempByteCounter = byteCounter; + } + + if (tempTextWidth < textWidth) tempTextWidth = textWidth; + + textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); + textSize.y = textHeight; + + return textSize; +} + // Get index position for a unicode character on font // NOTE: If codepoint is not found in the font it fallbacks to '?' int GetGlyphIndex(Font font, int codepoint)