/** * Navit, a modular navigation system. * Copyright (C) 2005-2008 Navit Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "config.h" #include "point.h" #include "graphics.h" #include "color.h" #include "plugin.h" #include "callback.h" #include "window.h" #include "navit.h" #include "debug.h" #include "navit/font/freetype/font_freetype.h" #ifdef HAVE_SHMEM #include #include #include #endif #ifdef HAVE_SOCKET #include #include #include #include #endif struct shmem_header { int flag; int w,h,bpp; }; #ifdef HAVE_XPM #define NAVIT_GD_XPM_TRANSPARENCY_HACK #endif static void emit_callback(struct graphics_priv *priv); static void image_setup(struct graphics_priv *gr); static struct shmem_header *shm_next(struct graphics_priv *gr); static void add_overlays(struct graphics_priv *overlay, gdImagePtr im); #ifdef NAVIT_GD_XPM_TRANSPARENCY_HACK #include BGD_DECLARE(gdImagePtr) gdImageCreateFromXpm (char *filename) { XpmInfo info; XpmImage image; int i, j, k, number; char buf[5]; gdImagePtr im = 0; int *pointer; int red = 0, green = 0, blue = 0, alpha = 0; int *colors; int ret; ret = XpmReadFileToXpmImage (filename, &image, &info); if (ret != XpmSuccess) return 0; if (!(im = gdImageCreate (image.width, image.height))) return 0; number = image.ncolors; if (overflow2(sizeof (int), number)) { return 0; } colors = (int *) gdMalloc (sizeof (int) * number); if (colors == NULL) return (0); for (i = 0; i < number; i++) { alpha = 0; switch (strlen (image.colorTable[i].c_color)) { case 4: if (!strcasecmp(image.colorTable[i].c_color,"none")) { red = 0; green = 0; blue = 0; alpha = 127; } else { buf[1] = '\0'; buf[0] = image.colorTable[i].c_color[1]; red = strtol (buf, NULL, 16); buf[0] = image.colorTable[i].c_color[3]; green = strtol (buf, NULL, 16); buf[0] = image.colorTable[i].c_color[5]; blue = strtol (buf, NULL, 16); } break; case 7: buf[2] = '\0'; buf[0] = image.colorTable[i].c_color[1]; buf[1] = image.colorTable[i].c_color[2]; red = strtol (buf, NULL, 16); buf[0] = image.colorTable[i].c_color[3]; buf[1] = image.colorTable[i].c_color[4]; green = strtol (buf, NULL, 16); buf[0] = image.colorTable[i].c_color[5]; buf[1] = image.colorTable[i].c_color[6]; blue = strtol (buf, NULL, 16); break; case 10: buf[3] = '\0'; buf[0] = image.colorTable[i].c_color[1]; buf[1] = image.colorTable[i].c_color[2]; buf[2] = image.colorTable[i].c_color[3]; red = strtol (buf, NULL, 16); red /= 64; buf[0] = image.colorTable[i].c_color[4]; buf[1] = image.colorTable[i].c_color[5]; buf[2] = image.colorTable[i].c_color[6]; green = strtol (buf, NULL, 16); green /= 64; buf[0] = image.colorTable[i].c_color[7]; buf[1] = image.colorTable[i].c_color[8]; buf[2] = image.colorTable[i].c_color[9]; blue = strtol (buf, NULL, 16); blue /= 64; break; case 13: buf[4] = '\0'; buf[0] = image.colorTable[i].c_color[1]; buf[1] = image.colorTable[i].c_color[2]; buf[2] = image.colorTable[i].c_color[3]; buf[3] = image.colorTable[i].c_color[4]; red = strtol (buf, NULL, 16); red /= 256; buf[0] = image.colorTable[i].c_color[5]; buf[1] = image.colorTable[i].c_color[6]; buf[2] = image.colorTable[i].c_color[7]; buf[3] = image.colorTable[i].c_color[8]; green = strtol (buf, NULL, 16); green /= 256; buf[0] = image.colorTable[i].c_color[9]; buf[1] = image.colorTable[i].c_color[10]; buf[2] = image.colorTable[i].c_color[11]; buf[3] = image.colorTable[i].c_color[12]; blue = strtol (buf, NULL, 16); blue /= 256; break; } colors[i] = gdImageColorResolveAlpha(im, red, green, blue, alpha); if (colors[i] == -1) fprintf (stderr, "ARRRGH\n"); } pointer = (int *) image.data; for (i = 0; i < image.height; i++) { for (j = 0; j < image.width; j++) { k = *pointer++; gdImageSetPixel (im, j, i, colors[k]); } } gdFree (colors); return (im); } #endif struct graphics_priv { gdImagePtr im; int w,h,flags,overlay,shmkey,shmsize,shmoffset; void *shm; struct shmem_header *shm_header; struct point p; struct callback *cb; struct callback_list *cbl; struct navit *nav; struct graphics_gc_priv *background; struct font_freetype_methods freetype_methods; struct window window; struct graphics_data_image image; struct graphics_priv *next,*overlays; GList *sockets; }; struct graphics_gc_priv { struct graphics_priv *gr; int color; int bgcolor; int width; struct color color2; unsigned char *dash_list; int dash_count; int dash_list_len; }; struct graphics_image_priv { gdImagePtr im; }; static void graphics_destroy(struct graphics_priv *gr) { gr->freetype_methods.destroy(); g_free(gr); } static void gc_destroy(struct graphics_gc_priv *gc) { if (gc->color != -1) gdImageColorDeallocate(gc->gr->im, gc->color); if (gc->bgcolor != -1) gdImageColorDeallocate(gc->gr->im, gc->bgcolor); g_free(gc->dash_list); g_free(gc); } static void gc_set_linewidth(struct graphics_gc_priv *gc, int w) { gc->width=w; } static void gc_set_dashes(struct graphics_gc_priv *gc, int w, int offset, unsigned char *dash_list, int n) { int i,count=0; g_free(gc->dash_list); gc->dash_list=g_new(unsigned char, n); for (i = 0 ; i < n ; i++) { gc->dash_list[i]=dash_list[i]; count+=dash_list[i]; } gc->dash_list_len=n; gc->dash_count=count; } static void gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) { gc->color2=*c; gc->color=gdImageColorAllocate(gc->gr->im, c->r>>8, c->g>>8, c->b>>8); } static void gc_set_background(struct graphics_gc_priv *gc, struct color *c) { gc->bgcolor=gdImageColorAllocate(gc->gr->im, c->r>>8, c->g>>8, c->b>>8); } static struct graphics_gc_methods gc_methods = { gc_destroy, gc_set_linewidth, gc_set_dashes, gc_set_foreground, gc_set_background }; static struct graphics_gc_priv *gc_new(struct graphics_priv *gr, struct graphics_gc_methods *meth) { struct graphics_gc_priv *ret=g_new0(struct graphics_gc_priv, 1); ret->gr=gr; ret->width=1; ret->color=-1; ret->bgcolor=-1; *meth=gc_methods; return ret; } static struct graphics_image_priv * image_new(struct graphics_priv *gr, struct graphics_image_methods *meth, char *name, int *w, int *h, struct point *hot, int rotation) { FILE *file; struct graphics_image_priv *ret=NULL; gdImagePtr im=NULL; int len; if (! name) return NULL; len=strlen(name); if (len < 4) return NULL; file=fopen(name,"r"); if (file) { if (!strcmp(name+len-4,".png")) #ifdef HAVE_GRAPHICS_GD_PNG im=gdImageCreateFromPng(file); #else im=NULL; #endif else if (!strcmp(name+len-4,".xpm")) im=gdImageCreateFromXpm(name); else if (!strcmp(name+len-4,".gif")) { im=gdImageCreateFromGif(file); } fclose(file); } if (im) { ret=g_new0(struct graphics_image_priv, 1); ret->im=im; *w=im->sx; *h=im->sy; hot->x=im->sx/2; hot->y=im->sy/2; } return ret; } static void draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) { int color[gc->dash_count],cc; int i,j,k=0; if (gc->dash_count) { cc=gc->color; for (i = 0 ; i < gc->dash_list_len ; i++) { for (j = 0 ; j < gc->dash_list[i] ; j++) { color[k++]=cc; } if (cc == gdTransparent) cc=gc->color; else cc=gdTransparent; } gdImageSetStyle(gr->im, color, gc->dash_count); } gdImageSetThickness(gr->im, gc->width); cc=gc->color; if (gr->flags & 8) { gdImageSetAntiAliased(gr->im, cc); cc=gdAntiAliased; } #ifdef GD_NO_IMAGE_OPEN_POLYGON for (i = 0 ; i < count-1 ; i++) gdImageLine(gr->im, p[i].x, p[i].y, p[i+1].x, p[i+1].y, gc->dash_count ? gdStyled : cc); #else gdImageOpenPolygon(gr->im, (gdPointPtr) p, count, gc->dash_count ? gdStyled : cc); #endif } static void draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) { int cc=gc->color; if (gr->flags & 8) { gdImageSetAntiAliased(gr->im, cc); cc=gdAntiAliased; } gdImageFilledPolygon(gr->im, (gdPointPtr) p, count, cc); } static void draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) { int cc=gc->color; if (gr->flags & 8) { gdImageSetAntiAliased(gr->im, cc); cc=gdAntiAliased; } gdImageFilledRectangle(gr->im, p->x, p->y, p->x+w, p->y+h, cc); } static void draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) { int cc=gc->color; if (gr->flags & 8) { gdImageSetAntiAliased(gr->im, cc); cc=gdAntiAliased; } gdImageSetThickness(gr->im, gc->width); gdImageArc(gr->im, p->x, p->y, r, r, 0, 360, cc); } static void 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) { struct font_freetype_text *t; struct font_freetype_glyph *g, **gp; gdImagePtr im; int i,x,y,w,h; t=gr->freetype_methods.text_new(text, (struct font_freetype_font *)font, dx, dy); struct color fgc,bgc,transparent = {0x0, 0x0, 0x0, 0x7f7f}; fgc=fg->color2; fgc.a=0; if (bg) { bgc=bg->color2; bgc.a=0; x=p->x << 6; y=p->y << 6; gp=t->glyph; i=t->glyph_count; while (i-- > 0) { g=*gp++; w=g->w; h=g->h; if (w && h) { im=gdImageCreateTrueColor(w+2, h+2); gr->freetype_methods.get_shadow(g,(unsigned char *)(im->tpixels),0,&bgc,&transparent); gdImageCopy(gr->im, im, ((x+g->x)>>6)-1, ((y+g->y)>>6)-1, 0, 0, w+2, h+2); gdImageDestroy(im); } x+=g->dx; y+=g->dy; } } else bgc=transparent; x=p->x << 6; y=p->y << 6; gp=t->glyph; i=t->glyph_count; while (i-- > 0) { g=*gp++; w=g->w; h=g->h; if (w && h) { im=gdImageCreateTrueColor(w, h); gr->freetype_methods.get_glyph(g,(unsigned char *)(im->tpixels),0,&fgc,&bgc,&transparent); gdImageCopy(gr->im, im, (x+g->x)>>6, (y+g->y)>>6, 0, 0, w, h); gdImageDestroy(im); } x+=g->dx; y+=g->dy; } gr->freetype_methods.text_destroy(t); } static void draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) { gdImageCopy(gr->im, img->im, p->x, p->y, 0, 0, img->im->sx, img->im->sy); } static void draw_drag(struct graphics_priv *gr, struct point *p) { if (p) gr->p=*p; else { gr->p.x=0; gr->p.y=0; } } static void background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc) { gr->background=gc; } static void draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) { FILE *pngout; #if 0 if (mode == draw_mode_begin && gr->background) { gdImageFilledRectangle(gr->im, 0, 0, gr->w, gr->h, gr->background->color); } #endif if (mode == draw_mode_end && !gr->overlay) { add_overlays(gr->overlays, gr->im); if (!(gr->flags & 1)) { #ifdef HAVE_GRAPHICS_GD_PNG rename("test.png","test.png.old"); pngout=fopen("test.png", "wb"); gdImagePng(gr->im, pngout); fclose(pngout); #else rename("test.gd","test.gd.old"); pngout=fopen("test.gd", "wb"); gdImageGd(gr->im, pngout); fclose(pngout); #endif } if (gr->flags & 2) { struct shmem_header *next=shm_next(gr); gr->shm_header->flag=1; dbg(lvl_debug,"next flag is %d\n",next->flag); if (!next->flag) { gr->shm_header=next; image_setup(gr); } } #ifdef HAVE_GRAPHICS_GD_PNG #ifdef HAVE_SOCKET if (gr->flags & 4) { int size, size_written; void *data=gdImagePngPtr(gr->im, &size); GList *sockets=gr->sockets; while (sockets) { int fd=socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { dbg(lvl_error,"socket failed\n"); } else { if (connect(fd, (struct sockaddr *)sockets->data, sizeof(struct sockaddr_in)) < 0) { dbg(lvl_error,"connect failed\n"); } else { size_written=write(fd, data, size); dbg(lvl_debug,"size %d vs %d\n",size, size_written); if (shutdown(fd, SHUT_RDWR) < 0) dbg(lvl_error,"shutdown failed\n"); } close(fd); } sockets=g_list_next(sockets); } gdFree(data); } #endif #endif } } static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int wraparound); static void add_overlays(struct graphics_priv *overlay, gdImagePtr im) { while (overlay) { if (overlay->background) { gdImagePtr res,src; int y,x; int bgcol=overlay->background->color; res=gdImageCreateTrueColor(overlay->w,overlay->h); src=gdImageCreateTrueColor(overlay->w,overlay->h); gdImageCopy(src, im, 0, 0, overlay->p.x, overlay->p.y, overlay->w, overlay->h); for (y = 0 ; y < overlay->h ; y++) { unsigned int *res_line=res->tpixels[y]; unsigned int *src_line=src->tpixels[y]; unsigned int *overlay_line=overlay->im->tpixels[y]; for (x = 0 ; x < overlay->w ; x++) { if (overlay_line[x] != bgcol) res_line[x]=overlay_line[x]; else res_line[x]=src_line[x]; } } gdImageCopy(im, res, overlay->p.x, overlay->p.y, 0, 0, overlay->w, overlay->h); gdImageDestroy(res); gdImageDestroy(src); } else gdImageCopy(im, overlay->im, overlay->p.x, overlay->p.y, 0, 0, overlay->w, overlay->h); overlay=overlay->next; } } static void * get_data(struct graphics_priv *this, char *type) { int b; struct point p; gdImagePtr im = this->im; dbg(lvl_debug,"type=%s\n",type); if (!strcmp(type,"window")) return &this->window; if (!strcmp(type,"image_png")) { if (this->overlays) { struct graphics_priv *overlay=this->overlays; im=gdImageCreateTrueColor(this->w,this->h); gdImageCopy(im, this->im, 0, 0, 0, 0, this->w, this->h); add_overlays(overlay, im); } if (this->image.data) gdFree(this->image.data); #ifdef HAVE_GRAPHICS_GD_PNG this->image.data=gdImagePngPtr(im, &this->image.size); #else this->image.data=NULL; #endif if (this->overlays) gdImageDestroy(im); return &this->image; } if (sscanf(type,"click_%d_%d_%d",&p.x,&p.y,&b) == 3) { dbg(lvl_debug,"click %d %d %d\n",p.x,p.y,b); callback_list_call_attr_3(this->cbl, attr_button, (void *)b, (void *)1, (void *)&p); } if (sscanf(type,"move_%d_%d",&p.x,&p.y) == 2) { dbg(lvl_debug,"move %d %d\n",p.x,p.y); callback_list_call_attr_1(this->cbl, attr_motion, (void *)&p); } return NULL; } static void image_free(struct graphics_priv *gr, struct graphics_image_priv *priv) { gdImageDestroy(priv->im); g_free(priv); } static void overlay_disable(struct graphics_priv *gr, int disable) { dbg(lvl_debug,"enter\n"); } static void overlay_resize(struct graphics_priv *gr, struct point *p, int w, int h, int wraparound) { dbg(lvl_debug,"enter\n"); } struct shmem_header * shm_next(struct graphics_priv *gr) { char *next=(char *)gr->shm_header+gr->shmoffset; if (next+gr->shmoffset > (char *)gr->shm+gr->shmsize) { dbg(lvl_debug,"wraparound\n"); return gr->shm; } dbg(lvl_debug,"next 0x%x (offset 0x%x)\n",next-(char *)gr->shm,gr->shmoffset); return (struct shmem_header *)next; } static void image_setup(struct graphics_priv *gr) { int i,*shm=(int *)(gr->shm_header+1); if (!gr->shmkey) return; for (i = 0 ; i < gr->h ; i++) gr->im->tpixels[i]=shm+gr->w*i; gr->shm_header->w=gr->w; gr->shm_header->h=gr->h; gr->shm_header->bpp=32; gr->shm_header->flag=0; } static void image_create(struct graphics_priv *gr) { dbg(lvl_debug,"shmkey %d\n",gr->shmkey); #ifdef HAVE_SHMEM if (gr->shmkey) { int size=gr->h*gr->w*sizeof(int); int shmid=shmget(gr->shmkey, size, 0666); dbg(lvl_debug,"shmid for key 0x%x is 0x%x\n",gr->shmkey, shmid); if (shmid < 0) dbg(lvl_debug,"shmget\n"); gr->shm=shmat(shmid, NULL, 0); if (!gr->shm) dbg(lvl_debug,"shmat\n"); gr->shm_header=gr->shm; gr->im=g_new0(gdImage,1); gr->im->tpixels=g_new(int *,gr->h); gr->im->sx=gr->w; gr->im->sy=gr->h; gr->im->transparent=-1; gr->im->trueColor=1; gr->im->alphaBlendingFlag=1; gr->im->thick=1; gr->im->cx2=gr->im->sx-1; gr->im->cy2=gr->im->sy-1; image_setup(gr); } else #endif gr->im=gdImageCreateTrueColor(gr->w,gr->h); } static void image_destroy(struct graphics_priv *gr) { #ifdef HAVE_SHMEM if (gr->shmkey) { shmdt(gr->shm); g_free(gr->im->tpixels); g_free(gr->im); } else #endif gdImageDestroy(gr->im); gr->im=NULL; } static int set_attr_do(struct graphics_priv *gr, struct attr *attr, int init) { char *s,*c,*n,*p; switch (attr->type) { case attr_w: if (gr->w != attr->u.num) { gr->w=attr->u.num; if (!init) { if (gr->im) image_destroy(gr); image_create(gr); emit_callback(gr); } } break; case attr_h: if (gr->h != attr->u.num) { gr->h=attr->u.num; if (!init) { if (gr->im) image_destroy(gr); image_create(gr); emit_callback(gr); } } break; case attr_flags: gr->flags=attr->u.num; break; case attr_shmkey: gr->shmkey=attr->u.num; break; case attr_shmsize: gr->shmsize=attr->u.num; break; case attr_shmoffset: gr->shmoffset=attr->u.num; break; #ifdef HAVE_SOCKET case attr_socket: g_list_foreach(gr->sockets, (GFunc)g_free, NULL); g_list_free(gr->sockets); gr->sockets=NULL; c=s=g_strdup(attr->u.str); dbg(lvl_debug,"s=%s\n",s); while (c) { n=strchr(c,','); if (n) *n++='\0'; p=strchr(c,':'); if (p) { *p++='\0'; struct sockaddr_in *sin=g_new(struct sockaddr_in, 1); sin->sin_family=AF_INET; sin->sin_port=ntohs(atoi(p)); if (inet_aton(c, &sin->sin_addr)) { gr->sockets=g_list_append(gr->sockets, sin); } else { dbg(lvl_error,"error in %s\n",c); g_free(sin); } dbg(lvl_debug,"host=%s port=%s\n",c,p); } else dbg(lvl_error,"error in format: %s\n",p); c=n; } g_free(s); #endif default: return 0; } return 1; } static int set_attr(struct graphics_priv *gr, struct attr *attr) { return set_attr_do(gr, attr, 0); } static struct graphics_methods graphics_methods = { graphics_destroy, draw_mode, draw_lines, draw_polygon, draw_rectangle, draw_circle, draw_text, draw_image, NULL, draw_drag, NULL, gc_new, background_gc, overlay_new, image_new, get_data, image_free, NULL, overlay_disable, overlay_resize, set_attr, NULL, /* show_native_keyboard */ NULL, /* hide_native_keyboard */ }; static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int wraparound) { struct font_priv * (*font_freetype_new)(void *meth); struct graphics_priv *ret; dbg(lvl_debug,"enter\n"); ret=g_new0(struct graphics_priv, 1); *meth=graphics_methods; font_freetype_new=plugin_get_category_font("freetype"); if (!font_freetype_new) return NULL; font_freetype_new(&ret->freetype_methods); ret->p=*p; ret->w=w; ret->h=h; ret->overlay=1; ret->flags=1; ret->im=gdImageCreateTrueColor(ret->w,ret->h); ret->next=gr->overlays; gr->overlays=ret; return ret; } static void emit_callback(struct graphics_priv *priv) { callback_list_call_attr_2(priv->cbl, attr_resize, (void *)priv->w, (void *)priv->h); } static struct graphics_priv * graphics_gd_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl) { struct font_priv * (*font_freetype_new)(void *meth); struct graphics_priv *ret; event_request_system("glib","graphics_gd_new"); font_freetype_new=plugin_get_category_font("freetype"); if (!font_freetype_new) return NULL; *meth=graphics_methods; ret=g_new0(struct graphics_priv, 1); font_freetype_new(&ret->freetype_methods); meth->font_new=(struct graphics_font_priv *(*)(struct graphics_priv *, struct graphics_font_methods *, char *, int, int))ret->freetype_methods.font_new; meth->get_text_bbox=ret->freetype_methods.get_text_bbox; ret->cb=callback_new_attr_1(callback_cast(emit_callback), attr_navit, ret); navit_add_callback(nav, ret->cb); ret->cbl=cbl; ret->nav=nav; ret->w=800; ret->h=600; while (*attrs) { set_attr_do(ret, *attrs, 1); attrs++; } if (!ret->im) image_create(ret); return ret; } void plugin_init(void) { plugin_register_category_graphics("gd", graphics_gd_new); }