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