xref: /dragonfly/sys/dev/video/fb/bmp/splash_bmp.c (revision 956939d5)
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  * $FreeBSD: src/sys/modules/splash/bmp/splash_bmp.c,v 1.10.2.3 2000/10/31 08:00:06 nyan Exp $
28  * $DragonFly: src/sys/dev/video/fb/bmp/splash_bmp.c,v 1.10 2007/09/15 13:18:40 swildner Exp $
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/linker.h>
35 #include <sys/fbio.h>
36 
37 #include <dev/video/fb/fbreg.h>
38 #include <dev/video/fb/splashreg.h>
39 #include <dev/video/fb/vgareg.h>
40 
41 #include <bus/isa/isareg.h>
42 
43 #define FADE_TIMEOUT	15	/* sec */
44 #define FADE_LEVELS	10
45 
46 static int splash_mode = -1;
47 static int splash_on = FALSE;
48 
49 static int bmp_start(video_adapter_t *);
50 static int bmp_end(video_adapter_t *);
51 static int bmp_splash(video_adapter_t *, int);
52 static int bmp_Init(const char *, int, int, int);
53 static int bmp_Draw(video_adapter_t *);
54 
55 static splash_decoder_t bmp_decoder = {
56     "splash_bmp", bmp_start, bmp_end, bmp_splash, SPLASH_IMAGE,
57 };
58 
59 SPLASH_DECODER(splash_bmp, bmp_decoder);
60 
61 static int
62 bmp_start(video_adapter_t *adp)
63 {
64     /* currently only 256-color modes are supported XXX */
65     static int		modes[] = {
66 			M_VESA_CG640x480,
67 			M_VESA_CG800x600,
68 			M_VESA_CG1024x768,
69 			M_CG640x480,
70     			/*
71 			 * As 320x200 doesn't generally look great,
72 			 * it's least preferred here.
73 			 */
74 			M_VGA_CG320,
75 			-1,
76     };
77     video_info_t 	info;
78     int			i;
79 
80     if ((bmp_decoder.data == NULL) || (bmp_decoder.data_size <= 0)) {
81 	kprintf("splash_bmp: No bitmap file found\n");
82 	return ENODEV;
83     }
84     for (i = 0; modes[i] >= 0; ++i) {
85 	if (((*vidsw[adp->va_index]->get_info)(adp, modes[i], &info) == 0)
86 	    && (bmp_Init((u_char *)bmp_decoder.data,
87 			 info.vi_width, info.vi_height, info.vi_depth) == 0))
88 	    break;
89     }
90     splash_mode = modes[i];
91     if (splash_mode < 0)
92 	kprintf("splash_bmp: No appropriate video mode found\n");
93     if (bootverbose)
94 	kprintf("bmp_start(): splash_mode:%d\n", splash_mode);
95     return ((splash_mode < 0) ? ENODEV : 0);
96 }
97 
98 static int
99 bmp_end(video_adapter_t *adp)
100 {
101     /* nothing to do */
102     return 0;
103 }
104 
105 static int
106 bmp_splash(video_adapter_t *adp, int on)
107 {
108     static u_char	pal[256*3];
109     static long		time_stamp;
110     u_char		tpal[256*3];
111     static int		fading = TRUE, brightness = FADE_LEVELS;
112     struct timeval	tv;
113     int			i;
114 
115     if (on) {
116 	if (!splash_on) {
117 	    /* set up the video mode and draw something */
118 	    if ((*vidsw[adp->va_index]->set_mode)(adp, splash_mode))
119 		return 1;
120 	    if (bmp_Draw(adp))
121 		return 1;
122 	    (*vidsw[adp->va_index]->save_palette)(adp, pal);
123 	    time_stamp = 0;
124 	    splash_on = TRUE;
125 	}
126 	/*
127 	 * This is a kludge to fade the image away.  This section of the
128 	 * code takes effect only after the system is completely up.
129 	 * FADE_TIMEOUT should be configurable.
130 	 */
131 	if (!cold) {
132 	    getmicrotime(&tv);
133 	    if (time_stamp == 0)
134 		time_stamp = tv.tv_sec;
135 	    if (tv.tv_sec > time_stamp + FADE_TIMEOUT) {
136 		if (fading)
137 		    if (brightness == 0) {
138 			fading = FALSE;
139 			brightness++;
140 		    }
141 		    else brightness--;
142 		else
143 		    if (brightness == FADE_LEVELS) {
144 			fading = TRUE;
145 			brightness--;
146 		    }
147 		    else brightness++;
148 		for (i = 0; i < sizeof(pal); ++i) {
149 		    tpal[i] = pal[i] * brightness / FADE_LEVELS;
150 		}
151 		(*vidsw[adp->va_index]->load_palette)(adp, tpal);
152 		time_stamp = tv.tv_sec;
153 	    }
154 	}
155 	return 0;
156     } else {
157 	/* the video mode will be restored by the caller */
158 	splash_on = FALSE;
159 	return 0;
160     }
161 }
162 
163 /*
164 ** Code to handle Microsoft DIB (".BMP") format images.
165 **
166 ** Blame me (msmith@freebsd.org) if this is broken, not Soren.
167 */
168 
169 typedef struct tagBITMAPFILEHEADER {    /* bmfh */
170     u_short	bfType;
171     int		bfSize;
172     u_short	bfReserved1;
173     u_short	bfReserved2;
174     int		bfOffBits;
175 } __packed BITMAPFILEHEADER;
176 
177 typedef struct tagBITMAPINFOHEADER {    /* bmih */
178     int		biSize;
179     int		biWidth;
180     int		biHeight;
181     short	biPlanes;
182     short	biBitCount;
183     int		biCompression;
184     int		biSizeImage;
185     int		biXPelsPerMeter;
186     int		biYPelsPerMeter;
187     int		biClrUsed;
188     int		biClrImportant;
189 } __packed BITMAPINFOHEADER;
190 
191 typedef struct tagRGBQUAD {     /* rgbq */
192     u_char	rgbBlue;
193     u_char	rgbGreen;
194     u_char	rgbRed;
195     u_char	rgbReserved;
196 } __packed RGBQUAD;
197 
198 typedef struct tagBITMAPINFO {  /* bmi */
199     BITMAPINFOHEADER	bmiHeader;
200     RGBQUAD		bmiColors[256];
201 } __packed BITMAPINFO;
202 
203 typedef struct tagBITMAPF
204 {
205     BITMAPFILEHEADER	bmfh;
206     BITMAPINFO		bmfi;
207 } __packed BITMAPF;
208 
209 #define BI_RGB		0
210 #define BI_RLE8		1
211 #define BI_RLE4		2
212 
213 /*
214 ** all we actually care about the image
215 */
216 typedef struct
217 {
218     int		width,height;		/* image dimensions */
219     int		swidth,sheight;		/* screen dimensions for the current mode */
220     u_char	depth;			/* image depth (1, 4, 8, 24 bits) */
221     u_char	sdepth;			/* screen depth (1, 4, 8 bpp) */
222     int		ncols;			/* number of colours */
223     u_char	palette[256][3];	/* raw palette data */
224     u_char	format;			/* one of the BI_* constants above */
225     const u_char *data;			/* pointer to the raw data */
226     const u_char *index;			/* running pointer to the data while drawing */
227     u_char	*vidmem;		/* video memory allocated for drawing */
228     video_adapter_t *adp;
229     int		bank;
230 } BMP_INFO;
231 
232 static BMP_INFO bmp_info;
233 
234 /*
235 ** bmp_SetPix
236 **
237 ** Given (info), set the pixel at (x),(y) to (val)
238 **
239 */
240 static void
241 bmp_SetPix(BMP_INFO *info, int x, int y, u_char val)
242 {
243     int		sofs, bofs;
244     int		newbank;
245 
246     /*
247      * range check to avoid explosions
248      */
249     if ((x < 0) || (x >= info->swidth) || (y < 0) || (y >= info->sheight))
250 	return;
251 
252     /*
253      * calculate offset into video memory;
254      * because 0,0 is bottom-left for DIB, we have to convert.
255      */
256     sofs = ((info->height - (y+1) + (info->sheight - info->height) / 2)
257 		* info->adp->va_line_width);
258     x += (info->swidth - info->width) / 2;
259 
260     switch(info->sdepth) {
261     case 4:
262     case 1:
263 	/* EGA/VGA planar modes */
264 	sofs += (x >> 3);
265 	newbank = sofs/info->adp->va_window_size;
266 	if (info->bank != newbank) {
267 	    (*vidsw[info->adp->va_index]->set_win_org)(info->adp, newbank*info->adp->va_window_size);
268 	    info->bank = newbank;
269 	}
270 	sofs %= info->adp->va_window_size;
271 	bofs = x & 0x7;				/* offset within byte */
272 	outw(GDCIDX, (0x8000 >> bofs) | 0x08);	/* bit mask */
273 	outw(GDCIDX, (val << 8) | 0x00);	/* set/reset */
274 	*(info->vidmem + sofs) ^= 0xff;		/* read-modify-write */
275 	break;
276 
277     case 8:
278 	sofs += x;
279 	newbank = sofs/info->adp->va_window_size;
280 	if (info->bank != newbank) {
281 	    (*vidsw[info->adp->va_index]->set_win_org)(info->adp, newbank*info->adp->va_window_size);
282 	    info->bank = newbank;
283 	}
284 	sofs %= info->adp->va_window_size;
285 	*(info->vidmem+sofs) = val;
286 	break;
287     }
288 }
289 
290 /*
291 ** bmp_DecodeRLE4
292 **
293 ** Given (data) pointing to a line of RLE4-format data and (line) being the starting
294 ** line onscreen, decode the line.
295 */
296 static void
297 bmp_DecodeRLE4(BMP_INFO *info, int line)
298 {
299     int		count;		/* run count */
300     u_char	val;
301     int		x,y;		/* screen position */
302 
303     x = 0;			/* starting position */
304     y = line;
305 
306     /* loop reading data */
307     for (;;) {
308 	/*
309 	 * encoded mode starts with a run length, and then a byte with
310 	 * two colour indexes to alternate between for the run
311 	 */
312 	if (*info->index) {
313 	    for (count = 0; count < *info->index; count++, x++) {
314 		if (count & 1) {		/* odd count, low nybble */
315 		    bmp_SetPix(info, x, y, *(info->index+1) & 0x0f);
316 		} else {			/* even count, high nybble */
317 		    bmp_SetPix(info, x, y, (*(info->index+1) >>4) & 0x0f);
318 		}
319 	    }
320 	    info->index += 2;
321         /*
322 	 * A leading zero is an escape; it may signal the end of the
323 	 * bitmap, a cursor move, or some absolute data.
324 	 */
325 	} else {	/* zero tag may be absolute mode or an escape */
326 	    switch (*(info->index+1)) {
327 	    case 0:				/* end of line */
328 		info->index += 2;
329 		return;
330 	    case 1:				/* end of bitmap */
331 		info->index = NULL;
332 		return;
333 	    case 2:				/* move */
334 		x += *(info->index + 2);	/* new coords */
335 		y += *(info->index + 3);
336 		info->index += 4;
337 		break;
338 	    default:				/* literal bitmap data */
339 		for (count = 0; count < *(info->index + 1); count++, x++) {
340 		    val = *(info->index + 2 + (count / 2));	/* byte with nybbles */
341 		    if (count & 1) {
342 			val &= 0xf;		/* get low nybble */
343 		    } else {
344 			val = (val >> 4);	/* get high nybble */
345 		    }
346 		    bmp_SetPix(info, x, y, val);
347 		}
348 		/* warning, this depends on integer truncation, do not hand-optimise! */
349 		info->index += 2 + ((count + 3) / 4) * 2;
350 		break;
351 	    }
352 	}
353     }
354 }
355 
356 /*
357 ** bmp_DecodeRLE8
358 ** Given (data) pointing to a line of RLE8-format data and (line) being the starting
359 ** line onscreen, decode the line.
360 */
361 static void
362 bmp_DecodeRLE8(BMP_INFO *info, int line)
363 {
364     int		count;		/* run count */
365     int		x,y;		/* screen position */
366 
367     x = 0;			/* starting position */
368     y = line;
369 
370     /* loop reading data */
371     for(;;) {
372 	/*
373 	 * encoded mode starts with a run length, and then a byte with
374 	 * two colour indexes to alternate between for the run
375 	 */
376 	if (*info->index) {
377 	    for (count = 0; count < *info->index; count++, x++)
378 		bmp_SetPix(info, x, y, *(info->index+1));
379 	    info->index += 2;
380         /*
381 	 * A leading zero is an escape; it may signal the end of the
382 	 * bitmap, a cursor move, or some absolute data.
383 	 */
384 	} else {	/* zero tag may be absolute mode or an escape */
385 	    switch(*(info->index+1)) {
386 	    case 0:				/* end of line */
387 		info->index += 2;
388 		return;
389 	    case 1:				/* end of bitmap */
390 		info->index = NULL;
391 		return;
392 	    case 2:				/* move */
393 		x += *(info->index + 2);	/* new coords */
394 		y += *(info->index + 3);
395 		info->index += 4;
396 		break;
397 	    default:				/* literal bitmap data */
398 		for (count = 0; count < *(info->index + 1); count++, x++)
399 		    bmp_SetPix(info, x, y, *(info->index + 2 + count));
400 		/* must be an even count */
401 		info->index += 2 + count + (count & 1);
402 		break;
403 	    }
404 	}
405     }
406 }
407 
408 /*
409 ** bmp_DecodeLine
410 **
411 ** Given (info) pointing to an image being decoded, (line) being the line currently
412 ** being displayed, decode a line of data.
413 */
414 static void
415 bmp_DecodeLine(BMP_INFO *info, int line)
416 {
417     int		x;
418     u_char	val, mask;
419     const u_char *p;
420 
421     switch(info->format) {
422     case BI_RGB:
423     	switch(info->depth) {
424 	case 8:
425 	    for (x = 0; x < info->width; x++, info->index++)
426 		bmp_SetPix(info, x, line, *info->index);
427 	    info->index += 3 - (--x % 4);
428 	    break;
429 	case 4:
430 	    p = info->index;
431 	    for (x = 0; x < info->width; x++) {
432 		if (x & 1) {
433 		    val = *p & 0xf;	/* get low nybble */
434 		    p++;
435 		} else {
436 		    val = *p >> 4;	/* get high nybble */
437 		}
438 		bmp_SetPix(info, x, line, val);
439 	    }
440 	    /* warning, this depends on integer truncation, do not hand-optimise! */
441 	    info->index += ((x + 7) / 8) * 4;
442 	    break;
443 	case 1:
444 	    p = info->index;
445 	    mask = 0x80;
446 	    for (x = 0; x < info->width; x++) {
447 		val = (*p & mask) ? 1 : 0;
448 		mask >>= 1;
449 		if (mask == 0) {
450 		    mask = 0x80;
451 		    p++;
452 		}
453 		bmp_SetPix(info, x, line, val);
454 	    }
455 	    /* warning, this depends on integer truncation, do not hand-optimise! */
456 	    info->index += ((x + 31) / 32) * 4;
457 	    break;
458 	}
459 	break;
460     case BI_RLE4:
461 	bmp_DecodeRLE4(info, line);
462 	break;
463     case BI_RLE8:
464 	bmp_DecodeRLE8(info, line);
465 	break;
466     }
467 }
468 
469 /*
470 ** bmp_Init
471 **
472 ** Given a pointer (data) to the image of a BMP file, fill in bmp_info with what
473 ** can be learnt from it.  Return nonzero if the file isn't usable.
474 **
475 ** Take screen dimensions (swidth), (sheight) and (sdepth) and make sure we
476 ** can work with these.
477 */
478 static int
479 bmp_Init(const char *data, int swidth, int sheight, int sdepth)
480 {
481     const BITMAPF *bmf = (const BITMAPF *)data;
482     int		pind;
483 
484     bmp_info.data = NULL;	/* assume setup failed */
485 
486     /* check file ID */
487     if (bmf->bmfh.bfType != 0x4d42) {
488 	kprintf("splash_bmp: not a BMP file\n");
489 	return(1);		/* XXX check word ordering for big-endian ports? */
490     }
491 
492     /* do we understand this bitmap format? */
493     if (bmf->bmfi.bmiHeader.biSize > sizeof(bmf->bmfi.bmiHeader)) {
494 	kprintf("splash_bmp: unsupported BMP format (size=%d)\n",
495 		bmf->bmfi.bmiHeader.biSize);
496 	return(1);
497     }
498 
499     /* save what we know about the screen */
500     bmp_info.swidth = swidth;
501     bmp_info.sheight = sheight;
502     bmp_info.sdepth = sdepth;
503 
504     /* where's the data? */
505     bmp_info.data = (const u_char *)data + bmf->bmfh.bfOffBits;
506 
507     /* image parameters */
508     bmp_info.width = bmf->bmfi.bmiHeader.biWidth;
509     bmp_info.height = bmf->bmfi.bmiHeader.biHeight;
510     bmp_info.depth = bmf->bmfi.bmiHeader.biBitCount;
511     bmp_info.format = bmf->bmfi.bmiHeader.biCompression;
512 
513     switch(bmp_info.format) {	/* check compression format */
514     case BI_RGB:
515     case BI_RLE4:
516     case BI_RLE8:
517 	break;
518     default:
519 	kprintf("splash_bmp: unsupported compression format\n");
520 	return(1);		/* unsupported compression format */
521     }
522 
523     /* palette details */
524     bmp_info.ncols = (bmf->bmfi.bmiHeader.biClrUsed);
525     bzero(bmp_info.palette,sizeof(bmp_info.palette));
526     if (bmp_info.ncols == 0) {	/* uses all of them */
527 	bmp_info.ncols = 1 << bmf->bmfi.bmiHeader.biBitCount;
528     }
529     if ((bmp_info.height > bmp_info.sheight) ||
530 	(bmp_info.width > bmp_info.swidth) ||
531 	(bmp_info.ncols > (1 << sdepth))) {
532 	if (bootverbose)
533 	    kprintf("splash_bmp: beyond screen capacity (%dx%d, %d colors)\n",
534 		   bmp_info.width, bmp_info.height, bmp_info.ncols);
535 	return(1);
536     }
537 
538     /* read palette */
539     for (pind = 0; pind < bmp_info.ncols; pind++) {
540 	bmp_info.palette[pind][0] = bmf->bmfi.bmiColors[pind].rgbRed;
541 	bmp_info.palette[pind][1] = bmf->bmfi.bmiColors[pind].rgbGreen;
542 	bmp_info.palette[pind][2] = bmf->bmfi.bmiColors[pind].rgbBlue;
543     }
544     return(0);
545 }
546 
547 /*
548 ** bmp_Draw
549 **
550 ** Render the image.  Return nonzero if that's not possible.
551 **
552 */
553 static int
554 bmp_Draw(video_adapter_t *adp)
555 {
556     int		line;
557 #if 0
558     int		i;
559 #endif
560 
561     if (bmp_info.data == NULL) {	/* init failed, do nothing */
562 	return(1);
563     }
564 
565     /* clear the screen */
566     bmp_info.vidmem = (u_char *)adp->va_window;
567     bmp_info.adp = adp;
568     (*vidsw[adp->va_index]->clear)(adp);
569     (*vidsw[adp->va_index]->set_win_org)(adp, 0);
570     bmp_info.bank = 0;
571 
572     /* initialise the info structure for drawing */
573     bmp_info.index = bmp_info.data;
574 
575     /* set the palette for our image */
576     (*vidsw[adp->va_index]->load_palette)(adp, (u_char *)&bmp_info.palette);
577 
578 #if 0
579     /* XXX: this is ugly, but necessary for EGA/VGA 1bpp/4bpp modes */
580     if ((adp->va_type == KD_EGA) || (adp->va_type == KD_VGA)) {
581 	inb(adp->va_crtc_addr + 6);		/* reset flip-flop */
582 	outb(ATC, 0x14);
583 	outb(ATC, 0);
584 	for (i = 0; i < 16; ++i) {
585 	    outb(ATC, i);
586 	    outb(ATC, i);
587 	}
588 	inb(adp->va_crtc_addr + 6);		/* reset flip-flop */
589 	outb(ATC, 0x20);			/* enable palette */
590 
591 	outw(GDCIDX, 0x0f01);			/* set/reset enable */
592 
593 	if (bmp_info.sdepth == 1)
594 	    outw(TSIDX, 0x0102);		/* unmask plane #0 */
595     }
596 #endif
597 
598     for (line = 0; (line < bmp_info.height) && bmp_info.index; line++) {
599 	bmp_DecodeLine(&bmp_info, line);
600     }
601     return(0);
602 }
603