3bdcbb729cb15c97cc8e101a8e745b8399abdc6c
[navit-package] / navit / graphics / gd / graphics_gd.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 Navit Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19
20 #include <glib.h>
21 #include <gd.h>
22 #include "config.h"
23 #include "point.h"
24 #include "graphics.h"
25 #include "color.h"
26 #include "plugin.h"
27 #include "callback.h"
28 #include "window.h"
29 #include "navit.h"
30 #include "debug.h"
31 #include "navit/font/freetype/font_freetype.h"
32
33 #define NAVIT_GD_XPM_TRANSPARENCY_HACK
34
35 #ifdef NAVIT_GD_XPM_TRANSPARENCY_HACK
36 #include <X11/xpm.h>
37
38 static void emit_callback(struct graphics_priv *priv);
39
40 BGD_DECLARE(gdImagePtr) gdImageCreateFromXpm (char *filename)
41 {
42   XpmInfo info;
43   XpmImage image;
44   int i, j, k, number;
45   char buf[5];
46   gdImagePtr im = 0;
47   int *pointer;
48   int red = 0, green = 0, blue = 0, alpha = 0;
49   int *colors;
50   int ret;
51
52   ret = XpmReadFileToXpmImage (filename, &image, &info);
53   if (ret != XpmSuccess)
54     return 0;
55
56   if (!(im = gdImageCreate (image.width, image.height)))
57     return 0;
58
59   number = image.ncolors;
60         if (overflow2(sizeof (int), number)) {
61                 return 0;
62         }
63   colors = (int *) gdMalloc (sizeof (int) * number);
64   if (colors == NULL)
65     return (0);
66   for (i = 0; i < number; i++)
67     {
68       alpha = 0;
69       switch (strlen (image.colorTable[i].c_color))
70         {
71         case 4:
72           if (!strcasecmp(image.colorTable[i].c_color,"none")) {
73             red = 0;
74             green = 0;
75             blue = 0;
76             alpha = 127;
77           } else {
78             buf[1] = '\0';
79             buf[0] = image.colorTable[i].c_color[1];
80             red = strtol (buf, NULL, 16);
81
82             buf[0] = image.colorTable[i].c_color[3];
83             green = strtol (buf, NULL, 16);
84
85             buf[0] = image.colorTable[i].c_color[5];
86             blue = strtol (buf, NULL, 16);
87           } 
88           break;
89         case 7:
90           buf[2] = '\0';
91           buf[0] = image.colorTable[i].c_color[1];
92           buf[1] = image.colorTable[i].c_color[2];
93           red = strtol (buf, NULL, 16);
94
95           buf[0] = image.colorTable[i].c_color[3];
96           buf[1] = image.colorTable[i].c_color[4];
97           green = strtol (buf, NULL, 16);
98
99           buf[0] = image.colorTable[i].c_color[5];
100           buf[1] = image.colorTable[i].c_color[6];
101           blue = strtol (buf, NULL, 16);
102           break;
103         case 10:
104           buf[3] = '\0';
105           buf[0] = image.colorTable[i].c_color[1];
106           buf[1] = image.colorTable[i].c_color[2];
107           buf[2] = image.colorTable[i].c_color[3];
108           red = strtol (buf, NULL, 16);
109           red /= 64;
110
111           buf[0] = image.colorTable[i].c_color[4];
112           buf[1] = image.colorTable[i].c_color[5];
113           buf[2] = image.colorTable[i].c_color[6];
114           green = strtol (buf, NULL, 16);
115           green /= 64;
116
117           buf[0] = image.colorTable[i].c_color[7];
118           buf[1] = image.colorTable[i].c_color[8];
119           buf[2] = image.colorTable[i].c_color[9];
120           blue = strtol (buf, NULL, 16);
121           blue /= 64;
122           break;
123         case 13:
124           buf[4] = '\0';
125           buf[0] = image.colorTable[i].c_color[1];
126           buf[1] = image.colorTable[i].c_color[2];
127           buf[2] = image.colorTable[i].c_color[3];
128           buf[3] = image.colorTable[i].c_color[4];
129           red = strtol (buf, NULL, 16);
130           red /= 256;
131
132           buf[0] = image.colorTable[i].c_color[5];
133           buf[1] = image.colorTable[i].c_color[6];
134           buf[2] = image.colorTable[i].c_color[7];
135           buf[3] = image.colorTable[i].c_color[8];
136           green = strtol (buf, NULL, 16);
137           green /= 256;
138
139           buf[0] = image.colorTable[i].c_color[9];
140           buf[1] = image.colorTable[i].c_color[10];
141           buf[2] = image.colorTable[i].c_color[11];
142           buf[3] = image.colorTable[i].c_color[12];
143           blue = strtol (buf, NULL, 16);
144           blue /= 256;
145           break;
146         }
147
148
149       colors[i] = gdImageColorResolveAlpha(im, red, green, blue, alpha);
150       if (colors[i] == -1)
151         fprintf (stderr, "ARRRGH\n");
152     }
153
154   pointer = (int *) image.data;
155   for (i = 0; i < image.height; i++)
156     {
157       for (j = 0; j < image.width; j++)
158         {
159           k = *pointer++;
160           gdImageSetPixel (im, j, i, colors[k]);
161         }
162     }
163   gdFree (colors);
164   return (im);
165 }
166 #endif
167
168
169 struct graphics_priv {
170         gdImagePtr im;
171         int w,h,flags,alpha,overlay;
172         struct point p;
173         struct callback *cb;
174         struct callback_list *cbl;
175         struct navit *nav;
176         struct graphics_gc_priv *background;
177         struct font_freetype_methods freetype_methods;
178         struct window window;
179         struct graphics_data_image image;
180         struct graphics_priv *next,*overlays;
181 };
182
183 struct graphics_gc_priv {
184         struct graphics_priv *gr;
185         int color;
186         int bgcolor;
187         int width;
188         unsigned char *dash_list;
189         int dash_count;
190         int dash_list_len;
191 };
192
193 struct graphics_image_priv {
194         gdImagePtr im;
195 };
196
197
198 static void
199 graphics_destroy(struct graphics_priv *gr)
200 {
201         g_free(gr);
202 }
203
204 static void
205 gc_destroy(struct graphics_gc_priv *gc)
206 {
207         if (gc->color != -1)
208                 gdImageColorDeallocate(gc->gr->im, gc->color);
209         if (gc->bgcolor != -1)
210                 gdImageColorDeallocate(gc->gr->im, gc->bgcolor);
211         g_free(gc->dash_list);
212         g_free(gc);
213 }
214
215 static void
216 gc_set_linewidth(struct graphics_gc_priv *gc, int w)
217 {
218         gc->width=w;
219 }
220
221 static void
222 gc_set_dashes(struct graphics_gc_priv *gc, int w, int offset, unsigned char *dash_list, int n)
223 {
224         int i,count=0;
225         g_free(gc->dash_list);
226         gc->dash_list=g_new(unsigned char, n);
227         for (i = 0 ; i < n ; i++) {
228                 gc->dash_list[i]=dash_list[i];
229                 count+=dash_list[i];
230         }
231         gc->dash_list_len=n;
232         gc->dash_count=count;
233 }
234
235 static void
236 gc_set_foreground(struct graphics_gc_priv *gc, struct color *c)
237 {
238         gc->color=gdImageColorAllocate(gc->gr->im, c->r>>8, c->g>>8, c->b>>8); 
239 }
240
241 static void
242 gc_set_background(struct graphics_gc_priv *gc, struct color *c)
243 {
244         gc->bgcolor=gdImageColorAllocate(gc->gr->im, c->r>>8, c->g>>8, c->b>>8);
245 }
246
247 static struct graphics_gc_methods gc_methods = {
248         gc_destroy,
249         gc_set_linewidth,
250         gc_set_dashes,  
251         gc_set_foreground,      
252         gc_set_background       
253 };
254
255 static struct graphics_gc_priv *gc_new(struct graphics_priv *gr, struct graphics_gc_methods *meth)
256 {
257         struct graphics_gc_priv *ret=g_new0(struct graphics_gc_priv, 1);
258         ret->gr=gr;
259         ret->width=1;
260         ret->color=-1;
261         ret->bgcolor=-1;
262         *meth=gc_methods;
263         return ret;
264 }
265
266
267 static struct graphics_image_priv *
268 image_new(struct graphics_priv *gr, struct graphics_image_methods *meth, char *name, int *w, int *h, struct point *hot, int rotation)
269 {
270         FILE *file;
271         struct graphics_image_priv *ret=NULL;
272         gdImagePtr im=NULL;
273         int len;
274
275         if (! name)
276                 return NULL;
277         len=strlen(name);
278         if (len < 4)
279                 return NULL;
280         file=fopen(name,"r");
281         if (file) {
282                 if (!strcmp(name+len-4,".png"))
283                         im=gdImageCreateFromPng(file);
284                 else if (!strcmp(name+len-4,".xpm"))
285                         im=gdImageCreateFromXpm(name);
286                 fclose(file);
287         }
288         if (im) {
289                 ret=g_new0(struct graphics_image_priv, 1);
290                 ret->im=im;
291                 *w=im->sx;
292                 *h=im->sy;
293                 hot->x=im->sx/2;
294                 hot->y=im->sy/2;
295         }
296         return ret;
297 }
298
299 static void
300 draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count)
301 {
302         int color[gc->dash_count],cc;
303         int i,j,k=0;
304
305         if (gc->dash_count) {
306                 cc=gc->color;
307                 for (i = 0 ; i < gc->dash_list_len ; i++) {
308                         for (j = 0 ; j  < gc->dash_list[i] ; j++) {
309                                 color[k++]=cc;
310                         }
311                         if (cc == gdTransparent)
312                                 cc=gc->color;
313                         else
314                                 cc=gdTransparent;
315                 }
316                 gdImageSetStyle(gr->im, color, gc->dash_count);
317         }
318         gdImageSetThickness(gr->im, gc->width);
319         gdImageOpenPolygon(gr->im, (gdPointPtr) p, count, gc->dash_count ? gdStyled : gc->color);  
320 }
321
322 static void
323 draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count)
324 {
325         
326         gdImageFilledPolygon(gr->im, (gdPointPtr) p, count, gc->color);  
327 }
328
329 static void
330 draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h)
331 {
332         gdImageFilledRectangle(gr->im, p->x, p->y, p->x+w, p->y+h, gc->color);
333 }
334
335 static void
336 draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r)
337 {
338         gdImageSetThickness(gr->im, gc->width);
339         gdImageArc(gr->im, p->x, p->y, r, r, 0, 360, gc->color);
340 }
341
342
343 static void
344 draw_text(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct graphics_gc_priv *bg, struct graphics_font_priv *font, char *text, struct point *p, int dx, int dy)
345 {
346         struct font_freetype_text *t;
347         struct font_freetype_glyph *g, **gp;
348         gdImagePtr im;
349         int i,x,y,w,h;
350         t=gr->freetype_methods.text_new(text, (struct font_freetype_font *)font, dx, dy);
351         struct color transparent = {0x0, 0x0, 0x0, 0x7f7f};
352         struct color white = {0xffff, 0xffff, 0xffff, 0x0};
353         struct color black = {0x0, 0x0, 0x0, 0x0};
354
355         x=p->x << 6;
356         y=p->y << 6;
357         gp=t->glyph;
358         i=t->glyph_count;
359         while (i-- > 0)
360         {
361                 g=*gp++;
362                 w=g->w;
363                 h=g->h;
364                 if (w && h) {
365                         im=gdImageCreateTrueColor(w+2, h+2);
366                         gr->freetype_methods.get_shadow(g,(unsigned char *)(im->tpixels),32,0,&white,&transparent);
367                         gdImageCopy(gr->im, im, ((x+g->x)>>6)-1, ((y+g->y)>>6)-1, 0, 0, w+2, h+2);
368                         gdImageDestroy(im);
369                 }
370                 x+=g->dx;
371                 y+=g->dy;
372         }
373         x=p->x << 6;
374         y=p->y << 6;
375         gp=t->glyph;
376         i=t->glyph_count;
377         while (i-- > 0)
378         {
379                 g=*gp++;
380                 w=g->w;
381                 h=g->h;
382                 if (w && h) {
383                         im=gdImageCreateTrueColor(w, h);
384                         gr->freetype_methods.get_glyph(g,(unsigned char *)(im->tpixels),32,0,&black,&white,&transparent);
385                         gdImageCopy(gr->im, im, (x+g->x)>>6, (y+g->y)>>6, 0, 0, w, h);
386                         gdImageDestroy(im);
387                 }
388                 x+=g->dx;
389                 y+=g->dy;
390         }
391         gr->freetype_methods.text_destroy(t);
392 }
393
394 static void
395 draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img)
396 {
397         gdImageCopy(gr->im, img->im, p->x, p->y, 0, 0, img->im->sx, img->im->sy);
398 }
399
400 static void
401 draw_image_warp(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, int count, char *data)
402 {
403 }
404
405 static void
406 draw_restore(struct graphics_priv *gr, struct point *p, int w, int h)
407 {
408 }
409
410 static void
411 draw_drag(struct graphics_priv *gr, struct point *p)
412 {
413         if (p)
414                 gr->p=*p;
415         else {
416                 gr->p.x=0;
417                 gr->p.y=0;
418         }
419 }
420
421
422 static void
423 background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc)
424 {
425         gr->background=gc;
426 }
427
428 static void
429 draw_mode(struct graphics_priv *gr, enum draw_mode_num mode)
430 {
431         FILE *pngout;
432 #if 0
433         if (mode == draw_mode_begin && gr->background) {
434                 gdImageFilledRectangle(gr->im, 0, 0, gr->w, gr->h, gr->background->color);
435         }
436 #endif
437         if (mode == draw_mode_end && !(gr->flags & 1)) {
438                 rename("test.png","test.png.old");
439                 pngout=fopen("test.png", "wb");
440                 gdImagePng(gr->im, pngout);
441                 fclose(pngout);
442         }
443 }
444
445 static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int alpha);
446
447 static void *
448 get_data(struct graphics_priv *this, char *type)
449 {
450         int b;
451         struct point p;
452         gdImagePtr im = this->im;
453         dbg(1,"type=%s\n",type);
454         if (!strcmp(type,"window"))
455                 return &this->window;
456         if (!strcmp(type,"image_png")) {
457                 if (this->overlays) {
458                         struct graphics_priv *overlay=this->overlays;
459                         im=gdImageCreateTrueColor(this->w,this->h);
460                         gdImageCopy(im, this->im, 0, 0, 0, 0, this->w, this->h);
461                         while (overlay) {
462                                 gdImageCopy(im, overlay->im, overlay->p.x, overlay->p.y, 0, 0, overlay->w, overlay->h);
463                                 overlay=overlay->next;
464                         }
465                 }
466                 if (this->image.data)
467                         gdFree(this->image.data);
468                 this->image.data=gdImagePngPtr(im, &this->image.size);
469                 if (this->overlays)
470                         gdImageDestroy(im);
471                 return &this->image;
472         }
473         if (sscanf(type,"click_%d_%d_%d",&p.x,&p.y,&b) == 3) {
474                 dbg(1,"click %d %d %d\n",p.x,p.y,b);
475                 callback_list_call_attr_3(this->cbl, attr_button, (void *)b, (void *)1, (void *)&p);
476         }
477         if (sscanf(type,"move_%d_%d",&p.x,&p.y) == 2) {
478                 dbg(1,"move %d %d\n",p.x,p.y);
479                 callback_list_call_attr_1(this->cbl, attr_motion, (void *)&p);
480         }
481         return NULL;
482 }
483
484
485 static void
486 image_free(struct graphics_priv *gr, struct graphics_image_priv *priv)
487 {       
488         gdImageDestroy(priv->im);
489         g_free(priv);
490 }
491
492 static void
493 overlay_disable(struct graphics_priv *gr, int disable)
494 {
495         dbg(0,"enter\n");
496 }
497
498 static void
499 overlay_resize(struct graphics_priv *gr, struct point *p, int w, int h, int alpha, int wraparound)
500 {
501         dbg(0,"enter\n");
502 }
503
504
505 static int
506 set_attr_do(struct graphics_priv *gr, struct attr *attr, int init)
507 {
508         switch (attr->type) {
509         case attr_w:
510                 if (gr->w != attr->u.num) {
511                         gr->w=attr->u.num;
512                         if (!init) {
513                                 if (gr->im)
514                                         gdImageDestroy(gr->im);
515                                 gr->im=gdImageCreateTrueColor(gr->w,gr->h);
516                                 emit_callback(gr);
517                         }
518                 }
519                 break;
520         case attr_h:
521                 if (gr->h != attr->u.num) {
522                         gr->h=attr->u.num;
523                         if (!init) {
524                                 if (gr->im)
525                                         gdImageDestroy(gr->im);
526                                 gr->im=gdImageCreateTrueColor(gr->w,gr->h);
527                                 emit_callback(gr);
528                         }
529                 }
530                 break;
531         case attr_flags:
532                 gr->flags=attr->u.num;
533                 break;
534         default:
535                 return 0;
536         }
537         return 1;
538 }
539
540
541 static int
542 set_attr(struct graphics_priv *gr, struct attr *attr)
543 {
544         return set_attr_do(gr, attr, 0);
545 }
546
547 static struct graphics_methods graphics_methods = {
548         graphics_destroy,
549         draw_mode,
550         draw_lines,
551         draw_polygon,
552         draw_rectangle,
553         draw_circle,
554         draw_text,
555         draw_image,
556         draw_image_warp,
557         draw_restore,
558         draw_drag,
559         NULL,
560         gc_new,
561         background_gc,
562         overlay_new,
563         image_new,
564         get_data,
565         image_free,
566         NULL,
567         overlay_disable,
568         overlay_resize,
569         set_attr,
570 };
571
572 static struct graphics_priv *
573 overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int alpha)
574 {
575         struct font_priv * (*font_freetype_new)(void *meth);
576         struct graphics_priv *ret;
577
578         dbg(0,"enter\n");
579         ret=g_new0(struct graphics_priv, 1);
580         *meth=graphics_methods;
581         font_freetype_new=plugin_get_font_type("freetype");
582         if (!font_freetype_new)
583                 return NULL;
584         font_freetype_new(&ret->freetype_methods);
585         ret->p=*p;
586         ret->w=w;
587         ret->h=h;
588         ret->alpha=alpha;
589         ret->overlay=1;
590         ret->flags=1;
591         ret->im=gdImageCreateTrueColor(ret->w,ret->h);
592         ret->next=gr->overlays;
593         gr->overlays=ret;
594
595         return ret;
596 }
597
598 static void
599 emit_callback(struct graphics_priv *priv)
600 {
601         callback_list_call_attr_2(priv->cbl, attr_resize, (void *)priv->w, (void *)priv->h);
602 }
603
604
605 static struct graphics_priv *
606 graphics_gd_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl)
607 {
608         struct font_priv * (*font_freetype_new)(void *meth);
609         struct graphics_priv *ret;
610         struct attr *attr;
611         event_request_system("glib","graphics_gd_new");
612         font_freetype_new=plugin_get_font_type("freetype");
613         if (!font_freetype_new)
614                 return NULL;
615         *meth=graphics_methods;
616         ret=g_new0(struct graphics_priv, 1);
617         font_freetype_new(&ret->freetype_methods);
618         meth->font_new=(struct graphics_font_priv *(*)(struct graphics_priv *, struct graphics_font_methods *, char *,  int, int))ret->freetype_methods.font_new;
619         meth->get_text_bbox=ret->freetype_methods.get_text_bbox;
620         ret->cb=callback_new_attr_1(callback_cast(emit_callback), attr_navit, ret);
621         navit_add_callback(nav, ret->cb);
622         ret->cbl=cbl;
623         ret->nav=nav;
624         ret->w=800;
625         ret->h=600;
626         while (*attrs) {
627                 set_attr_do(ret, *attrs, 1);
628                 attrs++;
629         }
630         if (!ret->im)
631                 ret->im=gdImageCreateTrueColor(ret->w,ret->h);
632         return ret;
633 }
634
635 void
636 plugin_init(void)
637 {
638         plugin_register_graphics_type("gd", graphics_gd_new);
639 }