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