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