Add gbs.conf
[profile/ivi/psplash.git] / psplash-fb.c
1 /*
2  *  pslash - a lightweight framebuffer splashscreen for embedded devices.
3  *
4  *  Copyright (c) 2006 Matthew Allum <mallum@o-hand.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2, or (at your option)
9  *  any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  */
17 #include "psplash.h"
18
19 void
20 psplash_fb_destroy (PSplashFB *fb)
21 {
22   if (fb->fd >= 0)
23     close (fb->fd);
24
25   free(fb);
26 }
27
28 static int
29 attempt_to_change_pixel_format (PSplashFB *fb,
30                                 struct fb_var_screeninfo *fb_var)
31 {
32   /* By default the framebuffer driver may have set an oversized
33    * yres_virtual to support VT scrolling via the panning interface.
34    *
35    * We don't try and maintain this since it's more likely that we
36    * will fail to increase the bpp if the driver's pre allocated
37    * framebuffer isn't large enough.
38    */
39   fb_var->yres_virtual = fb_var->yres;
40
41   /* First try setting an 8,8,8,0 pixel format so we don't have to do
42    * any conversions while drawing. */
43
44   fb_var->bits_per_pixel = 32;
45
46   fb_var->red.offset = 0;
47   fb_var->red.length = 8;
48
49   fb_var->green.offset = 8;
50   fb_var->green.length = 8;
51
52   fb_var->blue.offset = 16;
53   fb_var->blue.length = 8;
54
55   fb_var->transp.offset = 0;
56   fb_var->transp.length = 0;
57
58   if (ioctl (fb->fd, FBIOPUT_VSCREENINFO, fb_var) == 0)
59     {
60       fprintf(stdout, "Switched to a 32 bpp 8,8,8 frame buffer\n");
61       return 1;
62     }
63   else
64     {
65       fprintf(stderr,
66               "Error, failed to switch to a 32 bpp 8,8,8 frame buffer\n");
67     }
68
69   /* Otherwise try a 16bpp 5,6,5 format */
70
71   fb_var->bits_per_pixel = 16;
72
73   fb_var->red.offset = 11;
74   fb_var->red.length = 5;
75
76   fb_var->green.offset = 5;
77   fb_var->green.length = 6;
78
79   fb_var->blue.offset = 0;
80   fb_var->blue.length = 5;
81
82   fb_var->transp.offset = 0;
83   fb_var->transp.length = 0;
84
85   if (ioctl (fb->fd, FBIOPUT_VSCREENINFO, fb_var) == 0)
86     {
87       fprintf(stdout, "Switched to a 16 bpp 5,6,5 frame buffer\n");
88       return 1;
89     }
90   else
91     {
92       fprintf(stderr,
93               "Error, failed to switch to a 16 bpp 5,6,5 frame buffer\n");
94     }
95
96   return 0;
97 }
98
99 PSplashFB*
100 psplash_fb_new (int angle)
101 {
102   struct fb_var_screeninfo fb_var;
103   struct fb_fix_screeninfo fb_fix;
104   int                      off;
105   char                    *fbdev;
106
107   PSplashFB *fb = NULL;
108
109   fbdev = getenv("FBDEV");
110   if (fbdev == NULL)
111     fbdev = "/dev/fb0";
112
113   if ((fb = malloc (sizeof(PSplashFB))) == NULL)
114     {
115       perror ("Error no memory");
116       goto fail;
117     }
118
119   memset (fb, 0, sizeof(PSplashFB));
120
121   fb->fd = -1;
122
123   if ((fb->fd = open (fbdev, O_RDWR)) < 0)
124     {
125       perror ("Error opening /dev/fb0");
126       goto fail;
127     }
128
129   if (ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
130     {
131       perror ("Error getting variable framebuffer info");
132       goto fail;
133     }
134
135   if (fb_var.bits_per_pixel < 16)
136     {
137       fprintf(stderr,
138               "Error, no support currently for %i bpp frame buffers\n"
139               "Trying to change pixel format...\n",
140               fb_var.bits_per_pixel);
141       if (!attempt_to_change_pixel_format (fb, &fb_var))
142         goto fail;
143     }
144
145   if (ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
146     {
147       perror ("Error getting variable framebuffer info (2)");
148       goto fail;
149     }
150
151   /* NB: It looks like the fbdev concept of fixed vs variable screen info is
152    * broken. The line_length is part of the fixed info but it can be changed
153    * if you set a new pixel format. */
154   if (ioctl (fb->fd, FBIOGET_FSCREENINFO, &fb_fix) == -1)
155     {
156       perror ("Error getting fixed framebuffer info");
157       goto fail;
158     }
159
160   fb->real_width  = fb->width  = fb_var.xres;
161   fb->real_height = fb->height = fb_var.yres;
162   fb->bpp    = fb_var.bits_per_pixel;
163   fb->stride = fb_fix.line_length;
164   fb->type   = fb_fix.type;
165   fb->visual = fb_fix.visual;
166
167   fb->red_offset = fb_var.red.offset;
168   fb->red_length = fb_var.red.length;
169   fb->green_offset = fb_var.green.offset;
170   fb->green_length = fb_var.green.length;
171   fb->blue_offset = fb_var.blue.offset;
172   fb->blue_length = fb_var.blue.length;
173
174   if (fb->red_offset == 11 && fb->red_length == 5 &&
175       fb->green_offset == 5 && fb->green_length == 6 &&
176       fb->blue_offset == 0 && fb->blue_length == 5) {
177          fb->rgbmode = RGB565;
178   } else if (fb->red_offset == 0 && fb->red_length == 5 &&
179       fb->green_offset == 5 && fb->green_length == 6 &&
180       fb->blue_offset == 11 && fb->blue_length == 5) {
181          fb->rgbmode = BGR565;
182   } else if (fb->red_offset == 16 && fb->red_length == 8 &&
183       fb->green_offset == 8 && fb->green_length == 8 &&
184       fb->blue_offset == 0 && fb->blue_length == 8) {
185          fb->rgbmode = RGB888;
186   } else if (fb->red_offset == 0 && fb->red_length == 8 &&
187       fb->green_offset == 8 && fb->green_length == 8 &&
188       fb->blue_offset == 8 && fb->blue_length == 8) {
189          fb->rgbmode = BGR888;
190   } else {
191          fb->rgbmode = GENERIC;
192   }
193
194   DBG("width: %i, height: %i, bpp: %i, stride: %i",
195       fb->width, fb->height, fb->bpp, fb->stride);
196
197   fb->base = (char *) mmap ((caddr_t) NULL,
198                             /*fb_fix.smem_len */
199                             fb->stride * fb->height,
200                             PROT_READ|PROT_WRITE,
201                             MAP_SHARED,
202                             fb->fd, 0);
203
204   if (fb->base == (char *)-1)
205     {
206       perror("Error cannot mmap framebuffer ");
207       goto fail;
208     }
209
210   off = (unsigned long) fb_fix.smem_start % (unsigned long) getpagesize();
211
212   fb->data = fb->base + off;
213
214 #if 0
215   /* FIXME: No support for 8pp as yet  */
216   if (visual == FB_VISUAL_PSEUDOCOLOR
217       || visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
218   {
219     static struct fb_cmap cmap;
220
221     cmap.start = 0;
222     cmap.len = 16;
223     cmap.red = saved_red;
224     cmap.green = saved_green;
225     cmap.blue = saved_blue;
226     cmap.transp = NULL;
227
228     ioctl (fb, FBIOGETCMAP, &cmap);
229   }
230
231   if (!status)
232     atexit (bogl_done);
233   status = 2;
234 #endif
235
236   fb->angle = angle;
237
238   switch (fb->angle)
239     {
240     case 270:
241     case 90:
242       fb->width  = fb->real_height;
243       fb->height = fb->real_width;
244       break;
245     case 180:
246     case 0:
247     default:
248       break;
249     }
250
251   return fb;
252
253  fail:
254
255   if (fb)
256     psplash_fb_destroy (fb);
257
258   return NULL;
259 }
260
261 #define OFFSET(fb,x,y) (((y) * (fb)->stride) + ((x) * ((fb)->bpp >> 3)))
262
263 inline void
264 psplash_fb_plot_pixel (PSplashFB    *fb,
265                        int          x,
266                        int          y,
267                        uint8        red,
268                        uint8        green,
269                        uint8        blue)
270 {
271   int off;
272
273   if (x < 0 || x > fb->width-1 || y < 0 || y > fb->height-1)
274     return;
275
276   switch (fb->angle)
277     {
278     case 270:
279       off = OFFSET (fb, fb->height - y - 1, x);
280       break;
281     case 180:
282       off = OFFSET (fb, fb->width - x - 1, fb->height - y - 1);
283       break;
284     case 90:
285       off = OFFSET (fb, y, fb->width - x - 1);
286       break;
287     case 0:
288     default:
289       off = OFFSET (fb, x, y);
290       break;
291     }
292
293   if (fb->rgbmode == RGB565 || fb->rgbmode == RGB888) {
294     switch (fb->bpp)
295       {
296       case 24:
297       case 32:
298         *(fb->data + off)     = blue;
299         *(fb->data + off + 1) = green;
300         *(fb->data + off + 2) = red;
301         break;
302       case 16:
303         *(volatile uint16_t *) (fb->data + off)
304           = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
305         break;
306       default:
307         /* depth not supported yet */
308         break;
309       }
310   } else if (fb->rgbmode == BGR565 || fb->rgbmode == BGR888) {
311     switch (fb->bpp)
312       {
313       case 24:
314       case 32:
315         *(fb->data + off)     = red;
316         *(fb->data + off + 1) = green;
317         *(fb->data + off + 2) = blue;
318         break;
319       case 16:
320         *(volatile uint16_t *) (fb->data + off)
321           = ((blue >> 3) << 11) | ((green >> 2) << 5) | (red >> 3);
322         break;
323       default:
324         /* depth not supported yet */
325         break;
326       }
327   } else {
328     switch (fb->bpp)
329       {
330       case 32:
331         *(volatile uint32_t *) (fb->data + off)
332           = ((red >> (8 - fb->red_length)) << fb->red_offset) 
333               | ((green >> (8 - fb->green_length)) << fb->green_offset)
334               | ((blue >> (8 - fb->blue_length)) << fb->blue_offset);
335         break;
336       case 16:
337         *(volatile uint16_t *) (fb->data + off)
338           = ((red >> (8 - fb->red_length)) << fb->red_offset) 
339               | ((green >> (8 - fb->green_length)) << fb->green_offset)
340               | ((blue >> (8 - fb->blue_length)) << fb->blue_offset);
341         break;
342       default:
343         /* depth not supported yet */
344         break;
345       }
346   }
347 }
348
349 void
350 psplash_fb_draw_rect (PSplashFB    *fb,
351                       int          x,
352                       int          y,
353                       int          width,
354                       int          height,
355                       uint8        red,
356                       uint8        green,
357                       uint8        blue)
358 {
359   int dx, dy;
360
361   for (dy=0; dy < height; dy++)
362     for (dx=0; dx < width; dx++)
363         psplash_fb_plot_pixel (fb, x+dx, y+dy, red, green, blue);
364 }
365
366 void
367 psplash_fb_draw_image (PSplashFB    *fb,
368                        int          x,
369                        int          y,
370                        int          img_width,
371                        int          img_height,
372                        int          img_bytes_per_pixel,
373                        uint8       *rle_data)
374 {
375   uint8       *p = rle_data;
376   int          dx = 0, dy = 0,  total_len;
377   unsigned int len;
378
379   total_len = img_width * img_height * img_bytes_per_pixel;
380
381   /* FIXME: Optimise, check for over runs ... */
382   while ((p - rle_data) < total_len)
383     {
384       len = *(p++);
385
386       if (len & 128)
387         {
388           len -= 128;
389
390           if (len == 0) break;
391
392           do
393             {
394               if (img_bytes_per_pixel < 4 || *(p+3))
395                 psplash_fb_plot_pixel (fb, x+dx, y+dy, *(p), *(p+1), *(p+2));
396               if (++dx >= img_width) { dx=0; dy++; }
397             }
398           while (--len && (p - rle_data) < total_len);
399
400           p += img_bytes_per_pixel;
401         }
402       else
403         {
404           if (len == 0) break;
405
406           do
407             {
408               if (img_bytes_per_pixel < 4 || *(p+3))
409                 psplash_fb_plot_pixel (fb, x+dx, y+dy, *(p), *(p+1), *(p+2));
410               if (++dx >= img_width) { dx=0; dy++; }
411               p += img_bytes_per_pixel;
412             }
413           while (--len && (p - rle_data) < total_len);
414         }
415     }
416 }
417
418 /* Font rendering code based on BOGL by Ben Pfaff */
419
420 static int
421 psplash_font_glyph (const PSplashFont *font, wchar_t wc, u_int32_t **bitmap)
422 {
423   int mask = font->index_mask;
424   int i;
425
426   for (;;)
427     {
428       for (i = font->offset[wc & mask]; font->index[i]; i += 2)
429         {
430           if ((font->index[i] & ~mask) == (wc & ~mask))
431             {
432               if (bitmap != NULL)
433                 *bitmap = &font->content[font->index[i+1]];
434               return font->index[i] & mask;
435             }
436         }
437     }
438   return 0;
439 }
440
441 void
442 psplash_fb_text_size (PSplashFB          *fb,
443                       int                *width,
444                       int                *height,
445                       const PSplashFont  *font,
446                       const char         *text)
447 {
448   char   *c = (char*)text;
449   wchar_t wc;
450   int     k, n, w, h, mw;
451
452   n = strlen (text);
453   mw = h = w = 0;
454
455   mbtowc (0, 0, 0);
456   for (; (k = mbtowc (&wc, c, n)) > 0; c += k, n -= k)
457     {
458       if (*c == '\n')
459         {
460           if (w > mw)
461             mw = 0;
462           h += font->height;
463           continue;
464         }
465
466       w += psplash_font_glyph (font, wc, NULL);
467     }
468
469   *width  = (w > mw) ? w : mw;
470   *height = (h == 0) ? font->height : h;
471 }
472
473 void
474 psplash_fb_draw_text (PSplashFB         *fb,
475                       int                x,
476                       int                y,
477                       uint8              red,
478                       uint8              green,
479                       uint8              blue,
480                       const PSplashFont *font,
481                       const char        *text)
482 {
483   int     h, w, k, n, cx, cy, dx, dy;
484   char   *c = (char*)text;
485   wchar_t wc;
486
487   n = strlen (text);
488   h = font->height;
489   dx = dy = 0;
490
491   mbtowc (0, 0, 0);
492   for (; (k = mbtowc (&wc, c, n)) > 0; c += k, n -= k)
493     {
494       u_int32_t *glyph = NULL;
495
496       if (*c == '\n')
497         {
498           dy += h;
499           dx  = 0;
500           continue;
501         }
502
503       w = psplash_font_glyph (font, wc, &glyph);
504
505       if (glyph == NULL)
506         continue;
507
508       for (cy = 0; cy < h; cy++)
509         {
510           u_int32_t g = *glyph++;
511
512           for (cx = 0; cx < w; cx++)
513             {
514               if (g & 0x80000000)
515                 psplash_fb_plot_pixel (fb, x+dx+cx, y+dy+cy,
516                                        red, green, blue);
517               g <<= 1;
518             }
519         }
520
521       dx += w;
522     }
523 }
524