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
gfx_shadow_mark_dirty(uint32_t x,uint32_t y,uint32_t w,uint32_t h)820 gfx_shadow_mark_dirty(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
821 {
822 if (gfx_state.tg_dirty) {
823 if (x < gfx_state.tg_dirty_x1)
824 gfx_state.tg_dirty_x1 = x;
825 if (y < gfx_state.tg_dirty_y1)
826 gfx_state.tg_dirty_y1 = y;
827 if (x + w > gfx_state.tg_dirty_x2)
828 gfx_state.tg_dirty_x2 = x + w;
829 if (y + h > gfx_state.tg_dirty_y2)
830 gfx_state.tg_dirty_y2 = y + h;
831 } else {
832 gfx_state.tg_dirty = true;
833 gfx_state.tg_dirty_x1 = x;
834 gfx_state.tg_dirty_y1 = y;
835 gfx_state.tg_dirty_x2 = x + w;
836 gfx_state.tg_dirty_y2 = y + h;
837 }
838 }
839
840 static void
gfxfb_shadow_fill(uint32_t * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)841 gfxfb_shadow_fill(uint32_t *BltBuffer,
842 uint32_t DestinationX, uint32_t DestinationY,
843 uint32_t Width, uint32_t Height)
844 {
845 uint32_t fbX, fbY;
846
847 if (gfx_state.tg_shadow_fb == NULL)
848 return;
849
850 fbX = gfx_state.tg_fb.fb_width;
851 fbY = gfx_state.tg_fb.fb_height;
852
853 if (BltBuffer == NULL)
854 return;
855
856 if (DestinationX + Width > fbX)
857 Width = fbX - DestinationX;
858
859 if (DestinationY + Height > fbY)
860 Height = fbY - DestinationY;
861
862 uint32_t y2 = Height + DestinationY;
863 for (uint32_t y1 = DestinationY; y1 < y2; y1++) {
864 uint32_t off = y1 * fbX + DestinationX;
865
866 for (uint32_t x = 0; x < Width; x++) {
867 gfx_state.tg_shadow_fb[off + x] = *BltBuffer;
868 }
869 }
870
871 gfx_shadow_mark_dirty(DestinationX, DestinationY, Width, Height);
872 }
873
874 /*
875 * Write a pixel buffer into the shadow framebuffer.
876 */
877 static void
gfxfb_shadow_buf_write(void * BltBuffer,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)878 gfxfb_shadow_buf_write(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
879 uint32_t DestinationX, uint32_t DestinationY,
880 uint32_t Width, uint32_t Height, uint32_t Delta)
881 {
882 uint32_t fbW = gfx_state.tg_fb.fb_width;
883 uint32_t sy, dy;
884
885 if (Delta == 0)
886 Delta = Width * sizeof(*gfx_state.tg_shadow_fb);
887
888 for (sy = SourceY, dy = DestinationY; sy < SourceY + Height; sy++, dy++) {
889 uint32_t *src = (uint32_t *)((uint8_t *)BltBuffer + sy * Delta) +
890 SourceX;
891 uint32_t *dst = gfx_state.tg_shadow_fb + dy * fbW + DestinationX;
892
893 bcopy(src, dst, Width * sizeof(*gfx_state.tg_shadow_fb));
894 }
895
896 gfx_shadow_mark_dirty(DestinationX, DestinationY, Width, Height);
897 }
898
899 /*
900 * Copy a region within the shadow framebuffer.
901 */
902 static void
gfxfb_shadow_vid_to_vid(uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)903 gfxfb_shadow_vid_to_vid(uint32_t SourceX, uint32_t SourceY,
904 uint32_t DestinationX, uint32_t DestinationY,
905 uint32_t Width, uint32_t Height)
906 {
907 uint32_t fbW = gfx_state.tg_fb.fb_width;
908 uint32_t h = Height;
909 int step = 1;
910 uint32_t sy = SourceY, dy = DestinationY;
911
912 if (dy * fbW + DestinationX > sy * fbW + SourceX) {
913 sy += Height - 1;
914 dy += Height - 1;
915 step = -1;
916 }
917
918 while (Height-- > 0) {
919 bcopy(gfx_state.tg_shadow_fb + sy * fbW + SourceX,
920 gfx_state.tg_shadow_fb + dy * fbW + DestinationX,
921 Width * sizeof(*gfx_state.tg_shadow_fb));
922 sy += step;
923 dy += step;
924 }
925
926 gfx_shadow_mark_dirty(DestinationX, DestinationY, Width, h);
927 }
928
929 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)930 gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
931 uint32_t SourceX, uint32_t SourceY,
932 uint32_t DestinationX, uint32_t DestinationY,
933 uint32_t Width, uint32_t Height, uint32_t Delta)
934 {
935 int rv;
936
937 /*
938 * When a shadow framebuffer is present, redirect write operations into
939 * it regardless of the underlying display backend (EFI GOP or direct
940 * framebuffer). The real video is updated in bulk by gfx_fb_flush()
941 * once a logical output operation is complete.
942 *
943 * Reads (VideoToBltBuffer) are intentionally NOT redirected to shadow:
944 * the shadow only contains pixels that have been explicitly written
945 * through it, so unwritten regions (e.g. firmware content already on
946 * screen at boot) would return stale or uninitialised data. Instead,
947 * flush any pending shadow writes to video first so the
948 * backend-specific read below always sees the current framebuffer
949 * contents.
950 */
951 if (gfx_state.tg_shadow_fb != NULL &&
952 BltOperation == GfxFbBltVideoToBltBuffer)
953 gfx_fb_flush();
954
955 if (gfx_state.tg_shadow_fb != NULL &&
956 BltOperation != GfxFbBltVideoToBltBuffer) {
957 switch (BltOperation) {
958 case GfxFbBltVideoFill:
959 gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
960 Width, Height);
961 break;
962
963 case GfxFbBltBufferToVideo:
964 gfxfb_shadow_buf_write(BltBuffer, SourceX, SourceY,
965 DestinationX, DestinationY, Width, Height, Delta);
966 break;
967
968 case GfxFbBltVideoToVideo:
969 gfxfb_shadow_vid_to_vid(SourceX, SourceY,
970 DestinationX, DestinationY, Width, Height);
971 break;
972
973 default:
974 return (EINVAL);
975 }
976 return (0);
977 }
978
979 #if defined(EFI)
980 EFI_STATUS status;
981 EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
982 EFI_TPL tpl;
983
984 /*
985 * We assume Blt() does work, if not, we will need to build exception
986 * list case by case. We only have boot services during part of our
987 * exectution. Once terminate boot services, these operations cannot be
988 * done as they are provided by protocols that disappear when exit
989 * boot services.
990 */
991 if (gfx_state.tg_fb_type == FB_GOP && !ignore_gop_blt &&
992 boot_services_active) {
993 assert(gfx_state.tg_private != NULL);
994 gop = gfx_state.tg_private;
995 tpl = BS->RaiseTPL(TPL_NOTIFY);
996 switch (BltOperation) {
997 case GfxFbBltVideoFill:
998 status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
999 SourceX, SourceY, DestinationX, DestinationY,
1000 Width, Height, Delta);
1001 break;
1002
1003 case GfxFbBltVideoToBltBuffer:
1004 status = gop->Blt(gop, BltBuffer,
1005 EfiBltVideoToBltBuffer,
1006 SourceX, SourceY, DestinationX, DestinationY,
1007 Width, Height, Delta);
1008 break;
1009
1010 case GfxFbBltBufferToVideo:
1011 status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
1012 SourceX, SourceY, DestinationX, DestinationY,
1013 Width, Height, Delta);
1014 break;
1015
1016 case GfxFbBltVideoToVideo:
1017 status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
1018 SourceX, SourceY, DestinationX, DestinationY,
1019 Width, Height, Delta);
1020 break;
1021
1022 default:
1023 status = EFI_INVALID_PARAMETER;
1024 break;
1025 }
1026
1027 switch (status) {
1028 case EFI_SUCCESS:
1029 rv = 0;
1030 break;
1031
1032 case EFI_INVALID_PARAMETER:
1033 rv = EINVAL;
1034 break;
1035
1036 case EFI_DEVICE_ERROR:
1037 default:
1038 rv = EIO;
1039 break;
1040 }
1041
1042 BS->RestoreTPL(tpl);
1043 return (rv);
1044 }
1045 #endif
1046
1047 switch (BltOperation) {
1048 case GfxFbBltVideoFill:
1049 rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
1050 Width, Height);
1051 break;
1052
1053 case GfxFbBltVideoToBltBuffer:
1054 rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
1055 DestinationX, DestinationY, Width, Height, Delta);
1056 break;
1057
1058 case GfxFbBltBufferToVideo:
1059 rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
1060 DestinationX, DestinationY, Width, Height, Delta);
1061 break;
1062
1063 case GfxFbBltVideoToVideo:
1064 rv = gfxfb_blt_video_to_video(SourceX, SourceY,
1065 DestinationX, DestinationY, Width, Height);
1066 break;
1067
1068 default:
1069 rv = EINVAL;
1070 break;
1071 }
1072 return (rv);
1073 }
1074
1075 /*
1076 * Flush the dirty region of the shadow framebuffer to the real framebuffer.
1077 * Must be called after each logical output operation when shadow fb is in use.
1078 */
1079 void
gfx_fb_flush(void)1080 gfx_fb_flush(void)
1081 {
1082 uint32_t x, y, w, h, pitch;
1083
1084 if (gfx_state.tg_shadow_fb == NULL || !gfx_state.tg_dirty ||
1085 gfx_state.tg_fb_type == FB_TEXT)
1086 return;
1087
1088 x = gfx_state.tg_dirty_x1;
1089 y = gfx_state.tg_dirty_y1;
1090 pitch = gfx_state.tg_fb.fb_width;
1091
1092 if (x >= pitch || y >= gfx_state.tg_fb.fb_height)
1093 goto done;
1094
1095 w = gfx_state.tg_dirty_x2 - x;
1096 h = gfx_state.tg_dirty_y2 - y;
1097
1098 if (x + w > pitch)
1099 w = pitch - x;
1100 if (y + h > gfx_state.tg_fb.fb_height)
1101 h = gfx_state.tg_fb.fb_height - y;
1102
1103 #if defined(EFI)
1104 if (gfx_state.tg_fb_type == FB_GOP && !ignore_gop_blt &&
1105 boot_services_active) {
1106 EFI_GRAPHICS_OUTPUT_PROTOCOL *gop = gfx_state.tg_private;
1107 EFI_TPL tpl;
1108
1109 assert(gop != NULL);
1110 tpl = BS->RaiseTPL(TPL_NOTIFY);
1111 (void) gop->Blt(gop,
1112 (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)gfx_state.tg_shadow_fb,
1113 EfiBltBufferToVideo, x, y, x, y, w, h,
1114 pitch * sizeof(*gfx_state.tg_shadow_fb));
1115 BS->RestoreTPL(tpl);
1116 goto done;
1117 }
1118 #endif
1119 (void) gfxfb_blt_buffer_to_video(gfx_state.tg_shadow_fb,
1120 x, y, x, y, w, h, pitch * sizeof(*gfx_state.tg_shadow_fb));
1121
1122 done:
1123 gfx_state.tg_dirty = false;
1124 }
1125
1126 void
gfx_bitblt_bitmap(teken_gfx_t * state,const uint8_t * glyph,const teken_attr_t * a,uint32_t alpha,bool cursor)1127 gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
1128 const teken_attr_t *a, uint32_t alpha, bool cursor)
1129 {
1130 uint32_t width, height;
1131 uint32_t fgc, bgc, bpl, cc, o;
1132 int bpp, bit, byte;
1133 bool invert = false;
1134
1135 bpp = 4; /* We only generate BGRA */
1136 width = state->tg_font.vf_width;
1137 height = state->tg_font.vf_height;
1138 bpl = (width + 7) / 8; /* Bytes per source line. */
1139
1140 fgc = a->ta_fgcolor;
1141 bgc = a->ta_bgcolor;
1142 if (a->ta_format & TF_BOLD)
1143 fgc |= TC_LIGHT;
1144 if (a->ta_format & TF_BLINK)
1145 bgc |= TC_LIGHT;
1146
1147 fgc = gfx_fb_color_map(fgc);
1148 bgc = gfx_fb_color_map(bgc);
1149
1150 if (a->ta_format & TF_REVERSE)
1151 invert = !invert;
1152 if (cursor)
1153 invert = !invert;
1154 if (invert) {
1155 uint32_t tmp;
1156
1157 tmp = fgc;
1158 fgc = bgc;
1159 bgc = tmp;
1160 }
1161
1162 alpha = alpha << 24;
1163 fgc |= alpha;
1164 bgc |= alpha;
1165
1166 for (uint32_t y = 0; y < height; y++) {
1167 for (uint32_t x = 0; x < width; x++) {
1168 byte = y * bpl + x / 8;
1169 bit = 0x80 >> (x % 8);
1170 o = y * width * bpp + x * bpp;
1171 cc = glyph[byte] & bit ? fgc : bgc;
1172
1173 gfx_mem_wr4(state->tg_glyph,
1174 state->tg_glyph_size, o, cc);
1175 }
1176 }
1177 }
1178
1179 /*
1180 * Draw prepared glyph on terminal point p.
1181 */
1182 static void
gfx_fb_printchar(teken_gfx_t * state,const teken_pos_t * p)1183 gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p)
1184 {
1185 unsigned x, y, width, height;
1186
1187 width = state->tg_font.vf_width;
1188 height = state->tg_font.vf_height;
1189 x = state->tg_origin.tp_col + p->tp_col * width;
1190 y = state->tg_origin.tp_row + p->tp_row * height;
1191
1192 gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
1193 }
1194
1195 /*
1196 * Store char with its attribute to buffer and put it on screen.
1197 */
1198 void
gfx_fb_putchar(void * arg,const teken_pos_t * p,teken_char_t c,const teken_attr_t * a)1199 gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
1200 const teken_attr_t *a)
1201 {
1202 teken_gfx_t *state = arg;
1203 const uint8_t *glyph;
1204 int idx;
1205
1206 idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
1207 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
1208 return;
1209
1210 /* remove the cursor */
1211 if (state->tg_cursor_visible)
1212 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1213
1214 screen_buffer[idx].c = c;
1215 screen_buffer[idx].a = *a;
1216
1217 glyph = font_lookup(&state->tg_font, c, a);
1218 gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
1219 gfx_fb_printchar(state, p);
1220
1221 /* display the cursor */
1222 if (state->tg_cursor_visible) {
1223 const teken_pos_t *c;
1224
1225 c = teken_get_cursor(&state->tg_teken);
1226 gfx_fb_cursor_draw(state, c, true);
1227 }
1228
1229 gfx_fb_flush();
1230 }
1231
1232 void
gfx_fb_fill(void * arg,const teken_rect_t * r,teken_char_t c,const teken_attr_t * a)1233 gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c,
1234 const teken_attr_t *a)
1235 {
1236 teken_gfx_t *state = arg;
1237 const uint8_t *glyph;
1238 teken_pos_t p;
1239 struct text_pixel *row;
1240
1241 TSENTER();
1242
1243 /* remove the cursor */
1244 if (state->tg_cursor_visible)
1245 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1246
1247 glyph = font_lookup(&state->tg_font, c, a);
1248 gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
1249
1250 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
1251 p.tp_row++) {
1252 row = &screen_buffer[p.tp_row * state->tg_tp.tp_col];
1253 for (p.tp_col = r->tr_begin.tp_col;
1254 p.tp_col < r->tr_end.tp_col; p.tp_col++) {
1255 row[p.tp_col].c = c;
1256 row[p.tp_col].a = *a;
1257 gfx_fb_printchar(state, &p);
1258 }
1259 }
1260
1261 /* display the cursor */
1262 if (state->tg_cursor_visible) {
1263 const teken_pos_t *c;
1264
1265 c = teken_get_cursor(&state->tg_teken);
1266 gfx_fb_cursor_draw(state, c, true);
1267 }
1268
1269 gfx_fb_flush();
1270 TSEXIT();
1271 }
1272
1273 static void
gfx_fb_cursor_draw(teken_gfx_t * state,const teken_pos_t * pos,bool on)1274 gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *pos, bool on)
1275 {
1276 const uint8_t *glyph;
1277 teken_pos_t p;
1278 int idx;
1279
1280 p = *pos;
1281 if (p.tp_col >= state->tg_tp.tp_col)
1282 p.tp_col = state->tg_tp.tp_col - 1;
1283 if (p.tp_row >= state->tg_tp.tp_row)
1284 p.tp_row = state->tg_tp.tp_row - 1;
1285 idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
1286 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
1287 return;
1288
1289 glyph = font_lookup(&state->tg_font, screen_buffer[idx].c,
1290 &screen_buffer[idx].a);
1291 gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on);
1292 gfx_fb_printchar(state, &p);
1293
1294 state->tg_cursor = p;
1295 }
1296
1297 void
gfx_fb_cursor(void * arg,const teken_pos_t * p)1298 gfx_fb_cursor(void *arg, const teken_pos_t *p)
1299 {
1300 teken_gfx_t *state = arg;
1301
1302 /* Switch cursor off in old location and back on in new. */
1303 if (state->tg_cursor_visible) {
1304 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1305 gfx_fb_cursor_draw(state, p, true);
1306 gfx_fb_flush();
1307 }
1308 }
1309
1310 void
gfx_fb_param(void * arg,int cmd,unsigned int value)1311 gfx_fb_param(void *arg, int cmd, unsigned int value)
1312 {
1313 teken_gfx_t *state = arg;
1314 const teken_pos_t *c;
1315
1316 switch (cmd) {
1317 case TP_SETLOCALCURSOR:
1318 /*
1319 * 0 means normal (usually block), 1 means hidden, and
1320 * 2 means blinking (always block) for compatibility with
1321 * syscons. We don't support any changes except hiding,
1322 * so must map 2 to 0.
1323 */
1324 value = (value == 1) ? 0 : 1;
1325 /* FALLTHROUGH */
1326 case TP_SHOWCURSOR:
1327 c = teken_get_cursor(&state->tg_teken);
1328 gfx_fb_cursor_draw(state, c, true);
1329 if (value != 0)
1330 state->tg_cursor_visible = true;
1331 else
1332 state->tg_cursor_visible = false;
1333 gfx_fb_flush();
1334 break;
1335 default:
1336 /* Not yet implemented */
1337 break;
1338 }
1339 }
1340
1341 bool
is_same_pixel(struct text_pixel * px1,struct text_pixel * px2)1342 is_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
1343 {
1344 if (px1->c != px2->c)
1345 return (false);
1346
1347 /* Is there image stored? */
1348 if ((px1->a.ta_format & TF_IMAGE) ||
1349 (px2->a.ta_format & TF_IMAGE))
1350 return (false);
1351
1352 if (px1->a.ta_format != px2->a.ta_format)
1353 return (false);
1354 if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
1355 return (false);
1356 if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
1357 return (false);
1358
1359 return (true);
1360 }
1361
1362 static void
gfx_fb_copy_area(teken_gfx_t * state,const teken_rect_t * s,const teken_pos_t * d)1363 gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s,
1364 const teken_pos_t *d)
1365 {
1366 uint32_t sx, sy, dx, dy, width, height;
1367 uint32_t pitch, bytes;
1368 int step;
1369
1370 width = state->tg_font.vf_width;
1371 height = state->tg_font.vf_height;
1372
1373 sx = s->tr_begin.tp_col * width;
1374 sy = s->tr_begin.tp_row * height;
1375 dx = d->tp_col * width;
1376 dy = d->tp_row * height;
1377
1378 width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
1379
1380 /*
1381 * With no shadow fb, use video to video copy directly.
1382 */
1383 if (state->tg_shadow_fb == NULL) {
1384 (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
1385 sx + state->tg_origin.tp_col,
1386 sy + state->tg_origin.tp_row,
1387 dx + state->tg_origin.tp_col,
1388 dy + state->tg_origin.tp_row,
1389 width, height, 0);
1390 return;
1391 }
1392
1393 /*
1394 * With shadow fb, copy within shadow only. Every region that could
1395 * be the source of a copy was previously drawn through the shadow, so
1396 * reading from it here is always valid. Mark the destination dirty so
1397 * gfx_fb_flush() will push it to the real framebuffer.
1398 */
1399 step = 1;
1400 pitch = state->tg_fb.fb_width;
1401 bytes = width * sizeof (*state->tg_shadow_fb);
1402
1403 uint32_t ssx = sx + state->tg_origin.tp_col;
1404 uint32_t ssy = sy + state->tg_origin.tp_row;
1405 uint32_t dsx = dx + state->tg_origin.tp_col;
1406 uint32_t dsy = dy + state->tg_origin.tp_row;
1407 uint32_t dst_x = dsx;
1408 uint32_t dst_y = dsy;
1409 uint32_t dst_h = height;
1410
1411 /*
1412 * To handle overlapping areas, set up reverse copy here.
1413 * The origin offset cancels out in the comparison so the
1414 * terminal-relative form is equivalent.
1415 */
1416 if (dy * pitch + dx > sy * pitch + sx) {
1417 ssy += height;
1418 dsy += height;
1419 step = -step;
1420 }
1421
1422 while (height-- > 0) {
1423 uint32_t *source = &state->tg_shadow_fb[ssy * pitch + ssx];
1424 uint32_t *destination = &state->tg_shadow_fb[dsy * pitch + dsx];
1425
1426 bcopy(source, destination, bytes);
1427 ssy += step;
1428 dsy += step;
1429 }
1430
1431 gfx_shadow_mark_dirty(dst_x, dst_y, width, dst_h);
1432 }
1433
1434 static void
gfx_fb_copy_line(teken_gfx_t * state,int ncol,teken_pos_t * s,teken_pos_t * d)1435 gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
1436 {
1437 teken_rect_t sr;
1438 teken_pos_t dp;
1439 unsigned soffset, doffset;
1440 bool mark = false;
1441 int x;
1442
1443 soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
1444 doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
1445
1446 for (x = 0; x < ncol; x++) {
1447 if (is_same_pixel(&screen_buffer[soffset + x],
1448 &screen_buffer[doffset + x])) {
1449 if (mark) {
1450 gfx_fb_copy_area(state, &sr, &dp);
1451 mark = false;
1452 }
1453 } else {
1454 screen_buffer[doffset + x] = screen_buffer[soffset + x];
1455 if (mark) {
1456 /* update end point */
1457 sr.tr_end.tp_col = s->tp_col + x;
1458 } else {
1459 /* set up new rectangle */
1460 mark = true;
1461 sr.tr_begin.tp_col = s->tp_col + x;
1462 sr.tr_begin.tp_row = s->tp_row;
1463 sr.tr_end.tp_col = s->tp_col + x;
1464 sr.tr_end.tp_row = s->tp_row;
1465 dp.tp_col = d->tp_col + x;
1466 dp.tp_row = d->tp_row;
1467 }
1468 }
1469 }
1470 if (mark) {
1471 gfx_fb_copy_area(state, &sr, &dp);
1472 }
1473 }
1474
1475 void
gfx_fb_copy(void * arg,const teken_rect_t * r,const teken_pos_t * p)1476 gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
1477 {
1478 teken_gfx_t *state = arg;
1479 unsigned doffset, soffset;
1480 teken_pos_t d, s;
1481 int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
1482
1483 /*
1484 * Copying is a little tricky. We must make sure we do it in
1485 * correct order, to make sure we don't overwrite our own data.
1486 */
1487
1488 nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
1489 ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
1490
1491 if (p->tp_row + nrow > state->tg_tp.tp_row ||
1492 p->tp_col + ncol > state->tg_tp.tp_col)
1493 return;
1494
1495 soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
1496 doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
1497
1498 /* remove the cursor */
1499 if (state->tg_cursor_visible)
1500 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1501
1502 /*
1503 * Copy line by line.
1504 */
1505 if (doffset <= soffset) {
1506 s = r->tr_begin;
1507 d = *p;
1508 for (y = 0; y < nrow; y++) {
1509 s.tp_row = r->tr_begin.tp_row + y;
1510 d.tp_row = p->tp_row + y;
1511
1512 gfx_fb_copy_line(state, ncol, &s, &d);
1513 }
1514 } else {
1515 for (y = nrow - 1; y >= 0; y--) {
1516 s.tp_row = r->tr_begin.tp_row + y;
1517 d.tp_row = p->tp_row + y;
1518
1519 gfx_fb_copy_line(state, ncol, &s, &d);
1520 }
1521 }
1522
1523 /* display the cursor */
1524 if (state->tg_cursor_visible) {
1525 const teken_pos_t *c;
1526
1527 c = teken_get_cursor(&state->tg_teken);
1528 gfx_fb_cursor_draw(state, c, true);
1529 }
1530
1531 gfx_fb_flush();
1532 }
1533
1534 /*
1535 * Implements alpha blending for RGBA data, could use pixels for arguments,
1536 * but byte stream seems more generic.
1537 * The generic alpha blending is:
1538 * blend = alpha * fg + (1.0 - alpha) * bg.
1539 * Since our alpha is not from range [0..1], we scale appropriately.
1540 */
1541 static uint8_t
alpha_blend(uint8_t fg,uint8_t bg,uint8_t alpha)1542 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
1543 {
1544 uint16_t blend, h, l;
1545
1546 /* trivial corner cases */
1547 if (alpha == 0)
1548 return (bg);
1549 if (alpha == 0xFF)
1550 return (fg);
1551 blend = (alpha * fg + (0xFF - alpha) * bg);
1552 /* Division by 0xFF */
1553 h = blend >> 8;
1554 l = blend & 0xFF;
1555 if (h + l >= 0xFF)
1556 h++;
1557 return (h);
1558 }
1559
1560 /*
1561 * Implements alpha blending for RGBA data, could use pixels for arguments,
1562 * but byte stream seems more generic.
1563 * The generic alpha blending is:
1564 * blend = alpha * fg + (1.0 - alpha) * bg.
1565 * Since our alpha is not from range [0..1], we scale appropriately.
1566 */
1567 static void
bitmap_cpy(void * dst,void * src,uint32_t size)1568 bitmap_cpy(void *dst, void *src, uint32_t size)
1569 {
1570 #if defined(EFI)
1571 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
1572 #else
1573 struct paletteentry *ps, *pd;
1574 #endif
1575 uint32_t i;
1576 uint8_t a;
1577
1578 ps = src;
1579 pd = dst;
1580
1581 /*
1582 * we only implement alpha blending for depth 32.
1583 */
1584 for (i = 0; i < size; i ++) {
1585 a = ps[i].Reserved;
1586 pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
1587 pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
1588 pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
1589 pd[i].Reserved = a;
1590 }
1591 }
1592
1593 static void *
allocate_glyphbuffer(uint32_t width,uint32_t height)1594 allocate_glyphbuffer(uint32_t width, uint32_t height)
1595 {
1596 size_t size;
1597
1598 size = sizeof (*GlyphBuffer) * width * height;
1599 if (size != GlyphBufferSize) {
1600 free(GlyphBuffer);
1601 GlyphBuffer = malloc(size);
1602 if (GlyphBuffer == NULL)
1603 return (NULL);
1604 GlyphBufferSize = size;
1605 }
1606 return (GlyphBuffer);
1607 }
1608
1609 void
gfx_fb_cons_display(uint32_t x,uint32_t y,uint32_t width,uint32_t height,void * data)1610 gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
1611 void *data)
1612 {
1613 #if defined(EFI)
1614 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, *p;
1615 #else
1616 struct paletteentry *buf, *p;
1617 #endif
1618 size_t size;
1619
1620 /*
1621 * If we do have shadow fb, render into it only. The caller is
1622 * responsible for flushing the dirty region to the real framebuffer
1623 * via gfx_fb_flush() once a logical output operation is complete.
1624 */
1625 if (gfx_state.tg_shadow_fb != NULL) {
1626 uint32_t pitch = gfx_state.tg_fb.fb_width;
1627
1628 p = data;
1629 for (uint32_t row = 0; row < height; row++) {
1630 buf = (void *)(gfx_state.tg_shadow_fb +
1631 (y + row) * pitch + x);
1632 bitmap_cpy(buf, &p[row * width], width);
1633 }
1634 gfx_shadow_mark_dirty(x, y, width, height);
1635 return;
1636 }
1637
1638 /*
1639 * Common data to display is glyph, use preallocated
1640 * glyph buffer.
1641 */
1642 if (gfx_state.tg_glyph_size != GlyphBufferSize)
1643 (void) allocate_glyphbuffer(width, height);
1644
1645 size = width * height * sizeof(*buf);
1646 if (size == GlyphBufferSize)
1647 buf = GlyphBuffer;
1648 else
1649 buf = malloc(size);
1650 if (buf == NULL)
1651 return;
1652
1653 if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0,
1654 width, height, 0) == 0) {
1655 bitmap_cpy(buf, data, width * height);
1656 (void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y,
1657 width, height, 0);
1658 }
1659 if (buf != GlyphBuffer)
1660 free(buf);
1661 }
1662
1663 /*
1664 * Public graphics primitives.
1665 */
1666
1667 static int
isqrt(int num)1668 isqrt(int num)
1669 {
1670 int res = 0;
1671 int bit = 1 << 30;
1672
1673 /* "bit" starts at the highest power of four <= the argument. */
1674 while (bit > num)
1675 bit >>= 2;
1676
1677 while (bit != 0) {
1678 if (num >= res + bit) {
1679 num -= res + bit;
1680 res = (res >> 1) + bit;
1681 } else {
1682 res >>= 1;
1683 }
1684 bit >>= 2;
1685 }
1686 return (res);
1687 }
1688
1689 static uint32_t
gfx_fb_getcolor(void)1690 gfx_fb_getcolor(void)
1691 {
1692 uint32_t c;
1693 const teken_attr_t *ap;
1694
1695 ap = teken_get_curattr(&gfx_state.tg_teken);
1696 if (ap->ta_format & TF_REVERSE) {
1697 c = ap->ta_bgcolor;
1698 if (ap->ta_format & TF_BLINK)
1699 c |= TC_LIGHT;
1700 } else {
1701 c = ap->ta_fgcolor;
1702 if (ap->ta_format & TF_BOLD)
1703 c |= TC_LIGHT;
1704 }
1705
1706 return (gfx_fb_color_map(c));
1707 }
1708
1709 /* set pixel in framebuffer using gfx coordinates */
1710 void
gfx_fb_setpixel(uint32_t x,uint32_t y)1711 gfx_fb_setpixel(uint32_t x, uint32_t y)
1712 {
1713 uint32_t c;
1714
1715 if (gfx_state.tg_fb_type == FB_TEXT)
1716 return;
1717
1718 c = gfx_fb_getcolor();
1719
1720 if (x >= gfx_state.tg_fb.fb_width ||
1721 y >= gfx_state.tg_fb.fb_height)
1722 return;
1723
1724 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
1725 }
1726
1727 /*
1728 * draw rectangle in framebuffer using gfx coordinates.
1729 */
1730 void
gfx_fb_drawrect(uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t fill)1731 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1732 uint32_t fill)
1733 {
1734 uint32_t c;
1735
1736 if (gfx_state.tg_fb_type == FB_TEXT)
1737 return;
1738
1739 c = gfx_fb_getcolor();
1740
1741 if (fill != 0) {
1742 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1,
1743 y2 - y1, 0);
1744 } else {
1745 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1, 0);
1746 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y2, x2 - x1, 1, 0);
1747 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0);
1748 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0);
1749 }
1750 gfx_fb_flush();
1751 }
1752
1753 void
gfx_fb_line(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t wd)1754 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
1755 {
1756 int dx, sx, dy, sy;
1757 int err, e2, x2, y2, ed, width;
1758
1759 if (gfx_state.tg_fb_type == FB_TEXT)
1760 return;
1761
1762 width = wd;
1763 sx = x0 < x1? 1 : -1;
1764 sy = y0 < y1? 1 : -1;
1765 dx = x1 > x0? x1 - x0 : x0 - x1;
1766 dy = y1 > y0? y1 - y0 : y0 - y1;
1767 err = dx + dy;
1768 ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1769
1770 for (;;) {
1771 gfx_fb_setpixel(x0, y0);
1772 e2 = err;
1773 x2 = x0;
1774 if ((e2 << 1) >= -dx) { /* x step */
1775 e2 += dy;
1776 y2 = y0;
1777 while (e2 < ed * width &&
1778 (y1 != (uint32_t)y2 || dx > dy)) {
1779 y2 += sy;
1780 gfx_fb_setpixel(x0, y2);
1781 e2 += dx;
1782 }
1783 if (x0 == x1)
1784 break;
1785 e2 = err;
1786 err -= dy;
1787 x0 += sx;
1788 }
1789 if ((e2 << 1) <= dy) { /* y step */
1790 e2 = dx-e2;
1791 while (e2 < ed * width &&
1792 (x1 != (uint32_t)x2 || dx < dy)) {
1793 x2 += sx;
1794 gfx_fb_setpixel(x2, y0);
1795 e2 += dy;
1796 }
1797 if (y0 == y1)
1798 break;
1799 err += dx;
1800 y0 += sy;
1801 }
1802 }
1803 gfx_fb_flush();
1804 }
1805
1806 /*
1807 * quadratic Bézier curve limited to gradients without sign change.
1808 */
1809 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)1810 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1811 uint32_t y2, uint32_t wd)
1812 {
1813 int sx, sy, xx, yy, xy, width;
1814 int dx, dy, err, curvature;
1815 int i;
1816
1817 if (gfx_state.tg_fb_type == FB_TEXT)
1818 return;
1819
1820 width = wd;
1821 sx = x2 - x1;
1822 sy = y2 - y1;
1823 xx = x0 - x1;
1824 yy = y0 - y1;
1825 curvature = xx*sy - yy*sx;
1826
1827 if (sx*sx + sy*sy > xx*xx+yy*yy) {
1828 x2 = x0;
1829 x0 = sx + x1;
1830 y2 = y0;
1831 y0 = sy + y1;
1832 curvature = -curvature;
1833 }
1834 if (curvature != 0) {
1835 xx += sx;
1836 sx = x0 < x2? 1 : -1;
1837 xx *= sx;
1838 yy += sy;
1839 sy = y0 < y2? 1 : -1;
1840 yy *= sy;
1841 xy = (xx*yy) << 1;
1842 xx *= xx;
1843 yy *= yy;
1844 if (curvature * sx * sy < 0) {
1845 xx = -xx;
1846 yy = -yy;
1847 xy = -xy;
1848 curvature = -curvature;
1849 }
1850 dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1851 dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1852 xx += xx;
1853 yy += yy;
1854 err = dx + dy + xy;
1855 do {
1856 for (i = 0; i <= width; i++)
1857 gfx_fb_setpixel(x0 + i, y0);
1858 if (x0 == x2 && y0 == y2)
1859 return; /* last pixel -> curve finished */
1860 y1 = 2 * err < dx;
1861 if (2 * err > dy) {
1862 x0 += sx;
1863 dx -= xy;
1864 dy += yy;
1865 err += dy;
1866 }
1867 if (y1 != 0) {
1868 y0 += sy;
1869 dy -= xy;
1870 dx += xx;
1871 err += dx;
1872 }
1873 } while (dy < dx); /* gradient negates -> algorithm fails */
1874 }
1875 gfx_fb_line(x0, y0, x2, y2, width);
1876 gfx_fb_flush();
1877 }
1878
1879 /*
1880 * draw rectangle using terminal coordinates and current foreground color.
1881 */
1882 void
gfx_term_drawrect(uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2)1883 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1884 {
1885 int x1, y1, x2, y2;
1886 int xshift, yshift;
1887 int width, i;
1888 uint32_t vf_width, vf_height;
1889 teken_rect_t r;
1890
1891 if (gfx_state.tg_fb_type == FB_TEXT)
1892 return;
1893
1894 vf_width = gfx_state.tg_font.vf_width;
1895 vf_height = gfx_state.tg_font.vf_height;
1896 width = vf_width / 4; /* line width */
1897 xshift = (vf_width - width) / 2;
1898 yshift = (vf_height - width) / 2;
1899
1900 /* Shift coordinates */
1901 if (ux1 != 0)
1902 ux1--;
1903 if (uy1 != 0)
1904 uy1--;
1905 ux2--;
1906 uy2--;
1907
1908 /* mark area used in terminal */
1909 r.tr_begin.tp_col = ux1;
1910 r.tr_begin.tp_row = uy1;
1911 r.tr_end.tp_col = ux2 + 1;
1912 r.tr_end.tp_row = uy2 + 1;
1913
1914 term_image_display(&gfx_state, &r);
1915
1916 /*
1917 * Draw horizontal lines width points thick, shifted from outer edge.
1918 */
1919 x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
1920 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1921 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1922 gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1923 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1924 y2 += vf_height - yshift - width;
1925 gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1926
1927 /*
1928 * Draw vertical lines width points thick, shifted from outer edge.
1929 */
1930 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1931 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1932 y1 += vf_height;
1933 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1934 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1935 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1936 x1 += vf_width - xshift - width;
1937 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1938
1939 /* Draw upper left corner. */
1940 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1941 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1942 y1 += vf_height;
1943
1944 x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1945 x2 += vf_width;
1946 y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1947 for (i = 0; i <= width; i++)
1948 gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1949
1950 /* Draw lower left corner. */
1951 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1952 x1 += vf_width;
1953 y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1954 y1 += vf_height - yshift;
1955 x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1956 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1957 for (i = 0; i <= width; i++)
1958 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1959
1960 /* Draw upper right corner. */
1961 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1962 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1963 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1964 x2 += vf_width - xshift - width;
1965 y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1966 y2 += vf_height;
1967 for (i = 0; i <= width; i++)
1968 gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1969
1970 /* Draw lower right corner. */
1971 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1972 y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1973 y1 += vf_height - yshift;
1974 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1975 x2 += vf_width - xshift - width;
1976 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1977 for (i = 0; i <= width; i++)
1978 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1979 }
1980
1981 int
gfx_fb_putimage(png_t * png,uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2,uint32_t flags)1982 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1983 uint32_t uy2, uint32_t flags)
1984 {
1985 #if defined(EFI)
1986 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1987 #else
1988 struct paletteentry *p;
1989 #endif
1990 uint8_t *data;
1991 uint32_t i, j, x, y, fheight, fwidth;
1992 int rs, gs, bs;
1993 uint8_t r, g, b, a;
1994 bool scale = false;
1995 bool trace = false;
1996 teken_rect_t rect;
1997
1998 trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1999
2000 if (gfx_state.tg_fb_type == FB_TEXT) {
2001 if (trace)
2002 printf("Framebuffer not active.\n");
2003 return (1);
2004 }
2005
2006 if (png->color_type != PNG_TRUECOLOR_ALPHA) {
2007 if (trace)
2008 printf("Not truecolor image.\n");
2009 return (1);
2010 }
2011
2012 if (ux1 > gfx_state.tg_fb.fb_width ||
2013 uy1 > gfx_state.tg_fb.fb_height) {
2014 if (trace)
2015 printf("Top left coordinate off screen.\n");
2016 return (1);
2017 }
2018
2019 if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
2020 if (trace)
2021 printf("Image too large.\n");
2022 return (1);
2023 }
2024
2025 if (png->width < 1 || png->height < 1) {
2026 if (trace)
2027 printf("Image too small.\n");
2028 return (1);
2029 }
2030
2031 /*
2032 * If 0 was passed for either ux2 or uy2, then calculate the missing
2033 * part of the bottom right coordinate.
2034 */
2035 scale = true;
2036 if (ux2 == 0 && uy2 == 0) {
2037 /* Both 0, use the native resolution of the image */
2038 ux2 = ux1 + png->width;
2039 uy2 = uy1 + png->height;
2040 scale = false;
2041 } else if (ux2 == 0) {
2042 /* Set ux2 from uy2/uy1 to maintain aspect ratio */
2043 ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
2044 } else if (uy2 == 0) {
2045 /* Set uy2 from ux2/ux1 to maintain aspect ratio */
2046 uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
2047 }
2048
2049 if (ux2 > gfx_state.tg_fb.fb_width ||
2050 uy2 > gfx_state.tg_fb.fb_height) {
2051 if (trace)
2052 printf("Bottom right coordinate off screen.\n");
2053 return (1);
2054 }
2055
2056 fwidth = ux2 - ux1;
2057 fheight = uy2 - uy1;
2058
2059 /*
2060 * If the original image dimensions have been passed explicitly,
2061 * disable scaling.
2062 */
2063 if (fwidth == png->width && fheight == png->height)
2064 scale = false;
2065
2066 if (ux1 == 0) {
2067 /*
2068 * No top left X co-ordinate (real coordinates start at 1),
2069 * place as far right as it will fit.
2070 */
2071 ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
2072 ux1 = ux2 - fwidth;
2073 }
2074
2075 if (uy1 == 0) {
2076 /*
2077 * No top left Y co-ordinate (real coordinates start at 1),
2078 * place as far down as it will fit.
2079 */
2080 uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
2081 uy1 = uy2 - fheight;
2082 }
2083
2084 if (ux1 >= ux2 || uy1 >= uy2) {
2085 if (trace)
2086 printf("Image dimensions reversed.\n");
2087 return (1);
2088 }
2089
2090 if (fwidth < 2 || fheight < 2) {
2091 if (trace)
2092 printf("Target area too small\n");
2093 return (1);
2094 }
2095
2096 if (trace)
2097 printf("Image %ux%u -> %ux%u @%ux%u\n",
2098 png->width, png->height, fwidth, fheight, ux1, uy1);
2099
2100 rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
2101 rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
2102 rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
2103 rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
2104
2105 /*
2106 * mark area used in terminal
2107 */
2108 if (!(flags & FL_PUTIMAGE_NOSCROLL))
2109 term_image_display(&gfx_state, &rect);
2110
2111 if ((flags & FL_PUTIMAGE_BORDER))
2112 gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
2113
2114 data = malloc(fwidth * fheight * sizeof(*p));
2115 p = (void *)data;
2116 if (data == NULL) {
2117 if (trace)
2118 printf("Out of memory.\n");
2119 return (1);
2120 }
2121
2122 /*
2123 * Build image for our framebuffer.
2124 */
2125
2126 /* Helper to calculate the pixel index from the source png */
2127 #define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
2128
2129 /*
2130 * For each of the x and y directions, calculate the number of pixels
2131 * in the source image that correspond to a single pixel in the target.
2132 * Use fixed-point arithmetic with 16-bits for each of the integer and
2133 * fractional parts.
2134 */
2135 const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
2136 const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
2137
2138 rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) -
2139 ffs(gfx_state.tg_fb.fb_mask_red) + 1);
2140 gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) -
2141 ffs(gfx_state.tg_fb.fb_mask_green) + 1);
2142 bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) -
2143 ffs(gfx_state.tg_fb.fb_mask_blue) + 1);
2144
2145 uint32_t hc = 0;
2146 for (y = 0; y < fheight; y++) {
2147 uint32_t hc2 = (hc >> 9) & 0x7f;
2148 uint32_t hc1 = 0x80 - hc2;
2149
2150 uint32_t offset_y = hc >> 16;
2151 uint32_t offset_y1 = offset_y + 1;
2152
2153 uint32_t wc = 0;
2154 for (x = 0; x < fwidth; x++) {
2155 uint32_t wc2 = (wc >> 9) & 0x7f;
2156 uint32_t wc1 = 0x80 - wc2;
2157
2158 uint32_t offset_x = wc >> 16;
2159 uint32_t offset_x1 = offset_x + 1;
2160
2161 /* Target pixel index */
2162 j = y * fwidth + x;
2163
2164 if (!scale) {
2165 i = GETPIXEL(x, y);
2166 r = png->image[i];
2167 g = png->image[i + 1];
2168 b = png->image[i + 2];
2169 a = png->image[i + 3];
2170 } else {
2171 uint8_t pixel[4];
2172
2173 uint32_t p00 = GETPIXEL(offset_x, offset_y);
2174 uint32_t p01 = GETPIXEL(offset_x, offset_y1);
2175 uint32_t p10 = GETPIXEL(offset_x1, offset_y);
2176 uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
2177
2178 /*
2179 * Given a 2x2 array of pixels in the source
2180 * image, combine them to produce a single
2181 * value for the pixel in the target image.
2182 * Each column of pixels is combined using
2183 * a weighted average where the top and bottom
2184 * pixels contribute hc1 and hc2 respectively.
2185 * The calculation for bottom pixel pB and
2186 * top pixel pT is:
2187 * (pT * hc1 + pB * hc2) / (hc1 + hc2)
2188 * Once the values are determined for the two
2189 * columns of pixels, then the columns are
2190 * averaged together in the same way but using
2191 * wc1 and wc2 for the weightings.
2192 *
2193 * Since hc1 and hc2 are chosen so that
2194 * hc1 + hc2 == 128 (and same for wc1 + wc2),
2195 * the >> 14 below is a quick way to divide by
2196 * (hc1 + hc2) * (wc1 + wc2)
2197 */
2198 for (i = 0; i < 4; i++)
2199 pixel[i] = (
2200 (png->image[p00 + i] * hc1 +
2201 png->image[p01 + i] * hc2) * wc1 +
2202 (png->image[p10 + i] * hc1 +
2203 png->image[p11 + i] * hc2) * wc2)
2204 >> 14;
2205
2206 r = pixel[0];
2207 g = pixel[1];
2208 b = pixel[2];
2209 a = pixel[3];
2210 }
2211
2212 if (trace)
2213 printf("r/g/b: %x/%x/%x\n", r, g, b);
2214 /*
2215 * Rough colorspace reduction for 15/16 bit colors.
2216 */
2217 p[j].Red = r >> rs;
2218 p[j].Green = g >> gs;
2219 p[j].Blue = b >> bs;
2220 p[j].Reserved = a;
2221
2222 wc += wcstep;
2223 }
2224 hc += hcstep;
2225 }
2226
2227 gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
2228 free(data);
2229 gfx_fb_flush();
2230 return (0);
2231 }
2232
2233 /*
2234 * Reset font flags to FONT_AUTO.
2235 */
2236 void
reset_font_flags(void)2237 reset_font_flags(void)
2238 {
2239 struct fontlist *fl;
2240
2241 STAILQ_FOREACH(fl, &fonts, font_next) {
2242 fl->font_flags = FONT_AUTO;
2243 }
2244 }
2245
2246 /* Return w^2 + h^2 or 0, if the dimensions are unknown */
2247 static unsigned
edid_diagonal_squared(void)2248 edid_diagonal_squared(void)
2249 {
2250 unsigned w, h;
2251
2252 if (edid_info == NULL)
2253 return (0);
2254
2255 w = edid_info->display.max_horizontal_image_size;
2256 h = edid_info->display.max_vertical_image_size;
2257
2258 /* If either one is 0, we have aspect ratio, not size */
2259 if (w == 0 || h == 0)
2260 return (0);
2261
2262 /*
2263 * some monitors encode the aspect ratio instead of the physical size.
2264 */
2265 if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
2266 (w == 4 && h == 3) || (w == 5 && h == 4))
2267 return (0);
2268
2269 /*
2270 * translate cm to inch, note we scale by 100 here.
2271 */
2272 w = w * 100 / 254;
2273 h = h * 100 / 254;
2274
2275 /* Return w^2 + h^2 */
2276 return (w * w + h * h);
2277 }
2278
2279 /*
2280 * calculate pixels per inch.
2281 */
2282 static unsigned
gfx_get_ppi(void)2283 gfx_get_ppi(void)
2284 {
2285 unsigned dp, di;
2286
2287 di = edid_diagonal_squared();
2288 if (di == 0)
2289 return (0);
2290
2291 dp = gfx_state.tg_fb.fb_width *
2292 gfx_state.tg_fb.fb_width +
2293 gfx_state.tg_fb.fb_height *
2294 gfx_state.tg_fb.fb_height;
2295
2296 return (isqrt(dp / di));
2297 }
2298
2299 /*
2300 * Calculate font size from density independent pixels (dp):
2301 * ((16dp * ppi) / 160) * display_factor.
2302 * Here we are using fixed constants: 1dp == 160 ppi and
2303 * display_factor 2.
2304 *
2305 * We are rounding font size up and are searching for font which is
2306 * not smaller than calculated size value.
2307 */
2308 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)2309 gfx_get_font(teken_unit_t rows, teken_unit_t cols, teken_unit_t height,
2310 teken_unit_t width)
2311 {
2312 unsigned ppi, size;
2313 vt_font_bitmap_data_t *font = NULL;
2314 struct fontlist *fl, *next;
2315
2316 /* Text mode is not supported here. */
2317 if (gfx_state.tg_fb_type == FB_TEXT)
2318 return (NULL);
2319
2320 ppi = gfx_get_ppi();
2321 if (ppi == 0)
2322 return (NULL);
2323
2324 /*
2325 * We will search for 16dp font.
2326 * We are using scale up by 10 for roundup.
2327 */
2328 size = (16 * ppi * 10) / 160;
2329 /* Apply display factor 2. */
2330 size = roundup(size * 2, 10) / 10;
2331
2332 STAILQ_FOREACH(fl, &fonts, font_next) {
2333 /*
2334 * Skip too large fonts.
2335 */
2336 font = fl->font_data;
2337 if (height / font->vfbd_height < rows ||
2338 width / font->vfbd_width < cols)
2339 continue;
2340
2341 next = STAILQ_NEXT(fl, font_next);
2342
2343 /*
2344 * If this is last font or, if next font is smaller,
2345 * we have our font. Make sure, it actually is loaded.
2346 */
2347 if (next == NULL || next->font_data->vfbd_height < size) {
2348 if (font->vfbd_font == NULL ||
2349 fl->font_flags == FONT_RELOAD) {
2350 if (fl->font_load != NULL &&
2351 fl->font_name != NULL)
2352 font = fl->font_load(fl->font_name);
2353 }
2354 break;
2355 }
2356 font = NULL;
2357 }
2358
2359 return (font);
2360 }
2361
2362 static vt_font_bitmap_data_t *
set_font(teken_unit_t * rows,teken_unit_t * cols,teken_unit_t h,teken_unit_t w)2363 set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
2364 {
2365 vt_font_bitmap_data_t *font = NULL;
2366 struct fontlist *fl;
2367 unsigned height = h;
2368 unsigned width = w;
2369
2370 /*
2371 * First check for manually loaded font.
2372 */
2373 STAILQ_FOREACH(fl, &fonts, font_next) {
2374 if (fl->font_flags == FONT_MANUAL) {
2375 font = fl->font_data;
2376 if (font->vfbd_font == NULL && fl->font_load != NULL &&
2377 fl->font_name != NULL) {
2378 font = fl->font_load(fl->font_name);
2379 }
2380 if (font == NULL || font->vfbd_font == NULL)
2381 font = NULL;
2382 break;
2383 }
2384 }
2385
2386 if (font == NULL)
2387 font = gfx_get_font(*rows, *cols, h, w);
2388
2389 if (font != NULL) {
2390 *rows = height / font->vfbd_height;
2391 *cols = width / font->vfbd_width;
2392 return (font);
2393 }
2394
2395 /*
2396 * Find best font for these dimensions, or use default.
2397 * If height >= VT_FB_MAX_HEIGHT and width >= VT_FB_MAX_WIDTH,
2398 * do not use smaller font than our DEFAULT_FONT_DATA.
2399 */
2400 STAILQ_FOREACH(fl, &fonts, font_next) {
2401 font = fl->font_data;
2402 if ((*rows * font->vfbd_height <= height &&
2403 *cols * font->vfbd_width <= width) ||
2404 (height >= VT_FB_MAX_HEIGHT &&
2405 width >= VT_FB_MAX_WIDTH &&
2406 font->vfbd_height == DEFAULT_FONT_DATA.vfbd_height &&
2407 font->vfbd_width == DEFAULT_FONT_DATA.vfbd_width)) {
2408 if (font->vfbd_font == NULL ||
2409 fl->font_flags == FONT_RELOAD) {
2410 if (fl->font_load != NULL &&
2411 fl->font_name != NULL) {
2412 font = fl->font_load(fl->font_name);
2413 }
2414 if (font == NULL)
2415 continue;
2416 }
2417 *rows = height / font->vfbd_height;
2418 *cols = width / font->vfbd_width;
2419 break;
2420 }
2421 font = NULL;
2422 }
2423
2424 if (font == NULL) {
2425 /*
2426 * We have fonts sorted smallest last, try it before
2427 * falling back to builtin.
2428 */
2429 fl = STAILQ_LAST(&fonts, fontlist, font_next);
2430 if (fl != NULL && fl->font_load != NULL &&
2431 fl->font_name != NULL) {
2432 font = fl->font_load(fl->font_name);
2433 }
2434 if (font == NULL)
2435 font = &DEFAULT_FONT_DATA;
2436
2437 *rows = height / font->vfbd_height;
2438 *cols = width / font->vfbd_width;
2439 }
2440
2441 return (font);
2442 }
2443
2444 static void
cons_clear(void)2445 cons_clear(void)
2446 {
2447 char clear[] = { '\033', 'c' };
2448
2449 /* Reset terminal */
2450 teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
2451 gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
2452 }
2453
2454 void
setup_font(teken_gfx_t * state,teken_unit_t height,teken_unit_t width)2455 setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
2456 {
2457 vt_font_bitmap_data_t *font_data;
2458 teken_pos_t *tp = &state->tg_tp;
2459 char env[8];
2460 int i;
2461
2462 /*
2463 * set_font() will select a appropriate sized font for
2464 * the number of rows and columns selected. If we don't
2465 * have a font that will fit, then it will use the
2466 * default builtin font and adjust the rows and columns
2467 * to fit on the screen.
2468 */
2469 font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
2470
2471 if (font_data == NULL)
2472 panic("out of memory");
2473
2474 for (i = 0; i < VFNT_MAPS; i++) {
2475 state->tg_font.vf_map[i] =
2476 font_data->vfbd_font->vf_map[i];
2477 state->tg_font.vf_map_count[i] =
2478 font_data->vfbd_font->vf_map_count[i];
2479 }
2480
2481 state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
2482 state->tg_font.vf_height = font_data->vfbd_font->vf_height;
2483 state->tg_font.vf_width = font_data->vfbd_font->vf_width;
2484
2485 snprintf(env, sizeof (env), "%ux%u",
2486 state->tg_font.vf_width, state->tg_font.vf_height);
2487 env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
2488 env, font_set, env_nounset);
2489 }
2490
2491 /* Binary search for the glyph. Return 0 if not found. */
2492 static uint16_t
font_bisearch(const vfnt_map_t * map,uint32_t len,teken_char_t src)2493 font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
2494 {
2495 unsigned min, mid, max;
2496
2497 min = 0;
2498 max = len - 1;
2499
2500 /* Empty font map. */
2501 if (len == 0)
2502 return (0);
2503 /* Character below minimal entry. */
2504 if (src < map[0].vfm_src)
2505 return (0);
2506 /* Optimization: ASCII characters occur very often. */
2507 if (src <= map[0].vfm_src + map[0].vfm_len)
2508 return (src - map[0].vfm_src + map[0].vfm_dst);
2509 /* Character above maximum entry. */
2510 if (src > map[max].vfm_src + map[max].vfm_len)
2511 return (0);
2512
2513 /* Binary search. */
2514 while (max >= min) {
2515 mid = (min + max) / 2;
2516 if (src < map[mid].vfm_src)
2517 max = mid - 1;
2518 else if (src > map[mid].vfm_src + map[mid].vfm_len)
2519 min = mid + 1;
2520 else
2521 return (src - map[mid].vfm_src + map[mid].vfm_dst);
2522 }
2523
2524 return (0);
2525 }
2526
2527 /*
2528 * Return glyph bitmap. If glyph is not found, we will return bitmap
2529 * for the first (offset 0) glyph.
2530 */
2531 uint8_t *
font_lookup(const struct vt_font * vf,teken_char_t c,const teken_attr_t * a)2532 font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
2533 {
2534 uint16_t dst;
2535 size_t stride;
2536
2537 /* Substitute bold with normal if not found. */
2538 if (a->ta_format & TF_BOLD) {
2539 dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
2540 vf->vf_map_count[VFNT_MAP_BOLD], c);
2541 if (dst != 0)
2542 goto found;
2543 }
2544 dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
2545 vf->vf_map_count[VFNT_MAP_NORMAL], c);
2546
2547 found:
2548 stride = howmany(vf->vf_width, 8) * vf->vf_height;
2549 return (&vf->vf_bytes[dst * stride]);
2550 }
2551
2552 static int
load_mapping(int fd,struct vt_font * fp,int n)2553 load_mapping(int fd, struct vt_font *fp, int n)
2554 {
2555 size_t i, size;
2556 ssize_t rv;
2557 vfnt_map_t *mp;
2558
2559 if (fp->vf_map_count[n] == 0)
2560 return (0);
2561
2562 size = fp->vf_map_count[n] * sizeof(*mp);
2563 mp = malloc(size);
2564 if (mp == NULL)
2565 return (ENOMEM);
2566 fp->vf_map[n] = mp;
2567
2568 rv = read(fd, mp, size);
2569 if (rv < 0 || (size_t)rv != size) {
2570 free(fp->vf_map[n]);
2571 fp->vf_map[n] = NULL;
2572 return (EIO);
2573 }
2574
2575 for (i = 0; i < fp->vf_map_count[n]; i++) {
2576 mp[i].vfm_src = be32toh(mp[i].vfm_src);
2577 mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
2578 mp[i].vfm_len = be16toh(mp[i].vfm_len);
2579 }
2580 return (0);
2581 }
2582
2583 static int
builtin_mapping(struct vt_font * fp,int n)2584 builtin_mapping(struct vt_font *fp, int n)
2585 {
2586 size_t size;
2587 struct vfnt_map *mp;
2588
2589 if (n >= VFNT_MAPS)
2590 return (EINVAL);
2591
2592 if (fp->vf_map_count[n] == 0)
2593 return (0);
2594
2595 size = fp->vf_map_count[n] * sizeof(*mp);
2596 mp = malloc(size);
2597 if (mp == NULL)
2598 return (ENOMEM);
2599 fp->vf_map[n] = mp;
2600
2601 memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
2602 return (0);
2603 }
2604
2605 /*
2606 * Load font from builtin or from file.
2607 * We do need special case for builtin because the builtin font glyphs
2608 * are compressed and we do need to uncompress them.
2609 * Having single load_font() for both cases will help us to simplify
2610 * font switch handling.
2611 */
2612 static vt_font_bitmap_data_t *
load_font(char * path)2613 load_font(char *path)
2614 {
2615 int fd, i;
2616 uint32_t glyphs;
2617 struct font_header fh;
2618 struct fontlist *fl;
2619 vt_font_bitmap_data_t *bp;
2620 struct vt_font *fp;
2621 size_t size;
2622 ssize_t rv;
2623
2624 /* Get our entry from the font list. */
2625 STAILQ_FOREACH(fl, &fonts, font_next) {
2626 if (strcmp(fl->font_name, path) == 0)
2627 break;
2628 }
2629 if (fl == NULL)
2630 return (NULL); /* Should not happen. */
2631
2632 bp = fl->font_data;
2633 if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
2634 return (bp);
2635
2636 fd = -1;
2637 /*
2638 * Special case for builtin font.
2639 * Builtin font is the very first font we load, we do not have
2640 * previous loads to be released.
2641 */
2642 if (fl->font_flags == FONT_BUILTIN) {
2643 if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
2644 return (NULL);
2645
2646 fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
2647 fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
2648
2649 fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
2650 if (fp->vf_bytes == NULL) {
2651 free(fp);
2652 return (NULL);
2653 }
2654
2655 bp->vfbd_uncompressed_size =
2656 DEFAULT_FONT_DATA.vfbd_uncompressed_size;
2657 bp->vfbd_compressed_size =
2658 DEFAULT_FONT_DATA.vfbd_compressed_size;
2659
2660 if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
2661 fp->vf_bytes,
2662 DEFAULT_FONT_DATA.vfbd_compressed_size,
2663 DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
2664 free(fp->vf_bytes);
2665 free(fp);
2666 return (NULL);
2667 }
2668
2669 for (i = 0; i < VFNT_MAPS; i++) {
2670 fp->vf_map_count[i] =
2671 DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
2672 if (builtin_mapping(fp, i) != 0)
2673 goto free_done;
2674 }
2675
2676 bp->vfbd_font = fp;
2677 return (bp);
2678 }
2679
2680 fd = open(path, O_RDONLY);
2681 if (fd < 0)
2682 return (NULL);
2683
2684 size = sizeof(fh);
2685 rv = read(fd, &fh, size);
2686 if (rv < 0 || (size_t)rv != size) {
2687 bp = NULL;
2688 goto done;
2689 }
2690 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
2691 bp = NULL;
2692 goto done;
2693 }
2694 if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
2695 bp = NULL;
2696 goto done;
2697 }
2698 for (i = 0; i < VFNT_MAPS; i++)
2699 fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
2700
2701 glyphs = be32toh(fh.fh_glyph_count);
2702 fp->vf_width = fh.fh_width;
2703 fp->vf_height = fh.fh_height;
2704
2705 size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
2706 bp->vfbd_uncompressed_size = size;
2707 if ((fp->vf_bytes = malloc(size)) == NULL)
2708 goto free_done;
2709
2710 rv = read(fd, fp->vf_bytes, size);
2711 if (rv < 0 || (size_t)rv != size)
2712 goto free_done;
2713 for (i = 0; i < VFNT_MAPS; i++) {
2714 if (load_mapping(fd, fp, i) != 0)
2715 goto free_done;
2716 }
2717
2718 /*
2719 * Reset builtin flag now as we have full font loaded.
2720 */
2721 if (fl->font_flags == FONT_BUILTIN)
2722 fl->font_flags = FONT_AUTO;
2723
2724 /*
2725 * Release previously loaded entries. We can do this now, as
2726 * the new font is loaded. Note, there can be no console
2727 * output till the new font is in place and teken is notified.
2728 * We do need to keep fl->font_data for glyph dimensions.
2729 */
2730 STAILQ_FOREACH(fl, &fonts, font_next) {
2731 if (fl->font_data->vfbd_font == NULL)
2732 continue;
2733
2734 for (i = 0; i < VFNT_MAPS; i++)
2735 free(fl->font_data->vfbd_font->vf_map[i]);
2736 free(fl->font_data->vfbd_font->vf_bytes);
2737 free(fl->font_data->vfbd_font);
2738 fl->font_data->vfbd_font = NULL;
2739 }
2740
2741 bp->vfbd_font = fp;
2742 bp->vfbd_compressed_size = 0;
2743
2744 done:
2745 if (fd != -1)
2746 close(fd);
2747 return (bp);
2748
2749 free_done:
2750 for (i = 0; i < VFNT_MAPS; i++)
2751 free(fp->vf_map[i]);
2752 free(fp->vf_bytes);
2753 free(fp);
2754 bp = NULL;
2755 goto done;
2756 }
2757
2758 struct name_entry {
2759 char *n_name;
2760 SLIST_ENTRY(name_entry) n_entry;
2761 };
2762
2763 SLIST_HEAD(name_list, name_entry);
2764
2765 /* Read font names from index file. */
2766 static struct name_list *
read_list(char * fonts)2767 read_list(char *fonts)
2768 {
2769 struct name_list *nl;
2770 struct name_entry *np;
2771 char *dir, *ptr;
2772 char buf[PATH_MAX];
2773 int fd, len;
2774
2775 TSENTER();
2776
2777 dir = strdup(fonts);
2778 if (dir == NULL)
2779 return (NULL);
2780
2781 ptr = strrchr(dir, '/');
2782 *ptr = '\0';
2783
2784 fd = open(fonts, O_RDONLY);
2785 if (fd < 0)
2786 return (NULL);
2787
2788 nl = malloc(sizeof(*nl));
2789 if (nl == NULL) {
2790 close(fd);
2791 return (nl);
2792 }
2793
2794 SLIST_INIT(nl);
2795 while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
2796 if (*buf == '#' || *buf == '\0')
2797 continue;
2798
2799 if (bcmp(buf, "MENU", 4) == 0)
2800 continue;
2801
2802 if (bcmp(buf, "FONT", 4) == 0)
2803 continue;
2804
2805 ptr = strchr(buf, ':');
2806 if (ptr == NULL)
2807 continue;
2808 else
2809 *ptr = '\0';
2810
2811 np = malloc(sizeof(*np));
2812 if (np == NULL) {
2813 close(fd);
2814 return (nl); /* return what we have */
2815 }
2816 if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
2817 free(np);
2818 close(fd);
2819 return (nl); /* return what we have */
2820 }
2821 SLIST_INSERT_HEAD(nl, np, n_entry);
2822 }
2823 close(fd);
2824 TSEXIT();
2825 return (nl);
2826 }
2827
2828 /*
2829 * Read the font properties and insert new entry into the list.
2830 * The font list is built in descending order.
2831 */
2832 static bool
insert_font(char * name,FONT_FLAGS flags)2833 insert_font(char *name, FONT_FLAGS flags)
2834 {
2835 struct font_header fh;
2836 struct fontlist *fp, *previous, *entry, *next;
2837 size_t size;
2838 ssize_t rv;
2839 int fd;
2840 char *font_name;
2841
2842 TSENTER();
2843
2844 font_name = NULL;
2845 if (flags == FONT_BUILTIN) {
2846 /*
2847 * We only install builtin font once, while setting up
2848 * initial console. Since this will happen very early,
2849 * we assume asprintf will not fail. Once we have access to
2850 * files, the builtin font will be replaced by font loaded
2851 * from file.
2852 */
2853 if (!STAILQ_EMPTY(&fonts))
2854 return (false);
2855
2856 fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
2857 fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
2858
2859 (void) asprintf(&font_name, "%dx%d",
2860 DEFAULT_FONT_DATA.vfbd_width,
2861 DEFAULT_FONT_DATA.vfbd_height);
2862 } else {
2863 fd = open(name, O_RDONLY);
2864 if (fd < 0)
2865 return (false);
2866 rv = read(fd, &fh, sizeof(fh));
2867 close(fd);
2868 if (rv < 0 || (size_t)rv != sizeof(fh))
2869 return (false);
2870
2871 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
2872 sizeof(fh.fh_magic)) != 0)
2873 return (false);
2874 font_name = strdup(name);
2875 }
2876
2877 if (font_name == NULL)
2878 return (false);
2879
2880 /*
2881 * If we have an entry with the same glyph dimensions, replace
2882 * the file name and mark us. We only support unique dimensions.
2883 */
2884 STAILQ_FOREACH(entry, &fonts, font_next) {
2885 if (fh.fh_width == entry->font_data->vfbd_width &&
2886 fh.fh_height == entry->font_data->vfbd_height) {
2887 free(entry->font_name);
2888 entry->font_name = font_name;
2889 entry->font_flags = FONT_RELOAD;
2890 TSEXIT();
2891 return (true);
2892 }
2893 }
2894
2895 fp = calloc(sizeof(*fp), 1);
2896 if (fp == NULL) {
2897 free(font_name);
2898 return (false);
2899 }
2900 fp->font_data = calloc(sizeof(*fp->font_data), 1);
2901 if (fp->font_data == NULL) {
2902 free(font_name);
2903 free(fp);
2904 return (false);
2905 }
2906 fp->font_name = font_name;
2907 fp->font_flags = flags;
2908 fp->font_load = load_font;
2909 fp->font_data->vfbd_width = fh.fh_width;
2910 fp->font_data->vfbd_height = fh.fh_height;
2911
2912 if (STAILQ_EMPTY(&fonts)) {
2913 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2914 TSEXIT();
2915 return (true);
2916 }
2917
2918 previous = NULL;
2919 size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
2920
2921 STAILQ_FOREACH(entry, &fonts, font_next) {
2922 vt_font_bitmap_data_t *bd;
2923
2924 bd = entry->font_data;
2925 /* Should fp be inserted before the entry? */
2926 if (size > bd->vfbd_width * bd->vfbd_height) {
2927 if (previous == NULL) {
2928 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2929 } else {
2930 STAILQ_INSERT_AFTER(&fonts, previous, fp,
2931 font_next);
2932 }
2933 TSEXIT();
2934 return (true);
2935 }
2936 next = STAILQ_NEXT(entry, font_next);
2937 if (next == NULL ||
2938 size > next->font_data->vfbd_width *
2939 next->font_data->vfbd_height) {
2940 STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2941 TSEXIT();
2942 return (true);
2943 }
2944 previous = entry;
2945 }
2946 TSEXIT();
2947 return (true);
2948 }
2949
2950 static int
font_set(struct env_var * ev __unused,int flags __unused,const void * value)2951 font_set(struct env_var *ev __unused, int flags __unused, const void *value)
2952 {
2953 struct fontlist *fl;
2954 char *eptr;
2955 unsigned long x = 0, y = 0;
2956
2957 /*
2958 * Attempt to extract values from "XxY" string. In case of error,
2959 * we have unmaching glyph dimensions and will just output the
2960 * available values.
2961 */
2962 if (value != NULL) {
2963 x = strtoul(value, &eptr, 10);
2964 if (*eptr == 'x')
2965 y = strtoul(eptr + 1, &eptr, 10);
2966 }
2967 STAILQ_FOREACH(fl, &fonts, font_next) {
2968 if (fl->font_data->vfbd_width == x &&
2969 fl->font_data->vfbd_height == y)
2970 break;
2971 }
2972 if (fl != NULL) {
2973 /* Reset any FONT_MANUAL flag. */
2974 reset_font_flags();
2975
2976 /* Mark this font manually loaded */
2977 fl->font_flags = FONT_MANUAL;
2978 cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2979 return (CMD_OK);
2980 }
2981
2982 printf("Available fonts:\n");
2983 STAILQ_FOREACH(fl, &fonts, font_next) {
2984 printf(" %dx%d\n", fl->font_data->vfbd_width,
2985 fl->font_data->vfbd_height);
2986 }
2987 return (CMD_OK);
2988 }
2989
2990 void
bios_text_font(bool use_vga_font)2991 bios_text_font(bool use_vga_font)
2992 {
2993 if (use_vga_font)
2994 (void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
2995 else
2996 (void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
2997 }
2998
2999 void
autoload_font(bool bios)3000 autoload_font(bool bios)
3001 {
3002 struct name_list *nl;
3003 struct name_entry *np;
3004
3005 TSENTER();
3006
3007 nl = read_list("/boot/fonts/INDEX.fonts");
3008 if (nl == NULL)
3009 return;
3010
3011 while (!SLIST_EMPTY(nl)) {
3012 np = SLIST_FIRST(nl);
3013 SLIST_REMOVE_HEAD(nl, n_entry);
3014 if (insert_font(np->n_name, FONT_AUTO) == false)
3015 printf("failed to add font: %s\n", np->n_name);
3016 free(np->n_name);
3017 free(np);
3018 }
3019
3020 /*
3021 * If vga text mode was requested, load vga.font (8x16 bold) font.
3022 */
3023 if (bios) {
3024 bios_text_font(true);
3025 }
3026
3027 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
3028
3029 TSEXIT();
3030 }
3031
3032 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
3033
3034 static int
command_font(int argc,char * argv[])3035 command_font(int argc, char *argv[])
3036 {
3037 int i, c, rc;
3038 struct fontlist *fl;
3039 vt_font_bitmap_data_t *bd;
3040 bool list;
3041
3042 list = false;
3043 optind = 1;
3044 optreset = 1;
3045 rc = CMD_OK;
3046
3047 while ((c = getopt(argc, argv, "l")) != -1) {
3048 switch (c) {
3049 case 'l':
3050 list = true;
3051 break;
3052 case '?':
3053 default:
3054 return (CMD_ERROR);
3055 }
3056 }
3057
3058 argc -= optind;
3059 argv += optind;
3060
3061 if (argc > 1 || (list && argc != 0)) {
3062 printf("Usage: loadfont [-l] | [file.fnt]\n");
3063 return (CMD_ERROR);
3064 }
3065
3066 if (list) {
3067 STAILQ_FOREACH(fl, &fonts, font_next) {
3068 printf("font %s: %dx%d%s\n", fl->font_name,
3069 fl->font_data->vfbd_width,
3070 fl->font_data->vfbd_height,
3071 fl->font_data->vfbd_font == NULL? "" : " loaded");
3072 }
3073 return (CMD_OK);
3074 }
3075
3076 /* Clear scren */
3077 cons_clear();
3078
3079 if (argc == 1) {
3080 char *name = argv[0];
3081
3082 if (insert_font(name, FONT_MANUAL) == false) {
3083 printf("loadfont error: failed to load: %s\n", name);
3084 return (CMD_ERROR);
3085 }
3086
3087 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
3088 return (CMD_OK);
3089 }
3090
3091 if (argc == 0) {
3092 /*
3093 * Walk entire font list, release any loaded font, and set
3094 * autoload flag. The font list does have at least the builtin
3095 * default font.
3096 */
3097 STAILQ_FOREACH(fl, &fonts, font_next) {
3098 if (fl->font_data->vfbd_font != NULL) {
3099
3100 bd = fl->font_data;
3101 /*
3102 * Note the setup_font() is releasing
3103 * font bytes.
3104 */
3105 for (i = 0; i < VFNT_MAPS; i++)
3106 free(bd->vfbd_font->vf_map[i]);
3107 free(fl->font_data->vfbd_font);
3108 fl->font_data->vfbd_font = NULL;
3109 fl->font_data->vfbd_uncompressed_size = 0;
3110 fl->font_flags = FONT_AUTO;
3111 }
3112 }
3113 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
3114 }
3115 return (rc);
3116 }
3117
3118 bool
gfx_get_edid_resolution(struct vesa_edid_info * edid,edid_res_list_t * res)3119 gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
3120 {
3121 struct resolution *rp, *p;
3122
3123 /*
3124 * Walk detailed timings tables (4).
3125 */
3126 if ((edid->display.supported_features
3127 & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
3128 /* Walk detailed timing descriptors (4) */
3129 for (int i = 0; i < DET_TIMINGS; i++) {
3130 /*
3131 * Reserved value 0 is not used for display descriptor.
3132 */
3133 if (edid->detailed_timings[i].pixel_clock == 0)
3134 continue;
3135 if ((rp = malloc(sizeof(*rp))) == NULL)
3136 continue;
3137 rp->width = GET_EDID_INFO_WIDTH(edid, i);
3138 rp->height = GET_EDID_INFO_HEIGHT(edid, i);
3139 if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
3140 rp->height > 0 && rp->height <= EDID_MAX_LINES)
3141 TAILQ_INSERT_TAIL(res, rp, next);
3142 else
3143 free(rp);
3144 }
3145 }
3146
3147 /*
3148 * Walk standard timings list (8).
3149 */
3150 for (int i = 0; i < STD_TIMINGS; i++) {
3151 /* Is this field unused? */
3152 if (edid->standard_timings[i] == 0x0101)
3153 continue;
3154
3155 if ((rp = malloc(sizeof(*rp))) == NULL)
3156 continue;
3157
3158 rp->width = HSIZE(edid->standard_timings[i]);
3159 switch (RATIO(edid->standard_timings[i])) {
3160 case RATIO1_1:
3161 rp->height = HSIZE(edid->standard_timings[i]);
3162 if (edid->header.version > 1 ||
3163 edid->header.revision > 2) {
3164 rp->height = rp->height * 10 / 16;
3165 }
3166 break;
3167 case RATIO4_3:
3168 rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
3169 break;
3170 case RATIO5_4:
3171 rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
3172 break;
3173 case RATIO16_9:
3174 rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
3175 break;
3176 }
3177
3178 /*
3179 * Create resolution list in decreasing order, except keep
3180 * first entry (preferred timing mode).
3181 */
3182 TAILQ_FOREACH(p, res, next) {
3183 if (p->width * p->height < rp->width * rp->height) {
3184 /* Keep preferred mode first */
3185 if (TAILQ_FIRST(res) == p)
3186 TAILQ_INSERT_AFTER(res, p, rp, next);
3187 else
3188 TAILQ_INSERT_BEFORE(p, rp, next);
3189 break;
3190 }
3191 if (TAILQ_NEXT(p, next) == NULL) {
3192 TAILQ_INSERT_TAIL(res, rp, next);
3193 break;
3194 }
3195 }
3196 }
3197 return (!TAILQ_EMPTY(res));
3198 }
3199
3200 vm_offset_t
build_font_module(vm_offset_t addr)3201 build_font_module(vm_offset_t addr)
3202 {
3203 vt_font_bitmap_data_t *bd;
3204 struct vt_font *fd;
3205 struct preloaded_file *fp;
3206 size_t size;
3207 uint32_t checksum;
3208 int i;
3209 struct font_info fi;
3210 struct fontlist *fl;
3211 uint64_t fontp;
3212
3213 if (STAILQ_EMPTY(&fonts))
3214 return (addr);
3215
3216 /* We can't load first */
3217 if ((file_findfile(NULL, NULL)) == NULL) {
3218 printf("Can not load font module: %s\n",
3219 "the kernel is not loaded");
3220 return (addr);
3221 }
3222
3223 /* helper pointers */
3224 bd = NULL;
3225 STAILQ_FOREACH(fl, &fonts, font_next) {
3226 if (gfx_state.tg_font.vf_width == fl->font_data->vfbd_width &&
3227 gfx_state.tg_font.vf_height == fl->font_data->vfbd_height) {
3228 /*
3229 * Kernel does have better built in font.
3230 */
3231 if (fl->font_flags == FONT_BUILTIN)
3232 return (addr);
3233
3234 bd = fl->font_data;
3235 break;
3236 }
3237 }
3238 if (bd == NULL)
3239 return (addr);
3240 fd = bd->vfbd_font;
3241
3242 fi.fi_width = fd->vf_width;
3243 checksum = fi.fi_width;
3244 fi.fi_height = fd->vf_height;
3245 checksum += fi.fi_height;
3246 fi.fi_bitmap_size = bd->vfbd_uncompressed_size;
3247 checksum += fi.fi_bitmap_size;
3248
3249 size = roundup2(sizeof (struct font_info), 8);
3250 for (i = 0; i < VFNT_MAPS; i++) {
3251 fi.fi_map_count[i] = fd->vf_map_count[i];
3252 checksum += fi.fi_map_count[i];
3253 size += fd->vf_map_count[i] * sizeof (struct vfnt_map);
3254 size += roundup2(size, 8);
3255 }
3256 size += bd->vfbd_uncompressed_size;
3257
3258 fi.fi_checksum = -checksum;
3259
3260 fp = file_findfile(NULL, md_kerntype);
3261 if (fp == NULL)
3262 panic("can't find kernel file");
3263
3264 fontp = addr;
3265 addr += archsw.arch_copyin(&fi, addr, sizeof (struct font_info));
3266 addr = roundup2(addr, 8);
3267
3268 /* Copy maps. */
3269 for (i = 0; i < VFNT_MAPS; i++) {
3270 if (fd->vf_map_count[i] != 0) {
3271 addr += archsw.arch_copyin(fd->vf_map[i], addr,
3272 fd->vf_map_count[i] * sizeof (struct vfnt_map));
3273 addr = roundup2(addr, 8);
3274 }
3275 }
3276
3277 /* Copy the bitmap. */
3278 addr += archsw.arch_copyin(fd->vf_bytes, addr, fi.fi_bitmap_size);
3279
3280 /* Looks OK so far; populate control structure */
3281 file_addmetadata(fp, MODINFOMD_FONT, sizeof(fontp), &fontp);
3282 return (addr);
3283 }
3284
3285 vm_offset_t
build_splash_module(vm_offset_t addr,int type)3286 build_splash_module(vm_offset_t addr, int type)
3287 {
3288 struct preloaded_file *fp;
3289 struct splash_info si;
3290 const char *splash;
3291 png_t png;
3292 uint64_t splashp;
3293 int error;
3294
3295 /* We can't load first */
3296 if ((file_findfile(NULL, NULL)) == NULL) {
3297 printf("Can not load splash module: %s\n",
3298 "the kernel is not loaded");
3299 return (addr);
3300 }
3301
3302 fp = file_findfile(NULL, md_kerntype);
3303 if (fp == NULL)
3304 panic("can't find kernel file");
3305
3306 if (type == SPLASH_STARTUP)
3307 splash = getenv("splash");
3308 if (type == SPLASH_SHUTDOWN)
3309 splash = getenv("shutdown_splash");
3310
3311 if (splash == NULL)
3312 return (addr);
3313
3314 /* Parse png */
3315 if ((error = png_open(&png, splash)) != PNG_NO_ERROR) {
3316 return (addr);
3317 }
3318
3319 si.si_width = png.width;
3320 si.si_height = png.height;
3321 si.si_depth = png.bpp;
3322 splashp = addr;
3323 addr += archsw.arch_copyin(&si, addr, sizeof (struct splash_info));
3324 addr = roundup2(addr, 8);
3325
3326 /* Copy the bitmap. */
3327 addr += archsw.arch_copyin(png.image, addr, png.png_datalen);
3328
3329 if (type == SPLASH_STARTUP) {
3330 printf("Loading splash ok\n");
3331 file_addmetadata(fp, MODINFOMD_SPLASH,
3332 sizeof(splashp), &splashp);
3333 }
3334 if (type == SPLASH_SHUTDOWN) {
3335 printf("Loading shutdown splash ok\n");
3336 file_addmetadata(fp, MODINFOMD_SHTDWNSPLASH,
3337 sizeof(splashp), &splashp);
3338 }
3339 return (addr);
3340 }
3341