1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright 2020 Toomas Soome
5 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
6 * Copyright 2020 RackTop Systems, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI
32 * GOP Blt, and allows us to fill the rectangle on screen, copy
33 * rectangle from video to buffer and buffer to video and video to video.
34 * Such implementation does allow us to have almost identical implementation
35 * for both BIOS VBE and UEFI.
36 *
37 * ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red,
38 * Alpha) format, this allows us to only handle RGB data and not to worry
39 * about mixing RGB with indexed colors.
40 * Data exchange between memory buffer and video will translate BGRA
41 * and native format as following:
42 *
43 * 32-bit to/from 32-bit is trivial case.
44 * 32-bit to/from 24-bit is also simple - we just drop the alpha channel.
45 * 32-bit to/from 16-bit is more complicated, because we nee to handle
46 * data loss from 32-bit to 16-bit. While reading/writing from/to video, we
47 * need to apply masks of 16-bit color components. This will preserve
48 * colors for terminal text. For 32-bit truecolor PMG images, we need to
49 * translate 32-bit colors to 15/16 bit colors and this means data loss.
50 * There are different algorithms how to perform such color space reduction,
51 * we are currently using bitwise right shift to reduce color space and so far
52 * this technique seems to be sufficient (see also gfx_fb_putimage(), the
53 * end of for loop).
54 * 32-bit to/from 8-bit is the most troublesome because 8-bit colors are
55 * indexed. From video, we do get color indexes, and we do translate
56 * color index values to RGB. To write to video, we again need to translate
57 * RGB to color index. Additionally, we need to translate between VGA and
58 * console colors.
59 *
60 * Our internal color data is represented using BGRA format. But the hardware
61 * used indexed colors for 8-bit colors (0-255) and for this mode we do
62 * need to perform translation to/from BGRA and index values.
63 *
64 * - paletteentry RGB <-> index -
65 * BGRA BUFFER <----/ \ - VIDEO
66 * \ /
67 * - RGB (16/24/32) -
68 *
69 * To perform index to RGB translation, we use palette table generated
70 * from when we set up 8-bit mode video. We cannot read palette data from
71 * the hardware, because not all hardware supports reading it.
72 *
73 * BGRA to index is implemented in rgb_to_color_index() by searching
74 * palette array for closest match of RBG values.
75 *
76 * Note: In 8-bit mode, We do store first 16 colors to palette registers
77 * in VGA color order, this serves two purposes; firstly,
78 * if palette update is not supported, we still have correct 16 colors.
79 * Secondly, the kernel does get correct 16 colors when some other boot
80 * loader is used. However, the palette map for 8-bit colors is using
81 * console color ordering - this does allow us to skip translation
82 * from VGA colors to console colors, while we are reading RGB data.
83 */
84
85 #include <sys/param.h>
86 #include <assert.h>
87 #include <stand.h>
88 #include <teken.h>
89 #include <gfx_fb.h>
90 #include <sys/font.h>
91 #include <sys/splash.h>
92 #include <sys/linker.h>
93 #include <sys/module.h>
94 #include <sys/stdint.h>
95 #include <sys/endian.h>
96 #include <pnglite.h>
97 #include <bootstrap.h>
98 #include <lz4.h>
99 #if defined(EFI)
100 #include <efi.h>
101 #include <efilib.h>
102 #include <Protocol/GraphicsOutput.h>
103 #else
104 #include <vbe.h>
105 #endif
106
107 #include "modinfo.h"
108
109 /* VGA text mode does use bold font. */
110 #if !defined(VGA_8X16_FONT)
111 #define VGA_8X16_FONT "/boot/fonts/8x16b.fnt"
112 #endif
113 #if !defined(DEFAULT_8X16_FONT)
114 #define DEFAULT_8X16_FONT "/boot/fonts/8x16.fnt"
115 #endif
116
117 /*
118 * Must be sorted by font size in descending order
119 */
120 font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
121
122 #define DEFAULT_FONT_DATA font_data_8x16
123 extern vt_font_bitmap_data_t font_data_8x16;
124 teken_gfx_t gfx_state = { 0 };
125
126 static struct {
127 unsigned char r; /* Red percentage value. */
128 unsigned char g; /* Green percentage value. */
129 unsigned char b; /* Blue percentage value. */
130 } color_def[NCOLORS] = {
131 {0, 0, 0}, /* black */
132 {50, 0, 0}, /* dark red */
133 {0, 50, 0}, /* dark green */
134 {77, 63, 0}, /* dark yellow */
135 {20, 40, 64}, /* dark blue */
136 {50, 0, 50}, /* dark magenta */
137 {0, 50, 50}, /* dark cyan */
138 {75, 75, 75}, /* light gray */
139
140 {18, 20, 21}, /* dark gray */
141 {100, 0, 0}, /* light red */
142 {0, 100, 0}, /* light green */
143 {100, 100, 0}, /* light yellow */
144 {45, 62, 81}, /* light blue */
145 {100, 0, 100}, /* light magenta */
146 {0, 100, 100}, /* light cyan */
147 {100, 100, 100}, /* white */
148 };
149 uint32_t cmap[NCMAP];
150
151 /*
152 * Between console's palette and VGA's one:
153 * - blue and red are swapped (1 <-> 4)
154 * - yellow and cyan are swapped (3 <-> 6)
155 */
156 const int cons_to_vga_colors[NCOLORS] = {
157 0, 4, 2, 6, 1, 5, 3, 7,
158 8, 12, 10, 14, 9, 13, 11, 15
159 };
160
161 static const int vga_to_cons_colors[NCOLORS] = {
162 0, 1, 2, 3, 4, 5, 6, 7,
163 8, 9, 10, 11, 12, 13, 14, 15
164 };
165
166 /*
167 * It is reported very slow console draw in some systems.
168 * in order to exclude buggy gop->Blt(), we want option
169 * to use direct draw to framebuffer and avoid gop->Blt.
170 * Can be toggled with "gop" command.
171 */
172 bool ignore_gop_blt = false;
173
174 struct text_pixel *screen_buffer;
175 #if defined(EFI)
176 static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
177 #else
178 static struct paletteentry *GlyphBuffer;
179 #endif
180 static size_t GlyphBufferSize;
181
182 static bool insert_font(char *, FONT_FLAGS);
183 static int font_set(struct env_var *, int, const void *);
184 static void * allocate_glyphbuffer(uint32_t, uint32_t);
185 static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool);
186
187 /*
188 * Initialize gfx framework.
189 */
190 void
gfx_framework_init(void)191 gfx_framework_init(void)
192 {
193 /*
194 * Setup font list to have builtin font.
195 */
196 (void) insert_font(NULL, FONT_BUILTIN);
197 gfx_interp_ref(); /* Draw in the gfx interpreter for this thing */
198 }
199
200 static uint8_t *
gfx_get_fb_address(void)201 gfx_get_fb_address(void)
202 {
203 return (ptov((uint32_t)gfx_state.tg_fb.fb_addr));
204 }
205
206 /*
207 * Utility function to parse gfx mode line strings.
208 */
209 bool
gfx_parse_mode_str(char * str,int * x,int * y,int * depth)210 gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
211 {
212 char *p, *end;
213
214 errno = 0;
215 p = str;
216 *x = strtoul(p, &end, 0);
217 if (*x == 0 || errno != 0)
218 return (false);
219 if (*end != 'x')
220 return (false);
221 p = end + 1;
222 *y = strtoul(p, &end, 0);
223 if (*y == 0 || errno != 0)
224 return (false);
225 if (*end != 'x') {
226 *depth = -1; /* auto select */
227 } else {
228 p = end + 1;
229 *depth = strtoul(p, &end, 0);
230 if (*depth == 0 || errno != 0 || *end != '\0')
231 return (false);
232 }
233
234 return (true);
235 }
236
237 /*
238 * Returns true if we set the color from pre-existing environment, false if
239 * just used existing defaults.
240 */
241 static bool
gfx_fb_evalcolor(const char * envname,teken_color_t * cattr,ev_sethook_t sethook,ev_unsethook_t unsethook)242 gfx_fb_evalcolor(const char *envname, teken_color_t *cattr,
243 ev_sethook_t sethook, ev_unsethook_t unsethook)
244 {
245 const char *ptr;
246 char env[10];
247 int eflags = EV_VOLATILE | EV_NOKENV;
248 bool from_env = false;
249
250 ptr = getenv(envname);
251 if (ptr != NULL) {
252 *cattr = strtol(ptr, NULL, 10);
253
254 /*
255 * If we can't unset the value, then it's probably hooked
256 * properly and we can just carry on. Otherwise, we want to
257 * reinitialize it so that we can hook it for the console that
258 * we're resetting defaults for.
259 */
260 if (unsetenv(envname) != 0)
261 return (true);
262 from_env = true;
263
264 /*
265 * If we're carrying over an existing value, we *do* want that
266 * to propagate to the kenv.
267 */
268 eflags &= ~EV_NOKENV;
269 }
270
271 snprintf(env, sizeof(env), "%d", *cattr);
272 env_setenv(envname, eflags, env, sethook, unsethook);
273
274 return (from_env);
275 }
276
277 void
gfx_fb_setcolors(teken_attr_t * attr,ev_sethook_t sethook,ev_unsethook_t unsethook)278 gfx_fb_setcolors(teken_attr_t *attr, ev_sethook_t sethook,
279 ev_unsethook_t unsethook)
280 {
281 bool need_setattr = false;
282
283 /*
284 * On first run, we setup an environment hook to process any color
285 * changes. If the env is already set, we pick up fg and bg color
286 * values from the environment.
287 */
288 if (gfx_fb_evalcolor("teken.fg_color", &attr->ta_fgcolor,
289 sethook, unsethook))
290 need_setattr = true;
291 if (gfx_fb_evalcolor("teken.bg_color", &attr->ta_bgcolor,
292 sethook, unsethook))
293 need_setattr = true;
294
295 if (need_setattr)
296 teken_set_defattr(&gfx_state.tg_teken, attr);
297 }
298
299 static uint32_t
rgb_color_map(uint8_t index,uint32_t rmax,int roffset,uint32_t gmax,int goffset,uint32_t bmax,int boffset)300 rgb_color_map(uint8_t index, uint32_t rmax, int roffset,
301 uint32_t gmax, int goffset, uint32_t bmax, int boffset)
302 {
303 uint32_t color, code, gray, level;
304
305 if (index < NCOLORS) {
306 #define CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
307 return (CF(r, index) | CF(g, index) | CF(b, index));
308 #undef CF
309 }
310
311 #define CF(_f, _c) ((_f ## max & _c) << _f ## offset)
312 /* 6x6x6 color cube */
313 if (index > 15 && index < 232) {
314 uint32_t red, green, blue;
315
316 for (red = 0; red < 6; red++) {
317 for (green = 0; green < 6; green++) {
318 for (blue = 0; blue < 6; blue++) {
319 code = 16 + (red * 36) +
320 (green * 6) + blue;
321 if (code != index)
322 continue;
323 red = red ? (red * 40 + 55) : 0;
324 green = green ? (green * 40 + 55) : 0;
325 blue = blue ? (blue * 40 + 55) : 0;
326 color = CF(r, red);
327 color |= CF(g, green);
328 color |= CF(b, blue);
329 return (color);
330 }
331 }
332 }
333 }
334
335 /* colors 232-255 are a grayscale ramp */
336 for (gray = 0; gray < 24; gray++) {
337 level = (gray * 10) + 8;
338 code = 232 + gray;
339 if (code == index)
340 break;
341 }
342 return (CF(r, level) | CF(g, level) | CF(b, level));
343 #undef CF
344 }
345
346 /*
347 * Support for color mapping.
348 * For 8, 24 and 32 bit depth, use mask size 8.
349 * 15/16 bit depth needs to use mask size from mode,
350 * or we will lose color information from 32-bit to 15/16 bit translation.
351 */
352 uint32_t
gfx_fb_color_map(uint8_t index)353 gfx_fb_color_map(uint8_t index)
354 {
355 int rmask, gmask, bmask;
356 int roff, goff, boff, bpp;
357
358 roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
359 goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
360 boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
361 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
362
363 if (bpp == 2)
364 rmask = gfx_state.tg_fb.fb_mask_red >> roff;
365 else
366 rmask = 0xff;
367
368 if (bpp == 2)
369 gmask = gfx_state.tg_fb.fb_mask_green >> goff;
370 else
371 gmask = 0xff;
372
373 if (bpp == 2)
374 bmask = gfx_state.tg_fb.fb_mask_blue >> boff;
375 else
376 bmask = 0xff;
377
378 return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0));
379 }
380
381 /*
382 * Get indexed color from RGB. This function is used to write data to video
383 * memory when the adapter is set to use indexed colors.
384 * Since UEFI does only support 32-bit colors, we do not implement it for
385 * UEFI because there is no need for it and we do not have palette array
386 * for UEFI.
387 */
388 static uint8_t
rgb_to_color_index(uint8_t r,uint8_t g,uint8_t b)389 rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
390 {
391 #if !defined(EFI)
392 uint32_t color, best, dist, k;
393 int diff;
394
395 color = 0;
396 best = 255 * 255 * 255;
397 for (k = 0; k < NCMAP; k++) {
398 diff = r - pe8[k].Red;
399 dist = diff * diff;
400 diff = g - pe8[k].Green;
401 dist += diff * diff;
402 diff = b - pe8[k].Blue;
403 dist += diff * diff;
404
405 /* Exact match, exit the loop */
406 if (dist == 0)
407 break;
408
409 if (dist < best) {
410 color = k;
411 best = dist;
412 }
413 }
414 if (k == NCMAP)
415 k = color;
416 return (k);
417 #else
418 (void) r;
419 (void) g;
420 (void) b;
421 return (0);
422 #endif
423 }
424
425 int
generate_cons_palette(uint32_t * palette,int format,uint32_t rmax,int roffset,uint32_t gmax,int goffset,uint32_t bmax,int boffset)426 generate_cons_palette(uint32_t *palette, int format,
427 uint32_t rmax, int roffset, uint32_t gmax, int goffset,
428 uint32_t bmax, int boffset)
429 {
430 int i;
431
432 switch (format) {
433 case COLOR_FORMAT_VGA:
434 for (i = 0; i < NCOLORS; i++)
435 palette[i] = cons_to_vga_colors[i];
436 for (; i < NCMAP; i++)
437 palette[i] = i;
438 break;
439 case COLOR_FORMAT_RGB:
440 for (i = 0; i < NCMAP; i++)
441 palette[i] = rgb_color_map(i, rmax, roffset,
442 gmax, goffset, bmax, boffset);
443 break;
444 default:
445 return (ENODEV);
446 }
447
448 return (0);
449 }
450
451 static void
gfx_mem_wr1(uint8_t * base,size_t size,uint32_t o,uint8_t v)452 gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
453 {
454
455 if (o >= size)
456 return;
457 *(uint8_t *)(base + o) = v;
458 }
459
460 static void
gfx_mem_wr2(uint8_t * base,size_t size,uint32_t o,uint16_t v)461 gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
462 {
463
464 if (o >= size)
465 return;
466 *(uint16_t *)(base + o) = v;
467 }
468
469 static void
gfx_mem_wr4(uint8_t * base,size_t size,uint32_t o,uint32_t v)470 gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
471 {
472
473 if (o >= size)
474 return;
475 *(uint32_t *)(base + o) = v;
476 }
477
gfxfb_blt_fill(void * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)478 static int gfxfb_blt_fill(void *BltBuffer,
479 uint32_t DestinationX, uint32_t DestinationY,
480 uint32_t Width, uint32_t Height)
481 {
482 #if defined(EFI)
483 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
484 #else
485 struct paletteentry *p;
486 #endif
487 uint32_t data, bpp, pitch, y, x;
488 int roff, goff, boff;
489 size_t size;
490 off_t off;
491 uint8_t *destination;
492
493 if (BltBuffer == NULL)
494 return (EINVAL);
495
496 if (DestinationY + Height > gfx_state.tg_fb.fb_height)
497 return (EINVAL);
498
499 if (DestinationX + Width > gfx_state.tg_fb.fb_width)
500 return (EINVAL);
501
502 if (Width == 0 || Height == 0)
503 return (EINVAL);
504
505 p = BltBuffer;
506 roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
507 goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
508 boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
509
510 if (gfx_state.tg_fb.fb_bpp == 8) {
511 data = rgb_to_color_index(p->Red, p->Green, p->Blue);
512 } else {
513 data = (p->Red &
514 (gfx_state.tg_fb.fb_mask_red >> roff)) << roff;
515 data |= (p->Green &
516 (gfx_state.tg_fb.fb_mask_green >> goff)) << goff;
517 data |= (p->Blue &
518 (gfx_state.tg_fb.fb_mask_blue >> boff)) << boff;
519 }
520
521 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
522 pitch = gfx_state.tg_fb.fb_stride * bpp;
523 destination = gfx_get_fb_address();
524 size = gfx_state.tg_fb.fb_size;
525
526 for (y = DestinationY; y < Height + DestinationY; y++) {
527 off = y * pitch + DestinationX * bpp;
528 for (x = 0; x < Width; x++) {
529 switch (bpp) {
530 case 1:
531 gfx_mem_wr1(destination, size, off,
532 (data < NCOLORS) ?
533 cons_to_vga_colors[data] : data);
534 break;
535 case 2:
536 gfx_mem_wr2(destination, size, off, data);
537 break;
538 case 3:
539 gfx_mem_wr1(destination, size, off,
540 (data >> 16) & 0xff);
541 gfx_mem_wr1(destination, size, off + 1,
542 (data >> 8) & 0xff);
543 gfx_mem_wr1(destination, size, off + 2,
544 data & 0xff);
545 break;
546 case 4:
547 gfx_mem_wr4(destination, size, off, data);
548 break;
549 default:
550 return (EINVAL);
551 }
552 off += bpp;
553 }
554 }
555
556 return (0);
557 }
558
559 static int
gfxfb_blt_video_to_buffer(void * BltBuffer,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)560 gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
561 uint32_t DestinationX, uint32_t DestinationY,
562 uint32_t Width, uint32_t Height, uint32_t Delta)
563 {
564 #if defined(EFI)
565 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
566 #else
567 struct paletteentry *p;
568 #endif
569 uint32_t x, sy, dy;
570 uint32_t bpp, pitch, copybytes;
571 off_t off;
572 uint8_t *source, *destination, *sb;
573 uint8_t rm, rp, gm, gp, bm, bp;
574 bool bgra;
575
576 if (BltBuffer == NULL)
577 return (EINVAL);
578
579 if (SourceY + Height >
580 gfx_state.tg_fb.fb_height)
581 return (EINVAL);
582
583 if (SourceX + Width > gfx_state.tg_fb.fb_width)
584 return (EINVAL);
585
586 if (Width == 0 || Height == 0)
587 return (EINVAL);
588
589 if (Delta == 0)
590 Delta = Width * sizeof (*p);
591
592 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
593 pitch = gfx_state.tg_fb.fb_stride * bpp;
594
595 copybytes = Width * bpp;
596
597 rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
598 gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
599 bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
600 rm = gfx_state.tg_fb.fb_mask_red >> rp;
601 gm = gfx_state.tg_fb.fb_mask_green >> gp;
602 bm = gfx_state.tg_fb.fb_mask_blue >> bp;
603
604 /* If FB pixel format is BGRA, we can use direct copy. */
605 bgra = bpp == 4 &&
606 ffs(rm) - 1 == 8 && rp == 16 &&
607 ffs(gm) - 1 == 8 && gp == 8 &&
608 ffs(bm) - 1 == 8 && bp == 0;
609
610 for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
611 sy++, dy++) {
612 off = sy * pitch + SourceX * bpp;
613 source = gfx_get_fb_address() + off;
614 destination = (uint8_t *)BltBuffer + dy * Delta +
615 DestinationX * sizeof (*p);
616
617 if (bgra) {
618 bcopy(source, destination, copybytes);
619 } else {
620 for (x = 0; x < Width; x++) {
621 uint32_t c = 0;
622
623 p = (void *)(destination + x * sizeof (*p));
624 sb = source + x * bpp;
625 switch (bpp) {
626 case 1:
627 c = *sb;
628 break;
629 case 2:
630 c = *(uint16_t *)sb;
631 break;
632 case 3:
633 c = sb[0] << 16 | sb[1] << 8 | sb[2];
634 break;
635 case 4:
636 c = *(uint32_t *)sb;
637 break;
638 default:
639 return (EINVAL);
640 }
641
642 if (bpp == 1) {
643 *(uint32_t *)p = gfx_fb_color_map(
644 (c < 16) ?
645 vga_to_cons_colors[c] : c);
646 } else {
647 p->Red = (c >> rp) & rm;
648 p->Green = (c >> gp) & gm;
649 p->Blue = (c >> bp) & bm;
650 p->Reserved = 0;
651 }
652 }
653 }
654 }
655
656 return (0);
657 }
658
659 static int
gfxfb_blt_buffer_to_video(void * BltBuffer,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)660 gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
661 uint32_t DestinationX, uint32_t DestinationY,
662 uint32_t Width, uint32_t Height, uint32_t Delta)
663 {
664 #if defined(EFI)
665 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
666 #else
667 struct paletteentry *p;
668 #endif
669 uint32_t x, sy, dy;
670 uint32_t bpp, pitch, copybytes;
671 off_t off;
672 uint8_t *source, *destination;
673 uint8_t rm, rp, gm, gp, bm, bp;
674 bool bgra;
675
676 if (BltBuffer == NULL)
677 return (EINVAL);
678
679 if (DestinationY + Height >
680 gfx_state.tg_fb.fb_height)
681 return (EINVAL);
682
683 if (DestinationX + Width > gfx_state.tg_fb.fb_width)
684 return (EINVAL);
685
686 if (Width == 0 || Height == 0)
687 return (EINVAL);
688
689 if (Delta == 0)
690 Delta = Width * sizeof (*p);
691
692 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
693 pitch = gfx_state.tg_fb.fb_stride * bpp;
694
695 copybytes = Width * bpp;
696
697 rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
698 gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
699 bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
700 rm = gfx_state.tg_fb.fb_mask_red >> rp;
701 gm = gfx_state.tg_fb.fb_mask_green >> gp;
702 bm = gfx_state.tg_fb.fb_mask_blue >> bp;
703
704 /* If FB pixel format is BGRA, we can use direct copy. */
705 bgra = bpp == 4 &&
706 ffs(rm) - 1 == 8 && rp == 16 &&
707 ffs(gm) - 1 == 8 && gp == 8 &&
708 ffs(bm) - 1 == 8 && bp == 0;
709
710 for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
711 sy++, dy++) {
712 off = dy * pitch + DestinationX * bpp;
713 destination = gfx_get_fb_address() + off;
714
715 if (bgra) {
716 source = (uint8_t *)BltBuffer + sy * Delta +
717 SourceX * sizeof (*p);
718 bcopy(source, destination, copybytes);
719 } else {
720 for (x = 0; x < Width; x++) {
721 uint32_t c;
722
723 p = (void *)((uint8_t *)BltBuffer +
724 sy * Delta +
725 (SourceX + x) * sizeof (*p));
726 if (bpp == 1) {
727 c = rgb_to_color_index(p->Red,
728 p->Green, p->Blue);
729 } else {
730 c = (p->Red & rm) << rp |
731 (p->Green & gm) << gp |
732 (p->Blue & bm) << bp;
733 }
734 off = x * bpp;
735 switch (bpp) {
736 case 1:
737 gfx_mem_wr1(destination, copybytes,
738 off, (c < 16) ?
739 cons_to_vga_colors[c] : c);
740 break;
741 case 2:
742 gfx_mem_wr2(destination, copybytes,
743 off, c);
744 break;
745 case 3:
746 gfx_mem_wr1(destination, copybytes,
747 off, (c >> 16) & 0xff);
748 gfx_mem_wr1(destination, copybytes,
749 off + 1, (c >> 8) & 0xff);
750 gfx_mem_wr1(destination, copybytes,
751 off + 2, c & 0xff);
752 break;
753 case 4:
754 gfx_mem_wr4(destination, copybytes,
755 x * bpp, c);
756 break;
757 default:
758 return (EINVAL);
759 }
760 }
761 }
762 }
763
764 return (0);
765 }
766
767 static int
gfxfb_blt_video_to_video(uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)768 gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
769 uint32_t DestinationX, uint32_t DestinationY,
770 uint32_t Width, uint32_t Height)
771 {
772 uint32_t bpp, copybytes;
773 int pitch;
774 uint8_t *source, *destination;
775 off_t off;
776
777 if (SourceY + Height >
778 gfx_state.tg_fb.fb_height)
779 return (EINVAL);
780
781 if (SourceX + Width > gfx_state.tg_fb.fb_width)
782 return (EINVAL);
783
784 if (DestinationY + Height >
785 gfx_state.tg_fb.fb_height)
786 return (EINVAL);
787
788 if (DestinationX + Width > gfx_state.tg_fb.fb_width)
789 return (EINVAL);
790
791 if (Width == 0 || Height == 0)
792 return (EINVAL);
793
794 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
795 pitch = gfx_state.tg_fb.fb_stride * bpp;
796
797 copybytes = Width * bpp;
798
799 off = SourceY * pitch + SourceX * bpp;
800 source = gfx_get_fb_address() + off;
801 off = DestinationY * pitch + DestinationX * bpp;
802 destination = gfx_get_fb_address() + off;
803
804 if ((uintptr_t)destination > (uintptr_t)source) {
805 source += Height * pitch;
806 destination += Height * pitch;
807 pitch = -pitch;
808 }
809
810 while (Height-- > 0) {
811 bcopy(source, destination, copybytes);
812 source += pitch;
813 destination += pitch;
814 }
815
816 return (0);
817 }
818
819 static void
gfxfb_shadow_fill(uint32_t * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)820 gfxfb_shadow_fill(uint32_t *BltBuffer,
821 uint32_t DestinationX, uint32_t DestinationY,
822 uint32_t Width, uint32_t Height)
823 {
824 uint32_t fbX, fbY;
825
826 if (gfx_state.tg_shadow_fb == NULL)
827 return;
828
829 fbX = gfx_state.tg_fb.fb_width;
830 fbY = gfx_state.tg_fb.fb_height;
831
832 if (BltBuffer == NULL)
833 return;
834
835 if (DestinationX + Width > fbX)
836 Width = fbX - DestinationX;
837
838 if (DestinationY + Height > fbY)
839 Height = fbY - DestinationY;
840
841 uint32_t y2 = Height + DestinationY;
842 for (uint32_t y1 = DestinationY; y1 < y2; y1++) {
843 uint32_t off = y1 * fbX + DestinationX;
844
845 for (uint32_t x = 0; x < Width; x++) {
846 gfx_state.tg_shadow_fb[off + x] = *BltBuffer;
847 }
848 }
849 }
850
851 int
gfxfb_blt(void * BltBuffer,GFXFB_BLT_OPERATION BltOperation,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)852 gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
853 uint32_t SourceX, uint32_t SourceY,
854 uint32_t DestinationX, uint32_t DestinationY,
855 uint32_t Width, uint32_t Height, uint32_t Delta)
856 {
857 int rv;
858 #if defined(EFI)
859 EFI_STATUS status;
860 EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
861 EFI_TPL tpl;
862
863 /*
864 * We assume Blt() does work, if not, we will need to build exception
865 * list case by case. We only have boot services during part of our
866 * exectution. Once terminate boot services, these operations cannot be
867 * done as they are provided by protocols that disappear when exit
868 * boot services.
869 */
870 if (gfx_state.tg_fb_type == FB_GOP && !ignore_gop_blt &&
871 boot_services_active) {
872 assert(gfx_state.tg_private != NULL);
873 gop = gfx_state.tg_private;
874 tpl = BS->RaiseTPL(TPL_NOTIFY);
875 switch (BltOperation) {
876 case GfxFbBltVideoFill:
877 gfxfb_shadow_fill(BltBuffer, DestinationX,
878 DestinationY, Width, Height);
879 status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
880 SourceX, SourceY, DestinationX, DestinationY,
881 Width, Height, Delta);
882 break;
883
884 case GfxFbBltVideoToBltBuffer:
885 status = gop->Blt(gop, BltBuffer,
886 EfiBltVideoToBltBuffer,
887 SourceX, SourceY, DestinationX, DestinationY,
888 Width, Height, Delta);
889 break;
890
891 case GfxFbBltBufferToVideo:
892 status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
893 SourceX, SourceY, DestinationX, DestinationY,
894 Width, Height, Delta);
895 break;
896
897 case GfxFbBltVideoToVideo:
898 status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
899 SourceX, SourceY, DestinationX, DestinationY,
900 Width, Height, Delta);
901 break;
902
903 default:
904 status = EFI_INVALID_PARAMETER;
905 break;
906 }
907
908 switch (status) {
909 case EFI_SUCCESS:
910 rv = 0;
911 break;
912
913 case EFI_INVALID_PARAMETER:
914 rv = EINVAL;
915 break;
916
917 case EFI_DEVICE_ERROR:
918 default:
919 rv = EIO;
920 break;
921 }
922
923 BS->RestoreTPL(tpl);
924 return (rv);
925 }
926 #endif
927
928 switch (BltOperation) {
929 case GfxFbBltVideoFill:
930 gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
931 Width, Height);
932 rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
933 Width, Height);
934 break;
935
936 case GfxFbBltVideoToBltBuffer:
937 rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
938 DestinationX, DestinationY, Width, Height, Delta);
939 break;
940
941 case GfxFbBltBufferToVideo:
942 rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
943 DestinationX, DestinationY, Width, Height, Delta);
944 break;
945
946 case GfxFbBltVideoToVideo:
947 rv = gfxfb_blt_video_to_video(SourceX, SourceY,
948 DestinationX, DestinationY, Width, Height);
949 break;
950
951 default:
952 rv = EINVAL;
953 break;
954 }
955 return (rv);
956 }
957
958 void
gfx_bitblt_bitmap(teken_gfx_t * state,const uint8_t * glyph,const teken_attr_t * a,uint32_t alpha,bool cursor)959 gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
960 const teken_attr_t *a, uint32_t alpha, bool cursor)
961 {
962 uint32_t width, height;
963 uint32_t fgc, bgc, bpl, cc, o;
964 int bpp, bit, byte;
965 bool invert = false;
966
967 bpp = 4; /* We only generate BGRA */
968 width = state->tg_font.vf_width;
969 height = state->tg_font.vf_height;
970 bpl = (width + 7) / 8; /* Bytes per source line. */
971
972 fgc = a->ta_fgcolor;
973 bgc = a->ta_bgcolor;
974 if (a->ta_format & TF_BOLD)
975 fgc |= TC_LIGHT;
976 if (a->ta_format & TF_BLINK)
977 bgc |= TC_LIGHT;
978
979 fgc = gfx_fb_color_map(fgc);
980 bgc = gfx_fb_color_map(bgc);
981
982 if (a->ta_format & TF_REVERSE)
983 invert = !invert;
984 if (cursor)
985 invert = !invert;
986 if (invert) {
987 uint32_t tmp;
988
989 tmp = fgc;
990 fgc = bgc;
991 bgc = tmp;
992 }
993
994 alpha = alpha << 24;
995 fgc |= alpha;
996 bgc |= alpha;
997
998 for (uint32_t y = 0; y < height; y++) {
999 for (uint32_t x = 0; x < width; x++) {
1000 byte = y * bpl + x / 8;
1001 bit = 0x80 >> (x % 8);
1002 o = y * width * bpp + x * bpp;
1003 cc = glyph[byte] & bit ? fgc : bgc;
1004
1005 gfx_mem_wr4(state->tg_glyph,
1006 state->tg_glyph_size, o, cc);
1007 }
1008 }
1009 }
1010
1011 /*
1012 * Draw prepared glyph on terminal point p.
1013 */
1014 static void
gfx_fb_printchar(teken_gfx_t * state,const teken_pos_t * p)1015 gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p)
1016 {
1017 unsigned x, y, width, height;
1018
1019 width = state->tg_font.vf_width;
1020 height = state->tg_font.vf_height;
1021 x = state->tg_origin.tp_col + p->tp_col * width;
1022 y = state->tg_origin.tp_row + p->tp_row * height;
1023
1024 gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
1025 }
1026
1027 /*
1028 * Store char with its attribute to buffer and put it on screen.
1029 */
1030 void
gfx_fb_putchar(void * arg,const teken_pos_t * p,teken_char_t c,const teken_attr_t * a)1031 gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
1032 const teken_attr_t *a)
1033 {
1034 teken_gfx_t *state = arg;
1035 const uint8_t *glyph;
1036 int idx;
1037
1038 idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
1039 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
1040 return;
1041
1042 /* remove the cursor */
1043 if (state->tg_cursor_visible)
1044 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1045
1046 screen_buffer[idx].c = c;
1047 screen_buffer[idx].a = *a;
1048
1049 glyph = font_lookup(&state->tg_font, c, a);
1050 gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
1051 gfx_fb_printchar(state, p);
1052
1053 /* display the cursor */
1054 if (state->tg_cursor_visible) {
1055 const teken_pos_t *c;
1056
1057 c = teken_get_cursor(&state->tg_teken);
1058 gfx_fb_cursor_draw(state, c, true);
1059 }
1060 }
1061
1062 void
gfx_fb_fill(void * arg,const teken_rect_t * r,teken_char_t c,const teken_attr_t * a)1063 gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c,
1064 const teken_attr_t *a)
1065 {
1066 teken_gfx_t *state = arg;
1067 const uint8_t *glyph;
1068 teken_pos_t p;
1069 struct text_pixel *row;
1070
1071 TSENTER();
1072
1073 /* remove the cursor */
1074 if (state->tg_cursor_visible)
1075 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1076
1077 glyph = font_lookup(&state->tg_font, c, a);
1078 gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
1079
1080 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
1081 p.tp_row++) {
1082 row = &screen_buffer[p.tp_row * state->tg_tp.tp_col];
1083 for (p.tp_col = r->tr_begin.tp_col;
1084 p.tp_col < r->tr_end.tp_col; p.tp_col++) {
1085 row[p.tp_col].c = c;
1086 row[p.tp_col].a = *a;
1087 gfx_fb_printchar(state, &p);
1088 }
1089 }
1090
1091 /* display the cursor */
1092 if (state->tg_cursor_visible) {
1093 const teken_pos_t *c;
1094
1095 c = teken_get_cursor(&state->tg_teken);
1096 gfx_fb_cursor_draw(state, c, true);
1097 }
1098
1099 TSEXIT();
1100 }
1101
1102 static void
gfx_fb_cursor_draw(teken_gfx_t * state,const teken_pos_t * pos,bool on)1103 gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *pos, bool on)
1104 {
1105 const uint8_t *glyph;
1106 teken_pos_t p;
1107 int idx;
1108
1109 p = *pos;
1110 if (p.tp_col >= state->tg_tp.tp_col)
1111 p.tp_col = state->tg_tp.tp_col - 1;
1112 if (p.tp_row >= state->tg_tp.tp_row)
1113 p.tp_row = state->tg_tp.tp_row - 1;
1114 idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
1115 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
1116 return;
1117
1118 glyph = font_lookup(&state->tg_font, screen_buffer[idx].c,
1119 &screen_buffer[idx].a);
1120 gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on);
1121 gfx_fb_printchar(state, &p);
1122
1123 state->tg_cursor = p;
1124 }
1125
1126 void
gfx_fb_cursor(void * arg,const teken_pos_t * p)1127 gfx_fb_cursor(void *arg, const teken_pos_t *p)
1128 {
1129 teken_gfx_t *state = arg;
1130
1131 /* Switch cursor off in old location and back on in new. */
1132 if (state->tg_cursor_visible) {
1133 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1134 gfx_fb_cursor_draw(state, p, true);
1135 }
1136 }
1137
1138 void
gfx_fb_param(void * arg,int cmd,unsigned int value)1139 gfx_fb_param(void *arg, int cmd, unsigned int value)
1140 {
1141 teken_gfx_t *state = arg;
1142 const teken_pos_t *c;
1143
1144 switch (cmd) {
1145 case TP_SETLOCALCURSOR:
1146 /*
1147 * 0 means normal (usually block), 1 means hidden, and
1148 * 2 means blinking (always block) for compatibility with
1149 * syscons. We don't support any changes except hiding,
1150 * so must map 2 to 0.
1151 */
1152 value = (value == 1) ? 0 : 1;
1153 /* FALLTHROUGH */
1154 case TP_SHOWCURSOR:
1155 c = teken_get_cursor(&state->tg_teken);
1156 gfx_fb_cursor_draw(state, c, true);
1157 if (value != 0)
1158 state->tg_cursor_visible = true;
1159 else
1160 state->tg_cursor_visible = false;
1161 break;
1162 default:
1163 /* Not yet implemented */
1164 break;
1165 }
1166 }
1167
1168 bool
is_same_pixel(struct text_pixel * px1,struct text_pixel * px2)1169 is_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
1170 {
1171 if (px1->c != px2->c)
1172 return (false);
1173
1174 /* Is there image stored? */
1175 if ((px1->a.ta_format & TF_IMAGE) ||
1176 (px2->a.ta_format & TF_IMAGE))
1177 return (false);
1178
1179 if (px1->a.ta_format != px2->a.ta_format)
1180 return (false);
1181 if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
1182 return (false);
1183 if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
1184 return (false);
1185
1186 return (true);
1187 }
1188
1189 static void
gfx_fb_copy_area(teken_gfx_t * state,const teken_rect_t * s,const teken_pos_t * d)1190 gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s,
1191 const teken_pos_t *d)
1192 {
1193 uint32_t sx, sy, dx, dy, width, height;
1194 uint32_t pitch, bytes;
1195 int step;
1196
1197 width = state->tg_font.vf_width;
1198 height = state->tg_font.vf_height;
1199
1200 sx = s->tr_begin.tp_col * width;
1201 sy = s->tr_begin.tp_row * height;
1202 dx = d->tp_col * width;
1203 dy = d->tp_row * height;
1204
1205 width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
1206
1207 /*
1208 * With no shadow fb, use video to video copy.
1209 */
1210 if (state->tg_shadow_fb == NULL) {
1211 (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
1212 sx + state->tg_origin.tp_col,
1213 sy + state->tg_origin.tp_row,
1214 dx + state->tg_origin.tp_col,
1215 dy + state->tg_origin.tp_row,
1216 width, height, 0);
1217 return;
1218 }
1219
1220 /*
1221 * With shadow fb, we need to copy data on both shadow and video,
1222 * to preserve the consistency. We only read data from shadow fb.
1223 */
1224
1225 step = 1;
1226 pitch = state->tg_fb.fb_width;
1227 bytes = width * sizeof (*state->tg_shadow_fb);
1228
1229 /*
1230 * To handle overlapping areas, set up reverse copy here.
1231 */
1232 if (dy * pitch + dx > sy * pitch + sx) {
1233 sy += height;
1234 dy += height;
1235 step = -step;
1236 }
1237
1238 while (height-- > 0) {
1239 uint32_t *source = &state->tg_shadow_fb[sy * pitch + sx];
1240 uint32_t *destination = &state->tg_shadow_fb[dy * pitch + dx];
1241
1242 bcopy(source, destination, bytes);
1243 (void) gfxfb_blt(destination, GfxFbBltBufferToVideo,
1244 0, 0, dx + state->tg_origin.tp_col,
1245 dy + state->tg_origin.tp_row, width, 1, 0);
1246
1247 sy += step;
1248 dy += step;
1249 }
1250 }
1251
1252 static void
gfx_fb_copy_line(teken_gfx_t * state,int ncol,teken_pos_t * s,teken_pos_t * d)1253 gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
1254 {
1255 teken_rect_t sr;
1256 teken_pos_t dp;
1257 unsigned soffset, doffset;
1258 bool mark = false;
1259 int x;
1260
1261 soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
1262 doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
1263
1264 for (x = 0; x < ncol; x++) {
1265 if (is_same_pixel(&screen_buffer[soffset + x],
1266 &screen_buffer[doffset + x])) {
1267 if (mark) {
1268 gfx_fb_copy_area(state, &sr, &dp);
1269 mark = false;
1270 }
1271 } else {
1272 screen_buffer[doffset + x] = screen_buffer[soffset + x];
1273 if (mark) {
1274 /* update end point */
1275 sr.tr_end.tp_col = s->tp_col + x;
1276 } else {
1277 /* set up new rectangle */
1278 mark = true;
1279 sr.tr_begin.tp_col = s->tp_col + x;
1280 sr.tr_begin.tp_row = s->tp_row;
1281 sr.tr_end.tp_col = s->tp_col + x;
1282 sr.tr_end.tp_row = s->tp_row;
1283 dp.tp_col = d->tp_col + x;
1284 dp.tp_row = d->tp_row;
1285 }
1286 }
1287 }
1288 if (mark) {
1289 gfx_fb_copy_area(state, &sr, &dp);
1290 }
1291 }
1292
1293 void
gfx_fb_copy(void * arg,const teken_rect_t * r,const teken_pos_t * p)1294 gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
1295 {
1296 teken_gfx_t *state = arg;
1297 unsigned doffset, soffset;
1298 teken_pos_t d, s;
1299 int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
1300
1301 /*
1302 * Copying is a little tricky. We must make sure we do it in
1303 * correct order, to make sure we don't overwrite our own data.
1304 */
1305
1306 nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
1307 ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
1308
1309 if (p->tp_row + nrow > state->tg_tp.tp_row ||
1310 p->tp_col + ncol > state->tg_tp.tp_col)
1311 return;
1312
1313 soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
1314 doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
1315
1316 /* remove the cursor */
1317 if (state->tg_cursor_visible)
1318 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1319
1320 /*
1321 * Copy line by line.
1322 */
1323 if (doffset <= soffset) {
1324 s = r->tr_begin;
1325 d = *p;
1326 for (y = 0; y < nrow; y++) {
1327 s.tp_row = r->tr_begin.tp_row + y;
1328 d.tp_row = p->tp_row + y;
1329
1330 gfx_fb_copy_line(state, ncol, &s, &d);
1331 }
1332 } else {
1333 for (y = nrow - 1; y >= 0; y--) {
1334 s.tp_row = r->tr_begin.tp_row + y;
1335 d.tp_row = p->tp_row + y;
1336
1337 gfx_fb_copy_line(state, ncol, &s, &d);
1338 }
1339 }
1340
1341 /* display the cursor */
1342 if (state->tg_cursor_visible) {
1343 const teken_pos_t *c;
1344
1345 c = teken_get_cursor(&state->tg_teken);
1346 gfx_fb_cursor_draw(state, c, true);
1347 }
1348 }
1349
1350 /*
1351 * Implements alpha blending for RGBA data, could use pixels for arguments,
1352 * but byte stream seems more generic.
1353 * The generic alpha blending is:
1354 * blend = alpha * fg + (1.0 - alpha) * bg.
1355 * Since our alpha is not from range [0..1], we scale appropriately.
1356 */
1357 static uint8_t
alpha_blend(uint8_t fg,uint8_t bg,uint8_t alpha)1358 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
1359 {
1360 uint16_t blend, h, l;
1361
1362 /* trivial corner cases */
1363 if (alpha == 0)
1364 return (bg);
1365 if (alpha == 0xFF)
1366 return (fg);
1367 blend = (alpha * fg + (0xFF - alpha) * bg);
1368 /* Division by 0xFF */
1369 h = blend >> 8;
1370 l = blend & 0xFF;
1371 if (h + l >= 0xFF)
1372 h++;
1373 return (h);
1374 }
1375
1376 /*
1377 * Implements alpha blending for RGBA data, could use pixels for arguments,
1378 * but byte stream seems more generic.
1379 * The generic alpha blending is:
1380 * blend = alpha * fg + (1.0 - alpha) * bg.
1381 * Since our alpha is not from range [0..1], we scale appropriately.
1382 */
1383 static void
bitmap_cpy(void * dst,void * src,uint32_t size)1384 bitmap_cpy(void *dst, void *src, uint32_t size)
1385 {
1386 #if defined(EFI)
1387 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
1388 #else
1389 struct paletteentry *ps, *pd;
1390 #endif
1391 uint32_t i;
1392 uint8_t a;
1393
1394 ps = src;
1395 pd = dst;
1396
1397 /*
1398 * we only implement alpha blending for depth 32.
1399 */
1400 for (i = 0; i < size; i ++) {
1401 a = ps[i].Reserved;
1402 pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
1403 pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
1404 pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
1405 pd[i].Reserved = a;
1406 }
1407 }
1408
1409 static void *
allocate_glyphbuffer(uint32_t width,uint32_t height)1410 allocate_glyphbuffer(uint32_t width, uint32_t height)
1411 {
1412 size_t size;
1413
1414 size = sizeof (*GlyphBuffer) * width * height;
1415 if (size != GlyphBufferSize) {
1416 free(GlyphBuffer);
1417 GlyphBuffer = malloc(size);
1418 if (GlyphBuffer == NULL)
1419 return (NULL);
1420 GlyphBufferSize = size;
1421 }
1422 return (GlyphBuffer);
1423 }
1424
1425 void
gfx_fb_cons_display(uint32_t x,uint32_t y,uint32_t width,uint32_t height,void * data)1426 gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
1427 void *data)
1428 {
1429 #if defined(EFI)
1430 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, *p;
1431 #else
1432 struct paletteentry *buf, *p;
1433 #endif
1434 size_t size;
1435
1436 /*
1437 * If we do have shadow fb, we will use shadow to render data,
1438 * and copy shadow to video.
1439 */
1440 if (gfx_state.tg_shadow_fb != NULL) {
1441 uint32_t pitch = gfx_state.tg_fb.fb_width;
1442
1443 /* Copy rectangle line by line. */
1444 p = data;
1445 for (uint32_t sy = 0; sy < height; sy++) {
1446 buf = (void *)(gfx_state.tg_shadow_fb +
1447 (y - gfx_state.tg_origin.tp_row) * pitch +
1448 x - gfx_state.tg_origin.tp_col);
1449 bitmap_cpy(buf, &p[sy * width], width);
1450 (void) gfxfb_blt(buf, GfxFbBltBufferToVideo,
1451 0, 0, x, y, width, 1, 0);
1452 y++;
1453 }
1454 return;
1455 }
1456
1457 /*
1458 * Common data to display is glyph, use preallocated
1459 * glyph buffer.
1460 */
1461 if (gfx_state.tg_glyph_size != GlyphBufferSize)
1462 (void) allocate_glyphbuffer(width, height);
1463
1464 size = width * height * sizeof(*buf);
1465 if (size == GlyphBufferSize)
1466 buf = GlyphBuffer;
1467 else
1468 buf = malloc(size);
1469 if (buf == NULL)
1470 return;
1471
1472 if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0,
1473 width, height, 0) == 0) {
1474 bitmap_cpy(buf, data, width * height);
1475 (void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y,
1476 width, height, 0);
1477 }
1478 if (buf != GlyphBuffer)
1479 free(buf);
1480 }
1481
1482 /*
1483 * Public graphics primitives.
1484 */
1485
1486 static int
isqrt(int num)1487 isqrt(int num)
1488 {
1489 int res = 0;
1490 int bit = 1 << 30;
1491
1492 /* "bit" starts at the highest power of four <= the argument. */
1493 while (bit > num)
1494 bit >>= 2;
1495
1496 while (bit != 0) {
1497 if (num >= res + bit) {
1498 num -= res + bit;
1499 res = (res >> 1) + bit;
1500 } else {
1501 res >>= 1;
1502 }
1503 bit >>= 2;
1504 }
1505 return (res);
1506 }
1507
1508 static uint32_t
gfx_fb_getcolor(void)1509 gfx_fb_getcolor(void)
1510 {
1511 uint32_t c;
1512 const teken_attr_t *ap;
1513
1514 ap = teken_get_curattr(&gfx_state.tg_teken);
1515 if (ap->ta_format & TF_REVERSE) {
1516 c = ap->ta_bgcolor;
1517 if (ap->ta_format & TF_BLINK)
1518 c |= TC_LIGHT;
1519 } else {
1520 c = ap->ta_fgcolor;
1521 if (ap->ta_format & TF_BOLD)
1522 c |= TC_LIGHT;
1523 }
1524
1525 return (gfx_fb_color_map(c));
1526 }
1527
1528 /* set pixel in framebuffer using gfx coordinates */
1529 void
gfx_fb_setpixel(uint32_t x,uint32_t y)1530 gfx_fb_setpixel(uint32_t x, uint32_t y)
1531 {
1532 uint32_t c;
1533
1534 if (gfx_state.tg_fb_type == FB_TEXT)
1535 return;
1536
1537 c = gfx_fb_getcolor();
1538
1539 if (x >= gfx_state.tg_fb.fb_width ||
1540 y >= gfx_state.tg_fb.fb_height)
1541 return;
1542
1543 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
1544 }
1545
1546 /*
1547 * draw rectangle in framebuffer using gfx coordinates.
1548 */
1549 void
gfx_fb_drawrect(uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t fill)1550 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1551 uint32_t fill)
1552 {
1553 uint32_t c;
1554
1555 if (gfx_state.tg_fb_type == FB_TEXT)
1556 return;
1557
1558 c = gfx_fb_getcolor();
1559
1560 if (fill != 0) {
1561 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1,
1562 y2 - y1, 0);
1563 } else {
1564 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1, 0);
1565 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y2, x2 - x1, 1, 0);
1566 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0);
1567 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0);
1568 }
1569 }
1570
1571 void
gfx_fb_line(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t wd)1572 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
1573 {
1574 int dx, sx, dy, sy;
1575 int err, e2, x2, y2, ed, width;
1576
1577 if (gfx_state.tg_fb_type == FB_TEXT)
1578 return;
1579
1580 width = wd;
1581 sx = x0 < x1? 1 : -1;
1582 sy = y0 < y1? 1 : -1;
1583 dx = x1 > x0? x1 - x0 : x0 - x1;
1584 dy = y1 > y0? y1 - y0 : y0 - y1;
1585 err = dx + dy;
1586 ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1587
1588 for (;;) {
1589 gfx_fb_setpixel(x0, y0);
1590 e2 = err;
1591 x2 = x0;
1592 if ((e2 << 1) >= -dx) { /* x step */
1593 e2 += dy;
1594 y2 = y0;
1595 while (e2 < ed * width &&
1596 (y1 != (uint32_t)y2 || dx > dy)) {
1597 y2 += sy;
1598 gfx_fb_setpixel(x0, y2);
1599 e2 += dx;
1600 }
1601 if (x0 == x1)
1602 break;
1603 e2 = err;
1604 err -= dy;
1605 x0 += sx;
1606 }
1607 if ((e2 << 1) <= dy) { /* y step */
1608 e2 = dx-e2;
1609 while (e2 < ed * width &&
1610 (x1 != (uint32_t)x2 || dx < dy)) {
1611 x2 += sx;
1612 gfx_fb_setpixel(x2, y0);
1613 e2 += dy;
1614 }
1615 if (y0 == y1)
1616 break;
1617 err += dx;
1618 y0 += sy;
1619 }
1620 }
1621 }
1622
1623 /*
1624 * quadratic Bézier curve limited to gradients without sign change.
1625 */
1626 void
gfx_fb_bezier(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t wd)1627 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1628 uint32_t y2, uint32_t wd)
1629 {
1630 int sx, sy, xx, yy, xy, width;
1631 int dx, dy, err, curvature;
1632 int i;
1633
1634 if (gfx_state.tg_fb_type == FB_TEXT)
1635 return;
1636
1637 width = wd;
1638 sx = x2 - x1;
1639 sy = y2 - y1;
1640 xx = x0 - x1;
1641 yy = y0 - y1;
1642 curvature = xx*sy - yy*sx;
1643
1644 if (sx*sx + sy*sy > xx*xx+yy*yy) {
1645 x2 = x0;
1646 x0 = sx + x1;
1647 y2 = y0;
1648 y0 = sy + y1;
1649 curvature = -curvature;
1650 }
1651 if (curvature != 0) {
1652 xx += sx;
1653 sx = x0 < x2? 1 : -1;
1654 xx *= sx;
1655 yy += sy;
1656 sy = y0 < y2? 1 : -1;
1657 yy *= sy;
1658 xy = (xx*yy) << 1;
1659 xx *= xx;
1660 yy *= yy;
1661 if (curvature * sx * sy < 0) {
1662 xx = -xx;
1663 yy = -yy;
1664 xy = -xy;
1665 curvature = -curvature;
1666 }
1667 dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1668 dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1669 xx += xx;
1670 yy += yy;
1671 err = dx + dy + xy;
1672 do {
1673 for (i = 0; i <= width; i++)
1674 gfx_fb_setpixel(x0 + i, y0);
1675 if (x0 == x2 && y0 == y2)
1676 return; /* last pixel -> curve finished */
1677 y1 = 2 * err < dx;
1678 if (2 * err > dy) {
1679 x0 += sx;
1680 dx -= xy;
1681 dy += yy;
1682 err += dy;
1683 }
1684 if (y1 != 0) {
1685 y0 += sy;
1686 dy -= xy;
1687 dx += xx;
1688 err += dx;
1689 }
1690 } while (dy < dx); /* gradient negates -> algorithm fails */
1691 }
1692 gfx_fb_line(x0, y0, x2, y2, width);
1693 }
1694
1695 /*
1696 * draw rectangle using terminal coordinates and current foreground color.
1697 */
1698 void
gfx_term_drawrect(uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2)1699 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1700 {
1701 int x1, y1, x2, y2;
1702 int xshift, yshift;
1703 int width, i;
1704 uint32_t vf_width, vf_height;
1705 teken_rect_t r;
1706
1707 if (gfx_state.tg_fb_type == FB_TEXT)
1708 return;
1709
1710 vf_width = gfx_state.tg_font.vf_width;
1711 vf_height = gfx_state.tg_font.vf_height;
1712 width = vf_width / 4; /* line width */
1713 xshift = (vf_width - width) / 2;
1714 yshift = (vf_height - width) / 2;
1715
1716 /* Shift coordinates */
1717 if (ux1 != 0)
1718 ux1--;
1719 if (uy1 != 0)
1720 uy1--;
1721 ux2--;
1722 uy2--;
1723
1724 /* mark area used in terminal */
1725 r.tr_begin.tp_col = ux1;
1726 r.tr_begin.tp_row = uy1;
1727 r.tr_end.tp_col = ux2 + 1;
1728 r.tr_end.tp_row = uy2 + 1;
1729
1730 term_image_display(&gfx_state, &r);
1731
1732 /*
1733 * Draw horizontal lines width points thick, shifted from outer edge.
1734 */
1735 x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
1736 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1737 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1738 gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1739 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1740 y2 += vf_height - yshift - width;
1741 gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1742
1743 /*
1744 * Draw vertical lines width points thick, shifted from outer edge.
1745 */
1746 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1747 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1748 y1 += vf_height;
1749 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1750 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1751 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1752 x1 += vf_width - xshift - width;
1753 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1754
1755 /* Draw upper left corner. */
1756 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1757 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1758 y1 += vf_height;
1759
1760 x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1761 x2 += vf_width;
1762 y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1763 for (i = 0; i <= width; i++)
1764 gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1765
1766 /* Draw lower left corner. */
1767 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1768 x1 += vf_width;
1769 y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1770 y1 += vf_height - yshift;
1771 x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1772 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1773 for (i = 0; i <= width; i++)
1774 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1775
1776 /* Draw upper right corner. */
1777 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1778 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1779 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1780 x2 += vf_width - xshift - width;
1781 y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1782 y2 += vf_height;
1783 for (i = 0; i <= width; i++)
1784 gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1785
1786 /* Draw lower right corner. */
1787 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1788 y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1789 y1 += vf_height - yshift;
1790 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1791 x2 += vf_width - xshift - width;
1792 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1793 for (i = 0; i <= width; i++)
1794 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1795 }
1796
1797 int
gfx_fb_putimage(png_t * png,uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2,uint32_t flags)1798 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1799 uint32_t uy2, uint32_t flags)
1800 {
1801 #if defined(EFI)
1802 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1803 #else
1804 struct paletteentry *p;
1805 #endif
1806 uint8_t *data;
1807 uint32_t i, j, x, y, fheight, fwidth;
1808 int rs, gs, bs;
1809 uint8_t r, g, b, a;
1810 bool scale = false;
1811 bool trace = false;
1812 teken_rect_t rect;
1813
1814 trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1815
1816 if (gfx_state.tg_fb_type == FB_TEXT) {
1817 if (trace)
1818 printf("Framebuffer not active.\n");
1819 return (1);
1820 }
1821
1822 if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1823 if (trace)
1824 printf("Not truecolor image.\n");
1825 return (1);
1826 }
1827
1828 if (ux1 > gfx_state.tg_fb.fb_width ||
1829 uy1 > gfx_state.tg_fb.fb_height) {
1830 if (trace)
1831 printf("Top left coordinate off screen.\n");
1832 return (1);
1833 }
1834
1835 if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1836 if (trace)
1837 printf("Image too large.\n");
1838 return (1);
1839 }
1840
1841 if (png->width < 1 || png->height < 1) {
1842 if (trace)
1843 printf("Image too small.\n");
1844 return (1);
1845 }
1846
1847 /*
1848 * If 0 was passed for either ux2 or uy2, then calculate the missing
1849 * part of the bottom right coordinate.
1850 */
1851 scale = true;
1852 if (ux2 == 0 && uy2 == 0) {
1853 /* Both 0, use the native resolution of the image */
1854 ux2 = ux1 + png->width;
1855 uy2 = uy1 + png->height;
1856 scale = false;
1857 } else if (ux2 == 0) {
1858 /* Set ux2 from uy2/uy1 to maintain aspect ratio */
1859 ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1860 } else if (uy2 == 0) {
1861 /* Set uy2 from ux2/ux1 to maintain aspect ratio */
1862 uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1863 }
1864
1865 if (ux2 > gfx_state.tg_fb.fb_width ||
1866 uy2 > gfx_state.tg_fb.fb_height) {
1867 if (trace)
1868 printf("Bottom right coordinate off screen.\n");
1869 return (1);
1870 }
1871
1872 fwidth = ux2 - ux1;
1873 fheight = uy2 - uy1;
1874
1875 /*
1876 * If the original image dimensions have been passed explicitly,
1877 * disable scaling.
1878 */
1879 if (fwidth == png->width && fheight == png->height)
1880 scale = false;
1881
1882 if (ux1 == 0) {
1883 /*
1884 * No top left X co-ordinate (real coordinates start at 1),
1885 * place as far right as it will fit.
1886 */
1887 ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
1888 ux1 = ux2 - fwidth;
1889 }
1890
1891 if (uy1 == 0) {
1892 /*
1893 * No top left Y co-ordinate (real coordinates start at 1),
1894 * place as far down as it will fit.
1895 */
1896 uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
1897 uy1 = uy2 - fheight;
1898 }
1899
1900 if (ux1 >= ux2 || uy1 >= uy2) {
1901 if (trace)
1902 printf("Image dimensions reversed.\n");
1903 return (1);
1904 }
1905
1906 if (fwidth < 2 || fheight < 2) {
1907 if (trace)
1908 printf("Target area too small\n");
1909 return (1);
1910 }
1911
1912 if (trace)
1913 printf("Image %ux%u -> %ux%u @%ux%u\n",
1914 png->width, png->height, fwidth, fheight, ux1, uy1);
1915
1916 rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
1917 rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
1918 rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
1919 rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
1920
1921 /*
1922 * mark area used in terminal
1923 */
1924 if (!(flags & FL_PUTIMAGE_NOSCROLL))
1925 term_image_display(&gfx_state, &rect);
1926
1927 if ((flags & FL_PUTIMAGE_BORDER))
1928 gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1929
1930 data = malloc(fwidth * fheight * sizeof(*p));
1931 p = (void *)data;
1932 if (data == NULL) {
1933 if (trace)
1934 printf("Out of memory.\n");
1935 return (1);
1936 }
1937
1938 /*
1939 * Build image for our framebuffer.
1940 */
1941
1942 /* Helper to calculate the pixel index from the source png */
1943 #define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1944
1945 /*
1946 * For each of the x and y directions, calculate the number of pixels
1947 * in the source image that correspond to a single pixel in the target.
1948 * Use fixed-point arithmetic with 16-bits for each of the integer and
1949 * fractional parts.
1950 */
1951 const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1952 const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1953
1954 rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) -
1955 ffs(gfx_state.tg_fb.fb_mask_red) + 1);
1956 gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) -
1957 ffs(gfx_state.tg_fb.fb_mask_green) + 1);
1958 bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) -
1959 ffs(gfx_state.tg_fb.fb_mask_blue) + 1);
1960
1961 uint32_t hc = 0;
1962 for (y = 0; y < fheight; y++) {
1963 uint32_t hc2 = (hc >> 9) & 0x7f;
1964 uint32_t hc1 = 0x80 - hc2;
1965
1966 uint32_t offset_y = hc >> 16;
1967 uint32_t offset_y1 = offset_y + 1;
1968
1969 uint32_t wc = 0;
1970 for (x = 0; x < fwidth; x++) {
1971 uint32_t wc2 = (wc >> 9) & 0x7f;
1972 uint32_t wc1 = 0x80 - wc2;
1973
1974 uint32_t offset_x = wc >> 16;
1975 uint32_t offset_x1 = offset_x + 1;
1976
1977 /* Target pixel index */
1978 j = y * fwidth + x;
1979
1980 if (!scale) {
1981 i = GETPIXEL(x, y);
1982 r = png->image[i];
1983 g = png->image[i + 1];
1984 b = png->image[i + 2];
1985 a = png->image[i + 3];
1986 } else {
1987 uint8_t pixel[4];
1988
1989 uint32_t p00 = GETPIXEL(offset_x, offset_y);
1990 uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1991 uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1992 uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1993
1994 /*
1995 * Given a 2x2 array of pixels in the source
1996 * image, combine them to produce a single
1997 * value for the pixel in the target image.
1998 * Each column of pixels is combined using
1999 * a weighted average where the top and bottom
2000 * pixels contribute hc1 and hc2 respectively.
2001 * The calculation for bottom pixel pB and
2002 * top pixel pT is:
2003 * (pT * hc1 + pB * hc2) / (hc1 + hc2)
2004 * Once the values are determined for the two
2005 * columns of pixels, then the columns are
2006 * averaged together in the same way but using
2007 * wc1 and wc2 for the weightings.
2008 *
2009 * Since hc1 and hc2 are chosen so that
2010 * hc1 + hc2 == 128 (and same for wc1 + wc2),
2011 * the >> 14 below is a quick way to divide by
2012 * (hc1 + hc2) * (wc1 + wc2)
2013 */
2014 for (i = 0; i < 4; i++)
2015 pixel[i] = (
2016 (png->image[p00 + i] * hc1 +
2017 png->image[p01 + i] * hc2) * wc1 +
2018 (png->image[p10 + i] * hc1 +
2019 png->image[p11 + i] * hc2) * wc2)
2020 >> 14;
2021
2022 r = pixel[0];
2023 g = pixel[1];
2024 b = pixel[2];
2025 a = pixel[3];
2026 }
2027
2028 if (trace)
2029 printf("r/g/b: %x/%x/%x\n", r, g, b);
2030 /*
2031 * Rough colorspace reduction for 15/16 bit colors.
2032 */
2033 p[j].Red = r >> rs;
2034 p[j].Green = g >> gs;
2035 p[j].Blue = b >> bs;
2036 p[j].Reserved = a;
2037
2038 wc += wcstep;
2039 }
2040 hc += hcstep;
2041 }
2042
2043 gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
2044 free(data);
2045 return (0);
2046 }
2047
2048 /*
2049 * Reset font flags to FONT_AUTO.
2050 */
2051 void
reset_font_flags(void)2052 reset_font_flags(void)
2053 {
2054 struct fontlist *fl;
2055
2056 STAILQ_FOREACH(fl, &fonts, font_next) {
2057 fl->font_flags = FONT_AUTO;
2058 }
2059 }
2060
2061 /* Return w^2 + h^2 or 0, if the dimensions are unknown */
2062 static unsigned
edid_diagonal_squared(void)2063 edid_diagonal_squared(void)
2064 {
2065 unsigned w, h;
2066
2067 if (edid_info == NULL)
2068 return (0);
2069
2070 w = edid_info->display.max_horizontal_image_size;
2071 h = edid_info->display.max_vertical_image_size;
2072
2073 /* If either one is 0, we have aspect ratio, not size */
2074 if (w == 0 || h == 0)
2075 return (0);
2076
2077 /*
2078 * some monitors encode the aspect ratio instead of the physical size.
2079 */
2080 if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
2081 (w == 4 && h == 3) || (w == 5 && h == 4))
2082 return (0);
2083
2084 /*
2085 * translate cm to inch, note we scale by 100 here.
2086 */
2087 w = w * 100 / 254;
2088 h = h * 100 / 254;
2089
2090 /* Return w^2 + h^2 */
2091 return (w * w + h * h);
2092 }
2093
2094 /*
2095 * calculate pixels per inch.
2096 */
2097 static unsigned
gfx_get_ppi(void)2098 gfx_get_ppi(void)
2099 {
2100 unsigned dp, di;
2101
2102 di = edid_diagonal_squared();
2103 if (di == 0)
2104 return (0);
2105
2106 dp = gfx_state.tg_fb.fb_width *
2107 gfx_state.tg_fb.fb_width +
2108 gfx_state.tg_fb.fb_height *
2109 gfx_state.tg_fb.fb_height;
2110
2111 return (isqrt(dp / di));
2112 }
2113
2114 /*
2115 * Calculate font size from density independent pixels (dp):
2116 * ((16dp * ppi) / 160) * display_factor.
2117 * Here we are using fixed constants: 1dp == 160 ppi and
2118 * display_factor 2.
2119 *
2120 * We are rounding font size up and are searching for font which is
2121 * not smaller than calculated size value.
2122 */
2123 static vt_font_bitmap_data_t *
gfx_get_font(teken_unit_t rows,teken_unit_t cols,teken_unit_t height,teken_unit_t width)2124 gfx_get_font(teken_unit_t rows, teken_unit_t cols, teken_unit_t height,
2125 teken_unit_t width)
2126 {
2127 unsigned ppi, size;
2128 vt_font_bitmap_data_t *font = NULL;
2129 struct fontlist *fl, *next;
2130
2131 /* Text mode is not supported here. */
2132 if (gfx_state.tg_fb_type == FB_TEXT)
2133 return (NULL);
2134
2135 ppi = gfx_get_ppi();
2136 if (ppi == 0)
2137 return (NULL);
2138
2139 /*
2140 * We will search for 16dp font.
2141 * We are using scale up by 10 for roundup.
2142 */
2143 size = (16 * ppi * 10) / 160;
2144 /* Apply display factor 2. */
2145 size = roundup(size * 2, 10) / 10;
2146
2147 STAILQ_FOREACH(fl, &fonts, font_next) {
2148 /*
2149 * Skip too large fonts.
2150 */
2151 font = fl->font_data;
2152 if (height / font->vfbd_height < rows ||
2153 width / font->vfbd_width < cols)
2154 continue;
2155
2156 next = STAILQ_NEXT(fl, font_next);
2157
2158 /*
2159 * If this is last font or, if next font is smaller,
2160 * we have our font. Make sure, it actually is loaded.
2161 */
2162 if (next == NULL || next->font_data->vfbd_height < size) {
2163 if (font->vfbd_font == NULL ||
2164 fl->font_flags == FONT_RELOAD) {
2165 if (fl->font_load != NULL &&
2166 fl->font_name != NULL)
2167 font = fl->font_load(fl->font_name);
2168 }
2169 break;
2170 }
2171 font = NULL;
2172 }
2173
2174 return (font);
2175 }
2176
2177 static vt_font_bitmap_data_t *
set_font(teken_unit_t * rows,teken_unit_t * cols,teken_unit_t h,teken_unit_t w)2178 set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
2179 {
2180 vt_font_bitmap_data_t *font = NULL;
2181 struct fontlist *fl;
2182 unsigned height = h;
2183 unsigned width = w;
2184
2185 /*
2186 * First check for manually loaded font.
2187 */
2188 STAILQ_FOREACH(fl, &fonts, font_next) {
2189 if (fl->font_flags == FONT_MANUAL) {
2190 font = fl->font_data;
2191 if (font->vfbd_font == NULL && fl->font_load != NULL &&
2192 fl->font_name != NULL) {
2193 font = fl->font_load(fl->font_name);
2194 }
2195 if (font == NULL || font->vfbd_font == NULL)
2196 font = NULL;
2197 break;
2198 }
2199 }
2200
2201 if (font == NULL)
2202 font = gfx_get_font(*rows, *cols, h, w);
2203
2204 if (font != NULL) {
2205 *rows = height / font->vfbd_height;
2206 *cols = width / font->vfbd_width;
2207 return (font);
2208 }
2209
2210 /*
2211 * Find best font for these dimensions, or use default.
2212 * If height >= VT_FB_MAX_HEIGHT and width >= VT_FB_MAX_WIDTH,
2213 * do not use smaller font than our DEFAULT_FONT_DATA.
2214 */
2215 STAILQ_FOREACH(fl, &fonts, font_next) {
2216 font = fl->font_data;
2217 if ((*rows * font->vfbd_height <= height &&
2218 *cols * font->vfbd_width <= width) ||
2219 (height >= VT_FB_MAX_HEIGHT &&
2220 width >= VT_FB_MAX_WIDTH &&
2221 font->vfbd_height == DEFAULT_FONT_DATA.vfbd_height &&
2222 font->vfbd_width == DEFAULT_FONT_DATA.vfbd_width)) {
2223 if (font->vfbd_font == NULL ||
2224 fl->font_flags == FONT_RELOAD) {
2225 if (fl->font_load != NULL &&
2226 fl->font_name != NULL) {
2227 font = fl->font_load(fl->font_name);
2228 }
2229 if (font == NULL)
2230 continue;
2231 }
2232 *rows = height / font->vfbd_height;
2233 *cols = width / font->vfbd_width;
2234 break;
2235 }
2236 font = NULL;
2237 }
2238
2239 if (font == NULL) {
2240 /*
2241 * We have fonts sorted smallest last, try it before
2242 * falling back to builtin.
2243 */
2244 fl = STAILQ_LAST(&fonts, fontlist, font_next);
2245 if (fl != NULL && fl->font_load != NULL &&
2246 fl->font_name != NULL) {
2247 font = fl->font_load(fl->font_name);
2248 }
2249 if (font == NULL)
2250 font = &DEFAULT_FONT_DATA;
2251
2252 *rows = height / font->vfbd_height;
2253 *cols = width / font->vfbd_width;
2254 }
2255
2256 return (font);
2257 }
2258
2259 static void
cons_clear(void)2260 cons_clear(void)
2261 {
2262 char clear[] = { '\033', 'c' };
2263
2264 /* Reset terminal */
2265 teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
2266 gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
2267 }
2268
2269 void
setup_font(teken_gfx_t * state,teken_unit_t height,teken_unit_t width)2270 setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
2271 {
2272 vt_font_bitmap_data_t *font_data;
2273 teken_pos_t *tp = &state->tg_tp;
2274 char env[8];
2275 int i;
2276
2277 /*
2278 * set_font() will select a appropriate sized font for
2279 * the number of rows and columns selected. If we don't
2280 * have a font that will fit, then it will use the
2281 * default builtin font and adjust the rows and columns
2282 * to fit on the screen.
2283 */
2284 font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
2285
2286 if (font_data == NULL)
2287 panic("out of memory");
2288
2289 for (i = 0; i < VFNT_MAPS; i++) {
2290 state->tg_font.vf_map[i] =
2291 font_data->vfbd_font->vf_map[i];
2292 state->tg_font.vf_map_count[i] =
2293 font_data->vfbd_font->vf_map_count[i];
2294 }
2295
2296 state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
2297 state->tg_font.vf_height = font_data->vfbd_font->vf_height;
2298 state->tg_font.vf_width = font_data->vfbd_font->vf_width;
2299
2300 snprintf(env, sizeof (env), "%ux%u",
2301 state->tg_font.vf_width, state->tg_font.vf_height);
2302 env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
2303 env, font_set, env_nounset);
2304 }
2305
2306 /* Binary search for the glyph. Return 0 if not found. */
2307 static uint16_t
font_bisearch(const vfnt_map_t * map,uint32_t len,teken_char_t src)2308 font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
2309 {
2310 unsigned min, mid, max;
2311
2312 min = 0;
2313 max = len - 1;
2314
2315 /* Empty font map. */
2316 if (len == 0)
2317 return (0);
2318 /* Character below minimal entry. */
2319 if (src < map[0].vfm_src)
2320 return (0);
2321 /* Optimization: ASCII characters occur very often. */
2322 if (src <= map[0].vfm_src + map[0].vfm_len)
2323 return (src - map[0].vfm_src + map[0].vfm_dst);
2324 /* Character above maximum entry. */
2325 if (src > map[max].vfm_src + map[max].vfm_len)
2326 return (0);
2327
2328 /* Binary search. */
2329 while (max >= min) {
2330 mid = (min + max) / 2;
2331 if (src < map[mid].vfm_src)
2332 max = mid - 1;
2333 else if (src > map[mid].vfm_src + map[mid].vfm_len)
2334 min = mid + 1;
2335 else
2336 return (src - map[mid].vfm_src + map[mid].vfm_dst);
2337 }
2338
2339 return (0);
2340 }
2341
2342 /*
2343 * Return glyph bitmap. If glyph is not found, we will return bitmap
2344 * for the first (offset 0) glyph.
2345 */
2346 uint8_t *
font_lookup(const struct vt_font * vf,teken_char_t c,const teken_attr_t * a)2347 font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
2348 {
2349 uint16_t dst;
2350 size_t stride;
2351
2352 /* Substitute bold with normal if not found. */
2353 if (a->ta_format & TF_BOLD) {
2354 dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
2355 vf->vf_map_count[VFNT_MAP_BOLD], c);
2356 if (dst != 0)
2357 goto found;
2358 }
2359 dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
2360 vf->vf_map_count[VFNT_MAP_NORMAL], c);
2361
2362 found:
2363 stride = howmany(vf->vf_width, 8) * vf->vf_height;
2364 return (&vf->vf_bytes[dst * stride]);
2365 }
2366
2367 static int
load_mapping(int fd,struct vt_font * fp,int n)2368 load_mapping(int fd, struct vt_font *fp, int n)
2369 {
2370 size_t i, size;
2371 ssize_t rv;
2372 vfnt_map_t *mp;
2373
2374 if (fp->vf_map_count[n] == 0)
2375 return (0);
2376
2377 size = fp->vf_map_count[n] * sizeof(*mp);
2378 mp = malloc(size);
2379 if (mp == NULL)
2380 return (ENOMEM);
2381 fp->vf_map[n] = mp;
2382
2383 rv = read(fd, mp, size);
2384 if (rv < 0 || (size_t)rv != size) {
2385 free(fp->vf_map[n]);
2386 fp->vf_map[n] = NULL;
2387 return (EIO);
2388 }
2389
2390 for (i = 0; i < fp->vf_map_count[n]; i++) {
2391 mp[i].vfm_src = be32toh(mp[i].vfm_src);
2392 mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
2393 mp[i].vfm_len = be16toh(mp[i].vfm_len);
2394 }
2395 return (0);
2396 }
2397
2398 static int
builtin_mapping(struct vt_font * fp,int n)2399 builtin_mapping(struct vt_font *fp, int n)
2400 {
2401 size_t size;
2402 struct vfnt_map *mp;
2403
2404 if (n >= VFNT_MAPS)
2405 return (EINVAL);
2406
2407 if (fp->vf_map_count[n] == 0)
2408 return (0);
2409
2410 size = fp->vf_map_count[n] * sizeof(*mp);
2411 mp = malloc(size);
2412 if (mp == NULL)
2413 return (ENOMEM);
2414 fp->vf_map[n] = mp;
2415
2416 memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
2417 return (0);
2418 }
2419
2420 /*
2421 * Load font from builtin or from file.
2422 * We do need special case for builtin because the builtin font glyphs
2423 * are compressed and we do need to uncompress them.
2424 * Having single load_font() for both cases will help us to simplify
2425 * font switch handling.
2426 */
2427 static vt_font_bitmap_data_t *
load_font(char * path)2428 load_font(char *path)
2429 {
2430 int fd, i;
2431 uint32_t glyphs;
2432 struct font_header fh;
2433 struct fontlist *fl;
2434 vt_font_bitmap_data_t *bp;
2435 struct vt_font *fp;
2436 size_t size;
2437 ssize_t rv;
2438
2439 /* Get our entry from the font list. */
2440 STAILQ_FOREACH(fl, &fonts, font_next) {
2441 if (strcmp(fl->font_name, path) == 0)
2442 break;
2443 }
2444 if (fl == NULL)
2445 return (NULL); /* Should not happen. */
2446
2447 bp = fl->font_data;
2448 if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
2449 return (bp);
2450
2451 fd = -1;
2452 /*
2453 * Special case for builtin font.
2454 * Builtin font is the very first font we load, we do not have
2455 * previous loads to be released.
2456 */
2457 if (fl->font_flags == FONT_BUILTIN) {
2458 if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
2459 return (NULL);
2460
2461 fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
2462 fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
2463
2464 fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
2465 if (fp->vf_bytes == NULL) {
2466 free(fp);
2467 return (NULL);
2468 }
2469
2470 bp->vfbd_uncompressed_size =
2471 DEFAULT_FONT_DATA.vfbd_uncompressed_size;
2472 bp->vfbd_compressed_size =
2473 DEFAULT_FONT_DATA.vfbd_compressed_size;
2474
2475 if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
2476 fp->vf_bytes,
2477 DEFAULT_FONT_DATA.vfbd_compressed_size,
2478 DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
2479 free(fp->vf_bytes);
2480 free(fp);
2481 return (NULL);
2482 }
2483
2484 for (i = 0; i < VFNT_MAPS; i++) {
2485 fp->vf_map_count[i] =
2486 DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
2487 if (builtin_mapping(fp, i) != 0)
2488 goto free_done;
2489 }
2490
2491 bp->vfbd_font = fp;
2492 return (bp);
2493 }
2494
2495 fd = open(path, O_RDONLY);
2496 if (fd < 0)
2497 return (NULL);
2498
2499 size = sizeof(fh);
2500 rv = read(fd, &fh, size);
2501 if (rv < 0 || (size_t)rv != size) {
2502 bp = NULL;
2503 goto done;
2504 }
2505 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
2506 bp = NULL;
2507 goto done;
2508 }
2509 if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
2510 bp = NULL;
2511 goto done;
2512 }
2513 for (i = 0; i < VFNT_MAPS; i++)
2514 fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
2515
2516 glyphs = be32toh(fh.fh_glyph_count);
2517 fp->vf_width = fh.fh_width;
2518 fp->vf_height = fh.fh_height;
2519
2520 size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
2521 bp->vfbd_uncompressed_size = size;
2522 if ((fp->vf_bytes = malloc(size)) == NULL)
2523 goto free_done;
2524
2525 rv = read(fd, fp->vf_bytes, size);
2526 if (rv < 0 || (size_t)rv != size)
2527 goto free_done;
2528 for (i = 0; i < VFNT_MAPS; i++) {
2529 if (load_mapping(fd, fp, i) != 0)
2530 goto free_done;
2531 }
2532
2533 /*
2534 * Reset builtin flag now as we have full font loaded.
2535 */
2536 if (fl->font_flags == FONT_BUILTIN)
2537 fl->font_flags = FONT_AUTO;
2538
2539 /*
2540 * Release previously loaded entries. We can do this now, as
2541 * the new font is loaded. Note, there can be no console
2542 * output till the new font is in place and teken is notified.
2543 * We do need to keep fl->font_data for glyph dimensions.
2544 */
2545 STAILQ_FOREACH(fl, &fonts, font_next) {
2546 if (fl->font_data->vfbd_font == NULL)
2547 continue;
2548
2549 for (i = 0; i < VFNT_MAPS; i++)
2550 free(fl->font_data->vfbd_font->vf_map[i]);
2551 free(fl->font_data->vfbd_font->vf_bytes);
2552 free(fl->font_data->vfbd_font);
2553 fl->font_data->vfbd_font = NULL;
2554 }
2555
2556 bp->vfbd_font = fp;
2557 bp->vfbd_compressed_size = 0;
2558
2559 done:
2560 if (fd != -1)
2561 close(fd);
2562 return (bp);
2563
2564 free_done:
2565 for (i = 0; i < VFNT_MAPS; i++)
2566 free(fp->vf_map[i]);
2567 free(fp->vf_bytes);
2568 free(fp);
2569 bp = NULL;
2570 goto done;
2571 }
2572
2573 struct name_entry {
2574 char *n_name;
2575 SLIST_ENTRY(name_entry) n_entry;
2576 };
2577
2578 SLIST_HEAD(name_list, name_entry);
2579
2580 /* Read font names from index file. */
2581 static struct name_list *
read_list(char * fonts)2582 read_list(char *fonts)
2583 {
2584 struct name_list *nl;
2585 struct name_entry *np;
2586 char *dir, *ptr;
2587 char buf[PATH_MAX];
2588 int fd, len;
2589
2590 TSENTER();
2591
2592 dir = strdup(fonts);
2593 if (dir == NULL)
2594 return (NULL);
2595
2596 ptr = strrchr(dir, '/');
2597 *ptr = '\0';
2598
2599 fd = open(fonts, O_RDONLY);
2600 if (fd < 0)
2601 return (NULL);
2602
2603 nl = malloc(sizeof(*nl));
2604 if (nl == NULL) {
2605 close(fd);
2606 return (nl);
2607 }
2608
2609 SLIST_INIT(nl);
2610 while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
2611 if (*buf == '#' || *buf == '\0')
2612 continue;
2613
2614 if (bcmp(buf, "MENU", 4) == 0)
2615 continue;
2616
2617 if (bcmp(buf, "FONT", 4) == 0)
2618 continue;
2619
2620 ptr = strchr(buf, ':');
2621 if (ptr == NULL)
2622 continue;
2623 else
2624 *ptr = '\0';
2625
2626 np = malloc(sizeof(*np));
2627 if (np == NULL) {
2628 close(fd);
2629 return (nl); /* return what we have */
2630 }
2631 if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
2632 free(np);
2633 close(fd);
2634 return (nl); /* return what we have */
2635 }
2636 SLIST_INSERT_HEAD(nl, np, n_entry);
2637 }
2638 close(fd);
2639 TSEXIT();
2640 return (nl);
2641 }
2642
2643 /*
2644 * Read the font properties and insert new entry into the list.
2645 * The font list is built in descending order.
2646 */
2647 static bool
insert_font(char * name,FONT_FLAGS flags)2648 insert_font(char *name, FONT_FLAGS flags)
2649 {
2650 struct font_header fh;
2651 struct fontlist *fp, *previous, *entry, *next;
2652 size_t size;
2653 ssize_t rv;
2654 int fd;
2655 char *font_name;
2656
2657 TSENTER();
2658
2659 font_name = NULL;
2660 if (flags == FONT_BUILTIN) {
2661 /*
2662 * We only install builtin font once, while setting up
2663 * initial console. Since this will happen very early,
2664 * we assume asprintf will not fail. Once we have access to
2665 * files, the builtin font will be replaced by font loaded
2666 * from file.
2667 */
2668 if (!STAILQ_EMPTY(&fonts))
2669 return (false);
2670
2671 fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
2672 fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
2673
2674 (void) asprintf(&font_name, "%dx%d",
2675 DEFAULT_FONT_DATA.vfbd_width,
2676 DEFAULT_FONT_DATA.vfbd_height);
2677 } else {
2678 fd = open(name, O_RDONLY);
2679 if (fd < 0)
2680 return (false);
2681 rv = read(fd, &fh, sizeof(fh));
2682 close(fd);
2683 if (rv < 0 || (size_t)rv != sizeof(fh))
2684 return (false);
2685
2686 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
2687 sizeof(fh.fh_magic)) != 0)
2688 return (false);
2689 font_name = strdup(name);
2690 }
2691
2692 if (font_name == NULL)
2693 return (false);
2694
2695 /*
2696 * If we have an entry with the same glyph dimensions, replace
2697 * the file name and mark us. We only support unique dimensions.
2698 */
2699 STAILQ_FOREACH(entry, &fonts, font_next) {
2700 if (fh.fh_width == entry->font_data->vfbd_width &&
2701 fh.fh_height == entry->font_data->vfbd_height) {
2702 free(entry->font_name);
2703 entry->font_name = font_name;
2704 entry->font_flags = FONT_RELOAD;
2705 TSEXIT();
2706 return (true);
2707 }
2708 }
2709
2710 fp = calloc(sizeof(*fp), 1);
2711 if (fp == NULL) {
2712 free(font_name);
2713 return (false);
2714 }
2715 fp->font_data = calloc(sizeof(*fp->font_data), 1);
2716 if (fp->font_data == NULL) {
2717 free(font_name);
2718 free(fp);
2719 return (false);
2720 }
2721 fp->font_name = font_name;
2722 fp->font_flags = flags;
2723 fp->font_load = load_font;
2724 fp->font_data->vfbd_width = fh.fh_width;
2725 fp->font_data->vfbd_height = fh.fh_height;
2726
2727 if (STAILQ_EMPTY(&fonts)) {
2728 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2729 TSEXIT();
2730 return (true);
2731 }
2732
2733 previous = NULL;
2734 size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
2735
2736 STAILQ_FOREACH(entry, &fonts, font_next) {
2737 vt_font_bitmap_data_t *bd;
2738
2739 bd = entry->font_data;
2740 /* Should fp be inserted before the entry? */
2741 if (size > bd->vfbd_width * bd->vfbd_height) {
2742 if (previous == NULL) {
2743 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2744 } else {
2745 STAILQ_INSERT_AFTER(&fonts, previous, fp,
2746 font_next);
2747 }
2748 TSEXIT();
2749 return (true);
2750 }
2751 next = STAILQ_NEXT(entry, font_next);
2752 if (next == NULL ||
2753 size > next->font_data->vfbd_width *
2754 next->font_data->vfbd_height) {
2755 STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2756 TSEXIT();
2757 return (true);
2758 }
2759 previous = entry;
2760 }
2761 TSEXIT();
2762 return (true);
2763 }
2764
2765 static int
font_set(struct env_var * ev __unused,int flags __unused,const void * value)2766 font_set(struct env_var *ev __unused, int flags __unused, const void *value)
2767 {
2768 struct fontlist *fl;
2769 char *eptr;
2770 unsigned long x = 0, y = 0;
2771
2772 /*
2773 * Attempt to extract values from "XxY" string. In case of error,
2774 * we have unmaching glyph dimensions and will just output the
2775 * available values.
2776 */
2777 if (value != NULL) {
2778 x = strtoul(value, &eptr, 10);
2779 if (*eptr == 'x')
2780 y = strtoul(eptr + 1, &eptr, 10);
2781 }
2782 STAILQ_FOREACH(fl, &fonts, font_next) {
2783 if (fl->font_data->vfbd_width == x &&
2784 fl->font_data->vfbd_height == y)
2785 break;
2786 }
2787 if (fl != NULL) {
2788 /* Reset any FONT_MANUAL flag. */
2789 reset_font_flags();
2790
2791 /* Mark this font manually loaded */
2792 fl->font_flags = FONT_MANUAL;
2793 cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2794 return (CMD_OK);
2795 }
2796
2797 printf("Available fonts:\n");
2798 STAILQ_FOREACH(fl, &fonts, font_next) {
2799 printf(" %dx%d\n", fl->font_data->vfbd_width,
2800 fl->font_data->vfbd_height);
2801 }
2802 return (CMD_OK);
2803 }
2804
2805 void
bios_text_font(bool use_vga_font)2806 bios_text_font(bool use_vga_font)
2807 {
2808 if (use_vga_font)
2809 (void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
2810 else
2811 (void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
2812 }
2813
2814 void
autoload_font(bool bios)2815 autoload_font(bool bios)
2816 {
2817 struct name_list *nl;
2818 struct name_entry *np;
2819
2820 TSENTER();
2821
2822 nl = read_list("/boot/fonts/INDEX.fonts");
2823 if (nl == NULL)
2824 return;
2825
2826 while (!SLIST_EMPTY(nl)) {
2827 np = SLIST_FIRST(nl);
2828 SLIST_REMOVE_HEAD(nl, n_entry);
2829 if (insert_font(np->n_name, FONT_AUTO) == false)
2830 printf("failed to add font: %s\n", np->n_name);
2831 free(np->n_name);
2832 free(np);
2833 }
2834
2835 /*
2836 * If vga text mode was requested, load vga.font (8x16 bold) font.
2837 */
2838 if (bios) {
2839 bios_text_font(true);
2840 }
2841
2842 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2843
2844 TSEXIT();
2845 }
2846
2847 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
2848
2849 static int
command_font(int argc,char * argv[])2850 command_font(int argc, char *argv[])
2851 {
2852 int i, c, rc;
2853 struct fontlist *fl;
2854 vt_font_bitmap_data_t *bd;
2855 bool list;
2856
2857 list = false;
2858 optind = 1;
2859 optreset = 1;
2860 rc = CMD_OK;
2861
2862 while ((c = getopt(argc, argv, "l")) != -1) {
2863 switch (c) {
2864 case 'l':
2865 list = true;
2866 break;
2867 case '?':
2868 default:
2869 return (CMD_ERROR);
2870 }
2871 }
2872
2873 argc -= optind;
2874 argv += optind;
2875
2876 if (argc > 1 || (list && argc != 0)) {
2877 printf("Usage: loadfont [-l] | [file.fnt]\n");
2878 return (CMD_ERROR);
2879 }
2880
2881 if (list) {
2882 STAILQ_FOREACH(fl, &fonts, font_next) {
2883 printf("font %s: %dx%d%s\n", fl->font_name,
2884 fl->font_data->vfbd_width,
2885 fl->font_data->vfbd_height,
2886 fl->font_data->vfbd_font == NULL? "" : " loaded");
2887 }
2888 return (CMD_OK);
2889 }
2890
2891 /* Clear scren */
2892 cons_clear();
2893
2894 if (argc == 1) {
2895 char *name = argv[0];
2896
2897 if (insert_font(name, FONT_MANUAL) == false) {
2898 printf("loadfont error: failed to load: %s\n", name);
2899 return (CMD_ERROR);
2900 }
2901
2902 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2903 return (CMD_OK);
2904 }
2905
2906 if (argc == 0) {
2907 /*
2908 * Walk entire font list, release any loaded font, and set
2909 * autoload flag. The font list does have at least the builtin
2910 * default font.
2911 */
2912 STAILQ_FOREACH(fl, &fonts, font_next) {
2913 if (fl->font_data->vfbd_font != NULL) {
2914
2915 bd = fl->font_data;
2916 /*
2917 * Note the setup_font() is releasing
2918 * font bytes.
2919 */
2920 for (i = 0; i < VFNT_MAPS; i++)
2921 free(bd->vfbd_font->vf_map[i]);
2922 free(fl->font_data->vfbd_font);
2923 fl->font_data->vfbd_font = NULL;
2924 fl->font_data->vfbd_uncompressed_size = 0;
2925 fl->font_flags = FONT_AUTO;
2926 }
2927 }
2928 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2929 }
2930 return (rc);
2931 }
2932
2933 bool
gfx_get_edid_resolution(struct vesa_edid_info * edid,edid_res_list_t * res)2934 gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
2935 {
2936 struct resolution *rp, *p;
2937
2938 /*
2939 * Walk detailed timings tables (4).
2940 */
2941 if ((edid->display.supported_features
2942 & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
2943 /* Walk detailed timing descriptors (4) */
2944 for (int i = 0; i < DET_TIMINGS; i++) {
2945 /*
2946 * Reserved value 0 is not used for display descriptor.
2947 */
2948 if (edid->detailed_timings[i].pixel_clock == 0)
2949 continue;
2950 if ((rp = malloc(sizeof(*rp))) == NULL)
2951 continue;
2952 rp->width = GET_EDID_INFO_WIDTH(edid, i);
2953 rp->height = GET_EDID_INFO_HEIGHT(edid, i);
2954 if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
2955 rp->height > 0 && rp->height <= EDID_MAX_LINES)
2956 TAILQ_INSERT_TAIL(res, rp, next);
2957 else
2958 free(rp);
2959 }
2960 }
2961
2962 /*
2963 * Walk standard timings list (8).
2964 */
2965 for (int i = 0; i < STD_TIMINGS; i++) {
2966 /* Is this field unused? */
2967 if (edid->standard_timings[i] == 0x0101)
2968 continue;
2969
2970 if ((rp = malloc(sizeof(*rp))) == NULL)
2971 continue;
2972
2973 rp->width = HSIZE(edid->standard_timings[i]);
2974 switch (RATIO(edid->standard_timings[i])) {
2975 case RATIO1_1:
2976 rp->height = HSIZE(edid->standard_timings[i]);
2977 if (edid->header.version > 1 ||
2978 edid->header.revision > 2) {
2979 rp->height = rp->height * 10 / 16;
2980 }
2981 break;
2982 case RATIO4_3:
2983 rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
2984 break;
2985 case RATIO5_4:
2986 rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
2987 break;
2988 case RATIO16_9:
2989 rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
2990 break;
2991 }
2992
2993 /*
2994 * Create resolution list in decreasing order, except keep
2995 * first entry (preferred timing mode).
2996 */
2997 TAILQ_FOREACH(p, res, next) {
2998 if (p->width * p->height < rp->width * rp->height) {
2999 /* Keep preferred mode first */
3000 if (TAILQ_FIRST(res) == p)
3001 TAILQ_INSERT_AFTER(res, p, rp, next);
3002 else
3003 TAILQ_INSERT_BEFORE(p, rp, next);
3004 break;
3005 }
3006 if (TAILQ_NEXT(p, next) == NULL) {
3007 TAILQ_INSERT_TAIL(res, rp, next);
3008 break;
3009 }
3010 }
3011 }
3012 return (!TAILQ_EMPTY(res));
3013 }
3014
3015 vm_offset_t
build_font_module(vm_offset_t addr)3016 build_font_module(vm_offset_t addr)
3017 {
3018 vt_font_bitmap_data_t *bd;
3019 struct vt_font *fd;
3020 struct preloaded_file *fp;
3021 size_t size;
3022 uint32_t checksum;
3023 int i;
3024 struct font_info fi;
3025 struct fontlist *fl;
3026 uint64_t fontp;
3027
3028 if (STAILQ_EMPTY(&fonts))
3029 return (addr);
3030
3031 /* We can't load first */
3032 if ((file_findfile(NULL, NULL)) == NULL) {
3033 printf("Can not load font module: %s\n",
3034 "the kernel is not loaded");
3035 return (addr);
3036 }
3037
3038 /* helper pointers */
3039 bd = NULL;
3040 STAILQ_FOREACH(fl, &fonts, font_next) {
3041 if (gfx_state.tg_font.vf_width == fl->font_data->vfbd_width &&
3042 gfx_state.tg_font.vf_height == fl->font_data->vfbd_height) {
3043 /*
3044 * Kernel does have better built in font.
3045 */
3046 if (fl->font_flags == FONT_BUILTIN)
3047 return (addr);
3048
3049 bd = fl->font_data;
3050 break;
3051 }
3052 }
3053 if (bd == NULL)
3054 return (addr);
3055 fd = bd->vfbd_font;
3056
3057 fi.fi_width = fd->vf_width;
3058 checksum = fi.fi_width;
3059 fi.fi_height = fd->vf_height;
3060 checksum += fi.fi_height;
3061 fi.fi_bitmap_size = bd->vfbd_uncompressed_size;
3062 checksum += fi.fi_bitmap_size;
3063
3064 size = roundup2(sizeof (struct font_info), 8);
3065 for (i = 0; i < VFNT_MAPS; i++) {
3066 fi.fi_map_count[i] = fd->vf_map_count[i];
3067 checksum += fi.fi_map_count[i];
3068 size += fd->vf_map_count[i] * sizeof (struct vfnt_map);
3069 size += roundup2(size, 8);
3070 }
3071 size += bd->vfbd_uncompressed_size;
3072
3073 fi.fi_checksum = -checksum;
3074
3075 fp = file_findfile(NULL, md_kerntype);
3076 if (fp == NULL)
3077 panic("can't find kernel file");
3078
3079 fontp = addr;
3080 addr += archsw.arch_copyin(&fi, addr, sizeof (struct font_info));
3081 addr = roundup2(addr, 8);
3082
3083 /* Copy maps. */
3084 for (i = 0; i < VFNT_MAPS; i++) {
3085 if (fd->vf_map_count[i] != 0) {
3086 addr += archsw.arch_copyin(fd->vf_map[i], addr,
3087 fd->vf_map_count[i] * sizeof (struct vfnt_map));
3088 addr = roundup2(addr, 8);
3089 }
3090 }
3091
3092 /* Copy the bitmap. */
3093 addr += archsw.arch_copyin(fd->vf_bytes, addr, fi.fi_bitmap_size);
3094
3095 /* Looks OK so far; populate control structure */
3096 file_addmetadata(fp, MODINFOMD_FONT, sizeof(fontp), &fontp);
3097 return (addr);
3098 }
3099
3100 vm_offset_t
build_splash_module(vm_offset_t addr)3101 build_splash_module(vm_offset_t addr)
3102 {
3103 struct preloaded_file *fp;
3104 struct splash_info si;
3105 const char *splash;
3106 png_t png;
3107 uint64_t splashp;
3108 int error;
3109
3110 /* We can't load first */
3111 if ((file_findfile(NULL, NULL)) == NULL) {
3112 printf("Can not load splash module: %s\n",
3113 "the kernel is not loaded");
3114 return (addr);
3115 }
3116
3117 fp = file_findfile(NULL, md_kerntype);
3118 if (fp == NULL)
3119 panic("can't find kernel file");
3120
3121 splash = getenv("splash");
3122 if (splash == NULL)
3123 return (addr);
3124
3125 /* Parse png */
3126 if ((error = png_open(&png, splash)) != PNG_NO_ERROR) {
3127 return (addr);
3128 }
3129
3130 si.si_width = png.width;
3131 si.si_height = png.height;
3132 si.si_depth = png.bpp;
3133 splashp = addr;
3134 addr += archsw.arch_copyin(&si, addr, sizeof (struct splash_info));
3135 addr = roundup2(addr, 8);
3136
3137 /* Copy the bitmap. */
3138 addr += archsw.arch_copyin(png.image, addr, png.png_datalen);
3139
3140 printf("Loading splash ok\n");
3141 file_addmetadata(fp, MODINFOMD_SPLASH, sizeof(splashp), &splashp);
3142 return (addr);
3143 }
3144