1 /*- 2 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org> 3 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@freebsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $Id: splash_bmp.c,v 1.6 1999/02/05 11:52:13 yokota Exp $ 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/linker.h> 34 35 #include <machine/console.h> 36 37 #include <dev/fb/fbreg.h> 38 #include <dev/fb/splashreg.h> 39 40 #define FADE_TIMEOUT 15 /* sec */ 41 #define FADE_LEVELS 10 42 43 static int splash_mode = -1; 44 static int splash_on = FALSE; 45 46 static int bmp_start(video_adapter_t *adp); 47 static int bmp_end(video_adapter_t *adp); 48 static int bmp_splash(video_adapter_t *adp, int on); 49 static int bmp_Init(const char *data, int swidth, int sheight, int sdepth); 50 static int bmp_Draw(video_adapter_t *adp); 51 52 static splash_decoder_t bmp_decoder = { 53 "splash_bmp", bmp_start, bmp_end, bmp_splash, SPLASH_IMAGE, 54 }; 55 56 SPLASH_DECODER(splash_bmp, bmp_decoder); 57 58 static int 59 bmp_start(video_adapter_t *adp) 60 { 61 /* currently only 256-color modes are supported XXX */ 62 static int modes[] = { 63 M_VESA_CG640x480, 64 M_VESA_CG800x600, 65 M_VESA_CG1024x768, 66 /* 67 * As 320x200 doesn't generally look great, 68 * it's least preferred here. 69 */ 70 M_VGA_CG320, 71 -1, 72 }; 73 video_info_t info; 74 int i; 75 76 if ((bmp_decoder.data == NULL) || (bmp_decoder.data_size <= 0)) 77 return ENODEV; 78 for (i = 0; modes[i] >= 0; ++i) { 79 if (((*vidsw[adp->va_index]->get_info)(adp, modes[i], &info) == 0) 80 && (bmp_Init((u_char *)bmp_decoder.data, 81 info.vi_width, info.vi_height, info.vi_depth) == 0)) 82 break; 83 } 84 splash_mode = modes[i]; 85 if (bootverbose) 86 printf("bmp_start(): splash_mode:%d\n", splash_mode); 87 return ((splash_mode < 0) ? ENODEV : 0); 88 } 89 90 static int 91 bmp_end(video_adapter_t *adp) 92 { 93 /* nothing to do */ 94 return 0; 95 } 96 97 static int 98 bmp_splash(video_adapter_t *adp, int on) 99 { 100 static u_char pal[256*3]; 101 static long time_stamp; 102 u_char tpal[256*3]; 103 static int fading = TRUE, brightness = FADE_LEVELS; 104 struct timeval tv; 105 int i; 106 107 if (on) { 108 if (!splash_on) { 109 /* set up the video mode and draw something */ 110 if ((*vidsw[adp->va_index]->set_mode)(adp, splash_mode)) 111 return 1; 112 if (bmp_Draw(adp)) 113 return 1; 114 (*vidsw[adp->va_index]->save_palette)(adp, pal); 115 time_stamp = 0; 116 splash_on = TRUE; 117 } 118 /* 119 * This is a kludge to fade the image away. This section of the 120 * code takes effect only after the system is completely up. 121 * FADE_TIMEOUT should be configurable. 122 */ 123 if (!cold) { 124 getmicrotime(&tv); 125 if (time_stamp == 0) 126 time_stamp = tv.tv_sec; 127 if (tv.tv_sec > time_stamp + FADE_TIMEOUT) { 128 if (fading) 129 if (brightness == 0) { 130 fading = FALSE; 131 brightness++; 132 } 133 else brightness--; 134 else 135 if (brightness == FADE_LEVELS) { 136 fading = TRUE; 137 brightness--; 138 } 139 else brightness++; 140 for (i = 0; i < sizeof(pal); ++i) { 141 tpal[i] = pal[i] * brightness / FADE_LEVELS; 142 } 143 (*vidsw[adp->va_index]->load_palette)(adp, tpal); 144 time_stamp = tv.tv_sec; 145 } 146 } 147 return 0; 148 } else { 149 /* the video mode will be restored by the caller */ 150 splash_on = FALSE; 151 return 0; 152 } 153 } 154 155 /* 156 ** Code to handle Microsoft DIB (".BMP") format images. 157 ** 158 ** Blame me (msmith@freebsd.org) if this is broken, not Soren. 159 */ 160 161 typedef struct tagBITMAPFILEHEADER { /* bmfh */ 162 u_short bfType __attribute__ ((packed)); 163 int bfSize __attribute__ ((packed)); 164 u_short bfReserved1 __attribute__ ((packed)); 165 u_short bfReserved2 __attribute__ ((packed)); 166 int bfOffBits __attribute__ ((packed)); 167 } BITMAPFILEHEADER; 168 169 typedef struct tagBITMAPINFOHEADER { /* bmih */ 170 int biSize __attribute__ ((packed)); 171 int biWidth __attribute__ ((packed)); 172 int biHeight __attribute__ ((packed)); 173 short biPlanes __attribute__ ((packed)); 174 short biBitCount __attribute__ ((packed)); 175 int biCompression __attribute__ ((packed)); 176 int biSizeImage __attribute__ ((packed)); 177 int biXPelsPerMeter __attribute__ ((packed)); 178 int biYPelsPerMeter __attribute__ ((packed)); 179 int biClrUsed __attribute__ ((packed)); 180 int biClrImportant __attribute__ ((packed)); 181 } BITMAPINFOHEADER; 182 183 typedef struct tagRGBQUAD { /* rgbq */ 184 u_char rgbBlue __attribute__ ((packed)); 185 u_char rgbGreen __attribute__ ((packed)); 186 u_char rgbRed __attribute__ ((packed)); 187 u_char rgbReserved __attribute__ ((packed)); 188 } RGBQUAD; 189 190 typedef struct tagBITMAPINFO { /* bmi */ 191 BITMAPINFOHEADER bmiHeader __attribute__ ((packed)); 192 RGBQUAD bmiColors[256] __attribute__ ((packed)); 193 } BITMAPINFO; 194 195 typedef struct tagBITMAPF 196 { 197 BITMAPFILEHEADER bmfh __attribute__ ((packed)); 198 BITMAPINFO bmfi __attribute__ ((packed)); 199 } BITMAPF; 200 201 #define BI_RGB 0 202 #define BI_RLE8 1 203 #define BI_RLE4 2 204 205 /* 206 ** all we actually care about the image 207 */ 208 typedef struct 209 { 210 int width,height; /* image dimensions */ 211 int swidth,sheight; /* screen dimensions for the current mode */ 212 u_char sdepth; /* screen depth (1, 4, 8 bpp) */ 213 int ncols; /* number of colours */ 214 u_char palette[256][3]; /* raw palette data */ 215 u_char format; /* one of the BI_* constants above */ 216 u_char *data; /* pointer to the raw data */ 217 u_char *index; /* running pointer to the data while drawing */ 218 u_char *vidmem; /* video memory allocated for drawing */ 219 video_adapter_t *adp; 220 int bank; 221 } BMP_INFO; 222 223 static BMP_INFO bmp_info; 224 225 static void 226 fill(BMP_INFO *info, int x, int y, int xsize, int ysize) 227 { 228 u_char *window; 229 int banksize; 230 int bank; 231 int p; 232 233 banksize = info->adp->va_window_size; 234 bank = (info->adp->va_line_width*y + x)/banksize; 235 window = (u_char *)info->adp->va_window; 236 (*vidsw[info->adp->va_index]->set_win_org)(info->adp, bank*banksize); 237 while (ysize > 0) { 238 p = (info->adp->va_line_width*y + x)%banksize; 239 for (; (p + xsize <= banksize) && ysize > 0; --ysize, ++y) { 240 generic_bzero(window + p, xsize); 241 p += info->adp->va_line_width; 242 } 243 if (ysize <= 0) 244 break; 245 if (p < banksize) { 246 /* the last line crosses the window boundary */ 247 generic_bzero(window + p, banksize - p); 248 } 249 ++bank; /* next bank */ 250 (*vidsw[info->adp->va_index]->set_win_org)(info->adp, bank*banksize); 251 if (p < banksize) { 252 /* the remaining part of the last line */ 253 generic_bzero(window, p + xsize - banksize); 254 ++y; 255 --ysize; 256 } 257 } 258 info->bank = bank; 259 } 260 261 /* 262 ** bmp_SetPix 263 ** 264 ** Given (info), set the pixel at (x),(y) to (val) 265 ** 266 */ 267 static void 268 bmp_SetPix(BMP_INFO *info, int x, int y, u_char val) 269 { 270 int sofs, bofs; 271 u_char tpv, mask; 272 int newbank; 273 274 /* 275 * range check to avoid explosions 276 */ 277 if ((x < 0) || (x >= info->swidth) || (y < 0) || (y >= info->sheight)) 278 return; 279 280 /* 281 * calculate offset into video memory; 282 * because 0,0 is bottom-left for DIB, we have to convert. 283 */ 284 sofs = ((info->height - (y+1) + (info->sheight - info->height) / 2) 285 * info->adp->va_line_width); 286 287 switch(info->sdepth) { 288 case 1: 289 sofs += ((x + (info->swidth - info->width) / 2) >> 3); 290 bofs = x & 0x7; /* offset within byte */ 291 292 val &= 1; /* mask pixel value */ 293 mask = ~(0x80 >> bofs); /* calculate bit mask */ 294 tpv = *(info->vidmem+sofs) & mask; /* get screen contents, excluding masked bit */ 295 *(info->vidmem+sofs) = tpv | (val << (8-bofs)); /* write new bit */ 296 break; 297 298 /* XXX only correct for non-interleaved modes */ 299 case 4: 300 sofs += ((x + (info->swidth - info->width) / 2) >> 1); 301 bofs = x & 0x1; /* offset within byte */ 302 303 val &= 0xf; /* mask pixel value */ 304 mask = bofs ? 0x0f : 0xf0; /* calculate bit mask */ 305 tpv = *(info->vidmem+sofs) & mask; /* get screen contents, excluding masked bits */ 306 *(info->vidmem+sofs) = tpv | (val << (bofs ? 0 : 4)); /* write new bits */ 307 break; 308 309 case 8: 310 sofs += x + (info->swidth - info->width) / 2; 311 newbank = sofs/info->adp->va_window_size; 312 if (info->bank != newbank) { 313 (*vidsw[info->adp->va_index]->set_win_org)(info->adp, newbank*info->adp->va_window_size); 314 info->bank = newbank; 315 } 316 sofs %= info->adp->va_window_size; 317 *(info->vidmem+sofs) = val; 318 break; 319 } 320 } 321 322 /* 323 ** bmp_DecodeRLE4 324 ** 325 ** Given (data) pointing to a line of RLE4-format data and (line) being the starting 326 ** line onscreen, decode the line. 327 */ 328 static void 329 bmp_DecodeRLE4(BMP_INFO *info, int line) 330 { 331 int count; /* run count */ 332 u_char val; 333 int x,y; /* screen position */ 334 335 x = 0; /* starting position */ 336 y = line; 337 338 /* loop reading data */ 339 for (;;) { 340 /* 341 * encoded mode starts with a run length, and then a byte with 342 * two colour indexes to alternate between for the run 343 */ 344 if (*info->index) { 345 for (count = 0; count < *info->index; count++, x++) { 346 if (count & 1) { /* odd count, low nybble */ 347 bmp_SetPix(info, x, y, *(info->index+1) & 0x0f); 348 } else { /* even count, high nybble */ 349 bmp_SetPix(info, x, y, (*(info->index+1) >>4) & 0x0f); 350 } 351 } 352 info->index += 2; 353 /* 354 * A leading zero is an escape; it may signal the end of the 355 * bitmap, a cursor move, or some absolute data. 356 */ 357 } else { /* zero tag may be absolute mode or an escape */ 358 switch (*(info->index+1)) { 359 case 0: /* end of line */ 360 info->index += 2; 361 return; 362 case 1: /* end of bitmap */ 363 info->index = NULL; 364 return; 365 case 2: /* move */ 366 x += *(info->index + 2); /* new coords */ 367 y += *(info->index + 3); 368 info->index += 4; 369 break; 370 default: /* literal bitmap data */ 371 for (count = 0; count < *(info->index + 1); count++, x++) { 372 val = *(info->index + 2 + (count / 2)); /* byte with nybbles */ 373 if (count & 1) { 374 val &= 0xf; /* get low nybble */ 375 } else { 376 val = (val >> 4); /* get high nybble */ 377 } 378 bmp_SetPix(info, x, y, val); 379 } 380 /* warning, this depends on integer truncation, do not hand-optimise! */ 381 info->index += 2 + ((count + 3) / 4) * 2; 382 break; 383 } 384 } 385 } 386 } 387 388 /* 389 ** bmp_DecodeRLE8 390 ** Given (data) pointing to a line of RLE4-format data and (line) being the starting 391 ** line onscreen, decode the line. 392 */ 393 static void 394 bmp_DecodeRLE8(BMP_INFO *info, int line) 395 { 396 int count; /* run count */ 397 int x,y; /* screen position */ 398 399 x = 0; /* starting position */ 400 y = line; 401 402 /* loop reading data */ 403 for(;;) { 404 /* 405 * encoded mode starts with a run length, and then a byte with 406 * two colour indexes to alternate between for the run 407 */ 408 if (*info->index) { 409 for (count = 0; count < *info->index; count++, x++) 410 bmp_SetPix(info, x, y, *(info->index+1)); 411 info->index += 2; 412 /* 413 * A leading zero is an escape; it may signal the end of the 414 * bitmap, a cursor move, or some absolute data. 415 */ 416 } else { /* zero tag may be absolute mode or an escape */ 417 switch(*(info->index+1)) { 418 case 0: /* end of line */ 419 info->index += 2; 420 return; 421 case 1: /* end of bitmap */ 422 info->index = NULL; 423 return; 424 case 2: /* move */ 425 x += *(info->index + 2); /* new coords */ 426 y += *(info->index + 3); 427 info->index += 4; 428 break; 429 default: /* literal bitmap data */ 430 for (count = 0; count < *(info->index + 1); count++, x++) 431 bmp_SetPix(info, x, y, *(info->index + 2 + count)); 432 /* must be an even count */ 433 info->index += 2 + count + (count & 1); 434 break; 435 } 436 } 437 } 438 } 439 440 /* 441 ** bmp_DecodeLine 442 ** 443 ** Given (info) pointing to an image being decoded, (line) being the line currently 444 ** being displayed, decode a line of data. 445 */ 446 static void 447 bmp_DecodeLine(BMP_INFO *info, int line) 448 { 449 int x; 450 451 switch(info->format) { 452 case BI_RGB: 453 for (x = 0; x < info->width; x++, info->index++) 454 bmp_SetPix(info, x, line, *info->index); 455 info->index += 3 - (--x % 4); 456 break; 457 case BI_RLE4: 458 bmp_DecodeRLE4(info, line); 459 break; 460 case BI_RLE8: 461 bmp_DecodeRLE8(info, line); 462 break; 463 } 464 } 465 466 /* 467 ** bmp_Init 468 ** 469 ** Given a pointer (data) to the image of a BMP file, fill in bmp_info with what 470 ** can be learnt from it. Return nonzero if the file isn't usable. 471 ** 472 ** Take screen dimensions (swidth), (sheight) and (sdepth) and make sure we 473 ** can work with these. 474 */ 475 static int 476 bmp_Init(const char *data, int swidth, int sheight, int sdepth) 477 { 478 BITMAPF *bmf = (BITMAPF *)data; 479 int pind; 480 481 bmp_info.data = NULL; /* assume setup failed */ 482 483 /* check file ID */ 484 if (bmf->bmfh.bfType != 0x4d42) { 485 return(1); /* XXX check word ordering for big-endian ports? */ 486 } 487 488 /* save what we know about the screen */ 489 bmp_info.swidth = swidth; 490 bmp_info.sheight = sheight; 491 bmp_info.sdepth = sdepth; 492 493 /* where's the data? */ 494 bmp_info.data = (u_char *)data + bmf->bmfh.bfOffBits; 495 496 /* image parameters */ 497 bmp_info.width = bmf->bmfi.bmiHeader.biWidth; 498 bmp_info.height = bmf->bmfi.bmiHeader.biHeight; 499 bmp_info.format = bmf->bmfi.bmiHeader.biCompression; 500 501 switch(bmp_info.format) { /* check compression format */ 502 case BI_RGB: 503 case BI_RLE4: 504 case BI_RLE8: 505 break; 506 default: 507 return(1); /* unsupported compression format */ 508 } 509 510 /* palette details */ 511 bmp_info.ncols = (bmf->bmfi.bmiHeader.biClrUsed); 512 bzero(bmp_info.palette,sizeof(bmp_info.palette)); 513 if (bmp_info.ncols == 0) { /* uses all of them */ 514 bmp_info.ncols = 1 << bmf->bmfi.bmiHeader.biBitCount; 515 } 516 if ((bmp_info.height > bmp_info.sheight) || 517 (bmp_info.width > bmp_info.swidth) || 518 (bmp_info.ncols > (1 << sdepth))) { 519 return(1); /* beyond screen capacity */ 520 } 521 522 /* read palette */ 523 for (pind = 0; pind < bmp_info.ncols; pind++) { 524 bmp_info.palette[pind][0] = bmf->bmfi.bmiColors[pind].rgbRed; 525 bmp_info.palette[pind][1] = bmf->bmfi.bmiColors[pind].rgbGreen; 526 bmp_info.palette[pind][2] = bmf->bmfi.bmiColors[pind].rgbBlue; 527 } 528 return(0); 529 } 530 531 /* 532 ** bmp_Draw 533 ** 534 ** Render the image. Return nonzero if that's not possible. 535 ** 536 */ 537 static int 538 bmp_Draw(video_adapter_t *adp) 539 { 540 int line; 541 542 if (bmp_info.data == NULL) { /* init failed, do nothing */ 543 return(1); 544 } 545 546 /* clear the screen */ 547 bmp_info.vidmem = (u_char *)adp->va_window; 548 bmp_info.adp = adp; 549 /* XXX; the following line is correct only for 8bpp modes */ 550 fill(&bmp_info, 0, 0, bmp_info.swidth, bmp_info.sheight); 551 (*vidsw[adp->va_index]->set_win_org)(adp, 0); 552 bmp_info.bank = 0; 553 554 /* initialise the info structure for drawing */ 555 bmp_info.index = bmp_info.data; 556 557 /* set the palette for our image */ 558 (*vidsw[adp->va_index]->load_palette)(adp, (u_char *)&bmp_info.palette); 559 560 for (line = 0; (line < bmp_info.height) && bmp_info.index; line++) { 561 bmp_DecodeLine(&bmp_info, line); 562 } 563 return(0); 564 } 565