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