| 784 | } |
| 785 | |
| 786 | void DrawStringWithColors(const Surface &out, string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, const Rectangle &rect, TextRenderOptions opts) |
| 787 | { |
| 788 | const GameFontTables size = GetFontSizeFromUiFlags(opts.flags); |
| 789 | const text_color color = GetColorFromFlags(opts.flags); |
| 790 | |
| 791 | int charactersInLine = 0; |
| 792 | int lineWidth = 0; |
| 793 | if (HasAnyOf(opts.flags, (UiFlags::AlignCenter | UiFlags::AlignRight | UiFlags::KerningFitSpacing))) |
| 794 | lineWidth = GetLineWidth(fmt, args, argsLen, 0, size, opts.spacing, &charactersInLine); |
| 795 | |
| 796 | Point characterPosition { GetLineStartX(opts.flags, rect, lineWidth), rect.position.y }; |
| 797 | const int initialX = characterPosition.x; |
| 798 | |
| 799 | const int rightMargin = rect.position.x + rect.size.width; |
| 800 | const int bottomMargin = rect.size.height != 0 ? std::min(rect.position.y + rect.size.height + BaseLineOffset[size], out.h()) : out.h(); |
| 801 | |
| 802 | if (opts.lineHeight == -1) |
| 803 | opts.lineHeight = GetLineHeight(fmt, args, argsLen, size); |
| 804 | |
| 805 | if (HasAnyOf(opts.flags, UiFlags::VerticalCenter)) { |
| 806 | const int textHeight = static_cast<int>((CountNewlines(fmt, args, argsLen) + 1) * opts.lineHeight); |
| 807 | characterPosition.y += std::max(0, (rect.size.height - textHeight) / 2); |
| 808 | } |
| 809 | |
| 810 | characterPosition.y += BaseLineOffset[size]; |
| 811 | |
| 812 | const bool outlined = HasAnyOf(opts.flags, UiFlags::Outlined); |
| 813 | |
| 814 | const Surface clippedOut = ClipSurface(out, rect); |
| 815 | |
| 816 | CurrentFont currentFont; |
| 817 | const int originalSpacing = opts.spacing; |
| 818 | if (HasAnyOf(opts.flags, UiFlags::KerningFitSpacing)) { |
| 819 | opts.spacing = AdjustSpacingToFitHorizontally(lineWidth, originalSpacing, charactersInLine, rect.size.width); |
| 820 | if (opts.spacing != originalSpacing && HasAnyOf(opts.flags, UiFlags::AlignCenter | UiFlags::AlignRight)) { |
| 821 | const int adjustedLineWidth = GetLineWidth(fmt, args, argsLen, 0, size, opts.spacing, &charactersInLine); |
| 822 | characterPosition.x = GetLineStartX(opts.flags, rect, adjustedLineWidth); |
| 823 | } |
| 824 | } |
| 825 | |
| 826 | char32_t prev = U'\0'; |
| 827 | char32_t next; |
| 828 | string_view remaining = fmt; |
| 829 | FmtArgParser fmtArgParser { fmt, args, argsLen }; |
| 830 | size_t cpLen; |
| 831 | |
| 832 | // The current formatted argument value being processed. |
| 833 | string_view curFormatted; |
| 834 | text_color curFormattedColor; |
| 835 | |
| 836 | // The string that we're currently processing: either `remaining` or `curFormatted`. |
| 837 | string_view *str; |
| 838 | |
| 839 | for (; !(IsConsumed(curFormatted) && IsConsumed(remaining)); |
| 840 | str->remove_prefix(cpLen), prev = next) { |
| 841 | const bool isProcessingFormatArgValue = !IsConsumed(curFormatted); |
| 842 | str = isProcessingFormatArgValue ? &curFormatted : &remaining; |
| 843 | next = DecodeFirstUtf8CodePoint(*str, &cpLen); |
nothing calls this directly
no test coverage detected