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