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 | |
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')
-rw-r--r-- | src/ui/color.c | 67 | ||||
-rw-r--r-- | src/ui/color.h | 3 | ||||
-rw-r--r-- | src/ui/text.c | 234 | ||||
-rw-r--r-- | src/ui/text.h | 5 |
4 files changed, 153 insertions, 156 deletions
diff --git a/src/ui/color.c b/src/ui/color.c index 0f7d4368..adbe444b 100644 --- a/src/ui/color.c +++ b/src/ui/color.c | |||
@@ -785,8 +785,14 @@ static const iColor ansi8BitColors_[256] = { | |||
785 | { 255, 255, 255, 255 } | 785 | { 255, 255, 255, 255 } |
786 | }; | 786 | }; |
787 | 787 | ||
788 | iColor ansiForeground_Color(iRangecc escapeSequence, int fallback) { | 788 | void ansiColors_Color(iRangecc escapeSequence, int fgDefault, int bgDefault, |
789 | iColor clr = get_Color(fallback); | 789 | iColor *fg_out, iColor *bg_out) { |
790 | if (!fg_out && !bg_out) { | ||
791 | return; | ||
792 | } | ||
793 | iColor fg, bg; | ||
794 | iZap(fg); | ||
795 | iZap(bg); | ||
790 | for (const char *ch = escapeSequence.start; ch < escapeSequence.end; ch++) { | 796 | for (const char *ch = escapeSequence.start; ch < escapeSequence.end; ch++) { |
791 | char *endPtr; | 797 | char *endPtr; |
792 | unsigned long arg = strtoul(ch, &endPtr, 10); | 798 | unsigned long arg = strtoul(ch, &endPtr, 10); |
@@ -802,19 +808,37 @@ iColor ansiForeground_Color(iRangecc escapeSequence, int fallback) { | |||
802 | case 35: | 808 | case 35: |
803 | case 36: | 809 | case 36: |
804 | case 37: | 810 | case 37: |
805 | clr = ansi8BitColors_[arg - 30]; | 811 | fg = ansi8BitColors_[arg - 30]; |
806 | break; | 812 | break; |
807 | case 38: { | 813 | case 38: |
814 | case 48: { | ||
815 | iColor *dst = (arg == 38 ? &fg : &bg); | ||
808 | /* Extended foreground color. */ | 816 | /* Extended foreground color. */ |
809 | arg = strtoul(ch + 1, &endPtr, 10); | 817 | arg = strtoul(ch + 1, &endPtr, 10); |
810 | ch = endPtr; | 818 | ch = endPtr; |
811 | if (arg == 5) /* 8-bit palette */ { | 819 | if (arg == 5) /* 8-bit palette */ { |
812 | arg = strtoul(ch + 1, &endPtr, 10); | 820 | arg = strtoul(ch + 1, &endPtr, 10); |
813 | ch = endPtr; | 821 | ch = endPtr; |
814 | clr = ansi8BitColors_[iClamp(arg, 0, 255)]; | 822 | *dst = ansi8BitColors_[iClamp(arg, 0, 255)]; |
815 | } | 823 | } |
816 | break; | 824 | break; |
817 | } | 825 | } |
826 | case 39: | ||
827 | fg = get_Color(fgDefault); | ||
828 | break; | ||
829 | case 40: | ||
830 | case 41: | ||
831 | case 42: | ||
832 | case 43: | ||
833 | case 44: | ||
834 | case 45: | ||
835 | case 46: | ||
836 | case 47: | ||
837 | bg = ansi8BitColors_[arg - 40]; | ||
838 | break; | ||
839 | case 49: | ||
840 | bg = get_Color(bgDefault); | ||
841 | break; | ||
818 | case 90: | 842 | case 90: |
819 | case 91: | 843 | case 91: |
820 | case 92: | 844 | case 92: |
@@ -823,17 +847,36 @@ iColor ansiForeground_Color(iRangecc escapeSequence, int fallback) { | |||
823 | case 95: | 847 | case 95: |
824 | case 96: | 848 | case 96: |
825 | case 97: | 849 | case 97: |
826 | clr = ansi8BitColors_[8 + arg - 90]; | 850 | fg = ansi8BitColors_[8 + arg - 90]; |
827 | break; | 851 | break; |
828 | } | 852 | } |
829 | } | 853 | } |
830 | /* On light backgrounds, darken the colors to make them more legible. */ | 854 | /* Ensure legibility if only one of the colors is set. */ |
831 | if (get_HSLColor(tmBackground_ColorId).lum > 0.5f) { | 855 | /* TODO: Force darkening of the background color, unless it is also specified. */ |
832 | clr.r /= 2; | 856 | #if 0 |
833 | clr.g /= 2; | 857 | if (bg.a == 0 && !equal_Color(fg, get_Color(fgDefault))) { |
834 | clr.b /= 2; | 858 | if (delta_Color(fg, get_Color(tmBackground_ColorId)) < 64) { |
859 | const iHSLColor fgHsl = hsl_Color(fg); | ||
860 | iHSLColor legibleBg = get_HSLColor(tmBackground_ColorId); | ||
861 | if () | ||
862 | bg = rgb_HSLColor(bgHsl); | ||
863 | } | ||
864 | } | ||
865 | } | ||
866 | #endif | ||
867 | /* | ||
868 | if (!bg_out || get_HSLColor(tmBackground_ColorId).lum > 0.5f) { | ||
869 | fg.r /= 2; | ||
870 | fg.g /= 2; | ||
871 | fg.b /= 2; | ||
872 | } | ||
873 | */ | ||
874 | if (fg.a && fg_out) { | ||
875 | *fg_out = fg; | ||
876 | } | ||
877 | if (bg.a && bg_out) { | ||
878 | *bg_out = bg; | ||
835 | } | 879 | } |
836 | return clr; | ||
837 | } | 880 | } |
838 | 881 | ||
839 | iBool loadPalette_Color(const char *path) { | 882 | iBool loadPalette_Color(const char *path) { |
diff --git a/src/ui/color.h b/src/ui/color.h index 03172fcb..6602b226 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -255,7 +255,8 @@ iLocalDef void setHsl_Color(int color, iHSLColor hsl) { | |||
255 | iBool loadPalette_Color (const char *path); | 255 | iBool loadPalette_Color (const char *path); |
256 | void setThemePalette_Color (enum iColorTheme theme); | 256 | void setThemePalette_Color (enum iColorTheme theme); |
257 | 257 | ||
258 | iColor ansiForeground_Color (iRangecc escapeSequence, int fallback); | 258 | void ansiColors_Color (iRangecc escapeSequence, int fgDefault, int bgDefault, |
259 | iColor *fg_out, iColor *bg_out); /* can be NULL */ | ||
259 | const char * escape_Color (int color); | 260 | const char * escape_Color (int color); |
260 | enum iColorId parseEscape_Color (const char *cstr, const char **endp); | 261 | enum iColorId parseEscape_Color (const char *cstr, const char **endp); |
261 | 262 | ||
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; |
diff --git a/src/ui/text.h b/src/ui/text.h index 13a636d4..de76ed09 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -157,7 +157,7 @@ enum iAnsiFlag { | |||
157 | }; | 157 | }; |
158 | 158 | ||
159 | void setOpacity_Text (float opacity); | 159 | void setOpacity_Text (float opacity); |
160 | void setBaseAttributes_Text (int fontId, int colorId); /* current "normal" text attributes */ | 160 | void setBaseAttributes_Text (int fontId, int fgColorId); /* current "normal" text attributes */ |
161 | void setAnsiFlags_Text (int ansiFlags); | 161 | void setAnsiFlags_Text (int ansiFlags); |
162 | 162 | ||
163 | void cache_Text (int fontId, iRangecc text); /* pre-render glyphs */ | 163 | void cache_Text (int fontId, iRangecc text); /* pre-render glyphs */ |
@@ -187,7 +187,8 @@ iDeclareType(TextAttrib) | |||
187 | /* Initial attributes at the start of a text string. These may be modified by control | 187 | /* Initial attributes at the start of a text string. These may be modified by control |
188 | sequences inside a text run. */ | 188 | sequences inside a text run. */ |
189 | struct Impl_TextAttrib { | 189 | struct Impl_TextAttrib { |
190 | int16_t colorId; | 190 | int16_t fgColorId; |
191 | int16_t bgColorId; | ||
191 | struct { | 192 | struct { |
192 | uint16_t bold : 1; | 193 | uint16_t bold : 1; |
193 | uint16_t italic : 1; | 194 | uint16_t italic : 1; |