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