1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Toomas Soome <tsoome@me.com> 14 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. 15 * Copyright 2020 RackTop Systems, Inc. 16 */ 17 18 /* 19 * The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI 20 * GOP Blt, and allows us to fill the rectangle on screen, copy 21 * rectangle from video to buffer and buffer to video and video to video. 22 * Such implementation does allow us to have almost identical implementation 23 * for both BIOS VBE and UEFI. 24 * 25 * ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red, 26 * Alpha) format, this allows us to only handle RGB data and not to worry 27 * about mixing RGB with indexed colors. 28 * Data exchange between memory buffer and video will translate BGRA 29 * and native format as following: 30 * 31 * 32-bit to/from 32-bit is trivial case. 32 * 32-bit to/from 24-bit is also simple - we just drop the alpha channel. 33 * 32-bit to/from 16-bit is more complicated, because we nee to handle 34 * data loss from 32-bit to 16-bit. While reading/writing from/to video, we 35 * need to apply masks of 16-bit color components. This will preserve 36 * colors for terminal text. For 32-bit truecolor PMG images, we need to 37 * translate 32-bit colors to 15/16 bit colors and this means data loss. 38 * There are different algorithms how to perform such color space reduction, 39 * we are currently using bitwise right shift to reduce color space and so far 40 * this technique seems to be sufficient (see also gfx_fb_putimage(), the 41 * end of for loop). 42 * 32-bit to/from 8-bit is the most troublesome because 8-bit colors are 43 * indexed. From video, we do get color indexes, and we do translate 44 * color index values to RGB. To write to video, we again need to translate 45 * RGB to color index. Additionally, we need to translate between VGA and 46 * Sun colors. 47 * 48 * Our internal color data is represented using BGRA format. But the hardware 49 * used indexed colors for 8-bit colors (0-255) and for this mode we do 50 * need to perform translation to/from BGRA and index values. 51 * 52 * - paletteentry RGB <-> index - 53 * BGRA BUFFER <----/ \ - VIDEO 54 * \ / 55 * - RGB (16/24/32) - 56 * 57 * To perform index to RGB translation, we use palette table generated 58 * from when we set up 8-bit mode video. We cannot read palette data from 59 * the hardware, because not all hardware supports reading it. 60 * 61 * BGRA to index is implemented in rgb_to_color_index() by searching 62 * palette array for closest match of RBG values. 63 * 64 * Note: In 8-bit mode, We do store first 16 colors to palette registers 65 * in VGA color order, this serves two purposes; firstly, 66 * if palette update is not supported, we still have correct 16 colors. 67 * Secondly, the kernel does get correct 16 colors when some other boot 68 * loader is used. However, the palette map for 8-bit colors is using 69 * Sun color ordering - this does allow us to skip translation 70 * from VGA colors to Sun colors, while we are reading RGB data. 71 */ 72 73 #include <sys/cdefs.h> 74 #include <sys/param.h> 75 #include <stand.h> 76 #if defined(EFI) 77 #include <efi.h> 78 #include <efilib.h> 79 #else 80 #include <btxv86.h> 81 #include <vbe.h> 82 #endif 83 #include <sys/tem_impl.h> 84 #include <sys/consplat.h> 85 #include <sys/visual_io.h> 86 #include <sys/multiboot2.h> 87 #include <sys/font.h> 88 #include <sys/rgb.h> 89 #include <sys/endian.h> 90 #include <gfx_fb.h> 91 #include <pnglite.h> 92 #include <bootstrap.h> 93 #include <lz4.h> 94 95 /* VGA text mode does use bold font. */ 96 #if !defined(VGA_8X16_FONT) 97 #define VGA_8X16_FONT "/boot/fonts/8x16b.fnt" 98 #endif 99 #if !defined(DEFAULT_8X16_FONT) 100 #define DEFAULT_8X16_FONT "/boot/fonts/8x16.fnt" 101 #endif 102 103 /* 104 * Global framebuffer struct, to be updated with mode changes. 105 */ 106 multiboot_tag_framebuffer_t gfx_fb; 107 108 /* To support setenv, keep track of inverses and colors. */ 109 static int gfx_inverse = 0; 110 static int gfx_inverse_screen = 0; 111 static uint8_t gfx_fg = DEFAULT_ANSI_FOREGROUND; 112 static uint8_t gfx_bg = DEFAULT_ANSI_BACKGROUND; 113 #if defined(EFI) 114 static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer; 115 #else 116 static struct paletteentry *GlyphBuffer; 117 #endif 118 static size_t GlyphBufferSize; 119 120 int gfx_fb_cons_clear(struct vis_consclear *); 121 void gfx_fb_cons_copy(struct vis_conscopy *); 122 void gfx_fb_cons_display(struct vis_consdisplay *); 123 124 static bool insert_font(char *, FONT_FLAGS); 125 126 /* 127 * Set default operations to use bitmap based implementation. 128 * In case of UEFI, if GOP is available, we will switch to GOP based 129 * implementation. 130 * 131 * Also note, for UEFI we do attempt to boost the execution by setting 132 * Task Priority Level (TPL) to TPL_NOTIFY, which is highest priority 133 * usable in application. 134 */ 135 136 /* 137 * Translate platform specific FB address. 138 */ 139 static uint8_t * 140 gfx_get_fb_address(void) 141 { 142 #if defined(EFI) 143 return ((uint8_t *)(uintptr_t) 144 gfx_fb.framebuffer_common.framebuffer_addr); 145 #else 146 return ((uint8_t *)PTOV((uint32_t) 147 gfx_fb.framebuffer_common.framebuffer_addr & 0xffffffff)); 148 #endif 149 } 150 151 /* 152 * Generic platform callbacks for tem. 153 */ 154 void 155 plat_tem_get_prom_font_size(int *charheight, int *windowtop) 156 { 157 *charheight = 0; 158 *windowtop = 0; 159 } 160 161 void 162 plat_tem_get_colors(uint8_t *fg, uint8_t *bg) 163 { 164 *fg = gfx_fg; 165 *bg = gfx_bg; 166 } 167 168 void 169 plat_tem_get_inverses(int *inverse, int *inverse_screen) 170 { 171 *inverse = gfx_inverse; 172 *inverse_screen = gfx_inverse_screen; 173 } 174 175 /* 176 * Utility function to parse gfx mode line strings. 177 */ 178 bool 179 gfx_parse_mode_str(char *str, int *x, int *y, int *depth) 180 { 181 char *p, *end; 182 183 errno = 0; 184 p = str; 185 *x = strtoul(p, &end, 0); 186 if (*x == 0 || errno != 0) 187 return (false); 188 if (*end != 'x') 189 return (false); 190 p = end + 1; 191 *y = strtoul(p, &end, 0); 192 if (*y == 0 || errno != 0) 193 return (false); 194 if (*end != 'x') { 195 *depth = -1; /* auto select */ 196 } else { 197 p = end + 1; 198 *depth = strtoul(p, &end, 0); 199 if (*depth == 0 || errno != 0 || *end != '\0') 200 return (false); 201 } 202 203 return (true); 204 } 205 206 /* 207 * Support for color mapping. 208 * For 8, 24 and 32 bit depth, use mask size 8. 209 * 15/16 bit depth needs to use mask size from mode, 210 * or we will lose color information from 32-bit to 15/16 bit translation. 211 */ 212 uint32_t 213 gfx_fb_color_map(uint8_t index) 214 { 215 rgb_t rgb; 216 int bpp; 217 218 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3; 219 220 rgb.red.pos = 16; 221 if (bpp == 2) 222 rgb.red.size = gfx_fb.u.fb2.framebuffer_red_mask_size; 223 else 224 rgb.red.size = 8; 225 226 rgb.green.pos = 8; 227 if (bpp == 2) 228 rgb.green.size = gfx_fb.u.fb2.framebuffer_green_mask_size; 229 else 230 rgb.green.size = 8; 231 232 rgb.blue.pos = 0; 233 if (bpp == 2) 234 rgb.blue.size = gfx_fb.u.fb2.framebuffer_blue_mask_size; 235 else 236 rgb.blue.size = 8; 237 238 return (rgb_color_map(&rgb, index)); 239 } 240 241 static bool 242 color_name_to_ansi(const char *name, int *val) 243 { 244 if (strcasecmp(name, "black") == 0) { 245 *val = ANSI_COLOR_BLACK; 246 return (true); 247 } 248 if (strcasecmp(name, "red") == 0) { 249 *val = ANSI_COLOR_RED; 250 return (true); 251 } 252 if (strcasecmp(name, "green") == 0) { 253 *val = ANSI_COLOR_GREEN; 254 return (true); 255 } 256 if (strcasecmp(name, "yellow") == 0) { 257 *val = ANSI_COLOR_YELLOW; 258 return (true); 259 } 260 if (strcasecmp(name, "blue") == 0) { 261 *val = ANSI_COLOR_BLUE; 262 return (true); 263 } 264 if (strcasecmp(name, "magenta") == 0) { 265 *val = ANSI_COLOR_MAGENTA; 266 return (true); 267 } 268 if (strcasecmp(name, "cyan") == 0) { 269 *val = ANSI_COLOR_CYAN; 270 return (true); 271 } 272 if (strcasecmp(name, "white") == 0) { 273 *val = ANSI_COLOR_WHITE; 274 return (true); 275 } 276 return (false); 277 } 278 279 /* Callback to check and set colors */ 280 static int 281 gfx_set_colors(struct env_var *ev, int flags, const void *value) 282 { 283 int val = 0, limit; 284 char buf[2]; 285 const void *evalue; 286 287 if (value == NULL) 288 return (CMD_OK); 289 290 limit = 255; 291 292 if (color_name_to_ansi(value, &val)) { 293 snprintf(buf, sizeof (buf), "%d", val); 294 evalue = buf; 295 } else { 296 char *end; 297 298 errno = 0; 299 val = (int)strtol(value, &end, 0); 300 if (errno != 0 || *end != '\0') { 301 printf("Allowed values are either ansi color name or " 302 "number from range [0-255].\n"); 303 return (CMD_OK); 304 } 305 evalue = value; 306 } 307 308 /* invalid value? */ 309 if ((val < 0 || val > limit)) { 310 printf("Allowed values are either ansi color name or " 311 "number from range [0-255].\n"); 312 return (CMD_OK); 313 } 314 315 if (strcmp(ev->ev_name, "tem.fg_color") == 0) { 316 /* is it already set? */ 317 if (gfx_fg == val) 318 return (CMD_OK); 319 gfx_fg = val; 320 } 321 if (strcmp(ev->ev_name, "tem.bg_color") == 0) { 322 /* is it already set? */ 323 if (gfx_bg == val) 324 return (CMD_OK); 325 gfx_bg = val; 326 } 327 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); 328 plat_cons_update_mode(-1); 329 return (CMD_OK); 330 } 331 332 /* Callback to check and set inverses */ 333 static int 334 gfx_set_inverses(struct env_var *ev, int flags, const void *value) 335 { 336 int t, f; 337 338 if (value == NULL) 339 return (CMD_OK); 340 341 t = strcmp(value, "true"); 342 f = strcmp(value, "false"); 343 344 /* invalid value? */ 345 if (t != 0 && f != 0) 346 return (CMD_OK); 347 348 if (strcmp(ev->ev_name, "tem.inverse") == 0) { 349 /* is it already set? */ 350 if (gfx_inverse == (t == 0)) 351 return (CMD_OK); 352 gfx_inverse = (t == 0); 353 } 354 if (strcmp(ev->ev_name, "tem.inverse-screen") == 0) { 355 /* is it already set? */ 356 if (gfx_inverse_screen == (t == 0)) 357 return (CMD_OK); 358 gfx_inverse_screen = (t == 0); 359 } 360 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 361 plat_cons_update_mode(-1); 362 return (CMD_OK); 363 } 364 365 /* 366 * Initialize gfx framework. 367 */ 368 void 369 gfx_framework_init(void) 370 { 371 int rc, limit; 372 char *env, buf[2]; 373 374 if (gfx_fb.framebuffer_common.framebuffer_bpp < 24) 375 limit = 7; 376 else 377 limit = 255; 378 379 /* set up tem inverse controls */ 380 env = getenv("tem.inverse"); 381 if (env != NULL) { 382 if (strcmp(env, "true") == 0) 383 gfx_inverse = 1; 384 unsetenv("tem.inverse"); 385 } 386 387 env = getenv("tem.inverse-screen"); 388 if (env != NULL) { 389 if (strcmp(env, "true") == 0) 390 gfx_inverse_screen = 1; 391 unsetenv("tem.inverse-screen"); 392 } 393 394 if (gfx_inverse) 395 env = "true"; 396 else 397 env = "false"; 398 399 env_setenv("tem.inverse", EV_VOLATILE, env, gfx_set_inverses, 400 env_nounset); 401 402 if (gfx_inverse_screen) 403 env = "true"; 404 else 405 env = "false"; 406 407 env_setenv("tem.inverse-screen", EV_VOLATILE, env, gfx_set_inverses, 408 env_nounset); 409 410 /* set up tem color controls */ 411 env = getenv("tem.fg_color"); 412 if (env != NULL) { 413 rc = (int)strtol(env, NULL, 0); 414 if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16)) 415 gfx_fg = rc; 416 unsetenv("tem.fg_color"); 417 } 418 419 env = getenv("tem.bg_color"); 420 if (env != NULL) { 421 rc = (int)strtol(env, NULL, 0); 422 if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16)) 423 gfx_bg = rc; 424 unsetenv("tem.bg_color"); 425 } 426 427 snprintf(buf, sizeof (buf), "%d", gfx_fg); 428 env_setenv("tem.fg_color", EV_VOLATILE, buf, gfx_set_colors, 429 env_nounset); 430 snprintf(buf, sizeof (buf), "%d", gfx_bg); 431 env_setenv("tem.bg_color", EV_VOLATILE, buf, gfx_set_colors, 432 env_nounset); 433 434 /* 435 * Setup font list to have builtin font. 436 */ 437 (void) insert_font(NULL, FONT_BUILTIN); 438 } 439 440 /* 441 * Get indexed color from RGB. This function is used to write data to video 442 * memory when the adapter is set to use indexed colors. 443 * Since UEFI does only support 32-bit colors, we do not implement it for 444 * UEFI because there is no need for it and we do not have palette array 445 * for UEFI. 446 */ 447 static uint8_t 448 rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b) 449 { 450 #if !defined(EFI) 451 uint32_t color, best, dist, k; 452 int diff; 453 454 color = 0; 455 best = 255 * 255 * 255; 456 for (k = 0; k < NCMAP; k++) { 457 diff = r - pe8[k].Red; 458 dist = diff * diff; 459 diff = g - pe8[k].Green; 460 dist += diff * diff; 461 diff = b - pe8[k].Blue; 462 dist += diff * diff; 463 464 /* Exact match, exit the loop */ 465 if (dist == 0) 466 break; 467 468 if (dist < best) { 469 color = k; 470 best = dist; 471 } 472 } 473 if (k == NCMAP) 474 k = color; 475 return (k); 476 #else 477 (void) r; 478 (void) g; 479 (void) b; 480 return (0); 481 #endif 482 } 483 484 static void 485 gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v) 486 { 487 488 if (o >= size) 489 return; 490 *(uint8_t *)(base + o) = v; 491 } 492 493 static void 494 gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v) 495 { 496 497 if (o >= size) 498 return; 499 *(uint16_t *)(base + o) = v; 500 } 501 502 static void 503 gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v) 504 { 505 506 if (o >= size) 507 return; 508 *(uint32_t *)(base + o) = v; 509 } 510 511 static int 512 gfxfb_blt_fill(void *BltBuffer, 513 uint32_t DestinationX, uint32_t DestinationY, 514 uint32_t Width, uint32_t Height) 515 { 516 #if defined(EFI) 517 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; 518 #else 519 struct paletteentry *p; 520 #endif 521 uint32_t data, bpp, pitch, y, x; 522 size_t size; 523 off_t off; 524 uint8_t *destination; 525 526 if (BltBuffer == NULL) 527 return (EINVAL); 528 529 if (DestinationY + Height > 530 gfx_fb.framebuffer_common.framebuffer_height) 531 return (EINVAL); 532 533 if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width) 534 return (EINVAL); 535 536 if (Width == 0 || Height == 0) 537 return (EINVAL); 538 539 p = BltBuffer; 540 if (gfx_fb.framebuffer_common.framebuffer_bpp == 8) { 541 data = rgb_to_color_index(p->Red, p->Green, p->Blue); 542 } else { 543 data = (p->Red & 544 ((1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1)) << 545 gfx_fb.u.fb2.framebuffer_red_field_position; 546 data |= (p->Green & 547 ((1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1)) << 548 gfx_fb.u.fb2.framebuffer_green_field_position; 549 data |= (p->Blue & 550 ((1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1)) << 551 gfx_fb.u.fb2.framebuffer_blue_field_position; 552 } 553 554 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3; 555 pitch = gfx_fb.framebuffer_common.framebuffer_pitch; 556 destination = gfx_get_fb_address(); 557 size = gfx_fb.framebuffer_common.framebuffer_height * pitch; 558 559 for (y = DestinationY; y < Height + DestinationY; y++) { 560 off = y * pitch + DestinationX * bpp; 561 for (x = 0; x < Width; x++) { 562 switch (bpp) { 563 case 1: 564 gfx_mem_wr1(destination, size, off, 565 (data < NCOLORS) ? 566 solaris_color_to_pc_color[data] : data); 567 break; 568 case 2: 569 gfx_mem_wr2(destination, size, off, data); 570 break; 571 case 3: 572 gfx_mem_wr1(destination, size, off, 573 (data >> 16) & 0xff); 574 gfx_mem_wr1(destination, size, off + 1, 575 (data >> 8) & 0xff); 576 gfx_mem_wr1(destination, size, off + 2, 577 data & 0xff); 578 break; 579 case 4: 580 gfx_mem_wr4(destination, size, off, data); 581 break; 582 default: 583 return (EINVAL); 584 } 585 off += bpp; 586 } 587 } 588 589 return (0); 590 } 591 592 static int 593 gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY, 594 uint32_t DestinationX, uint32_t DestinationY, 595 uint32_t Width, uint32_t Height, uint32_t Delta) 596 { 597 #if defined(EFI) 598 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; 599 #else 600 struct paletteentry *p; 601 #endif 602 uint32_t x, sy, dy; 603 uint32_t bpp, pitch, copybytes; 604 off_t off; 605 uint8_t *source, *destination, *sb; 606 uint8_t rm, rp, gm, gp, bm, bp; 607 bool bgra; 608 609 if (BltBuffer == NULL) 610 return (EINVAL); 611 612 if (SourceY + Height > 613 gfx_fb.framebuffer_common.framebuffer_height) 614 return (EINVAL); 615 616 if (SourceX + Width > gfx_fb.framebuffer_common.framebuffer_width) 617 return (EINVAL); 618 619 if (Width == 0 || Height == 0) 620 return (EINVAL); 621 622 if (Delta == 0) 623 Delta = Width * sizeof (*p); 624 625 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3; 626 pitch = gfx_fb.framebuffer_common.framebuffer_pitch; 627 628 copybytes = Width * bpp; 629 630 rm = (1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1; 631 rp = gfx_fb.u.fb2.framebuffer_red_field_position; 632 gm = (1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1; 633 gp = gfx_fb.u.fb2.framebuffer_green_field_position; 634 bm = (1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1; 635 bp = gfx_fb.u.fb2.framebuffer_blue_field_position; 636 /* If FB pixel format is BGRA, we can use direct copy. */ 637 bgra = bpp == 4 && 638 gfx_fb.u.fb2.framebuffer_red_mask_size == 8 && 639 gfx_fb.u.fb2.framebuffer_red_field_position == 16 && 640 gfx_fb.u.fb2.framebuffer_green_mask_size == 8 && 641 gfx_fb.u.fb2.framebuffer_green_field_position == 8 && 642 gfx_fb.u.fb2.framebuffer_blue_mask_size == 8 && 643 gfx_fb.u.fb2.framebuffer_blue_field_position == 0; 644 645 for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY; 646 sy++, dy++) { 647 off = sy * pitch + SourceX * bpp; 648 source = gfx_get_fb_address() + off; 649 destination = (uint8_t *)BltBuffer + dy * Delta + 650 DestinationX * sizeof (*p); 651 652 if (bgra) { 653 bcopy(source, destination, copybytes); 654 } else { 655 for (x = 0; x < Width; x++) { 656 uint32_t c = 0; 657 658 p = (void *)(destination + x * sizeof (*p)); 659 sb = source + x * bpp; 660 switch (bpp) { 661 case 1: 662 c = *sb; 663 break; 664 case 2: 665 c = *(uint16_t *)sb; 666 break; 667 case 3: 668 c = sb[0] << 16 | sb[1] << 8 | sb[2]; 669 break; 670 case 4: 671 c = *(uint32_t *)sb; 672 break; 673 default: 674 return (EINVAL); 675 } 676 677 if (bpp == 1) { 678 *(uint32_t *)p = gfx_fb_color_map( 679 (c < NCOLORS) ? 680 pc_color_to_solaris_color[c] : c); 681 } else { 682 p->Red = (c >> rp) & rm; 683 p->Green = (c >> gp) & gm; 684 p->Blue = (c >> bp) & bm; 685 p->Reserved = 0; 686 } 687 } 688 } 689 } 690 691 return (0); 692 } 693 694 static int 695 gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY, 696 uint32_t DestinationX, uint32_t DestinationY, 697 uint32_t Width, uint32_t Height, uint32_t Delta) 698 { 699 #if defined(EFI) 700 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; 701 #else 702 struct paletteentry *p; 703 #endif 704 uint32_t x, sy, dy; 705 uint32_t bpp, pitch, copybytes; 706 off_t off; 707 uint8_t *source, *destination; 708 uint8_t rm, rp, gm, gp, bm, bp; 709 bool bgra; 710 711 if (BltBuffer == NULL) 712 return (EINVAL); 713 714 if (DestinationY + Height > 715 gfx_fb.framebuffer_common.framebuffer_height) 716 return (EINVAL); 717 718 if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width) 719 return (EINVAL); 720 721 if (Width == 0 || Height == 0) 722 return (EINVAL); 723 724 if (Delta == 0) 725 Delta = Width * sizeof (*p); 726 727 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3; 728 pitch = gfx_fb.framebuffer_common.framebuffer_pitch; 729 730 copybytes = Width * bpp; 731 732 rm = (1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1; 733 rp = gfx_fb.u.fb2.framebuffer_red_field_position; 734 gm = (1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1; 735 gp = gfx_fb.u.fb2.framebuffer_green_field_position; 736 bm = (1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1; 737 bp = gfx_fb.u.fb2.framebuffer_blue_field_position; 738 /* If FB pixel format is BGRA, we can use direct copy. */ 739 bgra = bpp == 4 && 740 gfx_fb.u.fb2.framebuffer_red_mask_size == 8 && 741 gfx_fb.u.fb2.framebuffer_red_field_position == 16 && 742 gfx_fb.u.fb2.framebuffer_green_mask_size == 8 && 743 gfx_fb.u.fb2.framebuffer_green_field_position == 8 && 744 gfx_fb.u.fb2.framebuffer_blue_mask_size == 8 && 745 gfx_fb.u.fb2.framebuffer_blue_field_position == 0; 746 747 for (sy = SourceY, dy = DestinationY; sy < Height + SourceY; 748 sy++, dy++) { 749 off = dy * pitch + DestinationX * bpp; 750 destination = gfx_get_fb_address() + off; 751 752 if (bgra) { 753 source = (uint8_t *)BltBuffer + sy * Delta + 754 SourceX * sizeof (*p); 755 bcopy(source, destination, copybytes); 756 } else { 757 for (x = 0; x < Width; x++) { 758 uint32_t c; 759 760 p = (void *)((uint8_t *)BltBuffer + 761 sy * Delta + 762 (SourceX + x) * sizeof (*p)); 763 if (bpp == 1) { 764 c = rgb_to_color_index(p->Red, 765 p->Green, p->Blue); 766 } else { 767 c = (p->Red & rm) << rp | 768 (p->Green & gm) << gp | 769 (p->Blue & bm) << bp; 770 } 771 off = x * bpp; 772 switch (bpp) { 773 case 1: 774 gfx_mem_wr1(destination, copybytes, 775 off, (c < NCOLORS) ? 776 solaris_color_to_pc_color[c] : c); 777 break; 778 case 2: 779 gfx_mem_wr2(destination, copybytes, 780 off, c); 781 break; 782 case 3: 783 gfx_mem_wr1(destination, copybytes, 784 off, (c >> 16) & 0xff); 785 gfx_mem_wr1(destination, copybytes, 786 off + 1, (c >> 8) & 0xff); 787 gfx_mem_wr1(destination, copybytes, 788 off + 2, c & 0xff); 789 break; 790 case 4: 791 gfx_mem_wr4(destination, copybytes, 792 off, c); 793 break; 794 default: 795 return (EINVAL); 796 } 797 } 798 } 799 } 800 801 return (0); 802 } 803 804 static int 805 gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY, 806 uint32_t DestinationX, uint32_t DestinationY, 807 uint32_t Width, uint32_t Height) 808 { 809 uint32_t bpp, copybytes; 810 int pitch; 811 uint8_t *source, *destination; 812 off_t off; 813 814 if (SourceY + Height > 815 gfx_fb.framebuffer_common.framebuffer_height) 816 return (EINVAL); 817 818 if (SourceX + Width > gfx_fb.framebuffer_common.framebuffer_width) 819 return (EINVAL); 820 821 if (DestinationY + Height > 822 gfx_fb.framebuffer_common.framebuffer_height) 823 return (EINVAL); 824 825 if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width) 826 return (EINVAL); 827 828 if (Width == 0 || Height == 0) 829 return (EINVAL); 830 831 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3; 832 pitch = gfx_fb.framebuffer_common.framebuffer_pitch; 833 834 copybytes = Width * bpp; 835 836 off = SourceY * pitch + SourceX * bpp; 837 source = gfx_get_fb_address() + off; 838 off = DestinationY * pitch + DestinationX * bpp; 839 destination = gfx_get_fb_address() + off; 840 841 /* 842 * To handle overlapping areas, set up reverse copy here. 843 */ 844 if ((uintptr_t)destination > (uintptr_t)source) { 845 source += Height * pitch; 846 destination += Height * pitch; 847 pitch = -pitch; 848 } 849 850 while (Height-- > 0) { 851 bcopy(source, destination, copybytes); 852 source += pitch; 853 destination += pitch; 854 } 855 856 return (0); 857 } 858 859 int 860 gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation, 861 uint32_t SourceX, uint32_t SourceY, 862 uint32_t DestinationX, uint32_t DestinationY, 863 uint32_t Width, uint32_t Height, uint32_t Delta) 864 { 865 int rv; 866 #if defined(EFI) 867 EFI_STATUS status; 868 extern EFI_GRAPHICS_OUTPUT *gop; 869 870 /* 871 * We assume Blt() does work, if not, we will need to build 872 * exception list case by case. 873 */ 874 if (gop != NULL) { 875 switch (BltOperation) { 876 case GfxFbBltVideoFill: 877 status = gop->Blt(gop, BltBuffer, EfiBltVideoFill, 878 SourceX, SourceY, DestinationX, DestinationY, 879 Width, Height, Delta); 880 break; 881 882 case GfxFbBltVideoToBltBuffer: 883 status = gop->Blt(gop, BltBuffer, 884 EfiBltVideoToBltBuffer, 885 SourceX, SourceY, DestinationX, DestinationY, 886 Width, Height, Delta); 887 break; 888 889 case GfxFbBltBufferToVideo: 890 status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo, 891 SourceX, SourceY, DestinationX, DestinationY, 892 Width, Height, Delta); 893 break; 894 895 case GfxFbBltVideoToVideo: 896 status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo, 897 SourceX, SourceY, DestinationX, DestinationY, 898 Width, Height, Delta); 899 break; 900 901 default: 902 status = EFI_INVALID_PARAMETER; 903 break; 904 } 905 906 switch (status) { 907 case EFI_SUCCESS: 908 rv = 0; 909 break; 910 911 case EFI_INVALID_PARAMETER: 912 rv = EINVAL; 913 break; 914 915 case EFI_DEVICE_ERROR: 916 default: 917 rv = EIO; 918 break; 919 } 920 921 return (rv); 922 } 923 #endif 924 925 switch (BltOperation) { 926 case GfxFbBltVideoFill: 927 rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY, 928 Width, Height); 929 break; 930 931 case GfxFbBltVideoToBltBuffer: 932 rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY, 933 DestinationX, DestinationY, Width, Height, Delta); 934 break; 935 936 case GfxFbBltBufferToVideo: 937 rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY, 938 DestinationX, DestinationY, Width, Height, Delta); 939 break; 940 941 case GfxFbBltVideoToVideo: 942 rv = gfxfb_blt_video_to_video(SourceX, SourceY, 943 DestinationX, DestinationY, Width, Height); 944 break; 945 946 default: 947 rv = EINVAL; 948 break; 949 } 950 return (rv); 951 } 952 953 /* 954 * visual io callbacks. 955 */ 956 int 957 gfx_fb_cons_clear(struct vis_consclear *ca) 958 { 959 int rv; 960 uint32_t data, width, height; 961 #if defined(EFI) 962 EFI_TPL tpl; 963 #endif 964 965 data = gfx_fb_color_map(ca->bg_color); 966 width = gfx_fb.framebuffer_common.framebuffer_width; 967 height = gfx_fb.framebuffer_common.framebuffer_height; 968 969 #if defined(EFI) 970 tpl = BS->RaiseTPL(TPL_NOTIFY); 971 #endif 972 rv = gfxfb_blt(&data, GfxFbBltVideoFill, 0, 0, 973 0, 0, width, height, 0); 974 #if defined(EFI) 975 BS->RestoreTPL(tpl); 976 #endif 977 978 return (rv); 979 } 980 981 void 982 gfx_fb_cons_copy(struct vis_conscopy *ma) 983 { 984 uint32_t width, height; 985 #if defined(EFI) 986 EFI_TPL tpl; 987 988 tpl = BS->RaiseTPL(TPL_NOTIFY); 989 #endif 990 991 width = ma->e_col - ma->s_col + 1; 992 height = ma->e_row - ma->s_row + 1; 993 994 (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo, ma->s_col, ma->s_row, 995 ma->t_col, ma->t_row, width, height, 0); 996 #if defined(EFI) 997 BS->RestoreTPL(tpl); 998 #endif 999 } 1000 1001 /* 1002 * Implements alpha blending for RGBA data, could use pixels for arguments, 1003 * but byte stream seems more generic. 1004 * The generic alpha blending is: 1005 * blend = alpha * fg + (1.0 - alpha) * bg. 1006 * Since our alpha is not from range [0..1], we scale appropriately. 1007 */ 1008 static uint8_t 1009 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha) 1010 { 1011 uint16_t blend, h, l; 1012 1013 /* trivial corner cases */ 1014 if (alpha == 0) 1015 return (bg); 1016 if (alpha == 0xFF) 1017 return (fg); 1018 blend = (alpha * fg + (0xFF - alpha) * bg); 1019 /* Division by 0xFF */ 1020 h = blend >> 8; 1021 l = blend & 0xFF; 1022 if (h + l >= 0xFF) 1023 h++; 1024 return (h); 1025 } 1026 1027 /* Copy memory to framebuffer or to memory. */ 1028 static void 1029 bitmap_cpy(void *dst, void *src, size_t size) 1030 { 1031 #if defined(EFI) 1032 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd; 1033 #else 1034 struct paletteentry *ps, *pd; 1035 #endif 1036 uint32_t i; 1037 uint8_t a; 1038 1039 ps = src; 1040 pd = dst; 1041 1042 /* 1043 * we only implement alpha blending for depth 32. 1044 */ 1045 for (i = 0; i < size; i++) { 1046 a = ps[i].Reserved; 1047 pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a); 1048 pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a); 1049 pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a); 1050 pd[i].Reserved = a; 1051 } 1052 } 1053 1054 static void * 1055 allocate_glyphbuffer(uint32_t width, uint32_t height) 1056 { 1057 size_t size; 1058 1059 size = sizeof (*GlyphBuffer) * width * height; 1060 if (size != GlyphBufferSize) { 1061 free(GlyphBuffer); 1062 GlyphBuffer = malloc(size); 1063 if (GlyphBuffer == NULL) 1064 return (NULL); 1065 GlyphBufferSize = size; 1066 } 1067 return (GlyphBuffer); 1068 } 1069 1070 void 1071 gfx_fb_cons_display(struct vis_consdisplay *da) 1072 { 1073 #if defined(EFI) 1074 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer; 1075 EFI_TPL tpl; 1076 #else 1077 struct paletteentry *BltBuffer; 1078 #endif 1079 uint32_t size; 1080 1081 /* make sure we will not write past FB */ 1082 if ((uint32_t)da->col >= gfx_fb.framebuffer_common.framebuffer_width || 1083 (uint32_t)da->row >= gfx_fb.framebuffer_common.framebuffer_height || 1084 (uint32_t)da->col + da->width > 1085 gfx_fb.framebuffer_common.framebuffer_width || 1086 (uint32_t)da->row + da->height > 1087 gfx_fb.framebuffer_common.framebuffer_height) 1088 return; 1089 1090 size = sizeof (*BltBuffer) * da->width * da->height; 1091 1092 /* 1093 * Common data to display is glyph, use preallocated 1094 * glyph buffer. 1095 */ 1096 if (tems.ts_pix_data_size != GlyphBufferSize) 1097 (void) allocate_glyphbuffer(da->width, da->height); 1098 1099 if (size == GlyphBufferSize) { 1100 BltBuffer = GlyphBuffer; 1101 } else { 1102 BltBuffer = malloc(size); 1103 } 1104 if (BltBuffer == NULL) 1105 return; 1106 1107 #if defined(EFI) 1108 tpl = BS->RaiseTPL(TPL_NOTIFY); 1109 #endif 1110 if (gfxfb_blt(BltBuffer, GfxFbBltVideoToBltBuffer, 1111 da->col, da->row, 0, 0, da->width, da->height, 0) == 0) { 1112 bitmap_cpy(BltBuffer, da->data, da->width * da->height); 1113 (void) gfxfb_blt(BltBuffer, GfxFbBltBufferToVideo, 1114 0, 0, da->col, da->row, da->width, da->height, 0); 1115 } 1116 1117 #if defined(EFI) 1118 BS->RestoreTPL(tpl); 1119 #endif 1120 if (BltBuffer != GlyphBuffer) 1121 free(BltBuffer); 1122 } 1123 1124 static void 1125 gfx_fb_cursor_impl(uint32_t fg, uint32_t bg, struct vis_conscursor *ca) 1126 { 1127 union pixel { 1128 #if defined(EFI) 1129 EFI_GRAPHICS_OUTPUT_BLT_PIXEL p; 1130 #else 1131 struct paletteentry p; 1132 #endif 1133 uint32_t p32; 1134 } *row; 1135 1136 /* 1137 * Build inverse image of the glyph. 1138 * Since xor has self-inverse property, drawing cursor 1139 * second time on the same spot, will restore the original content. 1140 */ 1141 for (screen_size_t i = 0; i < ca->height; i++) { 1142 row = (union pixel *)(GlyphBuffer + i * ca->width); 1143 for (screen_size_t j = 0; j < ca->width; j++) { 1144 row[j].p32 = (row[j].p32 ^ fg) ^ bg; 1145 } 1146 } 1147 } 1148 1149 void 1150 gfx_fb_display_cursor(struct vis_conscursor *ca) 1151 { 1152 union pixel { 1153 #if defined(EFI) 1154 EFI_GRAPHICS_OUTPUT_BLT_PIXEL p; 1155 #else 1156 struct paletteentry p; 1157 #endif 1158 uint32_t p32; 1159 } fg, bg; 1160 #if defined(EFI) 1161 EFI_TPL tpl; 1162 1163 tpl = BS->RaiseTPL(TPL_NOTIFY); 1164 #endif 1165 1166 fg.p.Reserved = 0; 1167 fg.p.Red = ca->fg_color.twentyfour[0]; 1168 fg.p.Green = ca->fg_color.twentyfour[1]; 1169 fg.p.Blue = ca->fg_color.twentyfour[2]; 1170 bg.p.Reserved = 0; 1171 bg.p.Red = ca->bg_color.twentyfour[0]; 1172 bg.p.Green = ca->bg_color.twentyfour[1]; 1173 bg.p.Blue = ca->bg_color.twentyfour[2]; 1174 1175 if (allocate_glyphbuffer(ca->width, ca->height) != NULL) { 1176 if (gfxfb_blt(GlyphBuffer, GfxFbBltVideoToBltBuffer, 1177 ca->col, ca->row, 0, 0, ca->width, ca->height, 0) == 0) 1178 gfx_fb_cursor_impl(fg.p32, bg.p32, ca); 1179 1180 (void) gfxfb_blt(GlyphBuffer, GfxFbBltBufferToVideo, 0, 0, 1181 ca->col, ca->row, ca->width, ca->height, 0); 1182 } 1183 1184 #if defined(EFI) 1185 BS->RestoreTPL(tpl); 1186 #endif 1187 } 1188 1189 /* 1190 * Public graphics primitives. 1191 */ 1192 1193 static int 1194 isqrt(int num) 1195 { 1196 int res = 0; 1197 int bit = 1 << 30; 1198 1199 /* "bit" starts at the highest power of four <= the argument. */ 1200 while (bit > num) 1201 bit >>= 2; 1202 1203 while (bit != 0) { 1204 if (num >= res + bit) { 1205 num -= res + bit; 1206 res = (res >> 1) + bit; 1207 } else 1208 res >>= 1; 1209 bit >>= 2; 1210 } 1211 return (res); 1212 } 1213 1214 /* set pixel in framebuffer using gfx coordinates */ 1215 void 1216 gfx_fb_setpixel(uint32_t x, uint32_t y) 1217 { 1218 uint32_t c; 1219 text_color_t fg, bg; 1220 1221 if (plat_stdout_is_framebuffer() == 0) 1222 return; 1223 1224 tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg); 1225 c = gfx_fb_color_map(fg); 1226 1227 if (x >= gfx_fb.framebuffer_common.framebuffer_width || 1228 y >= gfx_fb.framebuffer_common.framebuffer_height) 1229 return; 1230 1231 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0); 1232 } 1233 1234 /* 1235 * draw rectangle in framebuffer using gfx coordinates. 1236 */ 1237 void 1238 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2, 1239 uint32_t fill) 1240 { 1241 uint32_t c; 1242 text_color_t fg, bg; 1243 1244 if (plat_stdout_is_framebuffer() == 0) 1245 return; 1246 1247 tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg); 1248 c = gfx_fb_color_map(fg); 1249 1250 if (fill != 0) { 1251 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1252 y2 - y1, 0); 1253 } else { 1254 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1, 0); 1255 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y2, x2 - x1, 1, 0); 1256 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0); 1257 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0); 1258 } 1259 } 1260 1261 void 1262 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd) 1263 { 1264 int dx, sx, dy, sy; 1265 int err, e2, x2, y2, ed, width; 1266 1267 if (plat_stdout_is_framebuffer() == 0) 1268 return; 1269 1270 width = wd; 1271 sx = x0 < x1? 1 : -1; 1272 sy = y0 < y1? 1 : -1; 1273 dx = x1 > x0? x1 - x0 : x0 - x1; 1274 dy = y1 > y0? y1 - y0 : y0 - y1; 1275 err = dx + dy; 1276 ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy); 1277 1278 for (;;) { 1279 gfx_fb_setpixel(x0, y0); 1280 e2 = err; 1281 x2 = x0; 1282 if ((e2 << 1) >= -dx) { /* x step */ 1283 e2 += dy; 1284 y2 = y0; 1285 while (e2 < ed * width && 1286 (y1 != (uint32_t)y2 || dx > dy)) { 1287 y2 += sy; 1288 gfx_fb_setpixel(x0, y2); 1289 e2 += dx; 1290 } 1291 if (x0 == x1) 1292 break; 1293 e2 = err; 1294 err -= dy; 1295 x0 += sx; 1296 } 1297 if ((e2 << 1) <= dy) { /* y step */ 1298 e2 = dx-e2; 1299 while (e2 < ed * width && 1300 (x1 != (uint32_t)x2 || dx < dy)) { 1301 x2 += sx; 1302 gfx_fb_setpixel(x2, y0); 1303 e2 += dy; 1304 } 1305 if (y0 == y1) 1306 break; 1307 err += dx; 1308 y0 += sy; 1309 } 1310 } 1311 } 1312 1313 /* 1314 * quadratic Bézier curve limited to gradients without sign change. 1315 */ 1316 void 1317 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2, 1318 uint32_t y2, uint32_t wd) 1319 { 1320 int sx, sy, xx, yy, xy, width; 1321 int dx, dy, err, curvature; 1322 int i; 1323 1324 if (plat_stdout_is_framebuffer() == 0) 1325 return; 1326 1327 width = wd; 1328 sx = x2 - x1; 1329 sy = y2 - y1; 1330 xx = x0 - x1; 1331 yy = y0 - y1; 1332 curvature = xx*sy - yy*sx; 1333 1334 if (sx*sx + sy*sy > xx*xx+yy*yy) { 1335 x2 = x0; 1336 x0 = sx + x1; 1337 y2 = y0; 1338 y0 = sy + y1; 1339 curvature = -curvature; 1340 } 1341 if (curvature != 0) { 1342 xx += sx; 1343 sx = x0 < x2? 1 : -1; 1344 xx *= sx; 1345 yy += sy; 1346 sy = y0 < y2? 1 : -1; 1347 yy *= sy; 1348 xy = (xx*yy) << 1; 1349 xx *= xx; 1350 yy *= yy; 1351 if (curvature * sx * sy < 0) { 1352 xx = -xx; 1353 yy = -yy; 1354 xy = -xy; 1355 curvature = -curvature; 1356 } 1357 dx = 4 * sy * curvature * (x1 - x0) + xx - xy; 1358 dy = 4 * sx * curvature * (y0 - y1) + yy - xy; 1359 xx += xx; 1360 yy += yy; 1361 err = dx + dy + xy; 1362 do { 1363 for (i = 0; i <= width; i++) 1364 gfx_fb_setpixel(x0 + i, y0); 1365 if (x0 == x2 && y0 == y2) 1366 return; /* last pixel -> curve finished */ 1367 y1 = 2 * err < dx; 1368 if (2 * err > dy) { 1369 x0 += sx; 1370 dx -= xy; 1371 dy += yy; 1372 err += dy; 1373 } 1374 if (y1 != 0) { 1375 y0 += sy; 1376 dy -= xy; 1377 dx += xx; 1378 err += dx; 1379 } 1380 } while (dy < dx); /* gradient negates -> algorithm fails */ 1381 } 1382 gfx_fb_line(x0, y0, x2, y2, width); 1383 } 1384 1385 /* 1386 * draw rectangle using terminal coordinates and current foreground color. 1387 */ 1388 void 1389 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2) 1390 { 1391 int x1, y1, x2, y2; 1392 int xshift, yshift; 1393 int width, i; 1394 uint32_t vf_width, vf_height; 1395 1396 if (plat_stdout_is_framebuffer() == 0) 1397 return; 1398 1399 vf_width = tems.ts_font.vf_width; 1400 vf_height = tems.ts_font.vf_height; 1401 width = vf_width / 4; /* line width */ 1402 xshift = (vf_width - width) / 2; 1403 yshift = (vf_height - width) / 2; 1404 1405 /* Shift coordinates */ 1406 if (ux1 != 0) 1407 ux1--; 1408 if (uy1 != 0) 1409 uy1--; 1410 ux2--; 1411 uy2--; 1412 1413 /* mark area used in tem */ 1414 tem_image_display(tems.ts_active, uy1, ux1, uy2 + 1, ux2 + 1); 1415 1416 /* 1417 * Draw horizontal lines width points thick, shifted from outer edge. 1418 */ 1419 x1 = (ux1 + 1) * vf_width + tems.ts_p_offset.x; 1420 y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift; 1421 x2 = ux2 * vf_width + tems.ts_p_offset.x; 1422 gfx_fb_drawrect(x1, y1, x2, y1 + width, 1); 1423 y2 = uy2 * vf_height + tems.ts_p_offset.y; 1424 y2 += vf_height - yshift - width; 1425 gfx_fb_drawrect(x1, y2, x2, y2 + width, 1); 1426 1427 /* 1428 * Draw vertical lines width points thick, shifted from outer edge. 1429 */ 1430 x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift; 1431 y1 = uy1 * vf_height + tems.ts_p_offset.y; 1432 y1 += vf_height; 1433 y2 = uy2 * vf_height + tems.ts_p_offset.y; 1434 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1); 1435 x1 = ux2 * vf_width + tems.ts_p_offset.x; 1436 x1 += vf_width - xshift - width; 1437 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1); 1438 1439 /* Draw upper left corner. */ 1440 x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift; 1441 y1 = uy1 * vf_height + tems.ts_p_offset.y; 1442 y1 += vf_height; 1443 1444 x2 = ux1 * vf_width + tems.ts_p_offset.x; 1445 x2 += vf_width; 1446 y2 = uy1 * vf_height + tems.ts_p_offset.y + yshift; 1447 for (i = 0; i <= width; i++) 1448 gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i); 1449 1450 /* Draw lower left corner. */ 1451 x1 = ux1 * vf_width + tems.ts_p_offset.x; 1452 x1 += vf_width; 1453 y1 = uy2 * vf_height + tems.ts_p_offset.y; 1454 y1 += vf_height - yshift; 1455 x2 = ux1 * vf_width + tems.ts_p_offset.x + xshift; 1456 y2 = uy2 * vf_height + tems.ts_p_offset.y; 1457 for (i = 0; i <= width; i++) 1458 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i); 1459 1460 /* Draw upper right corner. */ 1461 x1 = ux2 * vf_width + tems.ts_p_offset.x; 1462 y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift; 1463 x2 = ux2 * vf_width + tems.ts_p_offset.x; 1464 x2 += vf_width - xshift - width; 1465 y2 = uy1 * vf_height + tems.ts_p_offset.y; 1466 y2 += vf_height; 1467 for (i = 0; i <= width; i++) 1468 gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i); 1469 1470 /* Draw lower right corner. */ 1471 x1 = ux2 * vf_width + tems.ts_p_offset.x; 1472 y1 = uy2 * vf_height + tems.ts_p_offset.y; 1473 y1 += vf_height - yshift; 1474 x2 = ux2 * vf_width + tems.ts_p_offset.x; 1475 x2 += vf_width - xshift - width; 1476 y2 = uy2 * vf_height + tems.ts_p_offset.y; 1477 for (i = 0; i <= width; i++) 1478 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i); 1479 } 1480 1481 int 1482 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2, 1483 uint32_t uy2, uint32_t flags) 1484 { 1485 #if defined(EFI) 1486 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; 1487 #else 1488 struct paletteentry *p; 1489 #endif 1490 struct vis_consdisplay da; 1491 uint32_t i, j, x, y, fheight, fwidth; 1492 uint8_t r, g, b, a; 1493 bool scale = false; 1494 bool trace = false; 1495 1496 trace = (flags & FL_PUTIMAGE_DEBUG) != 0; 1497 1498 if (plat_stdout_is_framebuffer() == 0) { 1499 if (trace) 1500 printf("Framebuffer not active.\n"); 1501 return (1); 1502 } 1503 1504 if (png->color_type != PNG_TRUECOLOR_ALPHA) { 1505 if (trace) 1506 printf("Not truecolor image.\n"); 1507 return (1); 1508 } 1509 1510 if (ux1 > gfx_fb.framebuffer_common.framebuffer_width || 1511 uy1 > gfx_fb.framebuffer_common.framebuffer_height) { 1512 if (trace) 1513 printf("Top left coordinate off screen.\n"); 1514 return (1); 1515 } 1516 1517 if (png->width > UINT16_MAX || png->height > UINT16_MAX) { 1518 if (trace) 1519 printf("Image too large.\n"); 1520 return (1); 1521 } 1522 1523 if (png->width < 1 || png->height < 1) { 1524 if (trace) 1525 printf("Image too small.\n"); 1526 return (1); 1527 } 1528 1529 /* 1530 * If 0 was passed for either ux2 or uy2, then calculate the missing 1531 * part of the bottom right coordinate. 1532 */ 1533 scale = true; 1534 if (ux2 == 0 && uy2 == 0) { 1535 /* Both 0, use the native resolution of the image */ 1536 ux2 = ux1 + png->width; 1537 uy2 = uy1 + png->height; 1538 scale = false; 1539 } else if (ux2 == 0) { 1540 /* Set ux2 from uy2/uy1 to maintain aspect ratio */ 1541 ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height; 1542 } else if (uy2 == 0) { 1543 /* Set uy2 from ux2/ux1 to maintain aspect ratio */ 1544 uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width; 1545 } 1546 1547 if (ux2 > gfx_fb.framebuffer_common.framebuffer_width || 1548 uy2 > gfx_fb.framebuffer_common.framebuffer_height) { 1549 if (trace) 1550 printf("Bottom right coordinate off screen.\n"); 1551 return (1); 1552 } 1553 1554 fwidth = ux2 - ux1; 1555 fheight = uy2 - uy1; 1556 1557 /* 1558 * If the original image dimensions have been passed explicitly, 1559 * disable scaling. 1560 */ 1561 if (fwidth == png->width && fheight == png->height) 1562 scale = false; 1563 1564 if (ux1 == 0) { 1565 /* 1566 * No top left X co-ordinate (real coordinates start at 1), 1567 * place as far right as it will fit. 1568 */ 1569 ux2 = gfx_fb.framebuffer_common.framebuffer_width - 1570 tems.ts_p_offset.x; 1571 ux1 = ux2 - fwidth; 1572 } 1573 1574 if (uy1 == 0) { 1575 /* 1576 * No top left Y co-ordinate (real coordinates start at 1), 1577 * place as far down as it will fit. 1578 */ 1579 uy2 = gfx_fb.framebuffer_common.framebuffer_height - 1580 tems.ts_p_offset.y; 1581 uy1 = uy2 - fheight; 1582 } 1583 1584 if (ux1 >= ux2 || uy1 >= uy2) { 1585 if (trace) 1586 printf("Image dimensions reversed.\n"); 1587 return (1); 1588 } 1589 1590 if (fwidth < 2 || fheight < 2) { 1591 if (trace) 1592 printf("Target area too small\n"); 1593 return (1); 1594 } 1595 1596 if (trace) 1597 printf("Image %ux%u -> %ux%u @%ux%u\n", 1598 png->width, png->height, fwidth, fheight, ux1, uy1); 1599 1600 da.col = ux1; 1601 da.row = uy1; 1602 da.width = fwidth; 1603 da.height = fheight; 1604 1605 /* 1606 * mark area used in tem 1607 */ 1608 if (!(flags & FL_PUTIMAGE_NOSCROLL)) { 1609 tem_image_display(tems.ts_active, 1610 da.row / tems.ts_font.vf_height, 1611 da.col / tems.ts_font.vf_width, 1612 (da.row + da.height) / tems.ts_font.vf_height, 1613 (da.col + da.width) / tems.ts_font.vf_width); 1614 } 1615 1616 if ((flags & FL_PUTIMAGE_BORDER)) 1617 gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0); 1618 1619 da.data = malloc(fwidth * fheight * sizeof (*p)); 1620 p = (void *)da.data; 1621 if (da.data == NULL) { 1622 if (trace) 1623 printf("Out of memory.\n"); 1624 return (1); 1625 } 1626 1627 /* 1628 * Build image for our framebuffer. 1629 */ 1630 1631 /* Helper to calculate the pixel index from the source png */ 1632 #define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp) 1633 1634 /* 1635 * For each of the x and y directions, calculate the number of pixels 1636 * in the source image that correspond to a single pixel in the target. 1637 * Use fixed-point arithmetic with 16-bits for each of the integer and 1638 * fractional parts. 1639 */ 1640 const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1); 1641 const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1); 1642 1643 uint32_t hc = 0; 1644 for (y = 0; y < fheight; y++) { 1645 uint32_t hc2 = (hc >> 9) & 0x7f; 1646 uint32_t hc1 = 0x80 - hc2; 1647 1648 uint32_t offset_y = hc >> 16; 1649 uint32_t offset_y1 = offset_y + 1; 1650 1651 uint32_t wc = 0; 1652 for (x = 0; x < fwidth; x++) { 1653 uint32_t wc2 = (wc >> 9) & 0x7f; 1654 uint32_t wc1 = 0x80 - wc2; 1655 1656 uint32_t offset_x = wc >> 16; 1657 uint32_t offset_x1 = offset_x + 1; 1658 1659 /* Target pixel index */ 1660 j = y * fwidth + x; 1661 1662 if (!scale) { 1663 i = GETPIXEL(x, y); 1664 r = png->image[i]; 1665 g = png->image[i + 1]; 1666 b = png->image[i + 2]; 1667 a = png->image[i + 3]; 1668 } else { 1669 uint8_t pixel[4]; 1670 1671 uint32_t p00 = GETPIXEL(offset_x, offset_y); 1672 uint32_t p01 = GETPIXEL(offset_x, offset_y1); 1673 uint32_t p10 = GETPIXEL(offset_x1, offset_y); 1674 uint32_t p11 = GETPIXEL(offset_x1, offset_y1); 1675 1676 /* 1677 * Given a 2x2 array of pixels in the source 1678 * image, combine them to produce a single 1679 * value for the pixel in the target image. 1680 * Each column of pixels is combined using 1681 * a weighted average where the top and bottom 1682 * pixels contribute hc1 and hc2 respectively. 1683 * The calculation for bottom pixel pB and 1684 * top pixel pT is: 1685 * (pT * hc1 + pB * hc2) / (hc1 + hc2) 1686 * Once the values are determined for the two 1687 * columns of pixels, then the columns are 1688 * averaged together in the same way but using 1689 * wc1 and wc2 for the weightings. 1690 * 1691 * Since hc1 and hc2 are chosen so that 1692 * hc1 + hc2 == 128 (and same for wc1 + wc2), 1693 * the >> 14 below is a quick way to divide by 1694 * (hc1 + hc2) * (wc1 + wc2) 1695 */ 1696 for (i = 0; i < 4; i++) 1697 pixel[i] = ( 1698 (png->image[p00 + i] * hc1 + 1699 png->image[p01 + i] * hc2) * wc1 + 1700 (png->image[p10 + i] * hc1 + 1701 png->image[p11 + i] * hc2) * wc2) 1702 >> 14; 1703 1704 r = pixel[0]; 1705 g = pixel[1]; 1706 b = pixel[2]; 1707 a = pixel[3]; 1708 } 1709 1710 if (trace) 1711 printf("r/g/b: %x/%x/%x\n", r, g, b); 1712 /* 1713 * Rough colorspace reduction for 15/16 bit colors. 1714 */ 1715 p[j].Red = r >> 1716 (8 - gfx_fb.u.fb2.framebuffer_red_mask_size); 1717 p[j].Green = g >> 1718 (8 - gfx_fb.u.fb2.framebuffer_green_mask_size); 1719 p[j].Blue = b >> 1720 (8 - gfx_fb.u.fb2.framebuffer_blue_mask_size); 1721 p[j].Reserved = a; 1722 1723 wc += wcstep; 1724 } 1725 hc += hcstep; 1726 } 1727 1728 gfx_fb_cons_display(&da); 1729 free(da.data); 1730 return (0); 1731 } 1732 1733 /* Return w^2 + h^2 or 0, if the dimensions are unknown */ 1734 static unsigned 1735 edid_diagonal_squared(void) 1736 { 1737 unsigned w, h; 1738 1739 if (edid_info == NULL) 1740 return (0); 1741 1742 w = edid_info->display.max_horizontal_image_size; 1743 h = edid_info->display.max_vertical_image_size; 1744 1745 /* If either one is 0, we have aspect ratio, not size */ 1746 if (w == 0 || h == 0) 1747 return (0); 1748 1749 /* 1750 * some monitors encode the aspect ratio instead of the physical size. 1751 */ 1752 if ((w == 16 && h == 9) || (w == 16 && h == 10) || 1753 (w == 4 && h == 3) || (w == 5 && h == 4)) 1754 return (0); 1755 1756 /* 1757 * translate cm to inch, note we scale by 100 here. 1758 */ 1759 w = w * 100 / 254; 1760 h = h * 100 / 254; 1761 1762 /* Return w^2 + h^2 */ 1763 return (w * w + h * h); 1764 } 1765 1766 /* 1767 * calculate pixels per inch. 1768 */ 1769 static unsigned 1770 gfx_get_ppi(void) 1771 { 1772 unsigned dp, di; 1773 1774 di = edid_diagonal_squared(); 1775 if (di == 0) 1776 return (0); 1777 1778 dp = gfx_fb.framebuffer_common.framebuffer_width * 1779 gfx_fb.framebuffer_common.framebuffer_width + 1780 gfx_fb.framebuffer_common.framebuffer_height * 1781 gfx_fb.framebuffer_common.framebuffer_height; 1782 1783 return (isqrt(dp / di)); 1784 } 1785 1786 /* 1787 * Calculate font size from density independent pixels (dp): 1788 * ((16dp * ppi) / 160) * display_factor. 1789 * Here we are using fixed constants: 1dp == 160 ppi and 1790 * display_factor 2. 1791 * 1792 * We are rounding font size up and are searching for font which is 1793 * not smaller than calculated size value. 1794 */ 1795 bitmap_data_t * 1796 gfx_get_font(void) 1797 { 1798 unsigned ppi, size; 1799 bitmap_data_t *font = NULL; 1800 struct fontlist *fl, *next; 1801 1802 /* Text mode is not supported here. */ 1803 if (gfx_fb.framebuffer_common.framebuffer_type == 1804 MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT) 1805 return (NULL); 1806 1807 ppi = gfx_get_ppi(); 1808 if (ppi == 0) 1809 return (NULL); 1810 1811 /* 1812 * We will search for 16dp font. 1813 * We are using scale up by 10 for roundup. 1814 */ 1815 size = (16 * ppi * 10) / 160; 1816 /* Apply display factor 2. */ 1817 size = roundup(size * 2, 10) / 10; 1818 1819 STAILQ_FOREACH(fl, &fonts, font_next) { 1820 next = STAILQ_NEXT(fl, font_next); 1821 /* 1822 * If this is last font or, if next font is smaller, 1823 * we have our font. Make sure, it actually is loaded. 1824 */ 1825 if (next == NULL || next->font_data->height < size) { 1826 font = fl->font_data; 1827 if (font->font == NULL || 1828 fl->font_flags == FONT_RELOAD) { 1829 if (fl->font_load != NULL && 1830 fl->font_name != NULL) 1831 font = fl->font_load(fl->font_name); 1832 } 1833 break; 1834 } 1835 } 1836 1837 return (font); 1838 } 1839 1840 static int 1841 load_mapping(int fd, struct font *fp, int n) 1842 { 1843 size_t i, size; 1844 ssize_t rv; 1845 struct font_map *mp; 1846 1847 if (fp->vf_map_count[n] == 0) 1848 return (0); 1849 1850 size = fp->vf_map_count[n] * sizeof (*mp); 1851 mp = malloc(size); 1852 if (mp == NULL) 1853 return (ENOMEM); 1854 fp->vf_map[n] = mp; 1855 1856 rv = read(fd, mp, size); 1857 if (rv < 0 || (size_t)rv != size) { 1858 free(fp->vf_map[n]); 1859 fp->vf_map[n] = NULL; 1860 return (EIO); 1861 } 1862 1863 for (i = 0; i < fp->vf_map_count[n]; i++) { 1864 mp[i].font_src = be32toh(mp[i].font_src); 1865 mp[i].font_dst = be16toh(mp[i].font_dst); 1866 mp[i].font_len = be16toh(mp[i].font_len); 1867 } 1868 return (0); 1869 } 1870 1871 static int 1872 builtin_mapping(struct font *fp, int n) 1873 { 1874 size_t size; 1875 struct font_map *mp; 1876 1877 if (n >= VFNT_MAPS) 1878 return (EINVAL); 1879 1880 if (fp->vf_map_count[n] == 0) 1881 return (0); 1882 1883 size = fp->vf_map_count[n] * sizeof (*mp); 1884 mp = malloc(size); 1885 if (mp == NULL) 1886 return (ENOMEM); 1887 fp->vf_map[n] = mp; 1888 1889 memcpy(mp, DEFAULT_FONT_DATA.font->vf_map[n], size); 1890 return (0); 1891 } 1892 1893 /* 1894 * Load font from builtin or from file. 1895 * We do need special case for builtin because the builtin font glyphs 1896 * are compressed and we do need to uncompress them. 1897 * Having single load_font() for both cases will help us to simplify 1898 * font switch handling. 1899 */ 1900 static bitmap_data_t * 1901 load_font(char *path) 1902 { 1903 int fd, i; 1904 uint32_t glyphs; 1905 struct font_header fh; 1906 struct fontlist *fl; 1907 bitmap_data_t *bp; 1908 struct font *fp; 1909 size_t size; 1910 ssize_t rv; 1911 1912 /* Get our entry from the font list. */ 1913 STAILQ_FOREACH(fl, &fonts, font_next) { 1914 if (strcmp(fl->font_name, path) == 0) 1915 break; 1916 } 1917 if (fl == NULL) 1918 return (NULL); /* Should not happen. */ 1919 1920 bp = fl->font_data; 1921 if (bp->font != NULL && fl->font_flags != FONT_RELOAD) 1922 return (bp); 1923 1924 fd = -1; 1925 /* 1926 * Special case for builtin font. 1927 * Builtin font is the very first font we load, we do not have 1928 * previous loads to be released. 1929 */ 1930 if (fl->font_flags == FONT_BUILTIN) { 1931 if ((fp = calloc(1, sizeof (struct font))) == NULL) 1932 return (NULL); 1933 1934 fp->vf_width = DEFAULT_FONT_DATA.width; 1935 fp->vf_height = DEFAULT_FONT_DATA.height; 1936 1937 fp->vf_bytes = malloc(DEFAULT_FONT_DATA.uncompressed_size); 1938 if (fp->vf_bytes == NULL) { 1939 free(fp); 1940 return (NULL); 1941 } 1942 1943 bp->uncompressed_size = DEFAULT_FONT_DATA.uncompressed_size; 1944 bp->compressed_size = DEFAULT_FONT_DATA.compressed_size; 1945 1946 if (lz4_decompress(DEFAULT_FONT_DATA.compressed_data, 1947 fp->vf_bytes, 1948 DEFAULT_FONT_DATA.compressed_size, 1949 DEFAULT_FONT_DATA.uncompressed_size, 0) != 0) { 1950 free(fp->vf_bytes); 1951 free(fp); 1952 return (NULL); 1953 } 1954 1955 for (i = 0; i < VFNT_MAPS; i++) { 1956 fp->vf_map_count[i] = 1957 DEFAULT_FONT_DATA.font->vf_map_count[i]; 1958 if (builtin_mapping(fp, i) != 0) 1959 goto free_done; 1960 } 1961 1962 bp->font = fp; 1963 return (bp); 1964 } 1965 1966 fd = open(path, O_RDONLY); 1967 if (fd < 0) { 1968 return (NULL); 1969 } 1970 1971 size = sizeof (fh); 1972 rv = read(fd, &fh, size); 1973 if (rv < 0 || (size_t)rv != size) { 1974 bp = NULL; 1975 goto done; 1976 } 1977 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0) { 1978 bp = NULL; 1979 goto done; 1980 } 1981 if ((fp = calloc(1, sizeof (struct font))) == NULL) { 1982 bp = NULL; 1983 goto done; 1984 } 1985 for (i = 0; i < VFNT_MAPS; i++) 1986 fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]); 1987 1988 glyphs = be32toh(fh.fh_glyph_count); 1989 fp->vf_width = fh.fh_width; 1990 fp->vf_height = fh.fh_height; 1991 1992 size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs; 1993 bp->uncompressed_size = size; 1994 if ((fp->vf_bytes = malloc(size)) == NULL) 1995 goto free_done; 1996 1997 rv = read(fd, fp->vf_bytes, size); 1998 if (rv < 0 || (size_t)rv != size) 1999 goto free_done; 2000 for (i = 0; i < VFNT_MAPS; i++) { 2001 if (load_mapping(fd, fp, i) != 0) 2002 goto free_done; 2003 } 2004 2005 /* 2006 * Reset builtin flag now as we have full font loaded. 2007 */ 2008 if (fl->font_flags == FONT_BUILTIN) 2009 fl->font_flags = FONT_AUTO; 2010 2011 /* 2012 * Release previously loaded entries. We can do this now, as 2013 * the new font is loaded. Note, there can be no console 2014 * output till the new font is in place and tem is notified. 2015 * We do need to keep fl->font_data for glyph dimensions. 2016 */ 2017 STAILQ_FOREACH(fl, &fonts, font_next) { 2018 if (fl->font_data->font == NULL) 2019 continue; 2020 2021 for (i = 0; i < VFNT_MAPS; i++) 2022 free(fl->font_data->font->vf_map[i]); 2023 free(fl->font_data->font->vf_bytes); 2024 free(fl->font_data->font); 2025 fl->font_data->font = NULL; 2026 } 2027 2028 bp->font = fp; 2029 bp->compressed_size = 0; 2030 2031 done: 2032 if (fd != -1) 2033 close(fd); 2034 return (bp); 2035 2036 free_done: 2037 for (i = 0; i < VFNT_MAPS; i++) 2038 free(fp->vf_map[i]); 2039 free(fp->vf_bytes); 2040 free(fp); 2041 bp = NULL; 2042 goto done; 2043 } 2044 2045 2046 struct name_entry { 2047 char *n_name; 2048 SLIST_ENTRY(name_entry) n_entry; 2049 }; 2050 2051 SLIST_HEAD(name_list, name_entry); 2052 2053 /* Read font names from index file. */ 2054 static struct name_list * 2055 read_list(char *fonts) 2056 { 2057 struct name_list *nl; 2058 struct name_entry *np; 2059 char buf[PATH_MAX]; 2060 int fd, len; 2061 2062 fd = open(fonts, O_RDONLY); 2063 if (fd < 0) 2064 return (NULL); 2065 2066 nl = malloc(sizeof (*nl)); 2067 if (nl == NULL) { 2068 close(fd); 2069 return (nl); 2070 } 2071 2072 SLIST_INIT(nl); 2073 while ((len = fgetstr(buf, sizeof (buf), fd)) > 0) { 2074 np = malloc(sizeof (*np)); 2075 if (np == NULL) { 2076 close(fd); 2077 return (nl); /* return what we have */ 2078 } 2079 np->n_name = strdup(buf); 2080 if (np->n_name == NULL) { 2081 free(np); 2082 close(fd); 2083 return (nl); /* return what we have */ 2084 } 2085 SLIST_INSERT_HEAD(nl, np, n_entry); 2086 } 2087 close(fd); 2088 return (nl); 2089 } 2090 2091 /* 2092 * Read the font properties and insert new entry into the list. 2093 * The font list is built in descending order. 2094 */ 2095 static bool 2096 insert_font(char *name, FONT_FLAGS flags) 2097 { 2098 struct font_header fh; 2099 struct fontlist *fp, *previous, *entry, *next; 2100 size_t size; 2101 ssize_t rv; 2102 int fd; 2103 char *font_name; 2104 2105 font_name = NULL; 2106 if (flags == FONT_BUILTIN) { 2107 /* 2108 * We only install builtin font once, while setting up 2109 * initial console. Since this will happen very early, 2110 * we assume asprintf will not fail. Once we have access to 2111 * files, the builtin font will be replaced by font loaded 2112 * from file. 2113 */ 2114 if (!STAILQ_EMPTY(&fonts)) 2115 return (false); 2116 2117 fh.fh_width = DEFAULT_FONT_DATA.width; 2118 fh.fh_height = DEFAULT_FONT_DATA.height; 2119 2120 (void) asprintf(&font_name, "%dx%d", 2121 DEFAULT_FONT_DATA.width, DEFAULT_FONT_DATA.height); 2122 } else { 2123 fd = open(name, O_RDONLY); 2124 if (fd < 0) 2125 return (false); 2126 rv = read(fd, &fh, sizeof (fh)); 2127 close(fd); 2128 if (rv < 0 || (size_t)rv != sizeof (fh)) 2129 return (false); 2130 2131 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, 2132 sizeof (fh.fh_magic)) != 0) 2133 return (false); 2134 font_name = strdup(name); 2135 } 2136 2137 if (font_name == NULL) 2138 return (false); 2139 2140 /* 2141 * If we have an entry with the same glyph dimensions, replace 2142 * the file name and mark us. We only support unique dimensions. 2143 */ 2144 STAILQ_FOREACH(entry, &fonts, font_next) { 2145 if (fh.fh_width == entry->font_data->width && 2146 fh.fh_height == entry->font_data->height) { 2147 free(entry->font_name); 2148 entry->font_name = font_name; 2149 entry->font_flags = FONT_RELOAD; 2150 return (true); 2151 } 2152 } 2153 2154 fp = calloc(sizeof (*fp), 1); 2155 if (fp == NULL) { 2156 free(font_name); 2157 return (false); 2158 } 2159 fp->font_data = calloc(sizeof (*fp->font_data), 1); 2160 if (fp->font_data == NULL) { 2161 free(font_name); 2162 free(fp); 2163 return (false); 2164 } 2165 fp->font_name = font_name; 2166 fp->font_flags = flags; 2167 fp->font_load = load_font; 2168 fp->font_data->width = fh.fh_width; 2169 fp->font_data->height = fh.fh_height; 2170 2171 if (STAILQ_EMPTY(&fonts)) { 2172 STAILQ_INSERT_HEAD(&fonts, fp, font_next); 2173 return (true); 2174 } 2175 2176 previous = NULL; 2177 size = fp->font_data->width * fp->font_data->height; 2178 2179 STAILQ_FOREACH(entry, &fonts, font_next) { 2180 /* Should fp be inserted before the entry? */ 2181 if (size > entry->font_data->width * entry->font_data->height) { 2182 if (previous == NULL) { 2183 STAILQ_INSERT_HEAD(&fonts, fp, font_next); 2184 } else { 2185 STAILQ_INSERT_AFTER(&fonts, previous, fp, 2186 font_next); 2187 } 2188 return (true); 2189 } 2190 next = STAILQ_NEXT(entry, font_next); 2191 if (next == NULL || 2192 size > next->font_data->width * next->font_data->height) { 2193 STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next); 2194 return (true); 2195 } 2196 previous = entry; 2197 } 2198 return (true); 2199 } 2200 2201 static int 2202 font_set(struct env_var *ev __unused, int flags __unused, const void *value) 2203 { 2204 struct fontlist *fl; 2205 char *eptr; 2206 unsigned long x = 0, y = 0; 2207 2208 /* 2209 * Attempt to extract values from "XxY" string. In case of error, 2210 * we have unmaching glyph dimensions and will just output the 2211 * available values. 2212 */ 2213 if (value != NULL) { 2214 x = strtoul(value, &eptr, 10); 2215 if (*eptr == 'x') 2216 y = strtoul(eptr + 1, &eptr, 10); 2217 } 2218 STAILQ_FOREACH(fl, &fonts, font_next) { 2219 if (fl->font_data->width == x && fl->font_data->height == y) 2220 break; 2221 } 2222 if (fl != NULL) { 2223 /* Reset any FONT_MANUAL flag. */ 2224 reset_font_flags(); 2225 2226 /* Mark this font manually loaded */ 2227 fl->font_flags = FONT_MANUAL; 2228 /* Trigger tem update. */ 2229 tems.update_font = true; 2230 plat_cons_update_mode(-1); 2231 return (CMD_OK); 2232 } 2233 2234 printf("Available fonts:\n"); 2235 STAILQ_FOREACH(fl, &fonts, font_next) { 2236 printf(" %dx%d\n", fl->font_data->width, 2237 fl->font_data->height); 2238 } 2239 return (CMD_OK); 2240 } 2241 2242 void 2243 bios_text_font(bool use_vga_font) 2244 { 2245 if (use_vga_font) 2246 (void) insert_font(VGA_8X16_FONT, FONT_MANUAL); 2247 else 2248 (void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL); 2249 tems.update_font = true; 2250 } 2251 2252 void 2253 autoload_font(bool bios) 2254 { 2255 struct name_list *nl; 2256 struct name_entry *np; 2257 2258 nl = read_list("/boot/fonts/fonts.dir"); 2259 if (nl == NULL) 2260 return; 2261 2262 while (!SLIST_EMPTY(nl)) { 2263 np = SLIST_FIRST(nl); 2264 SLIST_REMOVE_HEAD(nl, n_entry); 2265 if (insert_font(np->n_name, FONT_AUTO) == false) 2266 printf("failed to add font: %s\n", np->n_name); 2267 free(np->n_name); 2268 free(np); 2269 } 2270 2271 unsetenv("screen-font"); 2272 env_setenv("screen-font", EV_VOLATILE, NULL, font_set, env_nounset); 2273 2274 /* 2275 * If vga text mode was requested, load vga.font (8x16 bold) font. 2276 */ 2277 if (bios) { 2278 bios_text_font(true); 2279 } 2280 2281 /* Trigger tem update. */ 2282 tems.update_font = true; 2283 plat_cons_update_mode(-1); 2284 } 2285 2286 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font); 2287 2288 static int 2289 command_font(int argc, char *argv[]) 2290 { 2291 int i, c, rc = CMD_OK; 2292 struct fontlist *fl; 2293 bool list; 2294 2295 list = false; 2296 optind = 1; 2297 optreset = 1; 2298 rc = CMD_OK; 2299 2300 while ((c = getopt(argc, argv, "l")) != -1) { 2301 switch (c) { 2302 case 'l': 2303 list = true; 2304 break; 2305 case '?': 2306 default: 2307 return (CMD_ERROR); 2308 } 2309 } 2310 2311 argc -= optind; 2312 argv += optind; 2313 2314 if (argc > 1 || (list && argc != 0)) { 2315 printf("Usage: loadfont [-l] | [file.fnt]\n"); 2316 return (CMD_ERROR); 2317 } 2318 2319 if (list) { 2320 STAILQ_FOREACH(fl, &fonts, font_next) { 2321 printf("font %s: %dx%d%s\n", fl->font_name, 2322 fl->font_data->width, 2323 fl->font_data->height, 2324 fl->font_data->font == NULL? "" : " loaded"); 2325 } 2326 return (CMD_OK); 2327 } 2328 2329 if (argc == 1) { 2330 char *name = argv[0]; 2331 2332 if (insert_font(name, FONT_MANUAL) == false) { 2333 printf("loadfont error: failed to load: %s\n", name); 2334 return (CMD_ERROR); 2335 } 2336 2337 tems.update_font = true; 2338 plat_cons_update_mode(-1); 2339 return (CMD_OK); 2340 } 2341 2342 if (argc == 0) { 2343 /* 2344 * Walk entire font list, release any loaded font, and set 2345 * autoload flag. The font list does have at least the builtin 2346 * default font. 2347 */ 2348 STAILQ_FOREACH(fl, &fonts, font_next) { 2349 if (fl->font_data->font != NULL) { 2350 /* Note the tem is releasing font bytes */ 2351 for (i = 0; i < VFNT_MAPS; i++) 2352 free(fl->font_data->font->vf_map[i]); 2353 free(fl->font_data->font); 2354 fl->font_data->font = NULL; 2355 fl->font_data->uncompressed_size = 0; 2356 fl->font_flags = FONT_AUTO; 2357 } 2358 } 2359 tems.update_font = true; 2360 plat_cons_update_mode(-1); 2361 } 2362 return (rc); 2363 } 2364 2365 bool 2366 gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res) 2367 { 2368 struct resolution *rp, *p; 2369 2370 /* 2371 * Walk detailed timings tables (4). 2372 */ 2373 if ((edid->display.supported_features 2374 & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) { 2375 /* Walk detailed timing descriptors (4) */ 2376 for (int i = 0; i < DET_TIMINGS; i++) { 2377 /* 2378 * Reserved value 0 is not used for display decriptor. 2379 */ 2380 if (edid->detailed_timings[i].pixel_clock == 0) 2381 continue; 2382 if ((rp = malloc(sizeof (*rp))) == NULL) 2383 continue; 2384 rp->width = GET_EDID_INFO_WIDTH(edid, i); 2385 rp->height = GET_EDID_INFO_HEIGHT(edid, i); 2386 if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS && 2387 rp->height > 0 && rp->height <= EDID_MAX_LINES) 2388 TAILQ_INSERT_TAIL(res, rp, next); 2389 else 2390 free(rp); 2391 } 2392 } 2393 2394 /* 2395 * Walk standard timings list (8). 2396 */ 2397 for (int i = 0; i < STD_TIMINGS; i++) { 2398 /* Is this field unused? */ 2399 if (edid->standard_timings[i] == 0x0101) 2400 continue; 2401 2402 if ((rp = malloc(sizeof (*rp))) == NULL) 2403 continue; 2404 2405 rp->width = HSIZE(edid->standard_timings[i]); 2406 switch (RATIO(edid->standard_timings[i])) { 2407 case RATIO1_1: 2408 rp->height = HSIZE(edid->standard_timings[i]); 2409 if (edid->header.version > 1 || 2410 edid->header.revision > 2) { 2411 rp->height = rp->height * 10 / 16; 2412 } 2413 break; 2414 case RATIO4_3: 2415 rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4; 2416 break; 2417 case RATIO5_4: 2418 rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5; 2419 break; 2420 case RATIO16_9: 2421 rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16; 2422 break; 2423 } 2424 2425 /* 2426 * Create resolution list in decreasing order, except keep 2427 * first entry (preferred timing mode). 2428 */ 2429 TAILQ_FOREACH(p, res, next) { 2430 if (p->width * p->height < rp->width * rp->height) { 2431 /* Keep preferred mode first */ 2432 if (TAILQ_FIRST(res) == p) 2433 TAILQ_INSERT_AFTER(res, p, rp, next); 2434 else 2435 TAILQ_INSERT_BEFORE(p, rp, next); 2436 break; 2437 } 2438 if (TAILQ_NEXT(p, next) == NULL) { 2439 TAILQ_INSERT_TAIL(res, rp, next); 2440 break; 2441 } 2442 } 2443 } 2444 return (!TAILQ_EMPTY(res)); 2445 } 2446