2008-02-21 Rob Bradford <rob@openedhand.com>
[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 PSplashFB*
29 psplash_fb_new (int angle)
30 {
31   struct fb_var_screeninfo fb_var;
32   struct fb_fix_screeninfo fb_fix;
33   int                      off;
34   char                    *fbdev;
35
36   PSplashFB *fb = NULL;
37
38   fbdev = getenv("FBDEV");
39   if (fbdev == NULL)
40     fbdev = "/dev/fb0";
41
42   if ((fb = malloc (sizeof(PSplashFB))) == NULL)
43     {
44       perror ("Error no memory");
45       goto fail;
46     }
47   
48   memset (fb, 0, sizeof(PSplashFB));
49
50   fb->fd = -1;  
51
52   if ((fb->fd = open (fbdev, O_RDWR)) < 0)
53     {
54       perror ("Error opening /dev/fb0");
55       goto fail;
56     }
57
58   if (ioctl (fb->fd, FBIOGET_FSCREENINFO, &fb_fix) == -1
59       || ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
60     {
61       perror ("Error getting framebuffer info");
62       goto fail;
63     }
64   
65   if (fb_var.bits_per_pixel < 16)
66     {
67       fprintf(stderr,
68               "Error, no support currently for %i bpp frame buffers\n",
69               fb_var.bits_per_pixel);
70     }
71
72   fb->real_width  = fb->width  = fb_var.xres;
73   fb->real_height = fb->height = fb_var.yres;
74   fb->bpp    = fb_var.bits_per_pixel;
75   fb->stride = fb_fix.line_length;
76   fb->type   = fb_fix.type;
77   fb->visual = fb_fix.visual;
78
79   DBG("width: %i, height: %i, bpp: %i, stride: %i", 
80       fb->width, fb->height, fb->bpp, fb->stride);
81
82   fb->base = (char *) mmap ((caddr_t) NULL,
83                             /*fb_fix.smem_len */
84                             fb->stride * fb->height,
85                             PROT_READ|PROT_WRITE,
86                             MAP_SHARED,
87                             fb->fd, 0);
88     
89   if (fb->base == (char *)-1) 
90     {
91       perror("Error cannot mmap framebuffer ");
92       goto fail;
93     }
94
95   off = (unsigned long) fb_fix.smem_start % (unsigned long) getpagesize();
96
97   fb->data = fb->base + off;
98
99 #if 0
100   /* FIXME: No support for 8pp as yet  */
101   if (visual == FB_VISUAL_PSEUDOCOLOR
102       || visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
103   {
104     static struct fb_cmap cmap;
105
106     cmap.start = 0;
107     cmap.len = 16;
108     cmap.red = saved_red;
109     cmap.green = saved_green;
110     cmap.blue = saved_blue;
111     cmap.transp = NULL;
112
113     ioctl (fb, FBIOGETCMAP, &cmap);
114   }
115   
116   if (!status)
117     atexit (bogl_done);
118   status = 2;
119 #endif
120
121   fb->angle = angle;
122
123   switch (fb->angle)
124     {
125     case 270:
126     case 90:
127       fb->width  = fb->real_height;
128       fb->height = fb->real_width;
129       break;
130     case 180:
131     case 0:
132     default:
133       break;
134     }
135
136   return fb;
137
138  fail:
139
140   if (fb)
141     psplash_fb_destroy (fb);
142
143   return NULL;
144 }
145
146 #define OFFSET(fb,x,y) (((y) * (fb)->stride) + ((x) * ((fb)->bpp >> 3)))
147
148 inline void
149 psplash_fb_plot_pixel (PSplashFB    *fb, 
150                        int          x, 
151                        int          y, 
152                        uint8        red,
153                        uint8        green,
154                        uint8        blue)
155 {
156   int off;
157
158   if (x < 0 || x > fb->width-1 || y < 0 || y > fb->height-1)
159     return;
160
161   switch (fb->angle)
162     {
163     case 270:
164       off = OFFSET (fb, fb->height - y - 1, x);
165       break;
166     case 180:
167       off = OFFSET (fb, fb->width - x - 1, fb->height - y - 1);
168       break;
169     case 90:
170       off = OFFSET (fb, y, fb->width - x - 1);
171       break;
172     case 0:
173     default:
174       off = OFFSET (fb, x, y);
175       break;
176     }
177
178   /* FIXME: handle no RGB orderings */
179   switch (fb->bpp)
180     {
181     case 24:
182     case 32:
183       *(fb->data + off)     = red;
184       *(fb->data + off + 1) = green;
185       *(fb->data + off + 2) = blue;
186       break;
187     case 16:
188       *(volatile uint16 *) (fb->data + off) 
189         = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
190       break;
191     default:
192       /* depth not supported yet */
193       break;
194     }
195
196 }
197
198 void
199 psplash_fb_draw_rect (PSplashFB    *fb, 
200                       int          x, 
201                       int          y, 
202                       int          width, 
203                       int          height,
204                       uint8        red,
205                       uint8        green,
206                       uint8        blue)
207 {
208   int dx, dy;
209
210   for (dy=0; dy < height; dy++)   
211     for (dx=0; dx < width; dx++)
212         psplash_fb_plot_pixel (fb, x+dx, y+dy, red, green, blue); 
213 }
214
215 void
216 psplash_fb_draw_image (PSplashFB    *fb, 
217                        int          x, 
218                        int          y, 
219                        int          img_width, 
220                        int          img_height,
221                        int          img_bytes_per_pixel,
222                        uint8       *rle_data)
223 {
224   uint8       *p = rle_data;
225   int          dx = 0, dy = 0,  total_len;
226   unsigned int len;
227
228   total_len = img_width * img_height * img_bytes_per_pixel;
229
230   /* FIXME: Optimise, check for over runs ... */
231   while ((p - rle_data) < total_len)
232     {
233       len = *(p++);
234
235       if (len & 128)
236         {
237           len -= 128;
238
239           if (len == 0) break;
240
241           do
242             {
243               psplash_fb_plot_pixel (fb, x+dx, y+dy, *p, *(p+1), *(p+2)); 
244               if (++dx >= img_width) { dx=0; dy++; }
245             }
246           while (--len && (p - rle_data) < total_len);
247
248           p += img_bytes_per_pixel;
249         }
250       else
251         {
252           if (len == 0) break;
253
254           do
255             {
256               psplash_fb_plot_pixel (fb, x+dx, y+dy, *p, *(p+1), *(p+2)); 
257               if (++dx >= img_width) { dx=0; dy++; }
258               p += img_bytes_per_pixel;
259             }
260           while (--len && (p - rle_data) < total_len);
261         }
262     }
263 }
264
265 /* Font rendering code based on BOGL by Ben Pfaff */
266
267 static int
268 psplash_font_glyph (const PSplashFont *font, wchar_t wc, u_int32_t **bitmap)
269 {
270   int mask = font->index_mask;
271   int i;
272
273   for (;;)
274     {
275       for (i = font->offset[wc & mask]; font->index[i]; i += 2)
276         {
277           if ((font->index[i] & ~mask) == (wc & ~mask))
278             {
279               if (bitmap != NULL)
280                 *bitmap = &font->content[font->index[i+1]];
281               return font->index[i] & mask;
282             }
283         }
284     }
285   return 0;
286 }
287
288 void
289 psplash_fb_text_size (PSplashFB          *fb,
290                       int                *width, 
291                       int                *height,
292                       const PSplashFont  *font,
293                       const char         *text)
294 {
295   char   *c = (char*)text;
296   wchar_t wc;
297   int     k, n, w, h, mw;
298
299   n = strlen (text);
300   mw = h = w = 0;
301
302   mbtowc (0, 0, 0);
303   for (; (k = mbtowc (&wc, c, n)) > 0; c += k, n -= k)
304     {
305       if (*c == '\n')
306         {
307           if (w > mw)
308             mw = 0;
309           h += font->height;
310           continue;
311         }
312
313       w += psplash_font_glyph (font, wc, NULL);
314     }
315
316   *width  = (w > mw) ? w : mw;
317   *height = (h == 0) ? font->height : h;
318 }
319
320 void
321 psplash_fb_draw_text (PSplashFB         *fb, 
322                       int                x, 
323                       int                y, 
324                       uint8              red,
325                       uint8              green,
326                       uint8              blue,
327                       const PSplashFont *font,
328                       const char        *text)
329 {
330   int     h, w, k, n, cx, cy, dx, dy;
331   char   *c = (char*)text;
332   wchar_t wc;
333   
334   n = strlen (text);
335   h = font->height;
336   dx = dy = 0;
337    
338   mbtowc (0, 0, 0);
339   for (; (k = mbtowc (&wc, c, n)) > 0; c += k, n -= k)
340     {
341       u_int32_t *glyph = NULL;
342       
343       if (*c == '\n')
344         {
345           dy += h;
346           dx  = 0;
347           continue;
348         }
349       
350       w = psplash_font_glyph (font, wc, &glyph);
351       
352       if (glyph == NULL)
353         continue;
354
355       for (cy = 0; cy < h; cy++)
356         {
357           u_int32_t g = *glyph++;
358           
359           for (cx = 0; cx < w; cx++)
360             {
361               if (g & 0x80000000)
362                 psplash_fb_plot_pixel (fb, x+dx+cx, y+dy+cy, 
363                                        red, green, blue); 
364               g <<= 1;
365             }
366         }
367
368       dx += w;
369     }
370 }
371