Intial commit
[profile/ivi/w3m.git] / w3mimg / fb / fb.c
1 /* $Id: fb.c,v 1.16 2003/07/13 16:19:10 ukai Exp $ */
2 /**************************************************************************
3                 fb.c 0.3 Copyright (C) 2002, hito
4  **************************************************************************/
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <limits.h>
12 #include <errno.h>
13 #include <sys/ioctl.h>
14 #include <sys/mman.h>
15 #include <linux/fb.h>
16
17 #include "fb.h"
18
19 #define FB_ENV          "FRAMEBUFFER"
20 #define FB_DEFDEV       "/dev/fb0"
21
22 #define MONO_OFFSET_8BIT  0x40
23 #define COLORS_MONO_8BIT  0x40
24 #define MONO_MASK_8BIT    0xFC
25 #define MONO_SHIFT_8BIT   2
26
27 #define COLOR_OFFSET_8BIT 0x80
28 #define COLORS_8BIT       0x80
29 #define RED_MASK_8BIT     0xC0
30 #define GREEN_MASK_8BIT   0xE0
31 #define BLUE_MASK_8BIT    0xC0
32 #define RED_SHIFT_8BIT    1
33 #define GREEN_SHIFT_8BIT  3
34 #define BLUE_SHIFT_8BIT   6
35
36 #define FALSE 0
37 #define TRUE  1
38
39 #define IMAGE_SIZE_MAX 10000
40
41 static struct fb_cmap *fb_cmap_create(struct fb_fix_screeninfo *,
42                                       struct fb_var_screeninfo *);
43 static void fb_cmap_destroy(struct fb_cmap *cmap);
44 static int fb_fscrn_get(int fbfp, struct fb_fix_screeninfo *scinfo);
45 static void *fb_mmap(int fbfp, struct fb_fix_screeninfo *scinfo);
46 static int fb_munmap(void *buf, struct fb_fix_screeninfo *scinfo);
47 static int fb_vscrn_get(int fbfp, struct fb_var_screeninfo *scinfo);
48 static int fb_cmap_set(int fbfp, struct fb_cmap *cmap);
49 static int fb_cmap_get(int fbfp, struct fb_cmap *cmap);
50 static int fb_cmap_init(void);
51 static int fb_get_cmap_index(int r, int g, int b);
52 static unsigned long fb_get_packed_color(int r, int g, int b);
53
54 static struct fb_fix_screeninfo fscinfo;
55 static struct fb_var_screeninfo vscinfo;
56 static struct fb_cmap *cmap = NULL, *cmap_org = NULL;
57 static int is_open = FALSE;
58 static int fbfp = -1;
59 static size_t pixel_size = 0;
60 static unsigned char *buf = NULL;
61
62 int
63 fb_open(void)
64 {
65     char *fbdev = { FB_DEFDEV };
66
67     if (is_open == TRUE)
68         return 1;
69
70     if (getenv(FB_ENV)) {
71         fbdev = getenv(FB_ENV);
72     }
73
74     if ((fbfp = open(fbdev, O_RDWR)) == -1) {
75         fprintf(stderr, "open %s error\n", fbdev);
76         goto ERR_END;
77     }
78
79     if (fb_fscrn_get(fbfp, &fscinfo)) {
80         goto ERR_END;
81     }
82
83     if (fb_vscrn_get(fbfp, &vscinfo)) {
84         goto ERR_END;
85     }
86
87     if ((cmap = fb_cmap_create(&fscinfo, &vscinfo)) == (struct fb_cmap *)-1) {
88         goto ERR_END;
89     }
90
91     if (!(buf = fb_mmap(fbfp, &fscinfo))) {
92         fprintf(stderr, "Can't allocate memory.\n");
93         goto ERR_END;
94     }
95
96     if (fscinfo.type != FB_TYPE_PACKED_PIXELS) {
97         fprintf(stderr, "This type of framebuffer is not supported.\n");
98         goto ERR_END;
99     }
100
101     if (fscinfo.visual == FB_VISUAL_PSEUDOCOLOR && vscinfo.bits_per_pixel == 8) {
102         if (fb_cmap_get(fbfp, cmap)) {
103             fprintf(stderr, "Can't get color map.\n");
104             fb_cmap_destroy(cmap);
105             cmap = NULL;
106             goto ERR_END;
107         }
108
109         if (fb_cmap_init())
110             goto ERR_END;
111
112         pixel_size = 1;
113     }
114     else if ((fscinfo.visual == FB_VISUAL_TRUECOLOR ||
115               fscinfo.visual == FB_VISUAL_DIRECTCOLOR) &&
116              (vscinfo.bits_per_pixel == 15 ||
117               vscinfo.bits_per_pixel == 16 ||
118               vscinfo.bits_per_pixel == 24 || vscinfo.bits_per_pixel == 32)) {
119         pixel_size = (vscinfo.bits_per_pixel + 7) / CHAR_BIT;
120     }
121     else {
122         fprintf(stderr, "This type of framebuffer is not supported.\n");
123         goto ERR_END;
124     }
125
126     is_open = TRUE;
127     return 0;
128
129   ERR_END:
130     fb_close();
131     return 1;
132 }
133
134 void
135 fb_close(void)
136 {
137     if (is_open != TRUE)
138         return;
139
140     if (cmap != NULL) {
141         fb_cmap_destroy(cmap);
142         cmap = NULL;
143     }
144     if (cmap_org != NULL) {
145         fb_cmap_set(fbfp, cmap_org);
146         fb_cmap_destroy(cmap_org);
147         cmap = NULL;
148     }
149     if (buf != NULL) {
150         fb_munmap(buf, &fscinfo);
151         buf = NULL;
152     }
153
154     if (fbfp >= 0) {
155         close(fbfp);
156     }
157
158     is_open = FALSE;
159 }
160
161 /***********   fb_image_*  ***********/
162
163 FB_IMAGE *
164 fb_image_new(int width, int height)
165 {
166     FB_IMAGE *image;
167
168     if (is_open != TRUE)
169         return NULL;
170
171     if (width > IMAGE_SIZE_MAX || height > IMAGE_SIZE_MAX || width < 1
172         || height < 1)
173         return NULL;
174
175     image = malloc(sizeof(*image));
176     if (image == NULL)
177         return NULL;
178
179     image->data = calloc(sizeof(*(image->data)), width * height * pixel_size);
180     if (image->data == NULL) {
181         free(image);
182         return NULL;
183     }
184
185     image->num = 1;
186     image->id = 0;
187     image->delay = 0;
188
189     image->width = width;
190     image->height = height;
191     image->rowstride = width * pixel_size;
192     image->len = width * height * pixel_size;
193
194     return image;
195 }
196
197 void
198 fb_image_free(FB_IMAGE * image)
199 {
200     if (image == NULL)
201         return;
202
203     if (image->data != NULL)
204         free(image->data);
205
206     free(image);
207 }
208
209 void
210 fb_image_pset(FB_IMAGE * image, int x, int y, int r, int g, int b)
211 {
212     unsigned long work;
213
214     if (image == NULL || is_open != TRUE || x >= image->width
215         || y >= image->height)
216         return;
217
218     work = fb_get_packed_color(r, g, b);
219     memcpy(image->data + image->rowstride * y + pixel_size * x, &work,
220            pixel_size);
221 }
222
223 void
224 fb_image_fill(FB_IMAGE * image, int r, int g, int b)
225 {
226     unsigned long work;
227     int offset;
228
229     if (image == NULL || is_open != TRUE)
230         return;
231
232     work = fb_get_packed_color(r, g, b);
233
234     for (offset = 0; offset < image->len; offset += pixel_size) {
235         memcpy(image->data + offset, &work, pixel_size);
236     }
237 }
238
239 int
240 fb_image_draw(FB_IMAGE * image, int x, int y, int sx, int sy, int width,
241               int height)
242 {
243     int i, offset_fb, offset_img;
244
245     if (image == NULL || is_open != TRUE ||
246         sx > image->width || sy > image->height ||
247         x > fb_width() || y > fb_height())
248         return 1;
249
250     if (sx + width > image->width)
251         width = image->width - sx;
252
253     if (sy + height > image->height)
254         height = image->height - sy;
255
256     if (x + width > fb_width())
257         width = fb_width() - x;
258
259     if (y + height > fb_height())
260         height = fb_height() - y;
261
262     offset_fb = fscinfo.line_length * y + pixel_size * x;
263     offset_img = image->rowstride * sy + pixel_size * sx;
264     for (i = 0; i < height; i++) {
265         memcpy(buf + offset_fb, image->data + offset_img, pixel_size * width);
266         offset_fb += fscinfo.line_length;
267         offset_img += image->rowstride;
268     }
269
270     return 0;
271 }
272
273 void
274 fb_image_copy(FB_IMAGE * dest, FB_IMAGE * src)
275 {
276     if (dest == NULL || src == NULL)
277         return;
278
279     if (dest->len != src->len)
280         return;
281
282     memcpy(dest->data, src->data, src->len);
283 }
284
285 /***********   fb_frame_*  ***********/
286
287 FB_IMAGE **
288 fb_frame_new(int w, int h, int n)
289 {
290     FB_IMAGE **frame;
291     int i, error = 0;
292
293     if (w > IMAGE_SIZE_MAX || h > IMAGE_SIZE_MAX || w < 1 || h < 1 || n < 1)
294         return NULL;
295
296     frame = malloc(sizeof(*frame) * n);
297     if (frame == NULL)
298         return NULL;
299
300     for (i = 0; i < n; i++) {
301         frame[i] = fb_image_new(w, h);
302         frame[i]->num = n;
303         frame[i]->id = i;
304         frame[i]->delay = 1000;
305         if (frame[i] == NULL)
306             error = 1;
307     }
308
309     if (error) {
310         fb_frame_free(frame);
311         return NULL;
312     }
313
314     return frame;
315 }
316
317
318 void
319 fb_frame_free(FB_IMAGE ** frame)
320 {
321     int i, n;
322
323     if (frame == NULL)
324         return;
325
326     n = frame[0]->num;
327     for (i = 0; i < n; i++) {
328         fb_image_free(frame[i]);
329     }
330     free(frame);
331 }
332
333 int
334 fb_width(void)
335 {
336     if (is_open != TRUE)
337         return 0;
338
339     return vscinfo.xres;
340 }
341
342 int
343 fb_height(void)
344 {
345     if (is_open != TRUE)
346         return 0;
347
348     return vscinfo.yres;
349 }
350
351 int
352 fb_clear(int x, int y, int w, int h, int r, int g, int b)
353 {
354     int i, offset_fb;
355     static int rr = -1, gg = -1, bb = -1;
356     static char *tmp = NULL;
357
358     if (is_open != TRUE || x > fb_width() || y > fb_height())
359         return 1;
360
361     if (x < 0)
362         x = 0;
363     if (y < 0)
364         y = 0;
365
366     if (x + w > fb_width())
367         w = fb_width() - x;
368     if (y + h > fb_height())
369         h = fb_height() - y;
370
371     if (tmp == NULL) {
372         tmp = malloc(fscinfo.line_length);
373         if (tmp == NULL)
374             return 1;
375     }
376     if (rr != r || gg != g || bb != b) {
377         unsigned long work;
378         int ww = fb_width();
379
380         work = fb_get_packed_color(r, g, b);
381         for (i = 0; i < ww; i++)
382             memcpy(tmp + pixel_size * i, &work, pixel_size);
383         rr = r;
384         gg = g;
385         bb = b;
386     }
387     offset_fb = fscinfo.line_length * y + pixel_size * x;
388     for (i = 0; i < h; i++) {
389         memcpy(buf + offset_fb, tmp, pixel_size * w);
390         offset_fb += fscinfo.line_length;
391     }
392     return 0;
393 }
394
395 /********* static functions **************/
396 static unsigned long
397 fb_get_packed_color(int r, int g, int b)
398 {
399     if (pixel_size == 1) {
400         return fb_get_cmap_index(r, g, b);
401     }
402     else {
403         return
404             ((r >> (CHAR_BIT - vscinfo.red.length)) << vscinfo.red.offset) +
405             ((g >> (CHAR_BIT - vscinfo.green.length)) << vscinfo.green.
406              offset) +
407             ((b >> (CHAR_BIT - vscinfo.blue.length)) << vscinfo.blue.offset);
408     }
409 }
410
411 static int
412 fb_get_cmap_index(int r, int g, int b)
413 {
414     int work;
415     if ((r & GREEN_MASK_8BIT) == (g & GREEN_MASK_8BIT)
416         && (g & GREEN_MASK_8BIT) == (b & GREEN_MASK_8BIT)) {
417         work = (r >> MONO_SHIFT_8BIT) + MONO_OFFSET_8BIT;
418     }
419     else {
420         work = ((r & RED_MASK_8BIT) >> RED_SHIFT_8BIT)
421             + ((g & GREEN_MASK_8BIT) >> GREEN_SHIFT_8BIT)
422             + ((b & BLUE_MASK_8BIT) >> BLUE_SHIFT_8BIT)
423             + COLOR_OFFSET_8BIT;
424     }
425     return work;
426 }
427
428 static int
429 fb_cmap_init(void)
430 {
431     int lp;
432
433     if (cmap == NULL)
434         return 1;
435
436     if (cmap->len < COLOR_OFFSET_8BIT + COLORS_8BIT) {
437         fprintf(stderr, "Can't allocate enough color.\n");
438         return 1;
439     }
440
441     if (cmap_org == NULL) {
442         if ((cmap_org =
443              fb_cmap_create(&fscinfo, &vscinfo)) == (struct fb_cmap *)-1) {
444             return 1;
445         }
446
447         if (fb_cmap_get(fbfp, cmap_org)) {
448             fprintf(stderr, "Can't get color map.\n");
449             fb_cmap_destroy(cmap_org);
450             cmap_org = NULL;
451             return 1;
452         }
453     }
454
455     cmap->start = MONO_OFFSET_8BIT;
456     cmap->len = COLORS_8BIT + COLORS_MONO_8BIT;
457
458     for (lp = 0; lp < COLORS_MONO_8BIT; lp++) {
459         int c;
460         c = (lp << (MONO_SHIFT_8BIT + 8)) +
461             (lp ? (0xFFFF - (MONO_MASK_8BIT << 8)) : 0);
462         if (cmap->red)
463             *(cmap->red + lp) = c;
464         if (cmap->green)
465             *(cmap->green + lp) = c;
466         if (cmap->blue)
467             *(cmap->blue + lp) = c;
468     }
469
470     for (lp = 0; lp < COLORS_8BIT; lp++) {
471         int r, g, b;
472         r = lp & (RED_MASK_8BIT >> RED_SHIFT_8BIT);
473         g = lp & (GREEN_MASK_8BIT >> GREEN_SHIFT_8BIT);
474         b = lp & (BLUE_MASK_8BIT >> BLUE_SHIFT_8BIT);
475         if (cmap->red)
476             *(cmap->red + lp + COLORS_MONO_8BIT)
477                 = (r << (RED_SHIFT_8BIT + 8)) +
478                 (r ? (0xFFFF - (RED_MASK_8BIT << 8)) : 0);
479         if (cmap->green)
480             *(cmap->green + lp + COLORS_MONO_8BIT)
481                 = (g << (GREEN_SHIFT_8BIT + 8)) +
482                 (g ? (0xFFFF - (GREEN_MASK_8BIT << 8)) : 0);
483         if (cmap->blue)
484             *(cmap->blue + lp + COLORS_MONO_8BIT)
485                 = (b << (BLUE_SHIFT_8BIT + 8)) +
486                 (b ? (0xFFFF - (BLUE_MASK_8BIT << 8)) : 0);
487     }
488
489     if (fb_cmap_set(fbfp, cmap)) {
490         fb_cmap_destroy(cmap);
491         cmap = NULL;
492         fprintf(stderr, "Can't set color map.\n");
493         return 1;
494     }
495     return 0;
496 }
497
498 /*
499  * (struct fb_cmap) Device independent colormap information.
500  * 
501  * fb_cmap_create()     create colormap information
502  * fb_cmap_destroy()    destroy colormap information
503  * fb_cmap_get()        get information
504  * fb_cmap_set()        set information
505  */
506
507 #define LUT_MAX         (256)
508
509 static struct fb_cmap *
510 fb_cmap_create(struct fb_fix_screeninfo *fscinfo,
511                struct fb_var_screeninfo *vscinfo)
512 {
513     struct fb_cmap *cmap;
514     int cmaplen = LUT_MAX;
515
516     /* check the existence of colormap */
517     if (fscinfo->visual == FB_VISUAL_MONO01 ||
518         fscinfo->visual == FB_VISUAL_MONO10 ||
519         fscinfo->visual == FB_VISUAL_TRUECOLOR)
520         return NULL;
521
522     cmap = (struct fb_cmap *)malloc(sizeof(struct fb_cmap));
523     if (!cmap) {
524         perror("cmap malloc error\n");
525         return (struct fb_cmap *)-1;
526     }
527     memset(cmap, 0, sizeof(struct fb_cmap));
528
529     /* Allocates memory for a colormap */
530     if (vscinfo->red.length) {
531         cmap->red = (__u16 *) malloc(sizeof(__u16) * cmaplen);
532         if (!cmap->red) {
533             perror("red lut malloc error\n");
534             return (struct fb_cmap *)-1;
535         }
536     }
537     if (vscinfo->green.length) {
538         cmap->green = (__u16 *) malloc(sizeof(__u16) * cmaplen);
539         if (!cmap->green) {
540             if (vscinfo->red.length)
541                 free(cmap->red);
542             perror("green lut malloc error\n");
543             return (struct fb_cmap *)-1;
544         }
545     }
546     if (vscinfo->blue.length) {
547         cmap->blue = (__u16 *) malloc(sizeof(__u16) * cmaplen);
548         if (!cmap->blue) {
549             if (vscinfo->red.length)
550                 free(cmap->red);
551             if (vscinfo->green.length)
552                 free(cmap->green);
553             perror("blue lut malloc error\n");
554             return (struct fb_cmap *)-1;
555         }
556     }
557     if (vscinfo->transp.length) {
558         cmap->transp = (__u16 *) malloc(sizeof(__u16) * cmaplen);
559         if (!cmap->transp) {
560             if (vscinfo->red.length)
561                 free(cmap->red);
562             if (vscinfo->green.length)
563                 free(cmap->green);
564             if (vscinfo->blue.length)
565                 free(cmap->blue);
566             perror("transp lut malloc error\n");
567             return (struct fb_cmap *)-1;
568         }
569     }
570     cmap->len = cmaplen;
571     return cmap;
572 }
573
574 static void
575 fb_cmap_destroy(struct fb_cmap *cmap)
576 {
577     if (cmap->red)
578         free(cmap->red);
579     if (cmap->green)
580         free(cmap->green);
581     if (cmap->blue)
582         free(cmap->blue);
583     if (cmap->transp)
584         free(cmap->transp);
585     free(cmap);
586 }
587
588 static int
589 fb_cmap_get(int fbfp, struct fb_cmap *cmap)
590 {
591     if (ioctl(fbfp, FBIOGETCMAP, cmap)) {
592         perror("ioctl FBIOGETCMAP error\n");
593         return -1;
594     }
595     return 0;
596 }
597
598 static int
599 fb_cmap_set(int fbfp, struct fb_cmap *cmap)
600 {
601     if (ioctl(fbfp, FBIOPUTCMAP, cmap)) {
602         perror("ioctl FBIOPUTCMAP error\n");
603         return -1;
604     }
605     return 0;
606 }
607
608 /*
609  * access to framebuffer
610  * 
611  * fb_mmap()            map from framebuffer into memory
612  * fb_munmap()          deletes the mappings
613  */
614
615 static void *
616 fb_mmap(int fbfp, struct fb_fix_screeninfo *scinfo)
617 {
618     void *buf;
619     if ((buf = (unsigned char *)
620          mmap(NULL, scinfo->smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfp,
621               (off_t) 0))
622         == MAP_FAILED) {
623         perror("mmap error");
624         return NULL;
625     }
626     return buf;
627 }
628
629 static int
630 fb_munmap(void *buf, struct fb_fix_screeninfo *scinfo)
631 {
632     return munmap(buf, scinfo->smem_len);
633 }
634
635 /*
636  * (struct fb_fix_screeninfo) device independent fixed information
637  * 
638  * fb_fscrn_get()               get information
639  */
640 static int
641 fb_fscrn_get(int fbfp, struct fb_fix_screeninfo *scinfo)
642 {
643     if (ioctl(fbfp, FBIOGET_FSCREENINFO, scinfo)) {
644         perror("ioctl FBIOGET_FSCREENINFO error\n");
645         return -1;
646     }
647     return 0;
648 }
649
650 /*
651  * (struct fb_var_screeninfo) device independent variable information
652  * 
653  * fb_vscrn_get()               get information
654  */
655 static int
656 fb_vscrn_get(int fbfp, struct fb_var_screeninfo *scinfo)
657 {
658     if (ioctl(fbfp, FBIOGET_VSCREENINFO, scinfo)) {
659         perror("ioctl FBIOGET_VSCREENINFO error\n");
660         return -1;
661     }
662     return 0;
663 }