1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Toomas Soome <tsoome@me.com> 14 */ 15 16 /* 17 * Graphics support for loader emulation. 18 * The interface in loader and here needs some more development. 19 * We can get colormap from gfx_private, but loader is currently 20 * relying on tem fg/bg colors for drawing, once the menu code 21 * will get some facelift, we would need to provide colors as menu component 22 * attributes and stop depending on tem. 23 */ 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <stdbool.h> 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <sys/mman.h> 33 #include <sys/fbio.h> 34 #include <string.h> 35 #include "gfx_fb.h" 36 37 struct framebuffer fb; 38 39 #define max(x, y) ((x) >= (y) ? (x) : (y)) 40 41 static void gfx_fb_cons_display(uint32_t, uint32_t, 42 uint32_t, uint32_t, uint8_t *); 43 44 /* This colormap should be replaced by colormap query from kernel */ 45 typedef struct { 46 uint8_t red[16]; 47 uint8_t green[16]; 48 uint8_t blue[16]; 49 } text_cmap_t; 50 51 text_cmap_t cmap4_to_24 = { 52 /* BEGIN CSTYLED */ 53 /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 54 Wh+ Bk Bl Gr Cy Rd Mg Br Wh Bk+ Bl+ Gr+ Cy+ Rd+ Mg+ Yw */ 55 .red = {0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff}, 56 .green = {0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff}, 57 .blue = {0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00} 58 /* END CSTYLED */ 59 }; 60 61 const uint8_t solaris_color_to_pc_color[16] = { 62 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 63 }; 64 65 void 66 gfx_framework_init(void) 67 { 68 struct fbgattr attr; 69 struct gfxfb_info *gfxfb_info; 70 char buf[10]; 71 72 fb.fd = open("/dev/fb", O_RDWR); 73 if (fb.fd < 0) 74 return; 75 76 /* make sure we have GFX framebuffer */ 77 if (ioctl(fb.fd, VIS_GETIDENTIFIER, &fb.ident) < 0 || 78 strcmp(fb.ident.name, "illumos_fb") != 0) { 79 (void) close(fb.fd); 80 fb.fd = -1; 81 return; 82 } 83 84 if (ioctl(fb.fd, FBIOGATTR, &attr) < 0) { 85 (void) close(fb.fd); 86 fb.fd = -1; 87 return; 88 } 89 gfxfb_info = (struct gfxfb_info *)attr.sattr.dev_specific; 90 91 fb.fb_height = attr.fbtype.fb_height; 92 fb.fb_width = attr.fbtype.fb_width; 93 fb.fb_depth = attr.fbtype.fb_depth; 94 fb.fb_size = attr.fbtype.fb_size; 95 fb.fb_bpp = attr.fbtype.fb_depth >> 3; 96 if (attr.fbtype.fb_depth == 15) 97 fb.fb_bpp = 2; 98 fb.fb_pitch = gfxfb_info->pitch; 99 fb.terminal_origin_x = gfxfb_info->terminal_origin_x; 100 fb.terminal_origin_y = gfxfb_info->terminal_origin_y; 101 fb.font_width = gfxfb_info->font_width; 102 fb.font_height = gfxfb_info->font_height; 103 104 fb.red_mask_size = gfxfb_info->red_mask_size; 105 fb.red_field_position = gfxfb_info->red_field_position; 106 fb.green_mask_size = gfxfb_info->green_mask_size; 107 fb.green_field_position = gfxfb_info->green_field_position; 108 fb.blue_mask_size = gfxfb_info->blue_mask_size; 109 fb.blue_field_position = gfxfb_info->blue_field_position; 110 111 fb.fb_addr = (uint8_t *)mmap(0, fb.fb_size, (PROT_READ | PROT_WRITE), 112 MAP_SHARED, fb.fd, 0); 113 114 if (fb.fb_addr == NULL) { 115 (void) close(fb.fd); 116 fb.fd = -1; 117 return; 118 } 119 (void) snprintf(buf, sizeof (buf), "%d", fb.fb_height); 120 (void) setenv("screen-height", buf, 1); 121 (void) snprintf(buf, sizeof (buf), "%d", fb.fb_width); 122 (void) setenv("screen-width", buf, 1); 123 } 124 125 void 126 gfx_framework_fini(void) 127 { 128 if (fb.fd < 0) 129 return; 130 131 (void) munmap((caddr_t)fb.fb_addr, fb.fb_size); 132 (void) close(fb.fd); 133 fb.fd = -1; 134 } 135 136 static int 137 isqrt(int num) 138 { 139 int res = 0; 140 int bit = 1 << 30; 141 142 /* "bit" starts at the highest power of four <= the argument. */ 143 while (bit > num) 144 bit >>= 2; 145 146 while (bit != 0) { 147 if (num >= res + bit) { 148 num -= res + bit; 149 res = (res >> 1) + bit; 150 } else { 151 res >>= 1; 152 } 153 bit >>= 2; 154 } 155 return (res); 156 } 157 158 void 159 gfx_fb_setpixel(uint32_t x, uint32_t y) 160 { 161 uint32_t c, offset; 162 163 if (fb.fd < 0) 164 return; 165 c = 0; /* black */ 166 167 if (x >= fb.fb_width || y >= fb.fb_height) 168 return; 169 170 offset = y * fb.fb_pitch + x * fb.fb_bpp; 171 switch (fb.fb_depth) { 172 case 8: 173 fb.fb_addr[offset] = c & 0xff; 174 break; 175 case 15: 176 case 16: 177 *(uint16_t *)(fb.fb_addr + offset) = c & 0xffff; 178 break; 179 case 24: 180 fb.fb_addr[offset] = (c >> 16) & 0xff; 181 fb.fb_addr[offset + 1] = (c >> 8) & 0xff; 182 fb.fb_addr[offset + 2] = c & 0xff; 183 break; 184 case 32: 185 *(uint32_t *)(fb.fb_addr + offset) = c; 186 break; 187 } 188 } 189 190 void 191 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2, 192 uint32_t fill) 193 { 194 int x, y; 195 196 if (fb.fd < 0) 197 return; 198 199 for (y = y1; y <= y2; y++) { 200 if (fill || (y == y1) || (y == y2)) { 201 for (x = x1; x <= x2; x++) 202 gfx_fb_setpixel(x, y); 203 } else { 204 gfx_fb_setpixel(x1, y); 205 gfx_fb_setpixel(x2, y); 206 } 207 } 208 } 209 210 void 211 gfx_term_drawrect(uint32_t row1, uint32_t col1, uint32_t row2, uint32_t col2) 212 { 213 int x1, y1, x2, y2; 214 int xshift, yshift; 215 int width, i; 216 217 if (fb.fd < 0) 218 return; 219 220 width = fb.font_width / 4; /* line width */ 221 xshift = (fb.font_width - width) / 2; 222 yshift = (fb.font_height - width) / 2; 223 /* Terminal coordinates start from (1,1) */ 224 row1--; 225 col1--; 226 row2--; 227 col2--; 228 229 /* 230 * Draw horizontal lines width points thick, shifted from outer edge. 231 */ 232 x1 = (row1 + 1) * fb.font_width + fb.terminal_origin_x; 233 y1 = col1 * fb.font_height + fb.terminal_origin_y + yshift; 234 x2 = row2 * fb.font_width + fb.terminal_origin_x; 235 gfx_fb_drawrect(x1, y1, x2, y1 + width, 1); 236 y2 = col2 * fb.font_height + fb.terminal_origin_y; 237 y2 += fb.font_height - yshift - width; 238 gfx_fb_drawrect(x1, y2, x2, y2 + width, 1); 239 240 /* 241 * Draw vertical lines width points thick, shifted from outer edge. 242 */ 243 x1 = row1 * fb.font_width + fb.terminal_origin_x + xshift; 244 y1 = col1 * fb.font_height + fb.terminal_origin_y; 245 y1 += fb.font_height; 246 y2 = col2 * fb.font_height + fb.terminal_origin_y; 247 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1); 248 x1 = row2 * fb.font_width + fb.terminal_origin_x; 249 x1 += fb.font_width - xshift - width; 250 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1); 251 252 /* Draw upper left corner. */ 253 x1 = row1 * fb.font_width + fb.terminal_origin_x + xshift; 254 y1 = col1 * fb.font_height + fb.terminal_origin_y; 255 y1 += fb.font_height; 256 257 x2 = row1 * fb.font_width + fb.terminal_origin_x; 258 x2 += fb.font_width; 259 y2 = col1 * fb.font_height + fb.terminal_origin_y + yshift; 260 for (i = 0; i <= width; i++) 261 gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i); 262 263 /* Draw lower left corner. */ 264 x1 = row1 * fb.font_width + fb.terminal_origin_x; 265 x1 += fb.font_width; 266 y1 = col2 * fb.font_height + fb.terminal_origin_y; 267 y1 += fb.font_height - yshift; 268 x2 = row1 * fb.font_width + fb.terminal_origin_x + xshift; 269 y2 = col2 * fb.font_height + fb.terminal_origin_y; 270 for (i = 0; i <= width; i++) 271 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i); 272 273 /* Draw upper right corner. */ 274 x1 = row2 * fb.font_width + fb.terminal_origin_x; 275 y1 = col1 * fb.font_height + fb.terminal_origin_y + yshift; 276 x2 = row2 * fb.font_width + fb.terminal_origin_x; 277 x2 += fb.font_width - xshift - width; 278 y2 = col1 * fb.font_height + fb.terminal_origin_y; 279 y2 += fb.font_height; 280 for (i = 0; i <= width; i++) 281 gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i); 282 283 /* Draw lower right corner. */ 284 x1 = row2 * fb.font_width + fb.terminal_origin_x; 285 y1 = col2 * fb.font_height + fb.terminal_origin_y; 286 y1 += fb.font_height - yshift; 287 x2 = row2 * fb.font_width + fb.terminal_origin_x; 288 x2 += fb.font_width - xshift - width; 289 y2 = col2 * fb.font_height + fb.terminal_origin_y; 290 for (i = 0; i <= width; i++) 291 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i); 292 } 293 294 void 295 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t width) 296 { 297 int dx, sx, dy, sy; 298 int err, e2, x2, y2, ed; 299 300 if (fb.fd < 0) 301 return; 302 303 sx = x0 < x1? 1 : -1; 304 sy = y0 < y1? 1 : -1; 305 dx = abs(x1 - x0); 306 dy = abs(y1 - y0); 307 err = dx - dy; 308 ed = dx + dy == 0 ? 1 : isqrt(dx * dx + dy * dy); 309 310 if (dx != 0 && dy != 0) 311 width = (width + 1) >> 1; 312 313 for (;;) { 314 gfx_fb_setpixel(x0, y0); 315 e2 = err; 316 x2 = x0; 317 if ((e2 << 1) >= -dx) { /* x step */ 318 e2 += dy; 319 y2 = y0; 320 while (e2 < ed * width && (y1 != y2 || dx > dy)) { 321 y2 += sy; 322 gfx_fb_setpixel(x0, y2); 323 e2 += dx; 324 } 325 if (x0 == x1) 326 break; 327 e2 = err; 328 err -= dy; 329 x0 += sx; 330 } 331 if ((e2 << 1) <= dy) { /* y step */ 332 e2 = dx-e2; 333 while (e2 < ed * width && (x1 != x2 || dx < dy)) { 334 x2 += sx; 335 gfx_fb_setpixel(x2, y0); 336 e2 += dy; 337 } 338 if (y0 == y1) 339 break; 340 err += dx; 341 y0 += sy; 342 } 343 } 344 } 345 346 void 347 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2, 348 uint32_t y2, uint32_t wd) 349 { 350 int sx, sy, xx, yy, xy, width; 351 int dx, dy, err, curvature; 352 int i; 353 354 if (fb.fd < 0) 355 return; 356 357 width = wd; 358 sx = x2 - x1; 359 sy = y2 - y1; 360 xx = x0 - x1; 361 yy = y0 - y1; 362 curvature = xx*sy - yy*sx; 363 364 if (sx * sx + sy * sy > xx * xx + yy * yy) { 365 x2 = x0; 366 x0 = sx + x1; 367 y2 = y0; 368 y0 = sy + y1; 369 curvature = -curvature; 370 } 371 if (curvature != 0) { 372 xx += sx; 373 sx = x0 < x2? 1 : -1; 374 xx *= sx; 375 yy += sy; 376 sy = y0 < y2? 1 : -1; 377 yy *= sy; 378 xy = 2 * xx * yy; 379 xx *= xx; 380 yy *= yy; 381 if (curvature * sx * sy < 0) { 382 xx = -xx; 383 yy = -yy; 384 xy = -xy; 385 curvature = -curvature; 386 } 387 dx = 4 * sy * curvature * (x1 - x0) + xx - xy; 388 dy = 4 * sx * curvature * (y0 - y1) + yy - xy; 389 xx += xx; 390 yy += yy; 391 err = dx + dy + xy; 392 do { 393 for (i = 0; i <= width; i++) 394 gfx_fb_setpixel(x0 + i, y0); 395 if (x0 == x2 && y0 == y2) 396 return; /* last pixel -> curve finished */ 397 y1 = 2 * err < dx; 398 if (2 * err > dy) { 399 x0 += sx; 400 dx -= xy; 401 dy += yy; 402 err += dy; 403 } 404 if (y1 != 0) { 405 y0 += sy; 406 dy -= xy; 407 dx += xx; 408 err += dx; 409 } 410 } while (dy < dx); /* gradient negates -> algorithm fails */ 411 } 412 gfx_fb_line(x0, y0, x2, y2, width); 413 } 414 415 #define FL_PUTIMAGE_BORDER 0x1 416 #define FL_PUTIMAGE_NOSCROLL 0x2 417 #define FL_PUTIMAGE_DEBUG 0x80 418 419 int 420 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2, 421 uint32_t uy2, uint32_t flags) 422 { 423 uint32_t i, j, x, y, fheight, fwidth, color; 424 uint8_t r, g, b, a, *p, *data; 425 bool scale = false; 426 bool trace = false; 427 428 trace = (flags & FL_PUTIMAGE_DEBUG) != 0; 429 430 if (fb.fd < 0) { 431 if (trace) 432 printf("Framebuffer not active.\n"); 433 return (1); 434 } 435 436 if (png->color_type != PNG_TRUECOLOR_ALPHA) { 437 if (trace) 438 printf("Not truecolor image.\n"); 439 return (1); 440 } 441 442 if (ux1 > fb.fb_width || uy1 > fb.fb_height) { 443 if (trace) 444 printf("Top left coordinate off screen.\n"); 445 return (1); 446 } 447 448 if (png->width > UINT16_MAX || png->height > UINT16_MAX) { 449 if (trace) 450 printf("Image too large.\n"); 451 return (1); 452 } 453 454 if (png->width < 1 || png->height < 1) { 455 if (trace) 456 printf("Image too small.\n"); 457 return (1); 458 } 459 460 /* 461 * If 0 was passed for either ux2 or uy2, then calculate the missing 462 * part of the bottom right coordinate. 463 */ 464 scale = true; 465 if (ux2 == 0 && uy2 == 0) { 466 /* Both 0, use the native resolution of the image */ 467 ux2 = ux1 + png->width; 468 uy2 = uy1 + png->height; 469 scale = false; 470 } else if (ux2 == 0) { 471 /* Set ux2 from uy2/uy1 to maintain aspect ratio */ 472 ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height; 473 } else if (uy2 == 0) { 474 /* Set uy2 from ux2/ux1 to maintain aspect ratio */ 475 uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width; 476 } 477 478 if (ux2 > fb.fb_width || uy2 > fb.fb_height) { 479 if (trace) 480 printf("Bottom right coordinate off screen.\n"); 481 return (1); 482 } 483 484 fwidth = ux2 - ux1; 485 fheight = uy2 - uy1; 486 487 /* 488 * If the original image dimensions have been passed explicitly, 489 * disable scaling. 490 */ 491 if (fwidth == png->width && fheight == png->height) 492 scale = false; 493 494 if (ux1 == 0) { 495 /* 496 * No top left X co-ordinate (real coordinates start at 1), 497 * place as far right as it will fit. 498 */ 499 ux2 = fb.fb_width - fb.terminal_origin_x; 500 ux1 = ux2 - fwidth; 501 } 502 503 if (uy1 == 0) { 504 /* 505 * No top left Y co-ordinate (real coordinates start at 1), 506 * place as far down as it will fit. 507 */ 508 uy2 = fb.fb_height - fb.terminal_origin_y; 509 uy1 = uy2 - fheight; 510 } 511 512 if (ux1 >= ux2 || uy1 >= uy2) { 513 if (trace) 514 printf("Image dimensions reversed.\n"); 515 return (1); 516 } 517 518 if (fwidth < 2 || fheight < 2) { 519 if (trace) 520 printf("Target area too small\n"); 521 return (1); 522 } 523 524 if (trace) 525 printf("Image %ux%u -> %ux%u @%ux%u\n", 526 png->width, png->height, fwidth, fheight, ux1, uy1); 527 528 if ((flags & FL_PUTIMAGE_BORDER)) 529 gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0); 530 531 data = malloc(fwidth * fheight * fb.fb_bpp); 532 if (data == NULL) { 533 if (trace) 534 printf("Out of memory.\n"); 535 return (1); 536 } 537 538 /* 539 * Build image for our framebuffer. 540 */ 541 542 /* Helper to calculate the pixel index from the source png */ 543 #define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp) 544 545 /* 546 * For each of the x and y directions, calculate the number of pixels 547 * in the source image that correspond to a single pixel in the target. 548 * Use fixed-point arithmetic with 16-bits for each of the integer and 549 * fractional parts. 550 */ 551 const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1); 552 const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1); 553 554 uint32_t hc = 0; 555 for (y = 0; y < fheight; y++) { 556 uint32_t hc2 = (hc >> 9) & 0x7f; 557 uint32_t hc1 = 0x80 - hc2; 558 559 uint32_t offset_y = hc >> 16; 560 uint32_t offset_y1 = offset_y + 1; 561 562 uint32_t wc = 0; 563 for (x = 0; x < fwidth; x++) { 564 uint32_t wc2 = (wc >> 9) & 0x7f; 565 uint32_t wc1 = 0x80 - wc2; 566 567 uint32_t offset_x = wc >> 16; 568 uint32_t offset_x1 = offset_x + 1; 569 570 /* Target pixel index */ 571 j = (y * fwidth + x) * fb.fb_bpp; 572 573 if (!scale) { 574 i = GETPIXEL(x, y); 575 r = png->image[i]; 576 g = png->image[i + 1]; 577 b = png->image[i + 2]; 578 a = png->image[i + 3]; 579 } else { 580 uint8_t pixel[4]; 581 582 uint32_t p00 = GETPIXEL(offset_x, offset_y); 583 uint32_t p01 = GETPIXEL(offset_x, offset_y1); 584 uint32_t p10 = GETPIXEL(offset_x1, offset_y); 585 uint32_t p11 = GETPIXEL(offset_x1, offset_y1); 586 587 /* 588 * Given a 2x2 array of pixels in the source 589 * image, combine them to produce a single 590 * value for the pixel in the target image. 591 * Each column of pixels is combined using 592 * a weighted average where the top and bottom 593 * pixels contribute hc1 and hc2 respectively. 594 * The calculation for bottom pixel pB and 595 * top pixel pT is: 596 * (pT * hc1 + pB * hc2) / (hc1 + hc2) 597 * Once the values are determined for the two 598 * columns of pixels, then the columns are 599 * averaged together in the same way but using 600 * wc1 and wc2 for the weightings. 601 * 602 * Since hc1 and hc2 are chosen so that 603 * hc1 + hc2 == 128 (and same for wc1 + wc2), 604 * the >> 14 below is a quick way to divide by 605 * (hc1 + hc2) * (wc1 + wc2) 606 */ 607 for (i = 0; i < 4; i++) 608 pixel[i] = ( 609 (png->image[p00 + i] * hc1 + 610 png->image[p01 + i] * hc2) * wc1 + 611 (png->image[p10 + i] * hc1 + 612 png->image[p11 + i] * hc2) * wc2) 613 >> 14; 614 615 r = pixel[0]; 616 g = pixel[1]; 617 b = pixel[2]; 618 a = pixel[3]; 619 } 620 621 color = 622 r >> (8 - fb.red_mask_size) 623 << fb.red_field_position | 624 g >> (8 - fb.green_mask_size) 625 << fb.green_field_position | 626 b >> (8 - fb.blue_mask_size) 627 << fb.blue_field_position; 628 629 switch (fb.fb_depth) { 630 case 8: { 631 uint32_t best, dist, k; 632 int diff; 633 634 color = 0; 635 best = 256 * 256 * 256; 636 for (k = 0; k < 16; k++) { 637 diff = r - cmap4_to_24.red[k]; 638 dist = diff * diff; 639 diff = g - cmap4_to_24.green[k]; 640 dist += diff * diff; 641 diff = b - cmap4_to_24.blue[k]; 642 dist += diff * diff; 643 644 if (dist < best) { 645 color = k; 646 best = dist; 647 if (dist == 0) 648 break; 649 } 650 } 651 data[j] = solaris_color_to_pc_color[color]; 652 break; 653 } 654 case 15: 655 case 16: 656 *(uint16_t *)(data+j) = color; 657 break; 658 case 24: 659 p = (uint8_t *)&color; 660 data[j] = p[0]; 661 data[j+1] = p[1]; 662 data[j+2] = p[2]; 663 break; 664 case 32: 665 color |= a << 24; 666 *(uint32_t *)(data+j) = color; 667 break; 668 } 669 wc += wcstep; 670 } 671 hc += hcstep; 672 } 673 674 gfx_fb_cons_display(uy1, ux1, fwidth, fheight, data); 675 free(data); 676 return (0); 677 } 678 679 /* 680 * Implements alpha blending for RGBA data, could use pixels for arguments, 681 * but byte stream seems more generic. 682 * The generic alpha blending is: 683 * blend = alpha * fg + (1.0 - alpha) * bg. 684 * Since our alpha is not from range [0..1], we scale appropriately. 685 */ 686 static uint8_t 687 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha) 688 { 689 uint16_t blend, h, l; 690 691 /* trivial corner cases */ 692 if (alpha == 0) 693 return (bg); 694 if (alpha == 0xFF) 695 return (fg); 696 blend = (alpha * fg + (0xFF - alpha) * bg); 697 /* Division by 0xFF */ 698 h = blend >> 8; 699 l = blend & 0xFF; 700 if (h + l >= 0xFF) 701 h++; 702 return (h); 703 } 704 705 /* Copy memory to framebuffer or to memory. */ 706 static void 707 bitmap_cpy(uint8_t *dst, uint8_t *src, uint32_t len, int bpp) 708 { 709 uint32_t i; 710 uint8_t a; 711 712 switch (bpp) { 713 case 4: 714 for (i = 0; i < len; i += bpp) { 715 a = src[i+3]; 716 dst[i] = alpha_blend(src[i], dst[i], a); 717 dst[i+1] = alpha_blend(src[i+1], dst[i+1], a); 718 dst[i+2] = alpha_blend(src[i+2], dst[i+2], a); 719 dst[i+3] = a; 720 } 721 break; 722 default: 723 (void) memcpy(dst, src, len); 724 break; 725 } 726 } 727 728 /* 729 * gfx_fb_cons_display implements direct draw on frame buffer memory. 730 * It is needed till we have way to send bitmaps to tem, tem already has 731 * function to send data down to framebuffer. 732 */ 733 static void 734 gfx_fb_cons_display(uint32_t row, uint32_t col, 735 uint32_t width, uint32_t height, uint8_t *data) 736 { 737 uint32_t size; /* write size per scanline */ 738 uint8_t *fbp; /* fb + calculated offset */ 739 int i; 740 741 /* make sure we will not write past FB */ 742 if (col >= fb.fb_width || row >= fb.fb_height || 743 col + width > fb.fb_width || row + height > fb.fb_height) 744 return; 745 746 size = width * fb.fb_bpp; 747 fbp = fb.fb_addr + col * fb.fb_bpp + row * fb.fb_pitch; 748 749 /* write all scanlines in rectangle */ 750 for (i = 0; i < height; i++) { 751 uint8_t *dest = fbp + i * fb.fb_pitch; 752 uint8_t *src = data + i * size; 753 bitmap_cpy(dest, src, size, fb.fb_bpp); 754 } 755 } 756