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