diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-11-21 20:44:25 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-11-21 20:44:25 +0200 |
commit | e72764f55b49643160e387fb942ac113d66620cf (patch) | |
tree | 1eeefa631beb473fe85b6417d0f65d8a04cabfe2 /src/ui/text.c | |
parent | 18212214dda6f18bdc6bb21307668472b5726602 (diff) |
Apply ANSI SGR background colors
The background color of the current text run can be changed via ANSI SGR control sequences.
Improved the ANSI escape sequence regex to detect more than just SGR sequences, so they can be properly filtered out.
Diffstat (limited to 'src/ui/text.c')
-rw-r--r-- | src/ui/text.c | 234 |
1 files changed, 93 insertions, 141 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 91633f27..91ff137a 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -263,7 +263,7 @@ struct Impl_Text { | |||
263 | iRegExp * ansiEscape; | 263 | iRegExp * ansiEscape; |
264 | int ansiFlags; | 264 | int ansiFlags; |
265 | int baseFontId; /* base attributes (for restoring via escapes) */ | 265 | int baseFontId; /* base attributes (for restoring via escapes) */ |
266 | int baseColorId; | 266 | int baseFgColorId; |
267 | iBool missingGlyphs; /* true if a glyph couldn't be found */ | 267 | iBool missingGlyphs; /* true if a glyph couldn't be found */ |
268 | }; | 268 | }; |
269 | 269 | ||
@@ -390,35 +390,14 @@ static void deinitCache_Text_(iText *d) { | |||
390 | SDL_DestroyTexture(d->cache); | 390 | SDL_DestroyTexture(d->cache); |
391 | } | 391 | } |
392 | 392 | ||
393 | #if 0 | ||
394 | void loadUserFonts_Text(void) { | ||
395 | if (userFont_) { | ||
396 | delete_Block(userFont_); | ||
397 | userFont_ = NULL; | ||
398 | } | ||
399 | /* Load the system font. */ | ||
400 | const iPrefs *prefs = prefs_App(); | ||
401 | if (!isEmpty_String(&prefs->symbolFontPath)) { | ||
402 | iFile *f = new_File(&prefs->symbolFontPath); | ||
403 | if (open_File(f, readOnly_FileMode)) { | ||
404 | userFont_ = readAll_File(f); | ||
405 | } | ||
406 | else { | ||
407 | fprintf(stderr, "[Text] failed to open: %s\n", cstr_String(&prefs->symbolFontPath)); | ||
408 | } | ||
409 | iRelease(f); | ||
410 | } | ||
411 | } | ||
412 | #endif | ||
413 | |||
414 | void init_Text(iText *d, SDL_Renderer *render) { | 393 | void init_Text(iText *d, SDL_Renderer *render) { |
415 | iText *oldActive = activeText_; | 394 | iText *oldActive = activeText_; |
416 | activeText_ = d; | 395 | activeText_ = d; |
417 | init_Array(&d->fonts, sizeof(iFont)); | 396 | init_Array(&d->fonts, sizeof(iFont)); |
418 | d->contentFontSize = contentScale_Text_; | 397 | d->contentFontSize = contentScale_Text_; |
419 | d->ansiEscape = new_RegExp("[[()]([0-9;AB]*?)m", 0); | 398 | d->ansiEscape = new_RegExp("[[()][?]?([0-9;AB]*?)([ABCDEFGHJKSTfhilmn])", 0); |
420 | d->baseFontId = -1; | 399 | d->baseFontId = -1; |
421 | d->baseColorId = -1; | 400 | d->baseFgColorId = -1; |
422 | d->render = render; | 401 | d->render = render; |
423 | /* A grayscale palette for rasterized glyphs. */ { | 402 | /* A grayscale palette for rasterized glyphs. */ { |
424 | SDL_Color colors[256]; | 403 | SDL_Color colors[256]; |
@@ -459,9 +438,10 @@ void setOpacity_Text(float opacity) { | |||
459 | SDL_SetTextureAlphaMod(activeText_->cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f); | 438 | SDL_SetTextureAlphaMod(activeText_->cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f); |
460 | } | 439 | } |
461 | 440 | ||
462 | void setBaseAttributes_Text(int fontId, int colorId) { | 441 | void setBaseAttributes_Text(int fontId, int fgColorId) { |
463 | activeText_->baseFontId = fontId; | 442 | iText *d = activeText_; |
464 | activeText_->baseColorId = colorId; | 443 | d->baseFontId = fontId; |
444 | d->baseFgColorId = fgColorId; | ||
465 | } | 445 | } |
466 | 446 | ||
467 | void setAnsiFlags_Text(int ansiFlags) { | 447 | void setAnsiFlags_Text(int ansiFlags) { |
@@ -574,16 +554,6 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
574 | return overrideFont; | 554 | return overrideFont; |
575 | } | 555 | } |
576 | } | 556 | } |
577 | #if 0 | ||
578 | /* TODO: Put arrows in Smol Emoji. */ | ||
579 | /* Manual exceptions. */ { | ||
580 | if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) { | ||
581 | d = font_Text_(iosevka_FontId + d->sizeId); | ||
582 | *glyphIndex = glyphIndex_Font_(d, ch); | ||
583 | return d; | ||
584 | } | ||
585 | } | ||
586 | #endif | ||
587 | /* The font's own version of the glyph. */ | 557 | /* The font's own version of the glyph. */ |
588 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { | 558 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { |
589 | return d; | 559 | return d; |
@@ -609,69 +579,6 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
609 | } | 579 | } |
610 | } | 580 | } |
611 | } | 581 | } |
612 | #if 0 | ||
613 | const int fallbacks[] = { | ||
614 | notoEmoji_FontId, | ||
615 | symbols2_FontId, | ||
616 | symbols_FontId | ||
617 | }; | ||
618 | /* First fallback is Smol Emoji. */ | ||
619 | if (ch != 0x20) { | ||
620 | iForIndices(i, fallbacks) { | ||
621 | iFont *fallback = font_Text_(fallbacks[i] + d->sizeId); | ||
622 | if (fallback != d && (*glyphIndex = glyphIndex_Font_(fallback, ch)) != 0) { | ||
623 | return fallback; | ||
624 | } | ||
625 | } | ||
626 | } | ||
627 | /* Try Simplified Chinese. */ | ||
628 | if (ch >= 0x2e80) { | ||
629 | iFont *sc = font_Text_(chineseSimplified_FontId + d->sizeId); | ||
630 | if (sc != d && (*glyphIndex = glyphIndex_Font_(sc, ch)) != 0) { | ||
631 | return sc; | ||
632 | } | ||
633 | } | ||
634 | /* Could be Korean. */ | ||
635 | if (ch >= 0x3000) { | ||
636 | iFont *korean = font_Text_(korean_FontId + d->sizeId); | ||
637 | if (korean != d && (*glyphIndex = glyphIndex_Font_(korean, ch)) != 0) { | ||
638 | return korean; | ||
639 | } | ||
640 | } | ||
641 | /* Japanese perhaps? */ | ||
642 | if (ch > 0x3040) { | ||
643 | iFont *japanese = font_Text_(japanese_FontId + d->sizeId); | ||
644 | if (japanese != d && (*glyphIndex = glyphIndex_Font_(japanese, ch)) != 0) { | ||
645 | return japanese; | ||
646 | } | ||
647 | } | ||
648 | /* Maybe Arabic. */ | ||
649 | if (ch >= 0x600) { | ||
650 | iFont *arabic = font_Text_(arabic_FontId + d->sizeId); | ||
651 | if (arabic != d && (*glyphIndex = glyphIndex_Font_(arabic, ch)) != 0) { | ||
652 | return arabic; | ||
653 | } | ||
654 | } | ||
655 | #if defined (iPlatformApple) | ||
656 | /* White up arrow is used for the Shift key on macOS. Symbola's glyph is not a great | ||
657 | match to the other text, so use the UI font instead. */ | ||
658 | if ((ch == 0x2318 || ch == 0x21e7) && d == font_Text_(regular_FontId)) { | ||
659 | *glyphIndex = glyphIndex_Font_(d = font_Text_(defaultContentRegular_FontId), ch); | ||
660 | return d; | ||
661 | } | ||
662 | #endif | ||
663 | /* User's symbols font. */ { | ||
664 | iFont *sys = font_Text_(userSymbols_FontId + d->sizeId); | ||
665 | if (sys != d && (*glyphIndex = glyphIndex_Font_(sys, ch)) != 0) { | ||
666 | return sys; | ||
667 | } | ||
668 | } | ||
669 | /* Final fallback. */ | ||
670 | iFont *font = font_Text_(iosevka_FontId + d->sizeId); | ||
671 | if (d != font) { | ||
672 | *glyphIndex = glyphIndex_Font_(font, ch); | ||
673 | } | ||
674 | #endif // 0 | ||
675 | if (!*glyphIndex) { | 582 | if (!*glyphIndex) { |
676 | activeText_->missingGlyphs = iTrue; | 583 | activeText_->missingGlyphs = iTrue; |
677 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); | 584 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); |
@@ -680,7 +587,9 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
680 | } | 587 | } |
681 | 588 | ||
682 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { | 589 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { |
683 | iAssert(d->table); | 590 | if (!d->table) { |
591 | d->table = new_GlyphTable(); | ||
592 | } | ||
684 | iGlyph* glyph = NULL; | 593 | iGlyph* glyph = NULL; |
685 | void * node = value_Hash(&d->table->glyphs, glyphIndex); | 594 | void * node = value_Hash(&d->table->glyphs, glyphIndex); |
686 | if (node) { | 595 | if (node) { |
@@ -739,6 +648,7 @@ struct Impl_AttributedRun { | |||
739 | iTextAttrib attrib; | 648 | iTextAttrib attrib; |
740 | iFont *font; | 649 | iFont *font; |
741 | iColor fgColor_; /* any RGB color; A > 0 */ | 650 | iColor fgColor_; /* any RGB color; A > 0 */ |
651 | iColor bgColor_; /* any RGB color; A > 0 */ | ||
742 | struct { | 652 | struct { |
743 | uint8_t isLineBreak : 1; | 653 | uint8_t isLineBreak : 1; |
744 | // uint8_t isRTL : 1; | 654 | // uint8_t isRTL : 1; |
@@ -750,29 +660,45 @@ static iColor fgColor_AttributedRun_(const iAttributedRun *d) { | |||
750 | if (d->fgColor_.a) { | 660 | if (d->fgColor_.a) { |
751 | return d->fgColor_; | 661 | return d->fgColor_; |
752 | } | 662 | } |
753 | if (d->attrib.colorId == none_ColorId) { | 663 | if (d->attrib.fgColorId == none_ColorId) { |
754 | return (iColor){ 255, 255, 255, 255 }; | 664 | return (iColor){ 255, 255, 255, 255 }; |
755 | } | 665 | } |
756 | return get_Color(d->attrib.colorId); | 666 | return get_Color(d->attrib.fgColorId); |
667 | } | ||
668 | |||
669 | static iColor bgColor_AttributedRun_(const iAttributedRun *d) { | ||
670 | if (d->bgColor_.a) { | ||
671 | return d->bgColor_; | ||
672 | } | ||
673 | return (iColor){ 255, 255, 255, 0 }; | ||
674 | if (d->attrib.bgColorId == none_ColorId) { | ||
675 | return (iColor){ 255, 255, 255, 0 }; | ||
676 | } | ||
677 | return get_Color(d->attrib.bgColorId); | ||
757 | } | 678 | } |
758 | 679 | ||
759 | static void setFgColor_AttributedRun_(iAttributedRun *d, int colorId) { | 680 | static void setFgColor_AttributedRun_(iAttributedRun *d, int colorId) { |
760 | d->attrib.colorId = colorId; | 681 | d->attrib.fgColorId = colorId; |
761 | d->fgColor_.a = 0; | 682 | d->fgColor_.a = 0; |
762 | } | 683 | } |
763 | 684 | ||
685 | static void setBgColor_AttributedRun_(iAttributedRun *d, int colorId) { | ||
686 | d->attrib.bgColorId = colorId; | ||
687 | d->bgColor_.a = 0; | ||
688 | } | ||
689 | |||
764 | iDeclareType(AttributedText) | 690 | iDeclareType(AttributedText) |
765 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, | 691 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, |
766 | int colorId, int baseDir, iFont *baseFont, int baseColorId, | 692 | int colorId, int baseDir, iFont *baseFont, int baseFgColorId, |
767 | iChar overrideChar) | 693 | iChar overrideChar) |
768 | 694 | ||
769 | struct Impl_AttributedText { | 695 | struct Impl_AttributedText { |
770 | iRangecc source; /* original source text */ | 696 | iRangecc source; /* original source text */ |
771 | size_t maxLen; | 697 | size_t maxLen; |
772 | iFont * font; | 698 | iFont * font; |
773 | int colorId; | 699 | int fgColorId; |
774 | iFont * baseFont; | 700 | iFont * baseFont; |
775 | int baseColorId; | 701 | int baseFgColorId; |
776 | iBool isBaseRTL; | 702 | iBool isBaseRTL; |
777 | iArray runs; | 703 | iArray runs; |
778 | iArray logical; /* UTF-32 text in logical order (mixed directions; matches source) */ | 704 | iArray logical; /* UTF-32 text in logical order (mixed directions; matches source) */ |
@@ -785,9 +711,9 @@ struct Impl_AttributedText { | |||
785 | 711 | ||
786 | iDefineTypeConstructionArgs(AttributedText, | 712 | iDefineTypeConstructionArgs(AttributedText, |
787 | (iRangecc text, size_t maxLen, iFont *font, int colorId, | 713 | (iRangecc text, size_t maxLen, iFont *font, int colorId, |
788 | int baseDir, iFont *baseFont, int baseColorId, | 714 | int baseDir, iFont *baseFont, int baseFgColorId, |
789 | iChar overrideChar), | 715 | iChar overrideChar), |
790 | text, maxLen, font, colorId, baseDir, baseFont, baseColorId, | 716 | text, maxLen, font, colorId, baseDir, baseFont, baseFgColorId, |
791 | overrideChar) | 717 | overrideChar) |
792 | 718 | ||
793 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { | 719 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { |
@@ -897,7 +823,8 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
897 | } | 823 | } |
898 | iAttributedRun run = { | 824 | iAttributedRun run = { |
899 | .logical = { 0, length }, | 825 | .logical = { 0, length }, |
900 | .attrib = { .colorId = d->colorId, .isBaseRTL = d->isBaseRTL }, | 826 | .attrib = { .fgColorId = d->fgColorId, .bgColorId = none_ColorId, |
827 | .isBaseRTL = d->isBaseRTL }, | ||
901 | .font = d->font, | 828 | .font = d->font, |
902 | }; | 829 | }; |
903 | const int *logToSource = constData_Array(&d->logicalToSourceOffset); | 830 | const int *logToSource = constData_Array(&d->logicalToSourceOffset); |
@@ -935,12 +862,13 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
935 | if (match_RegExp(activeText_->ansiEscape, srcPos, d->source.end - srcPos, &m)) { | 862 | if (match_RegExp(activeText_->ansiEscape, srcPos, d->source.end - srcPos, &m)) { |
936 | finishRun_AttributedText_(d, &run, pos - 1); | 863 | finishRun_AttributedText_(d, &run, pos - 1); |
937 | const int ansi = activeText_->ansiFlags; | 864 | const int ansi = activeText_->ansiFlags; |
938 | if (ansi) { | 865 | if (ansi && capturedRange_RegExpMatch(&m, 2).start[0] == |
866 | 'm' /* Select Graphic Rendition */) { | ||
939 | const iRangecc sequence = capturedRange_RegExpMatch(&m, 1); | 867 | const iRangecc sequence = capturedRange_RegExpMatch(&m, 1); |
940 | /* Note: This styling is hardcoded to match `typesetOneLine_RunTypesetter_()`. */ | 868 | /* Note: This styling is hardcoded to match `typesetOneLine_RunTypesetter_()`. */ |
941 | if (ansi & allowFontStyle_AnsiFlag && equal_Rangecc(sequence, "1")) { | 869 | if (ansi & allowFontStyle_AnsiFlag && equal_Rangecc(sequence, "1")) { |
942 | run.attrib.bold = iTrue; | 870 | run.attrib.bold = iTrue; |
943 | if (d->baseColorId == tmParagraph_ColorId) { | 871 | if (d->baseFgColorId == tmParagraph_ColorId) { |
944 | setFgColor_AttributedRun_(&run, tmFirstParagraph_ColorId); | 872 | setFgColor_AttributedRun_(&run, tmFirstParagraph_ColorId); |
945 | } | 873 | } |
946 | attribFont = font_Text_(fontWithStyle_Text(fontId_Text_(d->baseFont), | 874 | attribFont = font_Text_(fontWithStyle_Text(fontId_Text_(d->baseFont), |
@@ -962,10 +890,13 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
962 | run.attrib.italic = iFalse; | 890 | run.attrib.italic = iFalse; |
963 | run.attrib.monospace = iFalse; | 891 | run.attrib.monospace = iFalse; |
964 | attribFont = run.font = d->baseFont; | 892 | attribFont = run.font = d->baseFont; |
965 | setFgColor_AttributedRun_(&run, d->baseColorId); | 893 | setFgColor_AttributedRun_(&run, d->baseFgColorId); |
894 | setBgColor_AttributedRun_(&run, none_ColorId); | ||
966 | } | 895 | } |
967 | else if (ansi & allowFg_AnsiFlag) { | 896 | else { |
968 | run.fgColor_ = ansiForeground_Color(sequence, tmParagraph_ColorId); | 897 | ansiColors_Color(sequence, d->baseFgColorId, none_ColorId, |
898 | ansi & allowFg_AnsiFlag ? &run.fgColor_ : NULL, | ||
899 | ansi & allowBg_AnsiFlag ? &run.bgColor_ : NULL); | ||
969 | } | 900 | } |
970 | } | 901 | } |
971 | pos += length_Rangecc(capturedRange_RegExpMatch(&m, 0)); | 902 | pos += length_Rangecc(capturedRange_RegExpMatch(&m, 0)); |
@@ -988,7 +919,7 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
988 | colorNum = esc - asciiBase_ColorEscape; | 919 | colorNum = esc - asciiBase_ColorEscape; |
989 | } | 920 | } |
990 | run.logical.start = pos + 1; | 921 | run.logical.start = pos + 1; |
991 | setFgColor_AttributedRun_(&run, colorNum >= 0 ? colorNum : d->colorId); | 922 | setFgColor_AttributedRun_(&run, colorNum >= 0 ? colorNum : d->fgColorId); |
992 | continue; | 923 | continue; |
993 | } | 924 | } |
994 | if (ch == '\n') { | 925 | if (ch == '\n') { |
@@ -1051,14 +982,15 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
1051 | } | 982 | } |
1052 | 983 | ||
1053 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, int colorId, | 984 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, int colorId, |
1054 | int baseDir, iFont *baseFont, int baseColorId, iChar overrideChar) { | 985 | int baseDir, iFont *baseFont, int baseFgColorId, |
1055 | d->source = text; | 986 | iChar overrideChar) { |
1056 | d->maxLen = maxLen ? maxLen : iInvalidSize; | 987 | d->source = text; |
1057 | d->font = font; | 988 | d->maxLen = maxLen ? maxLen : iInvalidSize; |
1058 | d->colorId = colorId; | 989 | d->font = font; |
1059 | d->baseFont = baseFont; | 990 | d->fgColorId = colorId; |
1060 | d->baseColorId = baseColorId; | 991 | d->baseFont = baseFont; |
1061 | d->isBaseRTL = iFalse; | 992 | d->baseFgColorId = baseFgColorId; |
993 | d->isBaseRTL = iFalse; | ||
1062 | init_Array(&d->runs, sizeof(iAttributedRun)); | 994 | init_Array(&d->runs, sizeof(iAttributedRun)); |
1063 | init_Array(&d->logical, sizeof(iChar)); | 995 | init_Array(&d->logical, sizeof(iChar)); |
1064 | init_Array(&d->visual, sizeof(iChar)); | 996 | init_Array(&d->visual, sizeof(iChar)); |
@@ -1412,7 +1344,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1412 | init_AttributedText(&attrText, args->text, args->maxLen, d, args->color, | 1344 | init_AttributedText(&attrText, args->text, args->maxLen, d, args->color, |
1413 | args->baseDir, | 1345 | args->baseDir, |
1414 | activeText_->baseFontId >= 0 ? font_Text_(activeText_->baseFontId) : d, | 1346 | activeText_->baseFontId >= 0 ? font_Text_(activeText_->baseFontId) : d, |
1415 | activeText_->baseColorId, | 1347 | activeText_->baseFgColorId, |
1416 | wrap ? wrap->overrideChar : 0); | 1348 | wrap ? wrap->overrideChar : 0); |
1417 | if (wrap) { | 1349 | if (wrap) { |
1418 | wrap->baseDir = attrText.isBaseRTL ? -1 : +1; | 1350 | wrap->baseDir = attrText.isBaseRTL ? -1 : +1; |
@@ -1464,7 +1396,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1464 | iRangei wrapPosRange = { 0, textLen }; | 1396 | iRangei wrapPosRange = { 0, textLen }; |
1465 | int wrapResumePos = textLen; /* logical position where next line resumes */ | 1397 | int wrapResumePos = textLen; /* logical position where next line resumes */ |
1466 | size_t wrapResumeRunIndex = runCount; /* index of run where next line resumes */ | 1398 | size_t wrapResumeRunIndex = runCount; /* index of run where next line resumes */ |
1467 | iTextAttrib attrib = { .colorId = args->color, .isBaseRTL = attrText.isBaseRTL }; | 1399 | iTextAttrib attrib = { .fgColorId = args->color, |
1400 | .bgColorId = none_ColorId, | ||
1401 | .isBaseRTL = attrText.isBaseRTL }; | ||
1468 | iTextAttrib wrapAttrib = attrib; | 1402 | iTextAttrib wrapAttrib = attrib; |
1469 | iTextAttrib lastAttrib = attrib; | 1403 | iTextAttrib lastAttrib = attrib; |
1470 | const int layoutBound = (wrap ? wrap->maxWidth : 0); | 1404 | const int layoutBound = (wrap ? wrap->maxWidth : 0); |
@@ -1712,6 +1646,12 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1712 | yCursor += d->height; | 1646 | yCursor += d->height; |
1713 | continue; | 1647 | continue; |
1714 | } | 1648 | } |
1649 | const iColor fgClr = fgColor_AttributedRun_(run); | ||
1650 | const iColor bgClr = bgColor_AttributedRun_(run); | ||
1651 | iBool isBgFilled = iFalse; | ||
1652 | if (~mode & permanentColorFlag_RunMode) { | ||
1653 | isBgFilled = (bgClr.a != 0) || (mode & fillBackground_RunMode); | ||
1654 | } | ||
1715 | iGlyphBuffer *buf = at_Array(&buffers, runIndex); | 1655 | iGlyphBuffer *buf = at_Array(&buffers, runIndex); |
1716 | shape_GlyphBuffer_(buf); | 1656 | shape_GlyphBuffer_(buf); |
1717 | iAssert(run->font == buf->font); | 1657 | iAssert(run->font == buf->font); |
@@ -1772,34 +1712,46 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1772 | bounds.size.x = iMax(bounds.size.x, dst.x + dst.w - orig.x); | 1712 | bounds.size.x = iMax(bounds.size.x, dst.x + dst.w - orig.x); |
1773 | bounds.size.y = iMax(bounds.size.y, yCursor + glyph->font->height); | 1713 | bounds.size.y = iMax(bounds.size.y, yCursor + glyph->font->height); |
1774 | } | 1714 | } |
1775 | if (mode & draw_RunMode && logicalText[logPos] > 0x20) { | 1715 | const iBool isSpace = (logicalText[logPos] == 0x20); |
1716 | if (mode & draw_RunMode && (isBgFilled || !isSpace)) { | ||
1776 | /* Draw the glyph. */ | 1717 | /* Draw the glyph. */ |
1777 | if (!isRasterized_Glyph_(glyph, hoff)) { | 1718 | if (!isSpace && !isRasterized_Glyph_(glyph, hoff)) { |
1778 | cacheSingleGlyph_Font_(run->font, glyphId); /* may cause cache reset */ | 1719 | cacheSingleGlyph_Font_(run->font, glyphId); /* may cause cache reset */ |
1779 | glyph = glyphByIndex_Font_(run->font, glyphId); | 1720 | glyph = glyphByIndex_Font_(run->font, glyphId); |
1780 | iAssert(isRasterized_Glyph_(glyph, hoff)); | 1721 | iAssert(isRasterized_Glyph_(glyph, hoff)); |
1781 | } | 1722 | } |
1782 | if (~mode & permanentColorFlag_RunMode) { | 1723 | if (~mode & permanentColorFlag_RunMode) { |
1783 | const iColor clr = fgColor_AttributedRun_(run); | 1724 | SDL_SetTextureColorMod(activeText_->cache, fgClr.r, fgClr.g, fgClr.b); |
1784 | SDL_SetTextureColorMod(activeText_->cache, clr.r, clr.g, clr.b); | ||
1785 | if (args->mode & fillBackground_RunMode) { | ||
1786 | SDL_SetRenderDrawColor(activeText_->render, clr.r, clr.g, clr.b, 0); | ||
1787 | } | ||
1788 | } | 1725 | } |
1789 | SDL_Rect src; | ||
1790 | memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); | ||
1791 | dst.x += origin_Paint.x; | 1726 | dst.x += origin_Paint.x; |
1792 | dst.y += origin_Paint.y; | 1727 | dst.y += origin_Paint.y; |
1793 | if (args->mode & fillBackground_RunMode) { | 1728 | if (isBgFilled) { |
1794 | /* Alpha blending looks much better if the RGB components don't change in | ||
1795 | the partially transparent pixels. */ | ||
1796 | /* TODO: Backgrounds of all glyphs should be cleared before drawing anything else. */ | 1729 | /* TODO: Backgrounds of all glyphs should be cleared before drawing anything else. */ |
1797 | SDL_RenderFillRect(activeText_->render, &dst); | 1730 | if (bgClr.a) { |
1731 | SDL_SetRenderDrawColor(activeText_->render, bgClr.r, bgClr.g, bgClr.b, 255); | ||
1732 | const SDL_Rect bgRect = { | ||
1733 | origin_Paint.x + orig.x + xCursor, | ||
1734 | origin_Paint.y + orig.y + yCursor, | ||
1735 | xAdvance, | ||
1736 | d->height, | ||
1737 | }; | ||
1738 | SDL_RenderFillRect(activeText_->render, &bgRect); | ||
1739 | } | ||
1740 | else if (args->mode & fillBackground_RunMode) { | ||
1741 | /* Alpha blending looks much better if the RGB components don't change | ||
1742 | in the partially transparent pixels. */ | ||
1743 | SDL_SetRenderDrawColor(activeText_->render, fgClr.r, fgClr.g, fgClr.b, 0); | ||
1744 | SDL_RenderFillRect(activeText_->render, &dst); | ||
1745 | } | ||
1746 | } | ||
1747 | if (!isSpace) { | ||
1748 | SDL_Rect src; | ||
1749 | memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); | ||
1750 | SDL_RenderCopy(activeText_->render, activeText_->cache, &src, &dst); | ||
1798 | } | 1751 | } |
1799 | SDL_RenderCopy(activeText_->render, activeText_->cache, &src, &dst); | ||
1800 | #if 0 | 1752 | #if 0 |
1801 | /* Show spaces and direction. */ | 1753 | /* Show spaces and direction. */ |
1802 | if (logicalText[logPos] == 0x20) { | 1754 | if (isSpace) { |
1803 | const iColor debug = get_Color(run->flags.isRTL ? yellow_ColorId : red_ColorId); | 1755 | const iColor debug = get_Color(run->flags.isRTL ? yellow_ColorId : red_ColorId); |
1804 | SDL_SetRenderDrawColor(text_.render, debug.r, debug.g, debug.b, 255); | 1756 | SDL_SetRenderDrawColor(text_.render, debug.r, debug.g, debug.b, 255); |
1805 | dst.w = xAdvance; | 1757 | dst.w = xAdvance; |