source: mainline/uspace/lib/ui/src/paint.c@ 45004f3

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 45004f3 was 81ec7e1, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Brace for a text mode menu separator entry

Pun intended.

  • Property mode set to 100644
File size: 12.9 KB
Line 
1/*
2 * Copyright (c) 2021 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libui
30 * @{
31 */
32/**
33 * @file Painting routines
34 */
35
36#include <errno.h>
37#include <gfx/color.h>
38#include <gfx/context.h>
39#include <gfx/render.h>
40#include <gfx/text.h>
41#include <ui/paint.h>
42#include <stdlib.h>
43#include <str.h>
44#include "../private/resource.h"
45
46/** Single box characters */
47static ui_box_chars_t single_box_chars = {
48 {
49 { "\u250c", "\u2500", "\u2510" },
50 { "\u2502", " ", "\u2502" },
51 { "\u2514", "\u2500", "\u2518" }
52 }
53};
54
55/** Double box characters */
56static ui_box_chars_t double_box_chars = {
57 {
58 { "\u2554", "\u2550", "\u2557" },
59 { "\u2551", " ", "\u2551" },
60 { "\u255a", "\u2550", "\u255d" }
61 }
62};
63
64/** Single horizontal brace characters */
65static ui_brace_chars_t single_hbrace_chars = {
66 .start = "\u251c",
67 .middle = "\u2500",
68 .end = "\u2524"
69};
70
71/** Double horizontal brace characters */
72static ui_brace_chars_t double_hbrace_chars = {
73 .start = "\u2560",
74 .middle = "\u2550",
75 .end = "\u2563"
76};
77
78/** Paint bevel.
79 *
80 * @param gc Graphic context
81 * @param rect Rectangle to paint into
82 * @param tlcolor Top-left color
83 * @param brcolor Bottom-right color
84 * @param thickness Bevel thickness in pixels
85 * @param inside Place to store rectangle of the interior or @c NULL
86 * @return EOK on success or an error code
87 */
88errno_t ui_paint_bevel(gfx_context_t *gc, gfx_rect_t *rect,
89 gfx_color_t *tlcolor, gfx_color_t *brcolor, gfx_coord_t thickness,
90 gfx_rect_t *inside)
91{
92 gfx_rect_t frect;
93 gfx_coord_t i;
94 errno_t rc;
95
96 /* Top left */
97
98 rc = gfx_set_color(gc, tlcolor);
99 if (rc != EOK)
100 goto error;
101
102 for (i = 0; i < thickness; i++) {
103 frect.p0.x = rect->p0.x + i;
104 frect.p0.y = rect->p0.y + i;
105 frect.p1.x = rect->p1.x - i - 1;
106 frect.p1.y = rect->p0.y + i + 1;
107 rc = gfx_fill_rect(gc, &frect);
108 if (rc != EOK)
109 goto error;
110
111 frect.p0.x = rect->p0.x + i;
112 frect.p0.y = rect->p0.y + i + 1;
113 frect.p1.x = rect->p0.x + i + 1;
114 frect.p1.y = rect->p1.y - i - 1;
115 rc = gfx_fill_rect(gc, &frect);
116 if (rc != EOK)
117 goto error;
118 }
119
120 /* Bottom right */
121
122 rc = gfx_set_color(gc, brcolor);
123 if (rc != EOK)
124 goto error;
125
126 for (i = 0; i < thickness; i++) {
127 frect.p0.x = rect->p0.x + i;
128 frect.p0.y = rect->p1.y - i - 1;
129 frect.p1.x = rect->p1.x - i - 1;
130 frect.p1.y = rect->p1.y - i;
131 rc = gfx_fill_rect(gc, &frect);
132 if (rc != EOK)
133 goto error;
134
135 frect.p0.x = rect->p1.x - i - 1;
136 frect.p0.y = rect->p0.y + i;
137 frect.p1.x = rect->p1.x - i;
138 frect.p1.y = rect->p1.y - i;
139 rc = gfx_fill_rect(gc, &frect);
140 if (rc != EOK)
141 goto error;
142 }
143
144 if (inside != NULL)
145 ui_paint_get_bevel_inside(gc, rect, thickness, inside);
146
147 return EOK;
148error:
149 return rc;
150}
151
152/** Get bevel interior rectangle.
153 *
154 * Get the bevel interior rectangle without painting it.
155 *
156 * @param gc Graphic context
157 * @param rect Rectangle to paint into
158 * @param thickness Bevel thickness in pixels
159 * @param inside Place to store rectangle of the interior
160 */
161void ui_paint_get_bevel_inside(gfx_context_t *gc, gfx_rect_t *rect,
162 gfx_coord_t thickness, gfx_rect_t *inside)
163{
164 inside->p0.x = rect->p0.x + thickness;
165 inside->p0.y = rect->p0.y + thickness;
166 inside->p1.x = rect->p1.x - thickness;
167 inside->p1.y = rect->p1.y - thickness;
168}
169
170/** Paint inset frame.
171 *
172 * @param resource UI resource
173 * @param rect Rectangle to paint onto
174 * @param inside Place to store inside rectangle or @c NULL
175 * @return EOK on success or an error code
176 */
177errno_t ui_paint_inset_frame(ui_resource_t *resource, gfx_rect_t *rect,
178 gfx_rect_t *inside)
179{
180 gfx_rect_t frame;
181 errno_t rc;
182
183 rc = ui_paint_bevel(resource->gc, rect,
184 resource->wnd_shadow_color, resource->wnd_highlight_color,
185 1, &frame);
186 if (rc != EOK)
187 goto error;
188
189 rc = ui_paint_bevel(resource->gc, &frame,
190 resource->wnd_frame_sh_color, resource->wnd_frame_hi_color,
191 1, inside);
192 if (rc != EOK)
193 goto error;
194
195 return EOK;
196error:
197 return rc;
198}
199
200/** Get inset frame interior rectangle.
201 *
202 * This allows one to get the interior rectangle without actually painting
203 * the inset frame.
204 *
205 * @param resource UI resource
206 * @param rect Rectangle to paint onto
207 * @param inside Place to store inside rectangle or @c NULL
208 */
209void ui_paint_get_inset_frame_inside(ui_resource_t *resource, gfx_rect_t *rect,
210 gfx_rect_t *inside)
211{
212 ui_paint_get_bevel_inside(resource->gc, rect, 2, inside);
213}
214
215/** Paint outset frame.
216 *
217 * @param resource UI resource
218 * @param rect Rectangle to paint onto
219 * @param inside Place to store inside rectangle or @c NULL
220 * @return EOK on success or an error code
221 */
222errno_t ui_paint_outset_frame(ui_resource_t *resource, gfx_rect_t *rect,
223 gfx_rect_t *inside)
224{
225 gfx_rect_t frame;
226 errno_t rc;
227
228 rc = ui_paint_bevel(resource->gc, rect,
229 resource->wnd_frame_hi_color, resource->wnd_frame_sh_color,
230 1, &frame);
231 if (rc != EOK)
232 goto error;
233
234 rc = ui_paint_bevel(resource->gc, &frame,
235 resource->wnd_highlight_color, resource->wnd_shadow_color,
236 1, inside);
237 if (rc != EOK)
238 goto error;
239
240 return EOK;
241error:
242 return rc;
243}
244
245/** Paint filled circle vertical scanline.
246 *
247 * @param gc Graphic context
248 * @param center Coordinates of the center of the circle
249 * @param x X-coordinate of the scanline
250 * @param y0 Lowest Y coordinate of the scanline (inclusive)
251 * @param y1 Highest Y coordinate of the scanline (inclusive)
252 * @param part Which part(s) of cicle to paint
253 * @return EOK on success or an error code
254 */
255static errno_t ui_paint_fcircle_line(gfx_context_t *gc, gfx_coord2_t *center,
256 gfx_coord_t x, gfx_coord_t y0, gfx_coord_t y1, ui_fcircle_part_t part)
257{
258 gfx_rect_t rect;
259 gfx_rect_t trect;
260
261 rect.p0.x = x;
262 rect.p0.y = y0;
263 rect.p1.x = x + 1;
264 rect.p1.y = y1;
265
266 /* Clip to upper-left/lower-right half of circle, if required */
267
268 if ((part & ui_fcircle_upleft) == 0) {
269 if (rect.p0.y < -rect.p1.x)
270 rect.p0.y = -rect.p1.x;
271 }
272
273 if ((part & ui_fcircle_lowright) == 0) {
274 if (rect.p1.y > -rect.p1.x)
275 rect.p1.y = -rect.p1.x;
276 }
277
278 /* If coordinates are reversed, there is nothing to do */
279 if (rect.p1.y <= rect.p0.y)
280 return EOK;
281
282 gfx_rect_translate(center, &rect, &trect);
283
284 return gfx_fill_rect(gc, &trect);
285}
286
287/** Paint filled circle scanlines corresponding to octant point.
288 *
289 * Fills the four vertical scanlines lying between the eight reflections
290 * of a circle point.
291 *
292 * @param gc Graphic context
293 * @param center Coordinates of the center
294 * @param r Radius in pixels
295 * @param part Which part(s) of cicle to paint
296 * @return EOK on success or an error code
297 */
298static int ui_paint_fcircle_point(gfx_context_t *gc, gfx_coord2_t *center,
299 gfx_coord2_t *p, ui_fcircle_part_t part)
300{
301 errno_t rc;
302
303 rc = ui_paint_fcircle_line(gc, center, +p->x, -p->y, p->y + 1, part);
304 if (rc != EOK)
305 return rc;
306 rc = ui_paint_fcircle_line(gc, center, -p->x, -p->y, p->y + 1, part);
307 if (rc != EOK)
308 return rc;
309 rc = ui_paint_fcircle_line(gc, center, +p->y, -p->x, p->x + 1, part);
310 if (rc != EOK)
311 return rc;
312 rc = ui_paint_fcircle_line(gc, center, -p->y, -p->x, p->x + 1, part);
313 if (rc != EOK)
314 return rc;
315
316 return EOK;
317}
318
319/** Paint a filled circle.
320 *
321 * @param gc Graphic context
322 * @param center Coordinates of the center
323 * @param r Radius in pixels
324 * @param part Which part(s) of cicle to paint
325 * @return EOK on success or an error code
326 */
327errno_t ui_paint_filled_circle(gfx_context_t *gc, gfx_coord2_t *center,
328 gfx_coord_t r, ui_fcircle_part_t part)
329{
330 gfx_coord2_t p;
331 gfx_coord_t f;
332 errno_t rc;
333
334 /* Run through one octant using circle midpoint algorithm */
335
336 p.x = r;
337 p.y = 0;
338 f = 1 - r;
339
340 rc = ui_paint_fcircle_point(gc, center, &p, part);
341 if (rc != EOK)
342 return rc;
343
344 while (p.x > p.y) {
345 if (f < 0) {
346 f = f + 2 * p.y + 3;
347 } else {
348 f = f + 2 * (p.y - p.x) + 5;
349 --p.x;
350 }
351 ++p.y;
352
353 rc = ui_paint_fcircle_point(gc, center, &p, part);
354 if (rc != EOK)
355 return rc;
356 }
357
358 return EOK;
359}
360
361/** Paint a text box.
362 *
363 * @param resource UI resource
364 * @param rect Rectangle inside which to paint the box
365 * @param style Box style
366 * @param color Color
367 * @return EOK on success or an error code
368 */
369errno_t ui_paint_text_box(ui_resource_t *resource, gfx_rect_t *rect,
370 ui_box_style_t style, gfx_color_t *color)
371{
372 errno_t rc;
373 gfx_text_fmt_t fmt;
374 gfx_rect_t srect;
375 gfx_coord2_t pos;
376 gfx_coord2_t dim;
377 size_t bufsz;
378 size_t off;
379 int i;
380 gfx_coord_t y;
381 char *str = NULL;
382 ui_box_chars_t *boxc = NULL;
383
384 gfx_rect_points_sort(rect, &srect);
385 gfx_rect_dims(&srect, &dim);
386
387 /* Is rectangle large enough to hold box? */
388 if (dim.x < 2 || dim.y < 2)
389 return EOK;
390
391 switch (style) {
392 case ui_box_single:
393 boxc = &single_box_chars;
394 break;
395 case ui_box_double:
396 boxc = &double_box_chars;
397 break;
398 }
399
400 if (boxc == NULL)
401 return EINVAL;
402
403 gfx_text_fmt_init(&fmt);
404 fmt.color = color;
405
406 bufsz = str_size(boxc->c[0][0]) +
407 str_size(boxc->c[0][1]) * (dim.x - 2) +
408 str_size(boxc->c[0][2]) + 1;
409
410 str = malloc(bufsz);
411 if (str == NULL)
412 return ENOMEM;
413
414 /* Top edge and corners */
415
416 str_cpy(str, bufsz, boxc->c[0][0]);
417 off = str_size(boxc->c[0][0]);
418
419 for (i = 1; i < dim.x - 1; i++) {
420 str_cpy(str + off, bufsz - off, boxc->c[0][1]);
421 off += str_size(boxc->c[0][1]);
422 }
423
424 str_cpy(str + off, bufsz - off, boxc->c[0][2]);
425 off += str_size(boxc->c[0][2]);
426 str[off] = '\0';
427
428 pos = rect->p0;
429 rc = gfx_puttext(resource->font, &pos, &fmt, str);
430 if (rc != EOK)
431 goto error;
432
433 /* Vertical edges */
434 for (y = rect->p0.y + 1; y < rect->p1.y - 1; y++) {
435 pos.y = y;
436
437 pos.x = rect->p0.x;
438 rc = gfx_puttext(resource->font, &pos, &fmt,
439 boxc->c[1][0]);
440 if (rc != EOK)
441 goto error;
442
443 pos.x = rect->p1.x - 1;
444 rc = gfx_puttext(resource->font, &pos, &fmt,
445 boxc->c[1][2]);
446 if (rc != EOK)
447 goto error;
448 }
449
450 /* Bottom edge and corners */
451
452 str_cpy(str, bufsz, boxc->c[2][0]);
453 off = str_size(boxc->c[2][0]);
454
455 for (i = 1; i < dim.x - 1; i++) {
456 str_cpy(str + off, bufsz - off, boxc->c[2][1]);
457 off += str_size(boxc->c[2][1]);
458 }
459
460 str_cpy(str + off, bufsz - off, boxc->c[2][2]);
461 off += str_size(boxc->c[2][2]);
462 str[off] = '\0';
463
464 pos.x = rect->p0.x;
465 pos.y = rect->p1.y - 1;
466 rc = gfx_puttext(resource->font, &pos, &fmt, str);
467 if (rc != EOK)
468 goto error;
469
470 free(str);
471 return EOK;
472error:
473 if (str != NULL)
474 free(str);
475 return rc;
476}
477
478/** Paint a text horizontal brace.
479 *
480 * @param resource UI resource
481 * @param rect Rectangle inside which to paint the brace (height should
482 * be 1).
483 * @param style Box style
484 * @param color Color
485 * @return EOK on success or an error code
486 */
487errno_t ui_paint_text_hbrace(ui_resource_t *resource, gfx_rect_t *rect,
488 ui_box_style_t style, gfx_color_t *color)
489{
490 errno_t rc;
491 gfx_text_fmt_t fmt;
492 gfx_rect_t srect;
493 gfx_coord2_t pos;
494 gfx_coord2_t dim;
495 size_t bufsz;
496 size_t off;
497 int i;
498 char *str = NULL;
499 ui_brace_chars_t *hbc = NULL;
500
501 gfx_rect_points_sort(rect, &srect);
502 gfx_rect_dims(&srect, &dim);
503
504 /* Is rectangle large enough to hold brace? */
505 if (dim.x < 2 || dim.y < 1)
506 return EOK;
507
508 switch (style) {
509 case ui_box_single:
510 hbc = &single_hbrace_chars;
511 break;
512 case ui_box_double:
513 hbc = &double_hbrace_chars;
514 break;
515 }
516
517 if (hbc == NULL)
518 return EINVAL;
519
520 gfx_text_fmt_init(&fmt);
521 fmt.color = color;
522
523 bufsz = str_size(hbc->start) +
524 str_size(hbc->middle) * (dim.x - 2) +
525 str_size(hbc->end) + 1;
526
527 str = malloc(bufsz);
528 if (str == NULL)
529 return ENOMEM;
530
531 str_cpy(str, bufsz, hbc->start);
532 off = str_size(hbc->start);
533
534 for (i = 1; i < dim.x - 1; i++) {
535 str_cpy(str + off, bufsz - off, hbc->middle);
536 off += str_size(hbc->middle);
537 }
538
539 str_cpy(str + off, bufsz - off, hbc->end);
540 off += str_size(hbc->end);
541 str[off] = '\0';
542
543 pos = rect->p0;
544 rc = gfx_puttext(resource->font, &pos, &fmt, str);
545 if (rc != EOK)
546 goto error;
547
548 free(str);
549 return EOK;
550error:
551 if (str != NULL)
552 free(str);
553 return rc;
554}
555
556/** @}
557 */
Note: See TracBrowser for help on using the repository browser.