freebsd-nq/sys/dev/fb/splash_bmp.c

644 lines
17 KiB
C
Raw Normal View History

/*-
* Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
* Copyright (c) 1999 Kazutaka YOKOTA <yokota@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
1999-08-28 01:08:13 +00:00
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
2004-05-30 20:27:19 +00:00
#include <sys/module.h>
#include <sys/linker.h>
The second phase of syscons reorganization. - Split syscons source code into manageable chunks and reorganize some of complicated functions. - Many static variables are moved to the softc structure. - Added a new key function, PREV. When this key is pressed, the vty immediately before the current vty will become foreground. Analogue to PREV, which is usually assigned to the PrntScrn key. PR: kern/10113 Submitted by: Christian Weisgerber <naddy@mips.rhein-neckar.de> - Modified the kernel console input function sccngetc() so that it handles function keys properly. - Reorganized the screen update routine. - VT switching code is reorganized. It now should be slightly more robust than before. - Added the DEVICE_RESUME function so that syscons no longer hooks the APM resume event directly. - New kernel configuration options: SC_NO_CUTPASTE, SC_NO_FONT_LOADING, SC_NO_HISTORY and SC_NO_SYSMOUSE. Various parts of syscons can be omitted so that the kernel size is reduced. SC_PIXEL_MODE Made the VESA 800x600 mode an option, rather than a standard part of syscons. SC_DISABLE_DDBKEY Disables the `debug' key combination. SC_ALT_MOUSE_IMAGE Inverse the character cell at the mouse cursor position in the text console, rather than drawing an arrow on the screen. Submitted by: Nick Hibma (n_hibma@FreeBSD.ORG) SC_DFLT_FONT makeoptions "SC_DFLT_FONT=_font_name_" Include the named font as the default font of syscons. 16-line, 14-line and 8-line font data will be compiled in. This option replaces the existing STD8X16FONT option, which loads 16-line font data only. - The VGA driver is split into /sys/dev/fb/vga.c and /sys/isa/vga_isa.c. - The video driver provides a set of ioctl commands to manipulate the frame buffer. - New kernel configuration option: VGA_WIDTH90 Enables 90 column modes: 90x25, 90x30, 90x43, 90x50, 90x60. These modes are mot always supported by the video card. PR: i386/7510 Submitted by: kbyanc@freedomnet.com and alexv@sui.gda.itesm.mx. - The header file machine/console.h is reorganized; its contents is now split into sys/fbio.h, sys/kbio.h (a new file) and sys/consio.h (another new file). machine/console.h is still maintained for compatibility reasons. - Kernel console selection/installation routines are fixed and slightly rebumped so that it should now be possible to switch between the interanl kernel console (sc or vt) and a remote kernel console (sio) again, as it was in 2.x, 3.0 and 3.1. - Screen savers and splash screen decoders Because of the header file reorganization described above, screen savers and splash screen decoders are slightly modified. After this update, /sys/modules/syscons/saver.h is no longer necessary and is removed.
1999-06-22 14:14:06 +00:00
#include <sys/fbio.h>
#include <dev/fb/fbreg.h>
#include <dev/fb/splashreg.h>
#ifndef PC98
#include <dev/fb/vgareg.h>
#include <isa/isareg.h>
#endif
#define FADE_TIMEOUT 15 /* sec */
#define FADE_LEVELS 10
static int splash_mode = -1;
static int splash_on = FALSE;
static int bmp_start(video_adapter_t *adp);
static int bmp_end(video_adapter_t *adp);
static int bmp_splash(video_adapter_t *adp, int on);
2002-11-11 10:17:01 +00:00
static int bmp_Init(char *data, int swidth, int sheight, int sdepth);
static int bmp_Draw(video_adapter_t *adp);
static splash_decoder_t bmp_decoder = {
"splash_bmp", bmp_start, bmp_end, bmp_splash, SPLASH_IMAGE,
};
SPLASH_DECODER(splash_bmp, bmp_decoder);
static int
bmp_start(video_adapter_t *adp)
{
/* currently only 256-color modes are supported XXX */
static int modes[] = {
#ifdef PC98
/*
* As 640x400 doesn't generally look great,
* it's least preferred here.
*/
M_PC98_PEGC640x400,
M_PC98_PEGC640x480,
M_PC98_EGC640x400,
#else
M_VESA_CG640x480,
M_VESA_CG800x600,
M_VESA_CG1024x768,
M_CG640x480,
/*
* As 320x200 doesn't generally look great,
* it's least preferred here.
*/
M_VGA_CG320,
#endif
-1,
};
video_info_t info;
int i;
if ((bmp_decoder.data == NULL) || (bmp_decoder.data_size <= 0)) {
printf("splash_bmp: No bitmap file found\n");
return ENODEV;
}
for (i = 0; modes[i] >= 0; ++i) {
if (((*vidsw[adp->va_index]->get_info)(adp, modes[i], &info) == 0)
&& (bmp_Init((u_char *)bmp_decoder.data,
info.vi_width, info.vi_height, info.vi_depth) == 0))
break;
}
splash_mode = modes[i];
if (splash_mode < 0)
printf("splash_bmp: No appropriate video mode found\n");
if (bootverbose)
printf("bmp_start(): splash_mode:%d\n", splash_mode);
return ((splash_mode < 0) ? ENODEV : 0);
}
static int
bmp_end(video_adapter_t *adp)
{
/* nothing to do */
return 0;
}
static int
bmp_splash(video_adapter_t *adp, int on)
{
static u_char pal[256*3];
static long time_stamp;
u_char tpal[256*3];
static int fading = TRUE, brightness = FADE_LEVELS;
struct timeval tv;
int i;
if (on) {
if (!splash_on) {
/* set up the video mode and draw something */
if ((*vidsw[adp->va_index]->set_mode)(adp, splash_mode))
return 1;
if (bmp_Draw(adp))
return 1;
(*vidsw[adp->va_index]->save_palette)(adp, pal);
time_stamp = 0;
splash_on = TRUE;
}
/*
* This is a kludge to fade the image away. This section of the
* code takes effect only after the system is completely up.
* FADE_TIMEOUT should be configurable.
*/
if (!cold) {
getmicrotime(&tv);
if (time_stamp == 0)
time_stamp = tv.tv_sec;
if (tv.tv_sec > time_stamp + FADE_TIMEOUT) {
if (fading)
if (brightness == 0) {
fading = FALSE;
brightness++;
}
else brightness--;
else
if (brightness == FADE_LEVELS) {
fading = TRUE;
brightness--;
}
else brightness++;
for (i = 0; i < sizeof(pal); ++i) {
tpal[i] = pal[i] * brightness / FADE_LEVELS;
}
(*vidsw[adp->va_index]->load_palette)(adp, tpal);
time_stamp = tv.tv_sec;
}
}
return 0;
} else {
/* the video mode will be restored by the caller */
splash_on = FALSE;
return 0;
}
}
/*
** Code to handle Microsoft DIB (".BMP") format images.
**
** Blame me (msmith@freebsd.org) if this is broken, not Soren.
*/
typedef struct tagBITMAPFILEHEADER { /* bmfh */
u_short bfType;
int bfSize;
u_short bfReserved1;
u_short bfReserved2;
int bfOffBits;
} __packed BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER { /* bmih */
int biSize;
int biWidth;
int biHeight;
short biPlanes;
short biBitCount;
int biCompression;
int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
int biClrUsed;
int biClrImportant;
} __packed BITMAPINFOHEADER;
typedef struct tagRGBQUAD { /* rgbq */
u_char rgbBlue;
u_char rgbGreen;
u_char rgbRed;
u_char rgbReserved;
} __packed RGBQUAD;
typedef struct tagBITMAPINFO { /* bmi */
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
} __packed BITMAPINFO;
typedef struct tagBITMAPF
{
BITMAPFILEHEADER bmfh;
BITMAPINFO bmfi;
} __packed BITMAPF;
#define BI_RGB 0
#define BI_RLE8 1
#define BI_RLE4 2
/*
** all we actually care about the image
*/
typedef struct
{
int width,height; /* image dimensions */
int swidth,sheight; /* screen dimensions for the current mode */
u_char depth; /* image depth (1, 4, 8, 24 bits) */
u_char sdepth; /* screen depth (1, 4, 8 bpp) */
int ncols; /* number of colours */
u_char palette[256][3]; /* raw palette data */
u_char format; /* one of the BI_* constants above */
u_char *data; /* pointer to the raw data */
u_char *index; /* running pointer to the data while drawing */
u_char *vidmem; /* video memory allocated for drawing */
video_adapter_t *adp;
int bank;
#ifdef PC98
u_char prev_val;
#endif
} BMP_INFO;
static BMP_INFO bmp_info;
/*
** bmp_SetPix
**
** Given (info), set the pixel at (x),(y) to (val)
**
*/
static void
bmp_SetPix(BMP_INFO *info, int x, int y, u_char val)
{
int sofs, bofs;
int newbank;
/*
* range check to avoid explosions
*/
if ((x < 0) || (x >= info->swidth) || (y < 0) || (y >= info->sheight))
return;
/*
* calculate offset into video memory;
* because 0,0 is bottom-left for DIB, we have to convert.
*/
sofs = ((info->height - (y+1) + (info->sheight - info->height) / 2)
* info->adp->va_line_width);
x += (info->swidth - info->width) / 2;
switch(info->sdepth) {
#ifdef PC98
case 4:
sofs += (x >> 3);
bofs = x & 0x7; /* offset within byte */
outb(0x7c, 0x80 | 0x40); /* GRCG on & RMW mode */
if (val != info->prev_val) {
outb(0x7e, (val & 1) ? 0xff : 0); /* tile B */
outb(0x7e, (val & 2) ? 0xff : 0); /* tile R */
outb(0x7e, (val & 4) ? 0xff : 0); /* tile G */
outb(0x7e, (val & 8) ? 0xff : 0); /* tile I */
info->prev_val = val;
}
*(info->vidmem+sofs) = (0x80 >> bofs); /* write new bit */
outb(0x7c, 0); /* GRCG off */
break;
#else
case 4:
case 1:
/* EGA/VGA planar modes */
sofs += (x >> 3);
newbank = sofs/info->adp->va_window_size;
if (info->bank != newbank) {
(*vidsw[info->adp->va_index]->set_win_org)(info->adp, newbank*info->adp->va_window_size);
info->bank = newbank;
}
sofs %= info->adp->va_window_size;
bofs = x & 0x7; /* offset within byte */
outw(GDCIDX, (0x8000 >> bofs) | 0x08); /* bit mask */
outw(GDCIDX, (val << 8) | 0x00); /* set/reset */
*(info->vidmem + sofs) ^= 0xff; /* read-modify-write */
break;
#endif
case 8:
sofs += x;
newbank = sofs/info->adp->va_window_size;
if (info->bank != newbank) {
(*vidsw[info->adp->va_index]->set_win_org)(info->adp, newbank*info->adp->va_window_size);
info->bank = newbank;
}
sofs %= info->adp->va_window_size;
*(info->vidmem+sofs) = val;
break;
}
}
/*
** bmp_DecodeRLE4
**
** Given (data) pointing to a line of RLE4-format data and (line) being the starting
** line onscreen, decode the line.
*/
static void
bmp_DecodeRLE4(BMP_INFO *info, int line)
{
int count; /* run count */
u_char val;
int x,y; /* screen position */
x = 0; /* starting position */
y = line;
/* loop reading data */
for (;;) {
/*
* encoded mode starts with a run length, and then a byte with
* two colour indexes to alternate between for the run
*/
if (*info->index) {
for (count = 0; count < *info->index; count++, x++) {
if (count & 1) { /* odd count, low nybble */
bmp_SetPix(info, x, y, *(info->index+1) & 0x0f);
} else { /* even count, high nybble */
bmp_SetPix(info, x, y, (*(info->index+1) >>4) & 0x0f);
}
}
info->index += 2;
/*
* A leading zero is an escape; it may signal the end of the
* bitmap, a cursor move, or some absolute data.
*/
} else { /* zero tag may be absolute mode or an escape */
switch (*(info->index+1)) {
case 0: /* end of line */
info->index += 2;
return;
case 1: /* end of bitmap */
info->index = NULL;
return;
case 2: /* move */
x += *(info->index + 2); /* new coords */
y += *(info->index + 3);
info->index += 4;
break;
default: /* literal bitmap data */
for (count = 0; count < *(info->index + 1); count++, x++) {
val = *(info->index + 2 + (count / 2)); /* byte with nybbles */
if (count & 1) {
val &= 0xf; /* get low nybble */
} else {
val = (val >> 4); /* get high nybble */
}
bmp_SetPix(info, x, y, val);
}
/* warning, this depends on integer truncation, do not hand-optimise! */
info->index += 2 + ((count + 3) / 4) * 2;
break;
}
}
}
}
/*
** bmp_DecodeRLE8
** Given (data) pointing to a line of RLE8-format data and (line) being the starting
** line onscreen, decode the line.
*/
static void
bmp_DecodeRLE8(BMP_INFO *info, int line)
{
int count; /* run count */
int x,y; /* screen position */
x = 0; /* starting position */
y = line;
/* loop reading data */
for(;;) {
/*
* encoded mode starts with a run length, and then a byte with
* two colour indexes to alternate between for the run
*/
if (*info->index) {
for (count = 0; count < *info->index; count++, x++)
bmp_SetPix(info, x, y, *(info->index+1));
info->index += 2;
/*
* A leading zero is an escape; it may signal the end of the
* bitmap, a cursor move, or some absolute data.
*/
} else { /* zero tag may be absolute mode or an escape */
switch(*(info->index+1)) {
case 0: /* end of line */
info->index += 2;
return;
case 1: /* end of bitmap */
info->index = NULL;
return;
case 2: /* move */
x += *(info->index + 2); /* new coords */
y += *(info->index + 3);
info->index += 4;
break;
default: /* literal bitmap data */
for (count = 0; count < *(info->index + 1); count++, x++)
bmp_SetPix(info, x, y, *(info->index + 2 + count));
/* must be an even count */
info->index += 2 + count + (count & 1);
break;
}
}
}
}
/*
** bmp_DecodeLine
**
** Given (info) pointing to an image being decoded, (line) being the line currently
** being displayed, decode a line of data.
*/
static void
bmp_DecodeLine(BMP_INFO *info, int line)
{
int x;
u_char val, mask, *p;
switch(info->format) {
case BI_RGB:
switch(info->depth) {
case 8:
for (x = 0; x < info->width; x++, info->index++)
bmp_SetPix(info, x, line, *info->index);
info->index += 3 - (--x % 4);
break;
case 4:
p = info->index;
for (x = 0; x < info->width; x++) {
if (x & 1) {
val = *p & 0xf; /* get low nybble */
p++;
} else {
val = *p >> 4; /* get high nybble */
}
bmp_SetPix(info, x, line, val);
}
/* warning, this depends on integer truncation, do not hand-optimise! */
info->index += ((x + 7) / 8) * 4;
break;
case 1:
p = info->index;
mask = 0x80;
for (x = 0; x < info->width; x++) {
val = (*p & mask) ? 1 : 0;
mask >>= 1;
if (mask == 0) {
mask = 0x80;
p++;
}
bmp_SetPix(info, x, line, val);
}
/* warning, this depends on integer truncation, do not hand-optimise! */
info->index += ((x + 31) / 32) * 4;
break;
}
break;
case BI_RLE4:
bmp_DecodeRLE4(info, line);
break;
case BI_RLE8:
bmp_DecodeRLE8(info, line);
break;
}
}
/*
** bmp_Init
**
** Given a pointer (data) to the image of a BMP file, fill in bmp_info with what
** can be learnt from it. Return nonzero if the file isn't usable.
**
** Take screen dimensions (swidth), (sheight) and (sdepth) and make sure we
** can work with these.
*/
static int
2002-11-11 10:17:01 +00:00
bmp_Init(char *data, int swidth, int sheight, int sdepth)
{
BITMAPF *bmf = (BITMAPF *)data;
int pind;
bmp_info.data = NULL; /* assume setup failed */
/* check file ID */
if (bmf->bmfh.bfType != 0x4d42) {
printf("splash_bmp: not a BMP file\n");
return(1); /* XXX check word ordering for big-endian ports? */
}
/* do we understand this bitmap format? */
if (bmf->bmfi.bmiHeader.biSize > sizeof(bmf->bmfi.bmiHeader)) {
printf("splash_bmp: unsupported BMP format (size=%d)\n",
bmf->bmfi.bmiHeader.biSize);
return(1);
}
/* save what we know about the screen */
bmp_info.swidth = swidth;
bmp_info.sheight = sheight;
bmp_info.sdepth = sdepth;
/* where's the data? */
bmp_info.data = (u_char *)data + bmf->bmfh.bfOffBits;
/* image parameters */
bmp_info.width = bmf->bmfi.bmiHeader.biWidth;
bmp_info.height = bmf->bmfi.bmiHeader.biHeight;
bmp_info.depth = bmf->bmfi.bmiHeader.biBitCount;
bmp_info.format = bmf->bmfi.bmiHeader.biCompression;
switch(bmp_info.format) { /* check compression format */
case BI_RGB:
case BI_RLE4:
case BI_RLE8:
break;
default:
printf("splash_bmp: unsupported compression format\n");
return(1); /* unsupported compression format */
}
/* palette details */
bmp_info.ncols = (bmf->bmfi.bmiHeader.biClrUsed);
bzero(bmp_info.palette,sizeof(bmp_info.palette));
if (bmp_info.ncols == 0) { /* uses all of them */
bmp_info.ncols = 1 << bmf->bmfi.bmiHeader.biBitCount;
}
if ((bmp_info.height > bmp_info.sheight) ||
(bmp_info.width > bmp_info.swidth) ||
(bmp_info.ncols > (1 << sdepth))) {
if (bootverbose)
printf("splash_bmp: beyond screen capacity (%dx%d, %d colors)\n",
bmp_info.width, bmp_info.height, bmp_info.ncols);
return(1);
}
/* read palette */
for (pind = 0; pind < bmp_info.ncols; pind++) {
bmp_info.palette[pind][0] = bmf->bmfi.bmiColors[pind].rgbRed;
bmp_info.palette[pind][1] = bmf->bmfi.bmiColors[pind].rgbGreen;
bmp_info.palette[pind][2] = bmf->bmfi.bmiColors[pind].rgbBlue;
}
return(0);
}
/*
** bmp_Draw
**
** Render the image. Return nonzero if that's not possible.
**
*/
static int
bmp_Draw(video_adapter_t *adp)
{
int line;
#if 0
#ifndef PC98
int i;
#endif
#endif
if (bmp_info.data == NULL) { /* init failed, do nothing */
return(1);
}
/* clear the screen */
bmp_info.vidmem = (u_char *)adp->va_window;
bmp_info.adp = adp;
(*vidsw[adp->va_index]->clear)(adp);
(*vidsw[adp->va_index]->set_win_org)(adp, 0);
bmp_info.bank = 0;
/* initialise the info structure for drawing */
bmp_info.index = bmp_info.data;
#ifdef PC98
bmp_info.prev_val = 255;
#endif
/* set the palette for our image */
(*vidsw[adp->va_index]->load_palette)(adp, (u_char *)&bmp_info.palette);
#if 0
#ifndef PC98
/* XXX: this is ugly, but necessary for EGA/VGA 1bpp/4bpp modes */
if ((adp->va_type == KD_EGA) || (adp->va_type == KD_VGA)) {
inb(adp->va_crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x14);
outb(ATC, 0);
for (i = 0; i < 16; ++i) {
outb(ATC, i);
outb(ATC, i);
}
inb(adp->va_crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x20); /* enable palette */
outw(GDCIDX, 0x0f01); /* set/reset enable */
if (bmp_info.sdepth == 1)
outw(TSIDX, 0x0102); /* unmask plane #0 */
}
#endif
#endif
for (line = 0; (line < bmp_info.height) && bmp_info.index; line++) {
bmp_DecodeLine(&bmp_info, line);
}
return(0);
}