blob: 22e30efafd1d3f97235c42564b9f405c1e832879 [file] [log] [blame]
Ben Clayton89549b12024-02-26 20:24:06 +00001// Copyright 2024 The Dawn & Tint Authors
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
5//
6// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
8//
9// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
Ben Clayton0fa7bb62024-03-05 20:27:55 +000028#include <array>
Ben Clayton89549b12024-02-26 20:24:06 +000029#include <cstring>
30
Ben Clayton0fa7bb62024-03-05 20:27:55 +000031#include "src/tint/utils/containers/hashmap.h"
Ben Clayton89549b12024-02-26 20:24:06 +000032#include "src/tint/utils/text/styled_text.h"
33#include "src/tint/utils/text/styled_text_printer.h"
34#include "src/tint/utils/text/styled_text_theme.h"
35#include "src/tint/utils/text/text_style.h"
36
37namespace tint {
38namespace {
39
40template <typename T>
41bool Equal(const std::optional<T>& lhs, const std::optional<T>& rhs) {
42 if (lhs.has_value() != rhs.has_value()) {
43 return false;
44 }
45 if (!lhs.has_value()) {
46 return true;
47 }
48 return lhs.value() == rhs.value();
49}
50
51#define ESCAPE "\u001b"
52
Ben Clayton0fa7bb62024-03-05 20:27:55 +000053class Printer24Bit : public StyledTextPrinter {
Ben Clayton89549b12024-02-26 20:24:06 +000054 public:
Ben Clayton0fa7bb62024-03-05 20:27:55 +000055 Printer24Bit(FILE* f, const StyledTextTheme& t) : file_(f), theme_(t) {}
Ben Clayton89549b12024-02-26 20:24:06 +000056
57 void Print(const StyledText& style_text) override {
58 StyledTextTheme::Attributes current;
59
60 style_text.Walk([&](std::string_view text, TextStyle text_style) {
61 auto style = theme_.Get(text_style);
62 if (!Equal(current.foreground, style.foreground)) {
63 current.foreground = style.foreground;
64 if (current.foreground.has_value()) {
65 fprintf(file_, ESCAPE "[38;2;%d;%d;%dm", //
66 static_cast<int>(style.foreground->r),
67 static_cast<int>(style.foreground->g),
68 static_cast<int>(style.foreground->b));
69 } else {
70 fprintf(file_, ESCAPE "[39m");
71 }
72 }
73 if (!Equal(current.background, style.background)) {
74 current.background = style.background;
75 if (current.background.has_value()) {
76 fprintf(file_, ESCAPE "[48;2;%d;%d;%dm", //
77 static_cast<int>(style.background->r),
78 static_cast<int>(style.background->g),
79 static_cast<int>(style.background->b));
80 } else {
81 fprintf(file_, ESCAPE "[49m");
82 }
83 }
84 if (!Equal(current.underlined, style.underlined)) {
85 current.underlined = style.underlined;
86 if (current.underlined == true) {
87 fprintf(file_, ESCAPE "[4m");
88 } else {
89 fprintf(file_, ESCAPE "[24m");
90 }
91 }
92 if (!Equal(current.bold, style.bold)) {
93 current.bold = style.bold;
94 if (current.bold == true) {
95 fprintf(file_, ESCAPE "[1m");
96 } else {
97 fprintf(file_, ESCAPE "[22m");
98 }
99 }
100 fwrite(text.data(), 1, text.size(), file_);
101 });
102 fprintf(file_, ESCAPE "[m");
103 fflush(file_);
104 }
105
106 private:
107 FILE* const file_;
108 const StyledTextTheme& theme_;
109};
110
Ben Clayton0fa7bb62024-03-05 20:27:55 +0000111class Printer8Bit : public StyledTextPrinter {
112 public:
113 static constexpr std::array<uint32_t, 256> kPalette = {
114 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0, 0x808080,
115 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, 0x000000, 0x00005f,
116 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7,
117 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f,
118 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7,
119 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f,
120 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7,
121 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f,
122 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5fd7d7,
123 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f,
124 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7,
125 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f,
126 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7,
127 0x87d7ff, 0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f,
128 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7,
129 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
130 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 0xafd7d7,
131 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 0xd70000, 0xd7005f,
132 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, 0xd75fd7,
133 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f,
134 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7,
135 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f,
136 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7,
137 0xff5fff, 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
138 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7,
139 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 0x080808, 0x121212,
140 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x585858, 0x606060, 0x666666,
141 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6,
142 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee,
143 };
144
145 Printer8Bit(FILE* f, const StyledTextTheme& t) : file_(f), theme_(t) {}
146
147 void Print(const StyledText& style_text) override {
148 StyledTextTheme::Attributes current;
149
150 style_text.Walk([&](std::string_view text, TextStyle text_style) {
151 auto style = theme_.Get(text_style);
152 if (!Equal(current.foreground, style.foreground)) {
153 current.foreground = style.foreground;
154 if (current.foreground.has_value()) {
155 uint8_t color = Quantize(*style.foreground);
156 fprintf(file_, ESCAPE "[38;5;%dm", static_cast<int>(color));
157 } else {
158 fprintf(file_, ESCAPE "[39m");
159 }
160 }
161 if (!Equal(current.background, style.background)) {
162 current.background = style.background;
163 if (current.background.has_value()) {
164 uint8_t color = Quantize(*style.background);
165 fprintf(file_, ESCAPE "[48;5;%dm", static_cast<int>(color));
166 } else {
167 fprintf(file_, ESCAPE "[49m");
168 }
169 }
170 if (!Equal(current.underlined, style.underlined)) {
171 current.underlined = style.underlined;
172 if (current.underlined == true) {
173 fprintf(file_, ESCAPE "[4m");
174 } else {
175 fprintf(file_, ESCAPE "[24m");
176 }
177 }
178 if (!Equal(current.bold, style.bold)) {
179 current.bold = style.bold;
180 if (current.bold == true) {
181 fprintf(file_, ESCAPE "[1m");
182 } else {
183 fprintf(file_, ESCAPE "[22m");
184 }
185 }
186 fwrite(text.data(), 1, text.size(), file_);
187 });
188 fprintf(file_, ESCAPE "[m");
189 fflush(file_);
190 }
191
192 private:
193 uint8_t Quantize(const StyledTextTheme::Color& color) {
194 return colors_.GetOrAdd(color, [&] {
195 uint8_t best_color = 0;
196 int best_score = std::numeric_limits<int>::max();
197 for (size_t i = 0; i < 256; i++) {
198 int r = static_cast<int>((kPalette[i] >> 16) & 0xff);
199 int g = static_cast<int>((kPalette[i] >> 8) & 0xff);
200 int b = static_cast<int>((kPalette[i] >> 0) & 0xff);
201 int diff = std::abs(r - static_cast<int>(color.r)) +
202 std::abs(g - static_cast<int>(color.g)) +
203 std::abs(b - static_cast<int>(color.b));
204 if (diff == 0) {
205 return static_cast<uint8_t>(i);
206 }
207 if (diff < best_score) {
208 best_score = diff;
209 best_color = static_cast<uint8_t>(i);
210 }
211 }
212 return best_color;
213 });
214 }
215
216 FILE* const file_;
217 const StyledTextTheme& theme_;
218 Hashmap<StyledTextTheme::Color, uint8_t, 16> colors_;
219};
Ben Clayton89549b12024-02-26 20:24:06 +0000220} // namespace
221
222std::unique_ptr<StyledTextPrinter> StyledTextPrinter::CreateANSI(FILE* out,
Ben Clayton0fa7bb62024-03-05 20:27:55 +0000223 const StyledTextTheme& theme,
224 ANSIColors colors) {
225 switch (colors) {
226 case ANSIColors::k24Bit:
227 return std::make_unique<Printer24Bit>(out, theme);
228 case ANSIColors::k8Bit:
229 return std::make_unique<Printer8Bit>(out, theme);
230 }
231
232 // Should be unreachable.
233 return std::make_unique<Printer8Bit>(out, theme);
Ben Clayton89549b12024-02-26 20:24:06 +0000234}
235
236} // namespace tint