[testsuite] [i386] work around fails with --enable-frame-pointer
[official-gcc.git] / gcc / text-art / canvas.cc
blob5bb33b2c6fac8c7fcf656c042b2107d6ba00d9b2
1 /* Canvas for random-access procedural text art.
2 Copyright (C) 2023-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #define INCLUDE_VECTOR
23 #include "system.h"
24 #include "coretypes.h"
25 #include "pretty-print.h"
26 #include "selftest.h"
27 #include "text-art/selftests.h"
28 #include "text-art/canvas.h"
30 using namespace text_art;
32 canvas::canvas (size_t size, const style_manager &style_mgr)
33 : m_cells (size_t (size.w, size.h)),
34 m_style_mgr (style_mgr)
36 m_cells.fill (cell_t (' '));
39 void
40 canvas::paint (coord_t coord, styled_unichar ch)
42 m_cells.set (coord, std::move (ch));
45 void
46 canvas::paint_text (coord_t coord, const styled_string &text)
48 for (auto ch : text)
50 paint (coord, ch);
51 if (ch.double_width_p ())
52 coord.x += 2;
53 else
54 coord.x++;
58 void
59 canvas::fill (rect_t rect, cell_t c)
61 for (int y = rect.get_min_y (); y < rect.get_next_y (); y++)
62 for (int x = rect.get_min_x (); x < rect.get_next_x (); x++)
63 paint(coord_t (x, y), c);
66 void
67 canvas::debug_fill ()
69 fill (rect_t (coord_t (0, 0), get_size ()), cell_t ('*'));
72 void
73 canvas::print_to_pp (pretty_printer *pp,
74 const char *per_line_prefix) const
76 for (int y = 0; y < m_cells.get_size ().h; y++)
78 style::id_t curr_style_id = 0;
79 if (per_line_prefix)
80 pp_string (pp, per_line_prefix);
82 pretty_printer line_pp;
83 line_pp.show_color = pp->show_color;
84 line_pp.url_format = pp->url_format;
85 const int final_x_in_row = get_final_x_in_row (y);
86 for (int x = 0; x <= final_x_in_row; x++)
88 if (x > 0)
90 const cell_t prev_cell = m_cells.get (coord_t (x - 1, y));
91 if (prev_cell.double_width_p ())
92 /* This cell is just a placeholder for the
93 2nd column of a double width cell; skip it. */
94 continue;
96 const cell_t cell = m_cells.get (coord_t (x, y));
97 if (cell.get_style_id () != curr_style_id)
99 m_style_mgr.print_any_style_changes (&line_pp,
100 curr_style_id,
101 cell.get_style_id ());
102 curr_style_id = cell.get_style_id ();
104 pp_unicode_character (&line_pp, cell.get_code ());
105 if (cell.emoji_variant_p ())
106 /* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
107 variation of the char. */
108 pp_unicode_character (&line_pp, 0xFE0F);
110 /* Reset the style at the end of each line. */
111 m_style_mgr.print_any_style_changes (&line_pp, curr_style_id, 0);
113 /* Print from line_pp to pp, stripping trailing whitespace from
114 the line. */
115 const char *line_buf = pp_formatted_text (&line_pp);
116 ::size_t len = strlen (line_buf);
117 while (len > 0)
119 if (line_buf[len - 1] == ' ')
120 len--;
121 else
122 break;
124 pp_append_text (pp, line_buf, line_buf + len);
125 pp_newline (pp);
129 DEBUG_FUNCTION void
130 canvas::debug (bool styled) const
132 pretty_printer pp;
133 if (styled)
135 pp_show_color (&pp) = true;
136 pp.url_format = determine_url_format (DIAGNOSTICS_URL_AUTO);
138 print_to_pp (&pp);
139 fprintf (stderr, "%s\n", pp_formatted_text (&pp));
142 /* Find right-most non-default cell in this row,
143 or -1 if all are default. */
146 canvas::get_final_x_in_row (int y) const
148 for (int x = m_cells.get_size ().w - 1; x >= 0; x--)
150 cell_t cell = m_cells.get (coord_t (x, y));
151 if (cell.get_code () != ' '
152 || cell.get_style_id () != style::id_plain)
153 return x;
155 return -1;
158 #if CHECKING_P
160 namespace selftest {
162 static void
163 test_blank ()
165 style_manager sm;
166 canvas c (canvas::size_t (5, 5), sm);
167 ASSERT_CANVAS_STREQ (c, false,
168 ("\n"
169 "\n"
170 "\n"
171 "\n"
172 "\n"));
175 static void
176 test_abc ()
178 style_manager sm;
179 canvas c (canvas::size_t (3, 3), sm);
180 c.paint (canvas::coord_t (0, 0), styled_unichar ('A'));
181 c.paint (canvas::coord_t (1, 1), styled_unichar ('B'));
182 c.paint (canvas::coord_t (2, 2), styled_unichar ('C'));
184 ASSERT_CANVAS_STREQ (c, false,
185 "A\n B\n C\n");
188 static void
189 test_debug_fill ()
191 style_manager sm;
192 canvas c (canvas::size_t (5, 3), sm);
193 c.debug_fill();
194 ASSERT_CANVAS_STREQ (c, false,
195 ("*****\n"
196 "*****\n"
197 "*****\n"));
200 static void
201 test_text ()
203 style_manager sm;
204 canvas c (canvas::size_t (6, 1), sm);
205 c.paint_text (canvas::coord_t (0, 0), styled_string (sm, "012345"));
206 ASSERT_CANVAS_STREQ (c, false,
207 ("012345\n"));
209 /* Paint an emoji character that should occupy two canvas columns when
210 printed. */
211 c.paint_text (canvas::coord_t (2, 0), styled_string ((cppchar_t)0x1f642));
212 ASSERT_CANVAS_STREQ (c, false,
213 ("01🙂45\n"));
216 static void
217 test_circle ()
219 canvas::size_t sz (30, 30);
220 style_manager sm;
221 canvas canvas (sz, sm);
222 canvas::coord_t center (sz.w / 2, sz.h / 2);
223 const int radius = 12;
224 const int radius_squared = radius * radius;
225 for (int x = 0; x < sz.w; x++)
226 for (int y = 0; y < sz.h; y++)
228 int dx = x - center.x;
229 int dy = y - center.y;
230 char ch = "AB"[(x + y) % 2];
231 if (dx * dx + dy * dy < radius_squared)
232 canvas.paint (canvas::coord_t (x, y), styled_unichar (ch));
234 ASSERT_CANVAS_STREQ
235 (canvas, false,
236 ("\n"
237 "\n"
238 "\n"
239 "\n"
240 " BABABABAB\n"
241 " ABABABABABABA\n"
242 " ABABABABABABABA\n"
243 " ABABABABABABABABA\n"
244 " ABABABABABABABABABA\n"
245 " ABABABABABABABABABABA\n"
246 " BABABABABABABABABABAB\n"
247 " BABABABABABABABABABABAB\n"
248 " ABABABABABABABABABABABA\n"
249 " BABABABABABABABABABABAB\n"
250 " ABABABABABABABABABABABA\n"
251 " BABABABABABABABABABABAB\n"
252 " ABABABABABABABABABABABA\n"
253 " BABABABABABABABABABABAB\n"
254 " ABABABABABABABABABABABA\n"
255 " BABABABABABABABABABABAB\n"
256 " BABABABABABABABABABAB\n"
257 " ABABABABABABABABABABA\n"
258 " ABABABABABABABABABA\n"
259 " ABABABABABABABABA\n"
260 " ABABABABABABABA\n"
261 " ABABABABABABA\n"
262 " BABABABAB\n"
263 "\n"
264 "\n"
265 "\n"));
268 static void
269 test_color_circle ()
271 const canvas::size_t sz (10, 10);
272 const canvas::coord_t center (sz.w / 2, sz.h / 2);
273 const int outer_r2 = 25;
274 const int inner_r2 = 10;
275 style_manager sm;
276 canvas c (sz, sm);
277 for (int x = 0; x < sz.w; x++)
278 for (int y = 0; y < sz.h; y++)
280 const int dist_from_center_squared
281 = ((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y));
282 if (dist_from_center_squared < outer_r2)
284 style s;
285 if (dist_from_center_squared < inner_r2)
286 s.m_fg_color = style::named_color::RED;
287 else
288 s.m_fg_color = style::named_color::GREEN;
289 c.paint (canvas::coord_t (x, y),
290 styled_unichar ('*', false, sm.get_or_create_id (s)));
293 ASSERT_EQ (sm.get_num_styles (), 3);
294 ASSERT_CANVAS_STREQ
295 (c, false,
296 ("\n"
297 " *****\n"
298 " *******\n"
299 " *********\n"
300 " *********\n"
301 " *********\n"
302 " *********\n"
303 " *********\n"
304 " *******\n"
305 " *****\n"));
306 ASSERT_CANVAS_STREQ
307 (c, true,
308 ("\n"
309 " \e[32m\e[K*****\e[m\e[K\n"
310 " \e[32m\e[K***\e[31m\e[K*\e[32m\e[K***\e[m\e[K\n"
311 " \e[32m\e[K**\e[31m\e[K*****\e[32m\e[K**\e[m\e[K\n"
312 " \e[32m\e[K**\e[31m\e[K*****\e[32m\e[K**\e[m\e[K\n"
313 " \e[32m\e[K*\e[31m\e[K*******\e[32m\e[K*\e[m\e[K\n"
314 " \e[32m\e[K**\e[31m\e[K*****\e[32m\e[K**\e[m\e[K\n"
315 " \e[32m\e[K**\e[31m\e[K*****\e[32m\e[K**\e[m\e[K\n"
316 " \e[32m\e[K***\e[31m\e[K*\e[32m\e[K***\e[m\e[K\n"
317 " \e[32m\e[K*****\e[m\e[K\n"));
320 static void
321 test_bold ()
323 auto_fix_quotes fix_quotes;
324 style_manager sm;
325 styled_string s (styled_string::from_fmt (sm, nullptr,
326 "before %qs after", "foo"));
327 canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
328 c.paint_text (canvas::coord_t (0, 0), s);
329 ASSERT_CANVAS_STREQ (c, false,
330 "before `foo' after\n");
331 ASSERT_CANVAS_STREQ (c, true,
332 "before `\e[00;01m\e[Kfoo\e[00m\e[K' after\n");
335 static void
336 test_emoji ()
338 style_manager sm;
339 styled_string s (0x26A0, /* U+26A0 WARNING SIGN. */
340 true);
341 canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
342 c.paint_text (canvas::coord_t (0, 0), s);
343 ASSERT_CANVAS_STREQ (c, false, "⚠️\n");
344 ASSERT_CANVAS_STREQ (c, true, "⚠️\n");
347 static void
348 test_emoji_2 ()
350 style_manager sm;
351 styled_string s;
352 s.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */
353 true));
354 s.append (styled_string (sm, "test"));
355 ASSERT_EQ (s.size (), 5);
356 ASSERT_EQ (s.calc_canvas_width (), 5);
357 canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
358 c.paint_text (canvas::coord_t (0, 0), s);
359 ASSERT_CANVAS_STREQ (c, false,
360 /* U+26A0 WARNING SIGN, as UTF-8: 0xE2 0x9A 0xA0. */
361 "\xE2\x9A\xA0"
362 /* U+FE0F VARIATION SELECTOR-16, as UTF-8: 0xEF 0xB8 0x8F. */
363 "\xEF\xB8\x8F"
364 "test\n");
367 static void
368 test_canvas_urls ()
370 style_manager sm;
371 canvas canvas (canvas::size_t (9, 3), sm);
372 styled_string foo_ss (sm, "foo");
373 foo_ss.set_url (sm, "https://www.example.com/foo");
374 styled_string bar_ss (sm, "bar");
375 bar_ss.set_url (sm, "https://www.example.com/bar");
376 canvas.paint_text(canvas::coord_t (1, 1), foo_ss);
377 canvas.paint_text(canvas::coord_t (5, 1), bar_ss);
379 ASSERT_CANVAS_STREQ (canvas, false,
380 ("\n"
381 " foo bar\n"
382 "\n"));
384 pretty_printer pp;
385 pp_show_color (&pp) = true;
386 pp.url_format = URL_FORMAT_ST;
387 assert_canvas_streq (SELFTEST_LOCATION, canvas, &pp,
388 (/* Line 1. */
389 "\n"
390 /* Line 2. */
392 "\33]8;;https://www.example.com/foo\33\\foo\33]8;;\33\\"
394 "\33]8;;https://www.example.com/bar\33\\bar\33]8;;\33\\"
395 "\n"
396 /* Line 3. */
397 "\n"));
401 pretty_printer pp;
402 pp_show_color (&pp) = true;
403 pp.url_format = URL_FORMAT_BEL;
404 assert_canvas_streq (SELFTEST_LOCATION, canvas, &pp,
405 (/* Line 1. */
406 "\n"
407 /* Line 2. */
409 "\33]8;;https://www.example.com/foo\afoo\33]8;;\a"
411 "\33]8;;https://www.example.com/bar\abar\33]8;;\a"
412 "\n"
413 /* Line 3. */
414 "\n"));
418 /* Run all selftests in this file. */
420 void
421 text_art_canvas_cc_tests ()
423 test_blank ();
424 test_abc ();
425 test_debug_fill ();
426 test_text ();
427 test_circle ();
428 test_color_circle ();
429 test_bold ();
430 test_emoji ();
431 test_emoji_2 ();
432 test_canvas_urls ();
435 } // namespace selftest
438 #endif /* #if CHECKING_P */