text_unicode_ranges.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*******************************************************************************************
  2. *
  3. * raylib [text] example - unicode ranges
  4. *
  5. * Example complexity rating: [★★★★] 4/4
  6. *
  7. * Example originally created with raylib 5.5, last time updated with raylib 5.6
  8. *
  9. * Example contributed by Vadim Gunko (@GuvaCode) and reviewed by Ramon Santamaria (@raysan5)
  10. *
  11. * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
  12. * BSD-like license that allows static linking with closed source software
  13. *
  14. * Copyright (c) 2025 Vadim Gunko (@GuvaCode) and Ramon Santamaria (@raysan5)
  15. *
  16. ********************************************************************************************/
  17. #include "raylib.h"
  18. #include <stdlib.h> // Required for: calloc(), free()
  19. //--------------------------------------------------------------------------------------
  20. // Module Functions Declaration
  21. //--------------------------------------------------------------------------------------
  22. // Add codepoint range to existing font
  23. static void AddCodepointRange(Font *font, const char *fontPath, int start, int stop);
  24. //------------------------------------------------------------------------------------
  25. // Program main entry point
  26. //------------------------------------------------------------------------------------
  27. int main(void)
  28. {
  29. // Initialization
  30. //--------------------------------------------------------------------------------------
  31. const int screenWidth = 800;
  32. const int screenHeight = 450;
  33. InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode ranges");
  34. // Load font with default Unicode range: Basic ASCII [32-127]
  35. Font font = LoadFont("resources/NotoSansTC-Regular.ttf");
  36. SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR);
  37. int unicodeRange = 0; // Track the ranges of codepoints added to font
  38. int prevUnicodeRange = 0; // Previous Unicode range to avoid reloading every frame
  39. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  40. //--------------------------------------------------------------------------------------
  41. // Main loop
  42. while (!WindowShouldClose()) // Detect window close button or ESC key
  43. {
  44. // Update
  45. //----------------------------------------------------------------------------------
  46. if (unicodeRange != prevUnicodeRange)
  47. {
  48. UnloadFont(font);
  49. // Load font with default Unicode range: Basic ASCII [32-127]
  50. font = LoadFont("resources/NotoSansTC-Regular.ttf");
  51. // Add required ranges to loaded font
  52. switch (unicodeRange)
  53. {
  54. /*
  55. case 5:
  56. {
  57. // Unicode range: Devanari, Arabic, Hebrew
  58. // WARNING: Glyphs not available on provided font!
  59. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x900, 0x97f); // Devanagari
  60. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x600, 0x6ff); // Arabic
  61. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x5d0, 0x5ea); // Hebrew
  62. }
  63. */
  64. case 4:
  65. {
  66. // Unicode range: CJK (Japanese and Chinese)
  67. // WARNING: Loading thousands of codepoints requires lot of time!
  68. // A better strategy is prefilter the required codepoints for the text
  69. // in the game and just load the required ones
  70. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x4e00, 0x9fff);
  71. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3400, 0x4dbf);
  72. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3000, 0x303f);
  73. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3040, 0x309f);
  74. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x30A0, 0x30ff);
  75. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x31f0, 0x31ff);
  76. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xff00, 0xffef);
  77. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xac00, 0xd7af);
  78. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1100, 0x11ff);
  79. }
  80. case 3:
  81. {
  82. // Unicode range: Cyrillic
  83. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x400, 0x4ff);
  84. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x500, 0x52f);
  85. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x2de0, 0x2Dff);
  86. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xa640, 0xA69f);
  87. }
  88. case 2:
  89. {
  90. // Unicode range: Greek
  91. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x370, 0x3ff);
  92. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1f00, 0x1fff);
  93. }
  94. case 1:
  95. {
  96. // Unicode range: European Languages
  97. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xc0, 0x17f);
  98. AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x180, 0x24f);
  99. //AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1e00, 0x1eff);
  100. //AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x2c60, 0x2c7f);
  101. }
  102. default: break;
  103. }
  104. prevUnicodeRange = unicodeRange;
  105. SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); // Set font atlas scale filter
  106. }
  107. if (IsKeyPressed(KEY_ZERO)) unicodeRange = 0;
  108. else if (IsKeyPressed(KEY_ONE)) unicodeRange = 1;
  109. else if (IsKeyPressed(KEY_TWO)) unicodeRange = 2;
  110. else if (IsKeyPressed(KEY_THREE)) unicodeRange = 3;
  111. else if (IsKeyPressed(KEY_FOUR)) unicodeRange = 4;
  112. //else if (IsKeyPressed(KEY_FIVE)) unicodeRange = 5;
  113. //----------------------------------------------------------------------------------
  114. // Draw
  115. //----------------------------------------------------------------------------------
  116. BeginDrawing();
  117. ClearBackground(RAYWHITE);
  118. DrawText("ADD CODEPOINTS: [1][2][3][4]", 20, 20, 20, MAROON);
  119. // Render test strings in different languages
  120. DrawTextEx(font, "> English: Hello World!", (Vector2){ 50, 70 }, 32, 1, DARKGRAY); // English
  121. DrawTextEx(font, "> Español: Hola mundo!", (Vector2){ 50, 120 }, 32, 1, DARKGRAY); // Spanish
  122. DrawTextEx(font, "> Ελληνικά: Γειά σου κόσμε!", (Vector2){ 50, 170 }, 32, 1, DARKGRAY); // Greek
  123. DrawTextEx(font, "> Русский: Привет мир!", (Vector2){ 50, 220 }, 32, 0, DARKGRAY); // Russian
  124. DrawTextEx(font, "> 中文: 你好世界!", (Vector2){ 50, 270 }, 32, 1, DARKGRAY); // Chinese
  125. DrawTextEx(font, "> 日本語: こんにちは世界!", (Vector2){ 50, 320 }, 32, 1, DARKGRAY); // Japanese
  126. //DrawTextEx(font, "देवनागरी: होला मुंडो!", (Vector2){ 50, 350 }, 32, 1, DARKGRAY); // Devanagari (glyphs not available in font)
  127. // Draw font texture scaled to screen
  128. float atlasScale = 380.0f/font.texture.width;
  129. DrawRectangleRec((Rectangle) { 400.0f, 16.0f, font.texture.width* atlasScale, font.texture.height* atlasScale }, BLACK);
  130. DrawTexturePro(font.texture, (Rectangle){ 0, 0, (float)font.texture.width, (float)font.texture.height },
  131. (Rectangle){ 400.0f, 16.0f, font.texture.width*atlasScale, font.texture.height*atlasScale }, (Vector2){ 0, 0 }, 0.0f, WHITE);
  132. DrawRectangleLines(400, 16, 380, 380, RED);
  133. DrawText(TextFormat("ATLAS SIZE: %ix%i px (x%02.2f)", font.texture.width, font.texture.height, atlasScale), 20, 380, 20, BLUE);
  134. DrawText(TextFormat("CODEPOINTS GLYPHS LOADED: %i", font.glyphCount), 20, 410, 20, LIME);
  135. // Display font attribution
  136. DrawText("Font: Noto Sans TC. License: SIL Open Font License 1.1", screenWidth - 300, screenHeight - 20, 10, GRAY);
  137. if (prevUnicodeRange != unicodeRange)
  138. {
  139. DrawRectangle(0, 0, screenWidth, screenHeight, Fade(WHITE, 0.8f));
  140. DrawRectangle(0, 125, screenWidth, 200, GRAY);
  141. DrawText("GENERATING FONT ATLAS...", 120, 210, 40, BLACK);
  142. }
  143. EndDrawing();
  144. //----------------------------------------------------------------------------------
  145. }
  146. // De-Initialization
  147. //--------------------------------------------------------------------------------------
  148. UnloadFont(font); // Unload font resource
  149. CloseWindow(); // Close window and OpenGL context
  150. //--------------------------------------------------------------------------------------
  151. return 0;
  152. }
  153. //--------------------------------------------------------------------------------------
  154. // Module Functions Definition
  155. //--------------------------------------------------------------------------------------
  156. // Add codepoint range to existing font
  157. static void AddCodepointRange(Font *font, const char *fontPath, int start, int stop)
  158. {
  159. int rangeSize = stop - start + 1;
  160. int currentRangeSize = font->glyphCount;
  161. // TODO: Load glyphs from provided vector font (if available),
  162. // add them to existing font, regenerating font image and texture
  163. int updatedCodepointCount = currentRangeSize + rangeSize;
  164. int *updatedCodepoints = (int *)RL_CALLOC(updatedCodepointCount, sizeof(int));
  165. // Get current codepoint list
  166. for (int i = 0; i < currentRangeSize; i++) updatedCodepoints[i] = font->glyphs[i].value;
  167. // Add new codepoints to list (provided range)
  168. for (int i = currentRangeSize; i < updatedCodepointCount; i++)
  169. updatedCodepoints[i] = start + (i - currentRangeSize);
  170. UnloadFont(*font);
  171. *font = LoadFontEx(fontPath, 32, updatedCodepoints, updatedCodepointCount);
  172. RL_FREE(updatedCodepoints);
  173. }