xref: /freebsd/sys/dev/fb/splash_bmp.c (revision 601752d5a7bef087e755da5a2b158fa35cb51ccb)
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