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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 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 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 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 * 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 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 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 * 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 * 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 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 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 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 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 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 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 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 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