-/*
- * pslash - a lightweight framebuffer splashscreen for embedded devices.
+/*
+ * pslash - a lightweight framebuffer splashscreen for embedded devices.
*
* Copyright (c) 2006 Matthew Allum <mallum@o-hand.com>
*
free(fb);
}
+static int
+attempt_to_change_pixel_format (PSplashFB *fb,
+ struct fb_var_screeninfo *fb_var)
+{
+ /* By default the framebuffer driver may have set an oversized
+ * yres_virtual to support VT scrolling via the panning interface.
+ *
+ * We don't try and maintain this since it's more likely that we
+ * will fail to increase the bpp if the driver's pre allocated
+ * framebuffer isn't large enough.
+ */
+ fb_var->yres_virtual = fb_var->yres;
+
+ /* First try setting an 8,8,8,0 pixel format so we don't have to do
+ * any conversions while drawing. */
+
+ fb_var->bits_per_pixel = 32;
+
+ fb_var->red.offset = 0;
+ fb_var->red.length = 8;
+
+ fb_var->green.offset = 8;
+ fb_var->green.length = 8;
+
+ fb_var->blue.offset = 16;
+ fb_var->blue.length = 8;
+
+ fb_var->transp.offset = 0;
+ fb_var->transp.length = 0;
+
+ if (ioctl (fb->fd, FBIOPUT_VSCREENINFO, fb_var) == 0)
+ {
+ fprintf(stdout, "Switched to a 32 bpp 8,8,8 frame buffer\n");
+ return 1;
+ }
+ else
+ {
+ fprintf(stderr,
+ "Error, failed to switch to a 32 bpp 8,8,8 frame buffer\n");
+ }
+
+ /* Otherwise try a 16bpp 5,6,5 format */
+
+ fb_var->bits_per_pixel = 16;
+
+ fb_var->red.offset = 11;
+ fb_var->red.length = 5;
+
+ fb_var->green.offset = 5;
+ fb_var->green.length = 6;
+
+ fb_var->blue.offset = 0;
+ fb_var->blue.length = 5;
+
+ fb_var->transp.offset = 0;
+ fb_var->transp.length = 0;
+
+ if (ioctl (fb->fd, FBIOPUT_VSCREENINFO, fb_var) == 0)
+ {
+ fprintf(stdout, "Switched to a 16 bpp 5,6,5 frame buffer\n");
+ return 1;
+ }
+ else
+ {
+ fprintf(stderr,
+ "Error, failed to switch to a 16 bpp 5,6,5 frame buffer\n");
+ }
+
+ return 0;
+}
+
PSplashFB*
-psplash_fb_new (void)
+psplash_fb_new (int angle)
{
struct fb_var_screeninfo fb_var;
struct fb_fix_screeninfo fb_fix;
perror ("Error no memory");
goto fail;
}
-
+
memset (fb, 0, sizeof(PSplashFB));
- fb->fd = -1;
+ fb->fd = -1;
if ((fb->fd = open (fbdev, O_RDWR)) < 0)
{
goto fail;
}
- if (ioctl (fb->fd, FBIOGET_FSCREENINFO, &fb_fix) == -1
- || ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
+ if (ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
{
- perror ("Error getting framebuffer info");
+ perror ("Error getting variable framebuffer info");
goto fail;
}
-
+
if (fb_var.bits_per_pixel < 16)
{
fprintf(stderr,
- "Error, no support currently for %i bpp frame buffers\n",
- fb_var.bits_per_pixel);
+ "Error, no support currently for %i bpp frame buffers\n"
+ "Trying to change pixel format...\n",
+ fb_var.bits_per_pixel);
+ if (!attempt_to_change_pixel_format (fb, &fb_var))
+ goto fail;
+ }
+
+ if (ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
+ {
+ perror ("Error getting variable framebuffer info (2)");
+ goto fail;
+ }
+
+ /* NB: It looks like the fbdev concept of fixed vs variable screen info is
+ * broken. The line_length is part of the fixed info but it can be changed
+ * if you set a new pixel format. */
+ if (ioctl (fb->fd, FBIOGET_FSCREENINFO, &fb_fix) == -1)
+ {
+ perror ("Error getting fixed framebuffer info");
+ goto fail;
}
fb->real_width = fb->width = fb_var.xres;
fb->type = fb_fix.type;
fb->visual = fb_fix.visual;
- DBG("width: %i, height: %i, bpp: %i, stride: %i",
+ fb->red_offset = fb_var.red.offset;
+ fb->red_length = fb_var.red.length;
+ fb->green_offset = fb_var.green.offset;
+ fb->green_length = fb_var.green.length;
+ fb->blue_offset = fb_var.blue.offset;
+ fb->blue_length = fb_var.blue.length;
+
+ if (fb->red_offset == 11 && fb->red_length == 5 &&
+ fb->green_offset == 5 && fb->green_length == 6 &&
+ fb->blue_offset == 0 && fb->blue_length == 5) {
+ fb->rgbmode = RGB565;
+ } else if (fb->red_offset == 0 && fb->red_length == 5 &&
+ fb->green_offset == 5 && fb->green_length == 6 &&
+ fb->blue_offset == 11 && fb->blue_length == 5) {
+ fb->rgbmode = BGR565;
+ } else if (fb->red_offset == 16 && fb->red_length == 8 &&
+ fb->green_offset == 8 && fb->green_length == 8 &&
+ fb->blue_offset == 0 && fb->blue_length == 8) {
+ fb->rgbmode = RGB888;
+ } else if (fb->red_offset == 0 && fb->red_length == 8 &&
+ fb->green_offset == 8 && fb->green_length == 8 &&
+ fb->blue_offset == 8 && fb->blue_length == 8) {
+ fb->rgbmode = BGR888;
+ } else {
+ fb->rgbmode = GENERIC;
+ }
+
+ DBG("width: %i, height: %i, bpp: %i, stride: %i",
fb->width, fb->height, fb->bpp, fb->stride);
fb->base = (char *) mmap ((caddr_t) NULL,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fb->fd, 0);
-
- if (fb->base == (char *)-1)
+
+ if (fb->base == (char *)-1)
{
perror("Error cannot mmap framebuffer ");
goto fail;
ioctl (fb, FBIOGETCMAP, &cmap);
}
-
+
if (!status)
atexit (bogl_done);
status = 2;
#endif
+ fb->angle = angle;
+
switch (fb->angle)
{
case 270:
return NULL;
}
+#define OFFSET(fb,x,y) (((y) * (fb)->stride) + ((x) * ((fb)->bpp >> 3)))
+
inline void
-psplash_fb_plot_pixel (PSplashFB *fb,
- int x,
- int y,
+psplash_fb_plot_pixel (PSplashFB *fb,
+ int x,
+ int y,
uint8 red,
uint8 green,
uint8 blue)
{
int off;
+ if (x < 0 || x > fb->width-1 || y < 0 || y > fb->height-1)
+ return;
+
switch (fb->angle)
{
case 270:
- off = ((fb->width - x) * fb->stride) + (y * (fb->bpp >> 3));
+ off = OFFSET (fb, fb->height - y - 1, x);
break;
case 180:
- off = ((fb->height - y) * fb->stride) + ((fb->width - x) * (fb->bpp >> 3));
+ off = OFFSET (fb, fb->width - x - 1, fb->height - y - 1);
break;
case 90:
- off = (x * fb->stride) + (y * (fb->bpp >> 3));
+ off = OFFSET (fb, y, fb->width - x - 1);
break;
case 0:
default:
- if (x < 0 || x > fb->width-1 || y < 0 || y > fb->height-1)
- return;
- off = (y * fb->stride) + (x * (fb->bpp >> 3));
- break;
- }
-
- /* FIXME: handle no RGB orderings */
- switch (fb->bpp)
- {
- case 24:
- case 32:
- *(fb->data + off) = red;
- *(fb->data + off + 1) = green;
- *(fb->data + off + 2) = blue;
- break;
- case 16:
- *(volatile uint16 *) (fb->data + off)
- = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
- break;
- default:
- /* depth not supported yet */
+ off = OFFSET (fb, x, y);
break;
}
+ if (fb->rgbmode == RGB565 || fb->rgbmode == RGB888) {
+ switch (fb->bpp)
+ {
+ case 24:
+ case 32:
+ *(fb->data + off) = blue;
+ *(fb->data + off + 1) = green;
+ *(fb->data + off + 2) = red;
+ break;
+ case 16:
+ *(volatile uint16_t *) (fb->data + off)
+ = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
+ break;
+ default:
+ /* depth not supported yet */
+ break;
+ }
+ } else if (fb->rgbmode == BGR565 || fb->rgbmode == BGR888) {
+ switch (fb->bpp)
+ {
+ case 24:
+ case 32:
+ *(fb->data + off) = red;
+ *(fb->data + off + 1) = green;
+ *(fb->data + off + 2) = blue;
+ break;
+ case 16:
+ *(volatile uint16_t *) (fb->data + off)
+ = ((blue >> 3) << 11) | ((green >> 2) << 5) | (red >> 3);
+ break;
+ default:
+ /* depth not supported yet */
+ break;
+ }
+ } else {
+ switch (fb->bpp)
+ {
+ case 32:
+ *(volatile uint32_t *) (fb->data + off)
+ = ((red >> (8 - fb->red_length)) << fb->red_offset)
+ | ((green >> (8 - fb->green_length)) << fb->green_offset)
+ | ((blue >> (8 - fb->blue_length)) << fb->blue_offset);
+ break;
+ case 16:
+ *(volatile uint16_t *) (fb->data + off)
+ = ((red >> (8 - fb->red_length)) << fb->red_offset)
+ | ((green >> (8 - fb->green_length)) << fb->green_offset)
+ | ((blue >> (8 - fb->blue_length)) << fb->blue_offset);
+ break;
+ default:
+ /* depth not supported yet */
+ break;
+ }
+ }
}
void
-psplash_fb_draw_rect (PSplashFB *fb,
- int x,
- int y,
- int width,
+psplash_fb_draw_rect (PSplashFB *fb,
+ int x,
+ int y,
+ int width,
int height,
uint8 red,
uint8 green,
{
int dx, dy;
- for (dy=0; dy < height; dy++)
+ for (dy=0; dy < height; dy++)
for (dx=0; dx < width; dx++)
- psplash_fb_plot_pixel (fb, x+dx, y+dy, red, green, blue);
+ psplash_fb_plot_pixel (fb, x+dx, y+dy, red, green, blue);
}
void
-psplash_fb_draw_image (PSplashFB *fb,
- int x,
- int y,
- int img_width,
+psplash_fb_draw_image (PSplashFB *fb,
+ int x,
+ int y,
+ int img_width,
int img_height,
int img_bytes_per_pixel,
uint8 *rle_data)
do
{
- psplash_fb_plot_pixel (fb, x+dx, y+dy, *p, *(p+1), *(p+2));
+ if (img_bytes_per_pixel < 4 || *(p+3))
+ psplash_fb_plot_pixel (fb, x+dx, y+dy, *(p), *(p+1), *(p+2));
if (++dx >= img_width) { dx=0; dy++; }
}
while (--len && (p - rle_data) < total_len);
do
{
- psplash_fb_plot_pixel (fb, x+dx, y+dy, *p, *(p+1), *(p+2));
+ if (img_bytes_per_pixel < 4 || *(p+3))
+ psplash_fb_plot_pixel (fb, x+dx, y+dy, *(p), *(p+1), *(p+2));
if (++dx >= img_width) { dx=0; dy++; }
p += img_bytes_per_pixel;
}
void
psplash_fb_text_size (PSplashFB *fb,
- int *width,
+ int *width,
int *height,
const PSplashFont *font,
const char *text)
}
void
-psplash_fb_draw_text (PSplashFB *fb,
- int x,
- int y,
+psplash_fb_draw_text (PSplashFB *fb,
+ int x,
+ int y,
uint8 red,
uint8 green,
uint8 blue,
int h, w, k, n, cx, cy, dx, dy;
char *c = (char*)text;
wchar_t wc;
-
+
n = strlen (text);
h = font->height;
dx = dy = 0;
-
+
mbtowc (0, 0, 0);
for (; (k = mbtowc (&wc, c, n)) > 0; c += k, n -= k)
{
u_int32_t *glyph = NULL;
-
+
if (*c == '\n')
{
dy += h;
dx = 0;
continue;
}
-
+
w = psplash_font_glyph (font, wc, &glyph);
-
+
if (glyph == NULL)
continue;
for (cy = 0; cy < h; cy++)
{
u_int32_t g = *glyph++;
-
+
for (cx = 0; cx < w; cx++)
{
if (g & 0x80000000)
- psplash_fb_plot_pixel (fb, x+dx+cx, y+dy+cy,
- red, green, blue);
+ psplash_fb_plot_pixel (fb, x+dx+cx, y+dy+cy,
+ red, green, blue);
g <<= 1;
}
}