xref: /freebsd/sys/dev/fb/splash_bmp.c (revision f9e730bbb343d0a6aff3a9d4a7ea18dc1cf3ddc5)
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.2 1999/01/11 17:32:22 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	300	/* sec */
41 
42 static int splash_mode = -1;
43 static int splash_on = FALSE;
44 
45 static int bmp_start(video_adapter_t *adp);
46 static int bmp_end(video_adapter_t *adp);
47 static int bmp_splash(video_adapter_t *adp, int on);
48 static int bmp_Init(const char *data, int swidth, int sheight, int sdepth);
49 static int bmp_Draw(video_adapter_t *adp);
50 
51 static splash_decoder_t bmp_decoder = {
52     "splash_bmp", bmp_start, bmp_end, bmp_splash, SPLASH_IMAGE,
53 };
54 
55 SPLASH_DECODER(splash_bmp, bmp_decoder);
56 
57 static int
58 bmp_start(video_adapter_t *adp)
59 {
60     static int		modes[] = {
61 			M_VGA_CG320,
62 			-1,
63     };
64     video_info_t 	info;
65     int			i;
66 
67     if ((bmp_decoder.data == NULL) || (bmp_decoder.data_size <= 0))
68 	return ENODEV;
69     for (i = 0; modes[i] >= 0; ++i) {
70 	if (((*vidsw[adp->va_index]->get_info)(adp, modes[i], &info) == 0)
71 	    && (bmp_Init((u_char *)bmp_decoder.data,
72 			 info.vi_width, info.vi_height, info.vi_depth) == 0))
73 	    break;
74     }
75     splash_mode = modes[i];
76     if (bootverbose)
77 	printf("bmp_start(): splash_mode:%d\n", splash_mode);
78     return ((splash_mode < 0) ? ENODEV : 0);
79 }
80 
81 static int
82 bmp_end(video_adapter_t *adp)
83 {
84     /* nothing to do */
85     return 0;
86 }
87 
88 static int
89 bmp_splash(video_adapter_t *adp, int on)
90 {
91     static u_char	pal[256*3];
92     static long		time_stamp;
93     struct timeval	tv;
94     int			i;
95 
96     if (on) {
97 	if (!splash_on) {
98 	    /* set up the video mode and draw something */
99 	    if ((*vidsw[adp->va_index]->set_mode)(adp, splash_mode))
100 		return 1;
101 	    if (bmp_Draw(adp))
102 		return 1;
103 	    (*vidsw[adp->va_index]->save_palette)(adp, pal);
104 	    time_stamp = 0;
105 	    splash_on = TRUE;
106 	}
107 	/*
108 	 * This is a kludge to fade the image away.  This section of the
109 	 * code takes effect only after the system is completely up.
110 	 */
111 	if (!cold) {
112 	    getmicrotime(&tv);
113 	    if (time_stamp == 0)
114 		time_stamp = tv.tv_sec;
115 	    if (tv.tv_sec > time_stamp + FADE_TIMEOUT) {
116 		for (i = 0; i < sizeof(pal); ++i) {
117 		    if (pal[i] > 40)
118 			pal[i] -= 4;
119 		}
120 		(*vidsw[adp->va_index]->load_palette)(adp, pal);
121 	    }
122 	}
123 	return 0;
124     } else {
125 	/* the video mode will be restored by the caller */
126 	splash_on = FALSE;
127 	return 0;
128     }
129 }
130 
131 /*
132 ** Code to handle Microsoft DIB (".BMP") format images.
133 **
134 ** Blame me (msmith@freebsd.org) if this is broken, not Soren.
135 */
136 
137 typedef struct tagBITMAPFILEHEADER {    /* bmfh */
138     u_short	bfType		__attribute__ ((packed));
139     int		bfSize		__attribute__ ((packed));
140     u_short	bfReserved1	__attribute__ ((packed));
141     u_short	bfReserved2	__attribute__ ((packed));
142     int		bfOffBits	__attribute__ ((packed));
143 } BITMAPFILEHEADER;
144 
145 typedef struct tagBITMAPINFOHEADER {    /* bmih */
146     int		biSize		__attribute__ ((packed));
147     int		biWidth		__attribute__ ((packed));
148     int		biHeight	__attribute__ ((packed));
149     short	biPlanes	__attribute__ ((packed));
150     short	biBitCount	__attribute__ ((packed));
151     int		biCompression	__attribute__ ((packed));
152     int		biSizeImage	__attribute__ ((packed));
153     int		biXPelsPerMeter	__attribute__ ((packed));
154     int		biYPelsPerMeter	__attribute__ ((packed));
155     int		biClrUsed	__attribute__ ((packed));
156     int		biClrImportant	__attribute__ ((packed));
157 } BITMAPINFOHEADER;
158 
159 typedef struct tagRGBQUAD {     /* rgbq */
160     u_char	rgbBlue		__attribute__ ((packed));
161     u_char	rgbGreen	__attribute__ ((packed));
162     u_char	rgbRed		__attribute__ ((packed));
163     u_char	rgbReserved	__attribute__ ((packed));
164 } RGBQUAD;
165 
166 typedef struct tagBITMAPINFO {  /* bmi */
167     BITMAPINFOHEADER	bmiHeader	__attribute__ ((packed));
168     RGBQUAD		bmiColors[256]	__attribute__ ((packed));
169 } BITMAPINFO;
170 
171 typedef struct tagBITMAPF
172 {
173     BITMAPFILEHEADER	bmfh	__attribute__ ((packed));
174     BITMAPINFO		bmfi	__attribute__ ((packed));
175 } BITMAPF;
176 
177 #define BI_RGB		0
178 #define BI_RLE8		1
179 #define BI_RLE4		2
180 
181 /*
182 ** all we actually care about the image
183 */
184 typedef struct
185 {
186     int		width,height;		/* image dimensions */
187     int		swidth,sheight;		/* screen dimensions for the current mode */
188     u_char	sdepth;			/* screen depth (1, 4, 8 bpp) */
189     int		ncols;			/* number of colours */
190     u_char	palette[256][3];	/* raw palette data */
191     u_char	format;			/* one of the BI_* constants above */
192     u_char	*data;			/* pointer to the raw data */
193     u_char	*index;			/* running pointer to the data while drawing */
194     u_char	*vidmem;		/* video memory allocated for drawing */
195 } BMP_INFO;
196 
197 static BMP_INFO bmp_info;
198 
199 /*
200 ** bmp_SetPix
201 **
202 ** Given (info), set the pixel at (x),(y) to (val)
203 **
204 */
205 static void
206 bmp_SetPix(BMP_INFO *info, int x, int y, u_char val)
207 {
208     int		sofs, bofs;
209     u_char	tpv, mask;
210 
211     /*
212      * range check to avoid explosions
213      */
214     if ((x < 0) || (x >= info->swidth) || (y < 0) || (y >= info->sheight))
215 	return;
216 
217     /*
218      * calculate offset into video memory;
219      * because 0,0 is bottom-left for DIB, we have to convert.
220      */
221     sofs = ((info->height - (y+1) + (info->sheight - info->height) / 2)
222 		* info->swidth) + x + (info->swidth - info->width) / 2;
223 
224     switch(info->sdepth) {
225     case 1:
226 	sofs = sofs >> 3;			/* correct for depth */
227 	bofs = x & 0x7;				/* offset within byte */
228 
229 	val &= 1;				/* mask pixel value */
230 	mask = ~(0x80 >> bofs);			/* calculate bit mask */
231 	tpv = *(info->vidmem+sofs) & mask;	/* get screen contents, excluding masked bit */
232 	*(info->vidmem+sofs) = tpv | (val << (8-bofs));	/* write new bit */
233 	break;
234 
235 	/* XXX only correct for non-interleaved modes */
236     case 4:
237 	sofs = sofs >> 1;			/* correct for depth */
238 	bofs = x & 0x1;				/* offset within byte */
239 
240 	val &= 0xf;				/* mask pixel value */
241 	mask = bofs ? 0x0f : 0xf0;		/* calculate bit mask */
242 	tpv = *(info->vidmem+sofs) & mask;	/* get screen contents, excluding masked bits */
243 	*(info->vidmem+sofs) = tpv | (val << (bofs ? 0 : 4));	/* write new bits */
244 	break;
245 
246     case 8:
247 	*(info->vidmem+sofs) = val;
248 	break;
249     }
250 }
251 
252 /*
253 ** bmp_DecodeRLE4
254 **
255 ** Given (data) pointing to a line of RLE4-format data and (line) being the starting
256 ** line onscreen, decode the line.
257 */
258 static void
259 bmp_DecodeRLE4(BMP_INFO *info, int line)
260 {
261     int		count;		/* run count */
262     u_char	val;
263     int		x,y;		/* screen position */
264 
265     x = 0;			/* starting position */
266     y = line;
267 
268     /* loop reading data */
269     for (;;) {
270 	/*
271 	 * encoded mode starts with a run length, and then a byte with
272 	 * two colour indexes to alternate between for the run
273 	 */
274 	if (*info->index) {
275 	    for (count = 0; count < *info->index; count++, x++) {
276 		if (count & 1) {		/* odd count, low nybble */
277 		    bmp_SetPix(info, x, y, *(info->index+1) & 0x0f);
278 		} else {			/* even count, high nybble */
279 		    bmp_SetPix(info, x, y, (*(info->index+1) >>4) & 0x0f);
280 		}
281 	    }
282 	    info->index += 2;
283         /*
284 	 * A leading zero is an escape; it may signal the end of the
285 	 * bitmap, a cursor move, or some absolute data.
286 	 */
287 	} else {	/* zero tag may be absolute mode or an escape */
288 	    switch (*(info->index+1)) {
289 	    case 0:				/* end of line */
290 		info->index += 2;
291 		return;
292 	    case 1:				/* end of bitmap */
293 		info->index = NULL;
294 		return;
295 	    case 2:				/* move */
296 		x += *(info->index + 2);	/* new coords */
297 		y += *(info->index + 3);
298 		info->index += 4;
299 		break;
300 	    default:				/* literal bitmap data */
301 		for (count = 0; count < *(info->index + 1); count++, x++) {
302 		    val = *(info->index + 2 + (count / 2));	/* byte with nybbles */
303 		    if (count & 1) {
304 			val &= 0xf;		/* get low nybble */
305 		    } else {
306 			val = (val >> 4);	/* get high nybble */
307 		    }
308 		    bmp_SetPix(info, x, y, val);
309 		}
310 		/* warning, this depends on integer truncation, do not hand-optimise! */
311 		info->index += 2 + ((count + 3) / 4) * 2;
312 		break;
313 	    }
314 	}
315     }
316 }
317 
318 /*
319 ** bmp_DecodeRLE8
320 ** Given (data) pointing to a line of RLE4-format data and (line) being the starting
321 ** line onscreen, decode the line.
322 */
323 static void
324 bmp_DecodeRLE8(BMP_INFO *info, int line)
325 {
326     int		count;		/* run count */
327     int		x,y;		/* screen position */
328 
329     x = 0;			/* starting position */
330     y = line;
331 
332     /* loop reading data */
333     for(;;) {
334 	/*
335 	 * encoded mode starts with a run length, and then a byte with
336 	 * two colour indexes to alternate between for the run
337 	 */
338 	if (*info->index) {
339 	    for (count = 0; count < *info->index; count++, x++)
340 		bmp_SetPix(info, x, y, *(info->index+1));
341 	    info->index += 2;
342         /*
343 	 * A leading zero is an escape; it may signal the end of the
344 	 * bitmap, a cursor move, or some absolute data.
345 	 */
346 	} else {	/* zero tag may be absolute mode or an escape */
347 	    switch(*(info->index+1)) {
348 	    case 0:				/* end of line */
349 		info->index += 2;
350 		return;
351 	    case 1:				/* end of bitmap */
352 		info->index = NULL;
353 		return;
354 	    case 2:				/* move */
355 		x += *(info->index + 2);	/* new coords */
356 		y += *(info->index + 3);
357 		info->index += 4;
358 		break;
359 	    default:				/* literal bitmap data */
360 		for (count = 0; count < *(info->index + 1); count++, x++)
361 		    bmp_SetPix(info, x, y, *(info->index + 2 + count));
362 		/* must be an even count */
363 		info->index += 2 + count + (count & 1);
364 		break;
365 	    }
366 	}
367     }
368 }
369 
370 /*
371 ** bmp_DecodeLine
372 **
373 ** Given (info) pointing to an image being decoded, (line) being the line currently
374 ** being displayed, decode a line of data.
375 */
376 static void
377 bmp_DecodeLine(BMP_INFO *info, int line)
378 {
379     int		x;
380 
381     switch(info->format) {
382     case BI_RGB:
383 	for (x = 0; x < info->width; x++, info->index++)
384 	    bmp_SetPix(info, x, line, *info->index);
385 	info->index += 3 - (--x % 4);
386 	break;
387     case BI_RLE4:
388 	bmp_DecodeRLE4(info, line);
389 	break;
390     case BI_RLE8:
391 	bmp_DecodeRLE8(info, line);
392 	break;
393     }
394 }
395 
396 /*
397 ** bmp_Init
398 **
399 ** Given a pointer (data) to the image of a BMP file, fill in bmp_info with what
400 ** can be learnt from it.  Return nonzero if the file isn't usable.
401 **
402 ** Take screen dimensions (swidth), (sheight) and (sdepth) and make sure we
403 ** can work with these.
404 */
405 static int
406 bmp_Init(const char *data, int swidth, int sheight, int sdepth)
407 {
408     BITMAPF	*bmf = (BITMAPF *)data;
409     int		pind;
410 
411     bmp_info.data = NULL;	/* assume setup failed */
412 #if 0
413     bmp_info.vidmem = vidmem;	/* remember where */
414 #endif
415 
416     /* check file ID */
417     if (bmf->bmfh.bfType != 0x4d42) {
418 	return(1);		/* XXX check word ordering for big-endian ports? */
419     }
420 
421     /* save what we know about the screen */
422     bmp_info.swidth = swidth;
423     bmp_info.sheight = sheight;
424     bmp_info.sdepth = sdepth;
425 
426     /* where's the data? */
427     bmp_info.data = (u_char *)data + bmf->bmfh.bfOffBits;
428 
429     /* image parameters */
430     bmp_info.width = bmf->bmfi.bmiHeader.biWidth;
431     bmp_info.height = bmf->bmfi.bmiHeader.biHeight;
432     bmp_info.format = bmf->bmfi.bmiHeader.biCompression;
433 
434     switch(bmp_info.format) {	/* check compression format */
435     case BI_RGB:
436     case BI_RLE4:
437     case BI_RLE8:
438 	break;
439     default:
440 	return(1);		/* unsupported compression format */
441     }
442 
443     /* palette details */
444     bmp_info.ncols = (bmf->bmfi.bmiHeader.biClrUsed);
445     bzero(bmp_info.palette,sizeof(bmp_info.palette));
446     if (bmp_info.ncols == 0) {	/* uses all of them */
447 	bmp_info.ncols = 1 << bmf->bmfi.bmiHeader.biBitCount;
448     }
449     if ((bmp_info.height > bmp_info.sheight) ||
450 	(bmp_info.width > bmp_info.swidth) ||
451 	(bmp_info.ncols > (1 << sdepth))) {
452 	    return(1);		/* beyond screen capacity */
453     }
454 
455     /* read palette */
456     for (pind = 0; pind < bmp_info.ncols; pind++) {
457 	bmp_info.palette[pind][0] = bmf->bmfi.bmiColors[pind].rgbRed;
458 	bmp_info.palette[pind][1] = bmf->bmfi.bmiColors[pind].rgbGreen;
459 	bmp_info.palette[pind][2] = bmf->bmfi.bmiColors[pind].rgbBlue;
460     }
461     return(0);
462 }
463 
464 /*
465 ** bmp_Draw
466 **
467 ** Render the image.  Return nonzero if that's not possible.
468 **
469 */
470 static int
471 bmp_Draw(video_adapter_t *adp)
472 {
473     int		line;
474 
475     if (bmp_info.data == NULL) {	/* init failed, do nothing */
476 	return(1);
477     }
478 
479     /* clear the screen */
480     bmp_info.vidmem = (u_char *)adp->va_window;
481     /* XXX; the following line is correct only for 8bpp modes */
482     bzero(bmp_info.vidmem, bmp_info.swidth * bmp_info.sheight);
483 
484     /* initialise the info structure for drawing */
485     bmp_info.index = bmp_info.data;
486 
487     /* set the palette for our image */
488     (*vidsw[adp->va_index]->load_palette)(adp, (u_char *)&bmp_info.palette);
489 
490     for (line = 0; (line < bmp_info.height) && bmp_info.index; line++) {
491 	bmp_DecodeLine(&bmp_info, line);
492     }
493     return(0);
494 }
495