summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-01 07:34:41 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-01 07:34:41 +0300
commit5fec4c221b860ef33a45ced9919bc356d6dbb3d3 (patch)
tree60627a0331482fe16d1395fae8a397dae866ab5e
parentb264791f65730b5e6c2414415cc4f4f1cb9253d8 (diff)
Text: Working on HarfBuzz
-rw-r--r--src/ui/text.c205
-rw-r--r--src/ui/text.h22
2 files changed, 186 insertions, 41 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index 411d82d3..54a6049f 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -257,19 +257,19 @@ static iText text_;
257static iBlock *userFont_; 257static iBlock *userFont_;
258 258
259static void initFonts_Text_(iText *d) { 259static void initFonts_Text_(iText *d) {
260 const float textSize = fontSize_UI * d->contentFontSize; 260 const float textSize = fontSize_UI * d->contentFontSize;
261 const float monoSize = textSize * 0.71f; 261 const float monoSize = textSize * 0.71f;
262 const float smallMonoSize = monoSize * 0.8f; 262 const float smallMonoSize = monoSize * 0.8f;
263 const iBlock *regularFont = &fontNunitoRegular_Embedded; 263 const iBlock *regularFont = &fontNunitoRegular_Embedded;
264 const iBlock *boldFont = &fontNunitoBold_Embedded; 264 const iBlock *boldFont = &fontNunitoBold_Embedded;
265 const iBlock *italicFont = &fontNunitoLightItalic_Embedded; 265 const iBlock *italicFont = &fontNunitoLightItalic_Embedded;
266 const iBlock *h12Font = &fontNunitoExtraBold_Embedded; 266 const iBlock *h12Font = &fontNunitoExtraBold_Embedded;
267 const iBlock *h3Font = &fontNunitoRegular_Embedded; 267 const iBlock *h3Font = &fontNunitoRegular_Embedded;
268 const iBlock *lightFont = &fontNunitoExtraLight_Embedded; 268 const iBlock *lightFont = &fontNunitoExtraLight_Embedded;
269 float scaling = 1.0f; /* glyph scaling (<=1.0), for increasing line spacing */ 269 float scaling = 1.0f; /* glyph scaling (<=1.0), for increasing line spacing */
270 float italicScaling= 1.0f; 270 float italicScaling = 1.0f;
271 float lightScaling = 1.0f; 271 float lightScaling = 1.0f;
272 float h123Scaling = 1.0f; /* glyph scaling (<=1.0), for increasing line spacing */ 272 float h123Scaling = 1.0f; /* glyph scaling (<=1.0), for increasing line spacing */
273 if (d->contentFont == firaSans_TextFont) { 273 if (d->contentFont == firaSans_TextFont) {
274 regularFont = &fontFiraSansRegular_Embedded; 274 regularFont = &fontFiraSansRegular_Embedded;
275 boldFont = &fontFiraSansSemiBold_Embedded; 275 boldFont = &fontFiraSansSemiBold_Embedded;
@@ -754,17 +754,18 @@ struct Impl_AttributedRun {
754}; 754};
755 755
756iDeclareType(AttributedText) 756iDeclareType(AttributedText)
757iDeclareTypeConstructionArgs(AttributedText, iRangecc text, iFont *font, iColor fgColor) 757iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, iColor fgColor)
758 758
759struct Impl_AttributedText { 759struct Impl_AttributedText {
760 iRangecc text; 760 iRangecc text;
761 size_t maxLen;
761 iFont * font; 762 iFont * font;
762 iColor fgColor; 763 iColor fgColor;
763 iArray runs; 764 iArray runs;
764}; 765};
765 766
766iDefineTypeConstructionArgs(AttributedText, (iRangecc text, iFont *font, iColor fgColor), 767iDefineTypeConstructionArgs(AttributedText, (iRangecc text, size_t maxLen, iFont *font, iColor fgColor),
767 text, font, fgColor) 768 text, maxLen, font, fgColor)
768 769
769static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, 770static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run,
770 const char *endAt) { 771 const char *endAt) {
@@ -774,13 +775,15 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run,
774 pushBack_Array(&d->runs, &finishedRun); 775 pushBack_Array(&d->runs, &finishedRun);
775 run->lineBreaks = 0; 776 run->lineBreaks = 0;
776 } 777 }
777 run->text.start = endAt; 778 run->text.start = run->text.end = endAt;
778} 779}
779 780
780static void prepare_AttributedText_(iAttributedText *d) { 781static void prepare_AttributedText_(iAttributedText *d) {
781 iAssert(isEmpty_Array(&d->runs)); 782 iAssert(isEmpty_Array(&d->runs));
783 size_t avail = d->maxLen;
782 const char *chPos = d->text.start; 784 const char *chPos = d->text.start;
783 iAttributedRun run = { .text = d->text, .font = d->font, .fgColor = d->fgColor }; 785 iAttributedRun run = { .text = (iRangecc){ d->text.start, d->text.start },
786 .font = d->font, .fgColor = d->fgColor };
784 while (chPos < d->text.end) { 787 while (chPos < d->text.end) {
785 const char *currentPos = chPos; 788 const char *currentPos = chPos;
786 if (*chPos == 0x1b) { /* ANSI escape. */ 789 if (*chPos == 0x1b) { /* ANSI escape. */
@@ -824,10 +827,13 @@ static void prepare_AttributedText_(iAttributedText *d) {
824 isFitzpatrickType_Char(ch)) { 827 isFitzpatrickType_Char(ch)) {
825 continue; 828 continue;
826 } 829 }
830 if (avail-- == 0) {
831 /* TODO: Check the combining class; only count base characters here. */
832 break;
833 }
827 const iGlyph *glyph = glyph_Font_(d->font, ch); 834 const iGlyph *glyph = glyph_Font_(d->font, ch);
828 /* TODO: Look for ANSI/color escapes. */
829 if (index_Glyph_(glyph) && glyph->font != run.font) { 835 if (index_Glyph_(glyph) && glyph->font != run.font) {
830 /* A different font is being used for this glyph. */ 836 /* A different font is being used for this character. */
831 finishRun_AttributedText_(d, &run, currentPos); 837 finishRun_AttributedText_(d, &run, currentPos);
832 run.font = glyph->font; 838 run.font = glyph->font;
833 } 839 }
@@ -837,8 +843,9 @@ static void prepare_AttributedText_(iAttributedText *d) {
837 } 843 }
838} 844}
839 845
840void init_AttributedText(iAttributedText *d, iRangecc text, iFont *font, iColor fgColor) { 846void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) {
841 d->text = text; 847 d->text = text;
848 d->maxLen = maxLen ? maxLen : iInvalidSize;
842 d->font = font; 849 d->font = font;
843 d->fgColor = fgColor; 850 d->fgColor = fgColor;
844 init_Array(&d->runs, sizeof(iAttributedRun)); 851 init_Array(&d->runs, sizeof(iAttributedRun));
@@ -991,9 +998,14 @@ static void cacheTextGlyphs_Font_(iFont *d, const iRangecc text) {
991 const char *oldPos = chPos; 998 const char *oldPos = chPos;
992 const iChar ch = nextChar_(&chPos, text.end); 999 const iChar ch = nextChar_(&chPos, text.end);
993 if (chPos == oldPos) break; 1000 if (chPos == oldPos) break;
994 const uint32_t glyphIndex = glyphIndex_Font_(d, ch); 1001 if (!isSpace_Char(ch) &&
995 if (glyphIndex) { 1002 !isDefaultIgnorable_Char(ch) &&
996 pushBack_Array(&glyphIndices, &glyphIndex); 1003 !isVariationSelector_Char(ch) &&
1004 !isFitzpatrickType_Char(ch)) {
1005 const uint32_t glyphIndex = glyphIndex_Font_(d, ch);
1006 if (glyphIndex) {
1007 pushBack_Array(&glyphIndices, &glyphIndex);
1008 }
997 } 1009 }
998 } 1010 }
999 cacheGlyphs_Font_(d, &glyphIndices); 1011 cacheGlyphs_Font_(d, &glyphIndices);
@@ -1025,6 +1037,7 @@ struct Impl_RunArgs {
1025 iRangecc text; 1037 iRangecc text;
1026 size_t maxLen; /* max characters to process */ 1038 size_t maxLen; /* max characters to process */
1027 iInt2 pos; 1039 iInt2 pos;
1040 iWrapText * wrap;
1028 int xposLimit; /* hard limit for wrapping */ 1041 int xposLimit; /* hard limit for wrapping */
1029 int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ 1042 int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */
1030 int color; 1043 int color;
@@ -1032,6 +1045,14 @@ struct Impl_RunArgs {
1032 int * runAdvance_out; 1045 int * runAdvance_out;
1033}; 1046};
1034 1047
1048static void notify_WrapText_(iWrapText *d, const char *ending, int advance) {
1049 if (d && d->wrapFunc) {
1050 const char *end = ending ? ending : d->wrapRange_.end;
1051 d->wrapFunc(d, (iRangecc){ d->wrapRange_.start, end }, advance);
1052 d->wrapRange_.start = end;
1053 }
1054}
1055
1035#if defined (LAGRANGE_ENABLE_HARFBUZZ) 1056#if defined (LAGRANGE_ENABLE_HARFBUZZ)
1036static iRect run_Font_(iFont *d, const iRunArgs *args) { 1057static iRect run_Font_(iFont *d, const iRunArgs *args) {
1037 const int mode = args->mode; 1058 const int mode = args->mode;
@@ -1040,22 +1061,54 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1040 float xCursor = 0.0f; 1061 float xCursor = 0.0f;
1041 float yCursor = 0.0f; 1062 float yCursor = 0.0f;
1042 float xCursorMax = 0.0f; 1063 float xCursorMax = 0.0f;
1064 iRangecc wrapRange = args->text;
1043 iAssert(args->text.end >= args->text.start); 1065 iAssert(args->text.end >= args->text.start);
1044 if (args->continueFrom_out) { 1066 if (args->continueFrom_out) {
1045 *args->continueFrom_out = args->text.end; 1067 *args->continueFrom_out = args->text.end;
1046 } 1068 }
1047 hb_buffer_t *hbBuf = hb_buffer_create(); 1069 hb_buffer_t *hbBuf = hb_buffer_create();
1048 /* Split the text into a number of attributed runs that specify exactly which font is 1070 /* Split the text into a number of attributed runs that specify exactly which
1049 used and other attributes such as color. (HarfBuzz shaping is done with one specific font.) */ 1071 font is used and other attributes such as color. (HarfBuzz shaping is done
1050 iAttributedText *attrText = new_AttributedText(args->text, d, get_Color(args->color)); 1072 with one specific font.) */
1051 iConstForEach(Array, i, &attrText->runs) { 1073 iAttributedText *attrText =
1052 const iAttributedRun *run = i.value; 1074 new_AttributedText(args->text, args->maxLen, d, get_Color(args->color));
1053 if (run->lineBreaks) { 1075 if (args->wrap) {
1076 /* TODO: Duplicated args? */
1077 iAssert(equalRange_Rangecc(args->wrap->text, args->text));
1078 /* Initialize the wrap range. */
1079 args->wrap->wrapRange_ = args->text;
1080 }
1081 const char *wrapResumePos = NULL;
1082 for (size_t runIndex = 0; runIndex < size_Array(&attrText->runs); runIndex++) {
1083 const iAttributedRun *run = at_Array(&attrText->runs, runIndex);
1084 iRangecc runText = run->text;
1085 if (wrapResumePos) {
1054 xCursor = 0.0f; 1086 xCursor = 0.0f;
1055 yCursor += d->height * run->lineBreaks; 1087 yCursor += d->height;
1088 runText.start = wrapResumePos;
1089 wrapResumePos = NULL;
1090 }
1091 else if (run->lineBreaks) {
1092 for (int i = 0; i < run->lineBreaks; i++) {
1093 /* One callback for each "wrapped" line even if they're empty. */
1094 notify_WrapText_(args->wrap, run->text.start, iRound(xCursor));
1095 xCursor = 0.0f;
1096 yCursor += d->height;
1097 }
1056 } 1098 }
1057 hb_buffer_clear_contents(hbBuf); 1099 hb_buffer_clear_contents(hbBuf);
1058 hb_buffer_add_utf8(hbBuf, run->text.start, size_Range(&run->text), 0, -1); 1100 /* Cluster values are used to determine offset inside the UTF-8 source string. */
1101 //hb_buffer_set_cluster_level(hbBuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
1102 for (const char *pos = runText.start; pos < runText.end; ) {
1103 iChar ucp = 0;
1104 const int len = decodeBytes_MultibyteChar(pos, runText.end, &ucp);
1105 if (len > 0) {
1106 hb_buffer_add(hbBuf, ucp, pos - runText.start);
1107 pos += len;
1108 }
1109 else break;
1110 }
1111 hb_buffer_set_content_type(hbBuf, HB_BUFFER_CONTENT_TYPE_UNICODE);
1059 hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR); /* TODO: FriBidi? */ 1112 hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR); /* TODO: FriBidi? */
1060 /* hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN); */ /* will be autodetected */ 1113 /* hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN); */ /* will be autodetected */
1061 hb_buffer_set_language(hbBuf, hb_language_from_string("en", -1)); /* TODO: language from document/UI, if known */ 1114 hb_buffer_set_language(hbBuf, hb_language_from_string("en", -1)); /* TODO: language from document/UI, if known */
@@ -1063,9 +1116,45 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1063 unsigned int glyphCount = 0; 1116 unsigned int glyphCount = 0;
1064 const hb_glyph_info_t * glyphInfo = hb_buffer_get_glyph_infos(hbBuf, &glyphCount); 1117 const hb_glyph_info_t * glyphInfo = hb_buffer_get_glyph_infos(hbBuf, &glyphCount);
1065 const hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(hbBuf, &glyphCount); 1118 const hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(hbBuf, &glyphCount);
1119 const char *breakPos = NULL;
1120 /* Check if this run needs to be wrapped. If so, we'll draw the portion that fits on
1121 the line, and re-run the loop resuming the run from the wrap point. */
1122 if (args->wrap && args->wrap->maxWidth > 0) {
1123 float x = xCursor;
1124 const char *safeBreak = NULL;
1125 for (unsigned int i = 0; i < glyphCount; i++) {
1126 const hb_glyph_info_t *info = &glyphInfo[i];
1127 const hb_codepoint_t glyphId = info->codepoint;
1128 const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId);
1129 const int glyphFlags = hb_glyph_info_get_glyph_flags(info);
1130 const float xOffset = run->font->xScale * glyphPos[i].x_offset;
1131 const float xAdvance = run->font->xScale * glyphPos[i].x_advance;
1132 const char *textPos = runText.start + info->cluster;
1133 if (~glyphFlags & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) {
1134 safeBreak = textPos;
1135 }
1136 if (x + xOffset + glyph->d[0].x + glyph->rect[0].size.x > args->wrap->maxWidth) {
1137 if (safeBreak) {
1138 breakPos = safeBreak;
1139 }
1140 else {
1141 breakPos = textPos;
1142 }
1143 break;
1144 }
1145 x += xAdvance;
1146 }
1147 /* Make a callback for each wrapped line. */
1148 if (breakPos) {
1149 notify_WrapText_(args->wrap, breakPos, iRound(x));
1150 }
1151 }
1066 /* Draw each glyph. */ 1152 /* Draw each glyph. */
1067 for (unsigned int i = 0; i < glyphCount; i++) { 1153 for (unsigned int i = 0; i < glyphCount; i++) {
1068 const hb_codepoint_t glyphId = glyphInfo[i].codepoint; 1154 const hb_glyph_info_t *info = &glyphInfo[i];
1155 const hb_codepoint_t glyphId = info->codepoint;
1156 const char *textPos = runText.start + info->cluster;
1157 const int glyphFlags = hb_glyph_info_get_glyph_flags(info);
1069 const float xOffset = run->font->xScale * glyphPos[i].x_offset; 1158 const float xOffset = run->font->xScale * glyphPos[i].x_offset;
1070 const float yOffset = run->font->yScale * glyphPos[i].y_offset; 1159 const float yOffset = run->font->yScale * glyphPos[i].y_offset;
1071 const float xAdvance = run->font->xScale * glyphPos[i].x_advance; 1160 const float xAdvance = run->font->xScale * glyphPos[i].x_advance;
@@ -1073,7 +1162,11 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1073 const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); 1162 const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId);
1074 const float xf = xCursor + xOffset; 1163 const float xf = xCursor + xOffset;
1075 const int hoff = enableHalfPixelGlyphs_Text ? (xf - ((int) xf) > 0.5f ? 1 : 0) : 0; 1164 const int hoff = enableHalfPixelGlyphs_Text ? (xf - ((int) xf) > 0.5f ? 1 : 0) : 0;
1076 /* draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset); */ 1165 if (textPos == breakPos) {
1166 runIndex--; /* stay on the same run */
1167 wrapResumePos = textPos;
1168 break;
1169 }
1077 /* Draw the glyph. */ { 1170 /* Draw the glyph. */ {
1078 SDL_Rect dst = { orig.x + xCursor + xOffset + glyph->d[hoff].x, 1171 SDL_Rect dst = { orig.x + xCursor + xOffset + glyph->d[hoff].x,
1079 orig.y + yCursor + yOffset + glyph->font->baseline + glyph->d[hoff].y, 1172 orig.y + yCursor + yOffset + glyph->font->baseline + glyph->d[hoff].y,
@@ -1098,17 +1191,18 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1098 iAssert(isRasterized_Glyph_(glyph, hoff)); 1191 iAssert(isRasterized_Glyph_(glyph, hoff));
1099 } 1192 }
1100 if (~mode & permanentColorFlag_RunMode) { 1193 if (~mode & permanentColorFlag_RunMode) {
1101 //const iColor clr = get_Color(colorNum); 1194 const iColor clr = run->fgColor;
1102 SDL_SetTextureColorMod(text_.cache, run->fgColor.r, run->fgColor.g, run->fgColor.b); 1195 SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b);
1103// if (args->mode & fillBackground_RunMode) { 1196 if (args->mode & fillBackground_RunMode) {
1104// SDL_SetRenderDrawColor(text_.render, clr.r, clr.g, clr.b, 0); 1197 SDL_SetRenderDrawColor(text_.render, clr.r, clr.g, clr.b, 0);
1105// } 1198 }
1106 } 1199 }
1107 SDL_Rect src; 1200 SDL_Rect src;
1108 memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); 1201 memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect));
1109 if (args->mode & fillBackground_RunMode) { 1202 if (args->mode & fillBackground_RunMode) {
1110 /* Alpha blending looks much better if the RGB components don't change in 1203 /* Alpha blending looks much better if the RGB components don't change in
1111 the partially transparent pixels. */ 1204 the partially transparent pixels. */
1205 /* TODO: Backgrounds of all glyphs should be cleared before drawing anything else. */
1112 SDL_RenderFillRect(text_.render, &dst); 1206 SDL_RenderFillRect(text_.render, &dst);
1113 } 1207 }
1114 SDL_RenderCopy(text_.render, text_.cache, &src, &dst); 1208 SDL_RenderCopy(text_.render, text_.cache, &src, &dst);
@@ -1119,6 +1213,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1119 xCursorMax = iMax(xCursorMax, xCursor); 1213 xCursorMax = iMax(xCursorMax, xCursor);
1120 } 1214 }
1121 } 1215 }
1216 /* The final, unbroken line. */
1217 notify_WrapText_(args->wrap, NULL, xCursor);
1122 if (args->runAdvance_out) { 1218 if (args->runAdvance_out) {
1123 *args->runAdvance_out = xCursorMax; 1219 *args->runAdvance_out = xCursorMax;
1124 } 1220 }
@@ -1207,6 +1303,35 @@ iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **e
1207 return init_I2(advance, height); 1303 return init_I2(advance, height);
1208} 1304}
1209 1305
1306iInt2 advance_WrapText(iWrapText *d, int fontId) {
1307 run_Font_(font_Text_(fontId),
1308 &(iRunArgs){ .mode = measure_RunMode | runFlagsFromId_(fontId),
1309 .text = d->text,
1310 .wrap = d
1311 });
1312 return d->cursor_out;
1313}
1314
1315iRect measure_WrapText(iWrapText *d, int fontId) {
1316 return run_Font_(font_Text_(fontId),
1317 &(iRunArgs){ .mode = measure_RunMode | runFlagsFromId_(fontId),
1318 .text = d->text,
1319 .wrap = d
1320 });
1321}
1322
1323void draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) {
1324 run_Font_(font_Text_(fontId),
1325 &(iRunArgs){ .mode = draw_RunMode | runFlagsFromId_(fontId) |
1326 (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) |
1327 (color & fillBackground_ColorId ? fillBackground_RunMode : 0),
1328 .text = d->text,
1329 .pos = pos,
1330 .wrap = d,
1331 .color = color,
1332 });
1333}
1334
1210iInt2 advance_Text(int fontId, const char *text) { 1335iInt2 advance_Text(int fontId, const char *text) {
1211 return advanceRange_Text(fontId, range_CStr(text)); 1336 return advanceRange_Text(fontId, range_CStr(text));
1212} 1337}
diff --git a/src/ui/text.h b/src/ui/text.h
index 5a099142..fe42a308 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
24 24
25#include <the_Foundation/rect.h> 25#include <the_Foundation/rect.h>
26#include <the_Foundation/string.h> 26#include <the_Foundation/string.h>
27#include <the_Foundation/vec2.h>
27 28
28#include <SDL_render.h> 29#include <SDL_render.h>
29 30
@@ -164,7 +165,8 @@ void draw_Text (int fontId, iInt2 pos, int color, const char *t
164void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); 165void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...);
165void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); 166void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...);
166void drawCenteredRange_Text (int fontId, iRect rect, iBool alignVisual, int color, iRangecc text); 167void drawCenteredRange_Text (int fontId, iRect rect, iBool alignVisual, int color, iRangecc text);
167void drawCenteredOutline_Text(int fontId, iRect rect, iBool alignVisual, int outlineColor, int fillColor, const char *text, ...); 168void drawCenteredOutline_Text(int fontId, iRect rect, iBool alignVisual, int outlineColor,
169 int fillColor, const char *text, ...);
168void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); 170void drawString_Text (int fontId, iInt2 pos, int color, const iString *text);
169void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); 171void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text);
170void drawRangeN_Text (int fontId, iInt2 pos, int color, iRangecc text, size_t maxLen); 172void drawRangeN_Text (int fontId, iInt2 pos, int color, iRangecc text, size_t maxLen);
@@ -172,6 +174,24 @@ void drawOutline_Text (int fontId, iInt2 pos, int outlineColor, int fi
172void drawBoundRange_Text (int fontId, iInt2 pos, int boundWidth, int color, iRangecc text); /* bound does not wrap */ 174void drawBoundRange_Text (int fontId, iInt2 pos, int boundWidth, int color, iRangecc text); /* bound does not wrap */
173int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ 175int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */
174 176
177iDeclareType(WrapText)
178
179struct Impl_WrapText {
180 /* arguments */
181 iRangecc text;
182 int maxWidth;
183 iBool (*wrapFunc)(iWrapText *, iRangecc wrappedText, int advance);
184 void * context;
185 /* output */
186 iInt2 cursor_out;
187 /* internal */
188 iRangecc wrapRange_;
189};
190
191iRect measure_WrapText (iWrapText *, int fontId);
192iInt2 advance_WrapText (iWrapText *, int fontId);
193void draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color);
194
175SDL_Texture * glyphCache_Text (void); 195SDL_Texture * glyphCache_Text (void);
176 196
177enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode }; 197enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode };