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