splash_bmp.c revision 42529
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.1 1999/01/11 03:34:56 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
42static int splash_mode = -1;
43static int splash_on = FALSE;
44
45static int bmp_start(video_adapter_t *adp);
46static int bmp_end(video_adapter_t *adp);
47static int bmp_splash(video_adapter_t *adp, int on);
48static int bmp_Init(const char *data, int swidth, int sheight, int sdepth);
49static int bmp_Draw(video_adapter_t *adp);
50
51static splash_decoder_t bmp_decoder = {
52    "splash_bmp", bmp_start, bmp_end, bmp_splash, SPLASH_IMAGE,
53};
54
55SPLASH_DECODER(splash_bmp, bmp_decoder);
56
57static int
58bmp_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
81static int
82bmp_end(video_adapter_t *adp)
83{
84    /* nothing to do */
85    return 0;
86}
87
88static int
89bmp_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
137typedef 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
145typedef 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
159typedef 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
166typedef struct tagBITMAPINFO {  /* bmi */
167    BITMAPINFOHEADER	bmiHeader	__attribute__ ((packed));
168    RGBQUAD		bmiColors[256]	__attribute__ ((packed));
169} BITMAPINFO;
170
171typedef 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*/
184typedef 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
197static BMP_INFO bmp_info;
198
199/*
200** bmp_SetPix
201**
202** Given (info), set the pixel at (x),(y) to (val)
203**
204*/
205static void
206bmp_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*/
258static void
259bmp_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*/
323static void
324bmp_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*/
376static void
377bmp_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*/
405static int
406bmp_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 = 2 << 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*/
470static int
471bmp_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