diff options
Diffstat (limited to 'graphics')
-rw-r--r-- | graphics/Makefile.am | 19 | ||||
-rw-r--r-- | graphics/gd/Makefile.am | 9 | ||||
-rw-r--r-- | graphics/gd/graphics_gd.c | 547 | ||||
-rw-r--r-- | graphics/gtk_drawing_area/Makefile.am | 5 | ||||
-rw-r--r-- | graphics/gtk_drawing_area/graphics_gtk_drawing_area.c | 1096 | ||||
-rw-r--r-- | graphics/gtk_gl_ext/graphics_gtk_gl_ext.c | 352 | ||||
-rw-r--r-- | graphics/null/Makefile.am | 9 | ||||
-rw-r--r-- | graphics/null/graphics_null.c | 231 | ||||
-rw-r--r-- | graphics/opengl/Makefile.am | 6 | ||||
-rw-r--r-- | graphics/opengl/graphics_opengl.c | 845 | ||||
-rw-r--r-- | graphics/qt_qpainter/Makefile.am | 10 | ||||
-rw-r--r-- | graphics/qt_qpainter/graphics_qt_qpainter.cpp | 1214 | ||||
-rw-r--r-- | graphics/sdl/Makefile.am | 5 | ||||
-rw-r--r-- | graphics/sdl/graphics_sdl.c | 2146 | ||||
-rw-r--r-- | graphics/sdl/raster.c | 2022 | ||||
-rw-r--r-- | graphics/sdl/raster.h | 25 | ||||
-rw-r--r-- | graphics/win32/Makefile.am | 9 | ||||
-rw-r--r-- | graphics/win32/graphics_win32.c | 1074 | ||||
-rw-r--r-- | graphics/win32/graphics_win32.h | 59 | ||||
-rw-r--r-- | graphics/win32/resources/resource.h | 4 | ||||
-rw-r--r-- | graphics/win32/xpm2bmp.c | 588 | ||||
-rw-r--r-- | graphics/win32/xpm2bmp.h | 44 |
22 files changed, 10319 insertions, 0 deletions
diff --git a/graphics/Makefile.am b/graphics/Makefile.am new file mode 100644 index 00000000..af999d00 --- /dev/null +++ b/graphics/Makefile.am @@ -0,0 +1,19 @@ +SUBDIRS=null +if GRAPHICS_GTK_DRAWING_AREA + SUBDIRS+=gtk_drawing_area +endif +if GRAPHICS_OPENGL + SUBDIRS+=opengl +endif +if GRAPHICS_QT_QPAINTER + SUBDIRS+=qt_qpainter +endif +if USE_GRAPHICS_SDL + SUBDIRS+=sdl +endif +if GRAPHICS_WIN32 + SUBDIRS+=win32 +endif +if GRAPHICS_GD + SUBDIRS+=gd +endif diff --git a/graphics/gd/Makefile.am b/graphics/gd/Makefile.am new file mode 100644 index 00000000..67fc270a --- /dev/null +++ b/graphics/gd/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ @GD_CFLAGS@ -I$(top_srcdir) -I$(top_srcdir)/navit -DMODULE=graphics_gd +libgraphics_gd_la_LDFLAGS = @GD_LIBS@ -module -avoid-version +if PLUGINS +modulegraphics_LTLIBRARIES = libgraphics_gd.la +else +noinst_LTLIBRARIES = libgraphics_gd.la +endif +libgraphics_gd_la_SOURCES = graphics_gd.c diff --git a/graphics/gd/graphics_gd.c b/graphics/gd/graphics_gd.c new file mode 100644 index 00000000..d53fcb62 --- /dev/null +++ b/graphics/gd/graphics_gd.c @@ -0,0 +1,547 @@ +/** + * 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 <glib.h> +#include <gd.h> +#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" + +#define NAVIT_GD_XPM_TRANSPARENCY_HACK + +#ifdef NAVIT_GD_XPM_TRANSPARENCY_HACK +#include <X11/xpm.h> + +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; + 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_gc_priv { + struct graphics_priv *gr; + int color; + int bgcolor; + int width; + 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) +{ + 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->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")) + im=gdImageCreateFromPng(file); + else if (!strcmp(name+len-4,".xpm")) + im=gdImageCreateFromXpm(name); + 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); + gdImageOpenPolygon(gr->im, (gdPointPtr) p, count, gc->dash_count ? gdStyled : gc->color); +} + +static void +draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + + gdImageFilledPolygon(gr->im, (gdPointPtr) p, count, gc->color); +} + +static void +draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) +{ + gdImageFilledRectangle(gr->im, p->x, p->y, p->x+w, p->y+h, gc->color); +} + +static void +draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) +{ + gdImageSetThickness(gr->im, gc->width); + gdImageArc(gr->im, p->x, p->y, r, r, 0, 360, gc->color); +} + + +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 transparent = {0x0, 0x0, 0x0, 0x7f7f}; + struct color white = {0xffff, 0xffff, 0xffff, 0x0}; + struct color black = {0x0, 0x0, 0x0, 0x0}; + + 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),32,0,&white,&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; + } + 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),32,0,&black,&white,&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_image_warp(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, int count, char *data) +{ +} + +static void +draw_restore(struct graphics_priv *gr, struct point *p, int w, int h) +{ +} + +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) { + rename("test.png","test.png.old"); + pngout=fopen("test.png", "wb"); + gdImagePng(gr->im, pngout); + fclose(pngout); + } +} + +static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int alpha); + +static void * +get_data(struct graphics_priv *this, char *type) +{ + int b; + struct point p; + if (!strcmp(type,"window")) + return &this->window; + if (!strcmp(type,"image_png")) { + if (this->image.data) + gdFree(this->image.data); + this->image.data=gdImagePngPtr(this->im, &this->image.size); + return &this->image; + } + if (sscanf(type,"click_%d_%d_%d",&p.x,&p.y,&b) == 3) { + if (this->image.data) + gdFree(this->image.data); + this->image.data=0; + this->image.size=0; + + callback_list_call_attr_3(this->cbl, attr_button, (void *)b, (void *)1, (void *)&p); + + return &this->image; + } + 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(0,"enter\n"); +} + + + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, + draw_image_warp, + draw_restore, + NULL, + NULL, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, + image_free, + NULL, + overlay_disable, +}; + +static struct graphics_priv * +overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int alpha) +{ + *meth=graphics_methods; + return NULL; +} + +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; + struct attr *attr; + event_request_system("glib","graphics_gtk_drawing_area_new"); + font_freetype_new=plugin_get_font_type("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; + attr=attr_search(attrs, NULL, attr_w); + if (attr) + ret->w=attr->u.num; + else + ret->w=800; + attr=attr_search(attrs, NULL, attr_h); + if (attr) + ret->h=attr->u.num; + else + ret->h=600; + ret->im=gdImageCreateTrueColor(ret->w,ret->h); + return ret; +} + +void +plugin_init(void) +{ + plugin_register_graphics_type("gd", graphics_gd_new); +} diff --git a/graphics/gtk_drawing_area/Makefile.am b/graphics/gtk_drawing_area/Makefile.am new file mode 100644 index 00000000..79da228d --- /dev/null +++ b/graphics/gtk_drawing_area/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @GTK2_CFLAGS@ -I$(top_srcdir) -I$(top_srcdir)/navit -DMODULE=graphics_gtk_drawing_area +modulegraphics_LTLIBRARIES = libgraphics_gtk_drawing_area.la +libgraphics_gtk_drawing_area_la_SOURCES = graphics_gtk_drawing_area.c +libgraphics_gtk_drawing_area_la_LDFLAGS = @GTK2_LIBS@ @IMLIB2_LIBS@ -module -avoid-version diff --git a/graphics/gtk_drawing_area/graphics_gtk_drawing_area.c b/graphics/gtk_drawing_area/graphics_gtk_drawing_area.c new file mode 100644 index 00000000..f9020067 --- /dev/null +++ b/graphics/gtk_drawing_area/graphics_gtk_drawing_area.c @@ -0,0 +1,1096 @@ +/** + * 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. + */ + +#define GDK_ENABLE_BROKEN +#include "config.h" +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/time.h> +#if !defined(GDK_Book) || !defined(GDK_Calendar) +#include <X11/XF86keysym.h> +#endif +#ifdef HAVE_IMLIB2 +#include <Imlib2.h> +#endif + +#ifndef _WIN32 +#include <gdk/gdkx.h> +#endif +#include "event.h" +#include "debug.h" +#include "point.h" +#include "graphics.h" +#include "color.h" +#include "item.h" +#include "window.h" +#include "callback.h" +#include "keys.h" +#include "plugin.h" +#include "navit/font/freetype/font_freetype.h" +#include "navit.h" + +#ifndef GDK_Book +#define GDK_Book XF86XK_Book +#endif +#ifndef GDK_Calendar +#define GDK_Calendar XF86XK_Calendar +#endif + + +struct graphics_priv { + GdkEventButton button_event; + int button_timeout; + GtkWidget *widget; + GtkWidget *win; + struct window window; + GdkDrawable *drawable; + GdkDrawable *background; + int background_ready; + GdkColormap *colormap; + struct point p; + struct point pclean; + int cleanup; + int width; + int height; + int win_w; + int win_h; + int visible; + int overlay_disabled; + int overlay_autodisabled; + int a; + int wraparound; + struct graphics_priv *parent; + struct graphics_priv *overlays; + struct graphics_priv *next; + struct graphics_gc_priv *background_gc; + enum draw_mode_num mode; + struct callback_list *cbl; + struct font_freetype_methods freetype_methods; + struct navit *nav; + int pid; + struct timeval button_press[8]; + struct timeval button_release[8]; +}; + + +struct graphics_gc_priv { + GdkGC *gc; + GdkPixmap *pixmap; + struct graphics_priv *gr; + int level; + unsigned char r,g,b,a; +}; + +struct graphics_image_priv { + GdkPixbuf *pixbuf; + int w; + int h; +}; + +static void +graphics_destroy(struct graphics_priv *gr) +{ +} + +static void +gc_destroy(struct graphics_gc_priv *gc) +{ + g_object_unref(gc->gc); + g_free(gc); +} + +static void +gc_set_linewidth(struct graphics_gc_priv *gc, int w) +{ + gdk_gc_set_line_attributes(gc->gc, w, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); +} + +static void +gc_set_dashes(struct graphics_gc_priv *gc, int w, int offset, unsigned char *dash_list, int n) +{ + gdk_gc_set_dashes(gc->gc, offset, (gint8 *)dash_list, n); + gdk_gc_set_line_attributes(gc->gc, w, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND); +} + +static void +gc_set_color(struct graphics_gc_priv *gc, struct color *c, int fg) +{ + GdkColor gdkc; + gdkc.pixel=0; + gdkc.red=c->r; + gdkc.green=c->g; + gdkc.blue=c->b; + gdk_colormap_alloc_color(gc->gr->colormap, &gdkc, FALSE, TRUE); + gdk_colormap_query_color(gc->gr->colormap, gdkc.pixel, &gdkc); + gc->r=gdkc.red >> 8; + gc->g=gdkc.green >> 8; + gc->b=gdkc.blue >> 8; + gc->a=c->a >> 8; + if (fg) { + gdk_gc_set_foreground(gc->gc, &gdkc); + gc->level=(c->r+c->g+c->b)/3; + } else + gdk_gc_set_background(gc->gc, &gdkc); +} + +static void +gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) +{ + gc_set_color(gc, c, 1); +} + +static void +gc_set_background(struct graphics_gc_priv *gc, struct color *c) +{ + gc_set_color(gc, c, 0); +} + +static void +gc_set_stipple(struct graphics_gc_priv *gc, struct graphics_image_priv *img) +{ + char data[2]={0x2,0x1}; + gdk_gc_set_fill(gc->gc, GDK_STIPPLED); + gc->pixmap=gdk_bitmap_create_from_data(gc->gr->widget->window, data, 2, 2); + gdk_gc_set_stipple(gc->gc, gc->pixmap); +} + +static struct graphics_gc_methods gc_methods = { + gc_destroy, + gc_set_linewidth, + gc_set_dashes, + gc_set_foreground, + gc_set_background, + gc_set_stipple, +}; + +static struct graphics_gc_priv *gc_new(struct graphics_priv *gr, struct graphics_gc_methods *meth) +{ + struct graphics_gc_priv *gc=g_new(struct graphics_gc_priv, 1); + + *meth=gc_methods; + gc->gc=gdk_gc_new(gr->widget->window); + gc->gr=gr; + return gc; +} + +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) +{ + GdkPixbuf *pixbuf; + struct graphics_image_priv *ret; + const char *option; + + if (*w == -1 && *h == -1) + pixbuf=gdk_pixbuf_new_from_file(name, NULL); + else + pixbuf=gdk_pixbuf_new_from_file_at_size(name, *w, *h, NULL); + if (! pixbuf) + return NULL; + if (rotation) { + GdkPixbuf *tmp; + switch (rotation) { + case 90: + rotation=270; + break; + case 180: + break; + case 270: + rotation=90; + break; + default: + return NULL; + } + tmp=gdk_pixbuf_rotate_simple(pixbuf, rotation); + g_object_unref(pixbuf); + if (! tmp) { + return NULL; + } + pixbuf=tmp; + } + ret=g_new0(struct graphics_image_priv, 1); + ret->pixbuf=pixbuf; + ret->w=gdk_pixbuf_get_width(pixbuf); + ret->h=gdk_pixbuf_get_height(pixbuf); + *w=ret->w; + *h=ret->h; + if (hot) { + option=gdk_pixbuf_get_option(pixbuf, "x_hot"); + if (option) + hot->x=atoi(option); + else + hot->x=ret->w/2-1; + option=gdk_pixbuf_get_option(pixbuf, "y_hot"); + if (option) + hot->y=atoi(option); + else + hot->y=ret->h/2-1; + } + return ret; +} + +static void +image_free(struct graphics_priv *gr, struct graphics_image_priv *priv) +{ + if (priv->pixbuf) + g_object_unref(priv->pixbuf); + g_free(priv); +} + +static void +draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + if (gr->mode == draw_mode_begin || gr->mode == draw_mode_end) + gdk_draw_lines(gr->drawable, gc->gc, (GdkPoint *)p, count); + if (gr->mode == draw_mode_end || gr->mode == draw_mode_cursor) + gdk_draw_lines(gr->widget->window, gc->gc, (GdkPoint *)p, count); +} + +static void +draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + if (gr->mode == draw_mode_begin || gr->mode == draw_mode_end) + gdk_draw_polygon(gr->drawable, gc->gc, TRUE, (GdkPoint *)p, count); + if (gr->mode == draw_mode_end || gr->mode == draw_mode_cursor) + gdk_draw_polygon(gr->widget->window, gc->gc, TRUE, (GdkPoint *)p, count); +} + +static void +draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) +{ + gdk_draw_rectangle(gr->drawable, gc->gc, TRUE, p->x, p->y, w, h); +} + +static void +draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) +{ + if (gr->mode == draw_mode_begin || gr->mode == draw_mode_end) + gdk_draw_arc(gr->drawable, gc->gc, FALSE, p->x-r/2, p->y-r/2, r, r, 0, 64*360); + if (gr->mode == draw_mode_end || gr->mode == draw_mode_cursor) + gdk_draw_arc(gr->widget->window, gc->gc, FALSE, p->x-r/2, p->y-r/2, r, r, 0, 64*360); +} + +static void +display_text_draw(struct font_freetype_text *text, struct graphics_priv *gr, struct graphics_gc_priv *fg, struct graphics_gc_priv *bg, struct point *p) +{ + int i,x,y; + struct font_freetype_glyph *g, **gp; + unsigned char *shadow; + struct color transparent={0x0,0x0,0x0,0x0}; + struct color white={0xffff,0xffff,0xffff,0xffff}; + + gp=text->glyph; + i=text->glyph_count; + x=p->x << 6; + y=p->y << 6; + while (i-- > 0) + { + g=*gp++; + if (g->w && g->h && bg ) { +#if 1 + shadow=g_malloc((g->w+2)*(g->h+2)); + if (gr->freetype_methods.get_shadow(g, shadow, 8, g->w+2, &white, &transparent)) + gdk_draw_gray_image(gr->drawable, bg->gc, ((x+g->x)>>6)-1, ((y+g->y)>>6)-1, g->w+2, g->h+2, GDK_RGB_DITHER_NONE, shadow, g->w+2); + g_free(shadow); +#else + GdkImage *image; + stride=(g->w+9)/8; + shadow=malloc(stride*(g->h+2)); + + gr->freetype_methods.get_shadow(g, shadow, 1, stride); + image=gdk_image_new_bitmap(gdk_visual_get_system(),shadow,g->w+2, g->h+2); + gdk_draw_image(gr->drawable, bg->gc, image, 0, 0, ((x+g->x)>>6)-1, ((y+g->y)>>6)-1, g->w+2, g->h+2); + g_object_unref(image); +#endif + + } + x+=g->dx; + y+=g->dy; + } + x=p->x << 6; + y=p->y << 6; + gp=text->glyph; + i=text->glyph_count; + while (i-- > 0) + { + g=*gp++; + if (g->w && g->h) + gdk_draw_gray_image(gr->drawable, fg->gc, (x+g->x)>>6, (y+g->y)>>6, g->w, g->h, GDK_RGB_DITHER_NONE, g->pixmap, g->w); + x+=g->dx; + y+=g->dy; + } +} + +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; + + if (! font) + { + dbg(0,"no font, returning\n"); + return; + } +#if 0 /* Temporarily disabled because it destroys text rendering of overlays and in gui internal in some places */ + /* + This needs an improvement, no one checks if the strings are visible + */ + if (p->x > gr->width-50 || p->y > gr->height-50) { + return; + } + if (p->x < -50 || p->y < -50) { + return; + } +#endif + + if (bg) { + if (bg->level > 32767) { + gdk_gc_set_function(fg->gc, GDK_AND_INVERT); + gdk_gc_set_function(bg->gc, GDK_OR); + } else { + gdk_gc_set_function(fg->gc, GDK_OR); + gdk_gc_set_function(bg->gc, GDK_AND_INVERT); + } + } + + t=gr->freetype_methods.text_new(text, (struct font_freetype_font *)font, dx, dy); + display_text_draw(t, gr, fg, bg, p); + gr->freetype_methods.text_destroy(t); + if (bg) { + gdk_gc_set_function(fg->gc, GDK_COPY); + gdk_gc_set_function(bg->gc, GDK_COPY); + } +#if 0 + { + struct point pnt[5]; + int i; + gr->freetype_methods.get_text_bbox(gr, font, text, dx, dy, pnt, 1); + for (i = 0 ; i < 4 ; i++) { + pnt[i].x+=p->x; + pnt[i].y+=p->y; + } + pnt[4]=pnt[0]; + gdk_draw_lines(gr->drawable, fg->gc, (GdkPoint *)pnt, 5); + } +#endif +} + +static void +draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) +{ + gdk_draw_pixbuf(gr->drawable, fg->gc, img->pixbuf, 0, 0, p->x, p->y, + img->w, img->h, GDK_RGB_DITHER_NONE, 0, 0); +} + +#ifdef HAVE_IMLIB2 +static void +draw_image_warp(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, int count, char *data) +{ + void *image; + int w,h; + dbg(1,"draw_image_warp data=%s\n", data); + image = imlib_load_image(data); + imlib_context_set_display(gdk_x11_drawable_get_xdisplay(gr->widget->window)); + imlib_context_set_colormap(gdk_x11_colormap_get_xcolormap(gtk_widget_get_colormap(gr->widget))); + imlib_context_set_visual(gdk_x11_visual_get_xvisual(gtk_widget_get_visual(gr->widget))); + imlib_context_set_drawable(gdk_x11_drawable_get_xid(gr->drawable)); + imlib_context_set_image(image); + w = imlib_image_get_width(); + h = imlib_image_get_height(); + if (count == 3) { + /* 0 1 + 2 */ + imlib_render_image_on_drawable_skewed(0, 0, w, h, p[0].x, p[0].y, p[1].x-p[0].x, p[1].y-p[0].y, p[2].x-p[0].x, p[2].y-p[0].y); + } + if (count == 2) { + /* 0 + 1 */ + imlib_render_image_on_drawable_skewed(0, 0, w, h, p[0].x, p[0].y, p[1].x-p[0].x, 0, 0, p[1].y-p[0].y); + } + if (count == 1) { + /* + 0 + */ + imlib_render_image_on_drawable_skewed(0, 0, w, h, p[0].x-w/2, p[0].y-h/2, w, 0, 0, h); + } + imlib_free_image(); +} +#endif + +static void +overlay_rect(struct graphics_priv *parent, struct graphics_priv *overlay, int clean, GdkRectangle *r) +{ + if (clean) { + r->x=overlay->pclean.x; + r->y=overlay->pclean.y; + } else { + r->x=overlay->p.x; + r->y=overlay->p.y; + } + r->width=overlay->width; + r->height=overlay->height; + if (!overlay->wraparound) + return; + if (r->x < 0) + r->x += parent->width; + if (r->y < 0) + r->y += parent->height; + if (r->width < 0) + r->width += parent->width; + if (r->height < 0) + r->height += parent->height; +} + +static void +overlay_draw(struct graphics_priv *parent, struct graphics_priv *overlay, GdkRectangle *r, GdkPixmap *pixmap, GdkGC *gc) +{ + GdkPixbuf *pixbuf,*pixbuf2; + guchar *pixels1, *pixels2, *p1, *p2; + int x,y; + int rowstride1,rowstride2; + int n_channels1,n_channels2; + GdkRectangle or,ir; + struct graphics_gc_priv *bg=overlay->background_gc; + + if (parent->overlay_disabled || overlay->overlay_disabled || overlay->overlay_autodisabled) + return; + dbg(1,"r->x=%d r->y=%d r->width=%d r->height=%d\n", r->x, r->y, r->width, r->height); + overlay_rect(parent, overlay, 0, &or); + dbg(1,"or.x=%d or.y=%d or.width=%d or.height=%d\n", or.x, or.y, or.width, or.height); + if (! gdk_rectangle_intersect(r, &or, &ir)) + return; + or.x-=r->x; + or.y-=r->y; + pixbuf=gdk_pixbuf_get_from_drawable(NULL, overlay->drawable, NULL, 0, 0, 0, 0, or.width, or.height); + pixbuf2=gdk_pixbuf_new(gdk_pixbuf_get_colorspace(pixbuf), TRUE, gdk_pixbuf_get_bits_per_sample(pixbuf), + or.width, or.height); + rowstride1 = gdk_pixbuf_get_rowstride (pixbuf); + rowstride2 = gdk_pixbuf_get_rowstride (pixbuf2); + pixels1=gdk_pixbuf_get_pixels (pixbuf); + pixels2=gdk_pixbuf_get_pixels (pixbuf2); + n_channels1 = gdk_pixbuf_get_n_channels (pixbuf); + n_channels2 = gdk_pixbuf_get_n_channels (pixbuf2); + for (y = 0 ; y < or.height ; y++) { + for (x = 0 ; x < or.width ; x++) { + p1 = pixels1 + y * rowstride1 + x * n_channels1; + p2 = pixels2 + y * rowstride2 + x * n_channels2; + p2[0]=p1[0]; + p2[1]=p1[1]; + p2[2]=p1[2]; + if (bg && p1[0] == bg->r && p1[1] == bg->g && p1[2] == bg->b) + p2[3]=bg->a; + else + p2[3]=overlay->a; + } + } + gdk_draw_pixbuf(pixmap, gc, pixbuf2, 0, 0, or.x, or.y, or.width, or.height, GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref(pixbuf); + g_object_unref(pixbuf2); +} + +static void +draw_restore(struct graphics_priv *gr, struct point *p, int w, int h) +{ + GtkWidget *widget=gr->widget; + gdk_draw_drawable(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE(widget)], + gr->drawable, + p->x, p->y, p->x, p->y, w, h); + +} + +static void +draw_drag(struct graphics_priv *gr, struct point *p) +{ + if (!gr->cleanup) { + gr->pclean=gr->p; + gr->cleanup=1; + } + 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=gc; +} + +static void +gtk_drawing_area_draw(struct graphics_priv *gr, GdkRectangle *r) +{ + GdkPixmap *pixmap; + GtkWidget *widget=gr->widget; + GdkGC *gc=widget->style->fg_gc[GTK_WIDGET_STATE(widget)]; + struct graphics_priv *overlay; + + if (! gr->drawable) + return; + pixmap = gdk_pixmap_new(widget->window, r->width, r->height, -1); + if ((gr->p.x || gr->p.y) && gr->background_gc) + gdk_draw_rectangle(pixmap, gr->background_gc->gc, TRUE, 0, 0, r->width, r->height); + gdk_draw_drawable(pixmap, gc, gr->drawable, r->x, r->y, gr->p.x, gr->p.y, r->width, r->height); + overlay=gr->overlays; + while (overlay) { + overlay_draw(gr,overlay,r,pixmap,gc); + overlay=overlay->next; + } + gdk_draw_drawable(widget->window, gc, pixmap, 0, 0, r->x, r->y, r->width, r->height); + g_object_unref(pixmap); +} + +static void +draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) +{ + GdkRectangle r; + struct graphics_priv *overlay; +#if 0 + if (mode == draw_mode_begin) { + if (! gr->parent && gr->background_gc) + gdk_draw_rectangle(gr->drawable, gr->background_gc->gc, TRUE, 0, 0, gr->width, gr->height); + } +#endif + if (mode == draw_mode_end && gr->mode != draw_mode_cursor) { + if (gr->parent) { + if (gr->cleanup) { + overlay_rect(gr->parent, gr, 1, &r); + gtk_drawing_area_draw(gr->parent, &r); + gr->cleanup=0; + } + overlay_rect(gr->parent, gr, 0, &r); + gtk_drawing_area_draw(gr->parent, &r); + } else { + r.x=0; + r.y=0; + r.width=gr->width; + r.height=gr->height; + gtk_drawing_area_draw(gr, &r); + overlay=gr->overlays; + while (overlay) { + overlay->cleanup=0; + overlay=overlay->next; + } + } + } + gr->mode=mode; +} + +/* Events */ + +static gint +configure(GtkWidget * widget, GdkEventConfigure * event, gpointer user_data) +{ + struct graphics_priv *gra=user_data; + if (! gra->visible) + return TRUE; + if (gra->drawable != NULL) { + g_object_unref(gra->drawable); + } + if(gra->background_ready && gra->background != NULL) { + g_object_unref(gra->background); + gra->background_ready = 0; + } +#ifndef _WIN32 + dbg(1,"window=%d\n", GDK_WINDOW_XID(widget->window)); +#endif + gra->width=widget->allocation.width; + gra->height=widget->allocation.height; + gra->drawable = gdk_pixmap_new(widget->window, gra->width, gra->height, -1); + callback_list_call_attr_2(gra->cbl, attr_resize, (void *)gra->width, (void *)gra->height); + return TRUE; +} + +static gint +expose(GtkWidget * widget, GdkEventExpose * event, gpointer user_data) +{ + struct graphics_priv *gra=user_data; + + gra->visible=1; + if (! gra->drawable) + configure(widget, NULL, user_data); + gtk_drawing_area_draw(gra, &event->area); +#if 0 + gdk_draw_drawable(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], + gra->drawable, event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); +#endif + + return FALSE; +} + +#if 0 +static gint +button_timeout(gpointer user_data) +{ +#if 0 + struct container *co=user_data; + int x=co->gra->gra->button_event.x; + int y=co->gra->gra->button_event.y; + int button=co->gra->gra->button_event.button; + + co->gra->gra->button_timeout=0; + popup(co, x, y, button); + + return FALSE; +#endif +} +#endif + +static int +tv_delta(struct timeval *old, struct timeval *new) +{ + if (new->tv_sec-old->tv_sec >= INT_MAX/1000) + return INT_MAX; + return (new->tv_sec-old->tv_sec)*1000+(new->tv_usec-old->tv_usec)/1000; +} + +static gint +button_press(GtkWidget * widget, GdkEventButton * event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + struct point p; + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + + if (event->button < 8) { + if (tv_delta(&this->button_press[event->button], &tv) < 100) + return FALSE; + this->button_press[event->button]= tv; + this->button_release[event->button].tv_sec=0; + this->button_release[event->button].tv_usec=0; + } + p.x=event->x; + p.y=event->y; + callback_list_call_attr_3(this->cbl, attr_button, (void *)1, (void *)event->button, (void *)&p); + return FALSE; +} + +static gint +button_release(GtkWidget * widget, GdkEventButton * event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + struct point p; + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + + if (event->button < 8) { + if (tv_delta(&this->button_release[event->button], &tv) < 100) + return FALSE; + this->button_release[event->button]= tv; + this->button_press[event->button].tv_sec=0; + this->button_press[event->button].tv_usec=0; + } + p.x=event->x; + p.y=event->y; + callback_list_call_attr_3(this->cbl, attr_button, (void *)0, (void *)event->button, (void *)&p); + return FALSE; +} + + + +static gint +scroll(GtkWidget * widget, GdkEventScroll * event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + struct point p; + int button; + + p.x=event->x; + p.y=event->y; + switch (event->direction) { + case GDK_SCROLL_UP: + button=4; + break; + case GDK_SCROLL_DOWN: + button=5; + break; + default: + button=-1; + break; + } + if (button != -1) { + callback_list_call_attr_3(this->cbl, attr_button, (void *)1, (void *)button, (void *)&p); + callback_list_call_attr_3(this->cbl, attr_button, (void *)0, (void *)button, (void *)&p); + } + return FALSE; +} + +static gint +motion_notify(GtkWidget * widget, GdkEventMotion * event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + struct point p; + + p.x=event->x; + p.y=event->y; + callback_list_call_attr_1(this->cbl, attr_motion, (void *)&p); + return FALSE; +} + +/* * + * * Exit navit (X pressed) + * * @param widget active widget + * * @param event the event (delete_event) + * * @param nav our Navit context + * * @returns TRUE + * */ +static gint +delete(GtkWidget *widget, GdkEventKey *event, struct navit *nav) +{ + navit_destroy(nav); + return TRUE; +} + +static gint +keypress(GtkWidget *widget, GdkEventKey *event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + int len,ucode; + char key[8]; + ucode=gdk_keyval_to_unicode(event->keyval); + len=g_unichar_to_utf8(ucode, key); + key[len]='\0'; + + switch (event->keyval) { + case GDK_Up: + key[0]=NAVIT_KEY_UP; + key[1]='\0'; + break; + case GDK_Down: + key[0]=NAVIT_KEY_DOWN; + key[1]='\0'; + break; + case GDK_Left: + key[0]=NAVIT_KEY_LEFT; + key[1]='\0'; + break; + case GDK_Right: + key[0]=NAVIT_KEY_RIGHT; + key[1]='\0'; + break; + case GDK_BackSpace: + key[0]=NAVIT_KEY_BACKSPACE; + key[1]='\0'; + break; + case GDK_Return: + case GDK_KP_Enter: + key[0]=NAVIT_KEY_RETURN; + key[1]='\0'; + break; + case GDK_Book: +#ifdef USE_HILDON + case GDK_F7: +#endif + key[0]=NAVIT_KEY_ZOOM_IN; + key[1]='\0'; + break; + case GDK_Calendar: +#ifdef USE_HILDON + case GDK_F8: +#endif + key[0]=NAVIT_KEY_ZOOM_OUT; + key[1]='\0'; + break; + } + if (key[0]) + callback_list_call_attr_1(this->cbl, attr_keypress, (void *)key); + else + dbg(0,"keyval 0x%x\n", event->keyval); + + return FALSE; +} + +static struct graphics_priv *graphics_gtk_drawing_area_new_helper(struct graphics_methods *meth); + +static void +overlay_disable(struct graphics_priv *gr, int disabled) +{ + gr->overlay_disabled=disabled; +} + +static void +overlay_resize(struct graphics_priv *this, struct point *p, int w, int h, int alpha, int wraparound) +{ + int changed = 0; + int w2,h2; + + if (w == 0) { + w2 = 1; + } else { + w2 = w; + } + + if (h == 0) { + h2 = 1; + } else { + h2 = h; + } + + this->p = *p; + if (this->width != w2) { + this->width = w2; + changed = 1; + } + + if (this->height != h2) { + this->height = h2; + changed = 1; + } + + this->a = alpha >> 8; + this->wraparound = wraparound; + + if (changed) { + // Set the drawables to the right sizes + g_object_unref(this->drawable); + g_object_unref(this->background); + + this->drawable=gdk_pixmap_new(this->parent->widget->window, w2, h2, -1); + this->background=gdk_pixmap_new(this->parent->widget->window, w2, h2, -1); + + if ((w == 0) || (h == 0)) { + this->overlay_autodisabled = 1; + } else { + this->overlay_autodisabled = 0; + } + + callback_list_call_attr_2(this->cbl, attr_resize, (void *)this->width, (void *)this->height); + } +} + +static struct graphics_priv * +overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int alpha, int wraparound) +{ + int w2,h2; + struct graphics_priv *this=graphics_gtk_drawing_area_new_helper(meth); + this->colormap=gr->colormap; + this->widget=gr->widget; + this->p=*p; + this->width=w; + this->height=h; + this->parent=gr; + + /* If either height or width is 0, we set it to 1 to avoid warnings, and + * disable the overlay. */ + if (h == 0) { + h2 = 1; + } else { + h2 = h; + } + + if (w == 0) { + w2 = 1; + } else { + w2 = w; + } + + this->background=gdk_pixmap_new(gr->widget->window, w2, h2, -1); + this->drawable=gdk_pixmap_new(gr->widget->window, w2, h2, -1); + + if ((w == 0) || (h == 0)) { + this->overlay_autodisabled = 1; + } else { + this->overlay_autodisabled = 0; + } + + this->next=gr->overlays; + this->a=alpha >> 8; + this->wraparound=wraparound; + gr->overlays=this; + return this; +} + +static int gtk_argc; +static char **gtk_argv={NULL}; + + +static int +graphics_gtk_drawing_area_fullscreen(struct window *w, int on) +{ + struct graphics_priv *gr=w->priv; + if (on) + gtk_window_fullscreen(GTK_WINDOW(gr->win)); + else + gtk_window_unfullscreen(GTK_WINDOW(gr->win)); + return 1; +} + +static void +graphics_gtk_drawing_area_disable_suspend(struct window *w) +{ + struct graphics_priv *gr=w->priv; + +#ifndef _WIN32 + if (gr->pid) + kill(gr->pid, SIGWINCH); +#else + dbg(1, "failed to kill() under Windows\n"); +#endif +} + +static void * +get_data(struct graphics_priv *this, char *type) +{ + FILE *f; + if (!strcmp(type,"gtk_widget")) + return this->widget; +#ifndef _WIN32 + if (!strcmp(type,"xwindow_id")) + return (void *)GDK_WINDOW_XID(this->widget->window); +#endif + if (!strcmp(type,"window")) { + char *cp = getenv("NAVIT_XID"); + unsigned xid = 0; + if (cp) + xid = strtol(cp, NULL, 0); + if (!xid) + this->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + else + this->win = gtk_plug_new(xid); + gtk_window_set_default_size(GTK_WINDOW(this->win), this->win_w, this->win_h); + dbg(1,"h= %i, w= %i\n",this->win_h, this->win_w); + gtk_window_set_title(GTK_WINDOW(this->win), "Navit"); + gtk_window_set_wmclass (GTK_WINDOW (this->win), "navit", "Navit"); + gtk_widget_realize(this->win); + gtk_container_add(GTK_CONTAINER(this->win), this->widget); + gtk_widget_show_all(this->win); + GTK_WIDGET_SET_FLAGS (this->widget, GTK_CAN_FOCUS); + gtk_widget_set_sensitive(this->widget, TRUE); + gtk_widget_grab_focus(this->widget); + g_signal_connect(G_OBJECT(this->widget), "key-press-event", G_CALLBACK(keypress), this); + g_signal_connect(G_OBJECT(this->win), "delete_event", G_CALLBACK(delete), this->nav); + this->window.fullscreen=graphics_gtk_drawing_area_fullscreen; + this->window.disable_suspend=graphics_gtk_drawing_area_disable_suspend; + this->window.priv=this; +#if !defined(_WIN32) && !defined(__CEGCC__) + f=popen("pidof /usr/bin/ipaq-sleep","r"); + if (f) { + fscanf(f,"%d",&this->pid); + dbg(1,"ipaq_sleep pid=%d\n", this->pid); + pclose(f); + } +#endif + return &this->window; + } + return NULL; +} + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, +#ifdef HAVE_IMLIB2 + draw_image_warp, +#else + NULL, +#endif + draw_restore, + draw_drag, + NULL, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, + image_free, + NULL, + overlay_disable, + overlay_resize, +}; + +static struct graphics_priv * +graphics_gtk_drawing_area_new_helper(struct graphics_methods *meth) +{ + struct font_priv * (*font_freetype_new)(void *meth); + font_freetype_new=plugin_get_font_type("freetype"); + if (!font_freetype_new) + return NULL; + struct graphics_priv *this=g_new0(struct graphics_priv,1); + font_freetype_new(&this->freetype_methods); + *meth=graphics_methods; + meth->font_new=(struct graphics_font_priv *(*)(struct graphics_priv *, struct graphics_font_methods *, char *, int, int))this->freetype_methods.font_new; + meth->get_text_bbox=this->freetype_methods.get_text_bbox; + + return this; +} + +static struct graphics_priv * +graphics_gtk_drawing_area_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl) +{ + int i; + GtkWidget *draw; + struct attr *attr; + + if (! event_request_system("glib","graphics_gtk_drawing_area_new")) + return NULL; + + draw=gtk_drawing_area_new(); + struct graphics_priv *this=graphics_gtk_drawing_area_new_helper(meth); + this->nav = nav; + this->widget=draw; + this->win_w=792; + if ((attr=attr_search(attrs, NULL, attr_w))) + this->win_w=attr->u.num; + this->win_h=547; + if ((attr=attr_search(attrs, NULL, attr_h))) + this->win_h=attr->u.num; + this->cbl=cbl; + this->colormap=gdk_colormap_new(gdk_visual_get_system(),FALSE); + gtk_widget_set_events(draw, GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_POINTER_MOTION_MASK|GDK_KEY_PRESS_MASK); + g_signal_connect(G_OBJECT(draw), "expose_event", G_CALLBACK(expose), this); + g_signal_connect(G_OBJECT(draw), "configure_event", G_CALLBACK(configure), this); + g_signal_connect(G_OBJECT(draw), "button_press_event", G_CALLBACK(button_press), this); + g_signal_connect(G_OBJECT(draw), "button_release_event", G_CALLBACK(button_release), this); + g_signal_connect(G_OBJECT(draw), "scroll_event", G_CALLBACK(scroll), this); + g_signal_connect(G_OBJECT(draw), "motion_notify_event", G_CALLBACK(motion_notify), this); + g_signal_connect(G_OBJECT(draw), "delete_event", G_CALLBACK(delete), nav); + + for (i = 0; i < 8; i++) { + this->button_press[i].tv_sec = 0; + this->button_press[i].tv_usec = 0; + this->button_release[i].tv_sec = 0; + this->button_release[i].tv_usec = 0; + } + + return this; +} + +void +plugin_init(void) +{ + gtk_init(>k_argc, >k_argv); + gtk_set_locale(); + plugin_register_graphics_type("gtk_drawing_area", graphics_gtk_drawing_area_new); +} diff --git a/graphics/gtk_gl_ext/graphics_gtk_gl_ext.c b/graphics/gtk_gl_ext/graphics_gtk_gl_ext.c new file mode 100644 index 00000000..7c94e0bd --- /dev/null +++ b/graphics/gtk_gl_ext/graphics_gtk_gl_ext.c @@ -0,0 +1,352 @@ +/** + * 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 <stdlib.h> +#include <gtk/gtk.h> +#include <gtk/gtkgl.h> +#include <GL/gl.h> +#include <GL/glu.h> +#include "point.h" +#include "graphics.h" +#include "container.h" + + +struct graphics_gra { + GtkWidget *widget; + int width; + int height; + int library_init; + int visible; + int buffer; +}; + +struct graphics_font { +}; + +struct graphics_gc { + double fr,fg,fb; + double br,bg,bb; + double width; + struct graphics_gra *gra; +}; + +static struct graphics_font *font_new(struct graphics *gr, int size) +{ + struct graphics_font *font=g_new(struct graphics_font, 1); + return font; +} + +static struct graphics_gc *gc_new(struct graphics *gr) +{ + struct graphics_gc *gc=g_new(struct graphics_gc, 1); + + gc->fr=1; + gc->fg=1; + gc->fb=1; + gc->br=0; + gc->bg=0; + gc->bb=0; + gc->width=1; + gc->gra=gr->gra; + return gc; +} + +static void +gc_set_linewidth(struct graphics_gc *gc, int w) +{ + gc->width=w; +} + +static void +gc_set_foreground(struct graphics_gc *gc, int r, int g, int b) +{ + gc->fr=r/65535.0; + gc->fg=g/65535.0; + gc->fb=b/65535.0; +} + +static void +gc_set_background(struct graphics_gc *gc, int r, int g, int b) +{ + gc->br=r/65535.0; + gc->bg=g/65535.0; + gc->bb=b/65535.0; +} + +static void +vertex(struct point *p) +{ + double x,y; + x=p->x; + y=p->y; + x/=792; + y/=469; + x-=0.5; + y=0.5-y; + glVertex3f(x,y,0); +} + +static void +draw_lines(struct graphics *gr, struct graphics_gc *gc, struct point *p, int count) +{ + int i; + + glLineWidth(gc->width); + glColor3f(gc->fr, gc->fg, gc->fb); + glBegin(GL_LINE_STRIP); + for (i=0 ; i < count ; i++) + vertex(p++); + glEnd(); +} + +static void +draw_polygon(struct graphics *gr, struct graphics_gc *gc, struct point *p, int count) +{ + int i; + double x,y; + glColor3f(gc->fr, gc->fg, gc->fb); + glBegin(GL_POLYGON); + for (i=0 ; i < count ; i++) + vertex(p++); + glEnd(); +} + + +static void +draw_circle(struct graphics *gr, struct graphics_gc *gc, struct point *p, int r) +{ + +} + +static void +draw_text(struct graphics *gr, struct graphics_gc *fg, struct graphics_gc *bg, struct graphics_font *font, unsigned char *text, int x, int y, int dx, int dy) +{ +} + +static void +draw_begin(struct graphics *gr) +{ + printf("draw_begin\n"); + glClearColor(gr->gc[0]->br, gr->gc[0]->bg, gr->gc[0]->bb, 0); + glNewList(1, GL_COMPILE); + gr->gra->buffer=1; +} + +static void +draw_end(struct graphics *gr) +{ + printf("draw_end\n"); + glEndList(); + gr->gra->buffer=0; +} + +static void realize(GtkWidget * widget, gpointer data) +{ + GdkGLContext *glcontext = gtk_widget_get_gl_context(widget); + GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget); + + GLUquadricObj *qobj; + static GLfloat light_diffuse[] = { 1.0, 0.0, 0.0, 1.0 }; + static GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; + + /*** OpenGL BEGIN ***/ + if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) + return; + + qobj = gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); +#if 0 + glNewList(1, GL_COMPILE); + gluSphere(qobj, 1.0, 20, 20); + glBegin(GL_LINE_STRIP); + glVertex3f(0.0,0.1,0.0); + glVertex3f(0.1,0.1,0.0); + glVertex3f(0.1,0.2,0.0); + glVertex3f(0.2,0.2,0.0); + glEnd(); + glEndList(); +#endif + +#if 0 + glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT0, GL_POSITION, light_position); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_DEPTH_TEST); +#endif + + glClearColor(1.0, 1.0, 1.0, 1.0); + glClearDepth(1.0); + + glViewport(0, 0, + widget->allocation.width, widget->allocation.height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(19.0, 1.0, 1.0, 10.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt(0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); + glTranslatef(0.0, 0.0, 0.0); + + gdk_gl_drawable_gl_end(gldrawable); + /*** OpenGL END ***/ +} + +static gboolean +configure(GtkWidget * widget, GdkEventConfigure * event, gpointer user_data) +{ + GdkGLContext *glcontext = gtk_widget_get_gl_context(widget); + GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget); + struct container *co=user_data; + struct graphics_gra *gra=co->gra->gra; + + + printf("configure %d %d\n",gra->width, gra->height); + gra->width=widget->allocation.width; + gra->height=widget->allocation.height; + + /*** OpenGL BEGIN ***/ + if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) + return FALSE; + + glViewport(0, 0, + widget->allocation.width, widget->allocation.height); + + gdk_gl_drawable_gl_end(gldrawable); + /*** OpenGL END ***/ + if (gra->visible) + graphics_resize(co, gra->width, gra->height); + + return TRUE; +} + +static gboolean +expose(GtkWidget * widget, GdkEventExpose * event, gpointer user_data) +{ + GdkGLContext *glcontext = gtk_widget_get_gl_context(widget); + GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget); + struct container *co=user_data; + struct graphics_gra *gra=co->gra->gra; + + printf("expose\n"); + if (! gra->visible) { + gra->visible=1; + configure(widget, NULL, user_data); + } + /*** OpenGL BEGIN ***/ + if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) + return FALSE; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glCallList(1); + + if (gdk_gl_drawable_is_double_buffered(gldrawable)) + gdk_gl_drawable_swap_buffers(gldrawable); + else + glFlush(); + + gdk_gl_drawable_gl_end(gldrawable); + /*** OpenGL END ***/ + + return TRUE; +} + + +struct graphics * +graphics_gtk_gl_area_new(struct container *co, GtkWidget **widget) +{ + GdkGLConfig *glconfig; + gint major, minor; + GtkWidget *drawing_area; + + struct graphics *this=g_new0(struct graphics,1); + this->draw_lines=draw_lines; + this->draw_polygon=draw_polygon; + this->draw_circle=draw_circle; + this->draw_text=draw_text; +#if 0 + this->draw_begin=draw_begin; + this->draw_end=draw_end; +#endif + this->gc_new=gc_new; + this->gc_set_linewidth=gc_set_linewidth; + this->gc_set_foreground=gc_set_foreground; + this->gc_set_background=gc_set_background; + this->font_new=font_new; + this->gra=g_new0(struct graphics_gra, 1); + + /* + * Init GtkGLExt. + */ + + gtk_gl_init(NULL, NULL); + + /* + * Query OpenGL extension version. + */ + + gdk_gl_query_version(&major, &minor); + g_print("OpenGL extension version - %d.%d\n", major, minor); + + /* + * Configure OpenGL-capable visual. + */ + + /* Try double-buffered visual */ + glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | + GDK_GL_MODE_DEPTH | + GDK_GL_MODE_DOUBLE); + if (glconfig == NULL) { + g_print("*** Cannot find the double-buffered visual.\n"); + g_print("*** Trying single-buffered visual.\n"); + + /* Try single-buffered visual */ + glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | + GDK_GL_MODE_DEPTH); + if (glconfig == NULL) { + g_print + ("*** No appropriate OpenGL-capable visual found.\n"); + exit(1); + } + } + + + drawing_area = gtk_drawing_area_new(); + + /* Set OpenGL-capability to the widget. */ + gtk_widget_set_gl_capability(drawing_area, + glconfig, + NULL, TRUE, GDK_GL_RGBA_TYPE); + + g_signal_connect_after(G_OBJECT(drawing_area), "realize", + G_CALLBACK(realize), NULL); + g_signal_connect(G_OBJECT(drawing_area), "configure_event", + G_CALLBACK(configure), co); + g_signal_connect(G_OBJECT(drawing_area), "expose_event", + G_CALLBACK(expose), co); + + *widget=drawing_area; + this->gra->widget=drawing_area; + return this; +} + + diff --git a/graphics/null/Makefile.am b/graphics/null/Makefile.am new file mode 100644 index 00000000..9fe6ffc1 --- /dev/null +++ b/graphics/null/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=graphics_null +if PLUGINS +modulegraphics_LTLIBRARIES = libgraphics_null.la +else +noinst_LTLIBRARIES = libgraphics_null.la +endif +libgraphics_null_la_SOURCES = graphics_null.c +libgraphics_null_la_LDFLAGS = -module -avoid-version diff --git a/graphics/null/graphics_null.c b/graphics/null/graphics_null.c new file mode 100644 index 00000000..f1936475 --- /dev/null +++ b/graphics/null/graphics_null.c @@ -0,0 +1,231 @@ +/** + * 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 <glib.h> +#include "config.h" +#include "point.h" +#include "graphics.h" +#include "color.h" +#include "plugin.h" + +static int dummy; +static struct graphics_priv { + int dummy; +} graphics_priv; + +static struct graphics_font_priv { + int dummy; +} graphics_font_priv; + +static struct graphics_gc_priv { + int dummy; +} graphics_gc_priv; + +static struct graphics_image_priv { + int dummy; +} graphics_image_priv; + +static void +graphics_destroy(struct graphics_priv *gr) +{ +} + +static void font_destroy(struct graphics_font_priv *font) +{ + +} + +static struct graphics_font_methods font_methods = { + font_destroy +}; + +static struct graphics_font_priv *font_new(struct graphics_priv *gr, struct graphics_font_methods *meth, char *font, int size, int flags) +{ + *meth=font_methods; + return &graphics_font_priv; +} + +static void +gc_destroy(struct graphics_gc_priv *gc) +{ +} + +static void +gc_set_linewidth(struct graphics_gc_priv *gc, int w) +{ +} + +static void +gc_set_dashes(struct graphics_gc_priv *gc, int w, int offset, unsigned char *dash_list, int n) +{ +} + +static void +gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) +{ +} + +static void +gc_set_background(struct graphics_gc_priv *gc, struct color *c) +{ +} + +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) +{ + *meth=gc_methods; + return &graphics_gc_priv; +} + +static struct graphics_image_priv * +image_new(struct graphics_priv *gr, struct graphics_image_methods *meth, char *path, int *w, int *h, struct point *hot, int rotation) +{ + return &graphics_image_priv; +} + +static void +draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ +} + +static void +draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ +} + +static void +draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) +{ +} + +static void +draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) +{ +} + + +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) +{ +} + +static void +draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) +{ +} + +static void +draw_image_warp(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, int count, char *data) +{ +} + +static void +draw_restore(struct graphics_priv *gr, struct point *p, int w, int h) +{ +} + +static void draw_drag(struct graphics_priv *gr, struct point *p) +{ +} + +static void +background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc) +{ +} + +static void +draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) +{ +} + +static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int alpha, int wraparound); + +static void * +get_data(struct graphics_priv *this, char *type) +{ + return &dummy; +} + +static void image_free(struct graphics_priv *gr, struct graphics_image_priv *priv) +{ +} + +static void get_text_bbox(struct graphics_priv *gr, struct graphics_font_priv *font, char *text, int dx, int dy, struct point *ret, int estimate) +{ +} + +static void overlay_disable(struct graphics_priv *gr, int disable) +{ +} + +static void overlay_resize(struct graphics_priv *gr, struct point *p, int w, int h, int alpha, int wraparound) +{ +} + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, + draw_image_warp, + draw_restore, + draw_drag, + font_new, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, + image_free, + get_text_bbox, + overlay_disable, + overlay_resize, +}; + +static struct graphics_priv * +overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int alpha, int wraparound) +{ + *meth=graphics_methods; + return &graphics_priv; +} + + +static struct graphics_priv * +graphics_null_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl) +{ + *meth=graphics_methods; + return &graphics_priv; +} + +void +plugin_init(void) +{ + plugin_register_graphics_type("null", graphics_null_new); +} diff --git a/graphics/opengl/Makefile.am b/graphics/opengl/Makefile.am new file mode 100644 index 00000000..02d356dd --- /dev/null +++ b/graphics/opengl/Makefile.am @@ -0,0 +1,6 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ @OPENGL_CFLAGS@ @GLC_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=graphics_opengl +modulegraphics_LTLIBRARIES = libgraphics_opengl.la +libgraphics_opengl_la_SOURCES = graphics_opengl.c +libgraphics_opengl_la_LIBADD = @OPENGL_LIBS@ @GLC_LIBS@ +libgraphics_opengl_la_LDFLAGS = -module -avoid-version diff --git a/graphics/opengl/graphics_opengl.c b/graphics/opengl/graphics_opengl.c new file mode 100644 index 00000000..a7163035 --- /dev/null +++ b/graphics/opengl/graphics_opengl.c @@ -0,0 +1,845 @@ +/** + * 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 <math.h> +#include <glib.h> +#include "config.h" +#include <GL/glc.h> +#include "point.h" +#include "graphics.h" +#include "color.h" +#include "plugin.h" + +#include "debug.h" + +#include <GL/glut.h> + + +void CALLBACK tessBeginCB(GLenum which); +void CALLBACK tessEndCB(); +void CALLBACK tessErrorCB(GLenum errorCode); +void CALLBACK tessVertexCB(const GLvoid *data); +void CALLBACK tessVertexCB2(const GLvoid *data); +void CALLBACK tessCombineCB(const GLdouble newVertex[3], const GLdouble *neighborVertex[4], + const GLfloat neighborWeight[4], GLdouble **outData); + + +struct graphics_priv { + int button_timeout; + struct point p; + int width; + int height; + int library_init; + int visible; + struct graphics_priv *parent; + struct graphics_priv *overlays; + struct graphics_priv *next; + struct graphics_gc_priv *background_gc; + enum draw_mode_num mode; + void (*resize_callback)(void *data, int w, int h); + void *resize_callback_data; + void (*motion_callback)(void *data, struct point *p); + void *motion_callback_data; + void (*button_callback)(void *data, int press, int button, struct point *p); + void *button_callback_data; + GLuint DLid; +}; + +struct graphics_font_priv { +#if 0 + FT_Face face; +#endif +}; + +struct graphics_gc_priv { + struct graphics_priv *gr; + float fr,fg,fb,fa; + float br,bg,bb,ba; + int linewidth; +}; + +struct graphics_image_priv { + int w; + int h; +}; + +static void +graphics_destroy(struct graphics_priv *gr) +{ +} + +int frame=0; + +// http://quesoglc.sourceforge.net/tutorial.php + + +static void font_destroy(struct graphics_font_priv *font) +{ + g_free(font); + /* TODO: free font->face */ +} + +static struct graphics_font_methods font_methods = { + font_destroy +}; + +static struct graphics_font_priv *font_new(struct graphics_priv *gr, struct graphics_font_methods *meth, char *fontfamily, int size) +{ +#if 0 + char **filename=fontlist; + struct graphics_font_priv *font=g_new(struct graphics_font_priv, 1); + + *meth=font_methods; + + if (!gr->library_init) { + FT_Init_FreeType( &gr->library ); + gr->library_init=1; + } + + while (*filename) { + if (!FT_New_Face( gr->library, *filename, 0, &font->face )) + break; + filename++; + } + if (! *filename) { + g_warning("Failed to load font, no labelling"); + g_free(font); + return NULL; + } + FT_Set_Char_Size(font->face, 0, size, 300, 300); + FT_Select_Charmap(font->face, FT_ENCODING_UNICODE); + return font; +#endif + return NULL; +} + +static void +gc_destroy(struct graphics_gc_priv *gc) +{ + g_free(gc); +} + +static void +gc_set_linewidth(struct graphics_gc_priv *gc, int w) +{ + gc->linewidth=w; +} + +static void +gc_set_dashes(struct graphics_gc_priv *gc, int width, int offset, unsigned char *dash_list, int n) +{ +#if 0 + gdk_gc_set_dashes(gc->gc, offset, (gint8 *)dash_list, n); + gdk_gc_set_line_attributes(gc->gc, width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND); +#endif +} + + +static void +gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) +{ + gc->fr=c->r/65535.0; + gc->fg=c->g/65535.0; + gc->fb=c->b/65535.0; + gc->fa=c->a/65535.0; +// printf("new alpha : %i\n",c->a); +} + +static void +gc_set_background(struct graphics_gc_priv *gc, struct color *c) +{ + gc->br=c->r/65535.0; + gc->bg=c->g/65535.0; + gc->bb=c->b/65535.0; + gc->ba=c->a/65535.0; +} + +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 *gc=g_new(struct graphics_gc_priv, 1); + + *meth=gc_methods; + gc->gr=gr; + gc->linewidth=1; + return gc; +} + + +static struct graphics_image_priv * +image_new(struct graphics_priv *gr, struct graphics_image_methods *meth, char *name, int *w, int *h) +{ +#if 0 + GdkPixbuf *pixbuf; + struct graphics_image_priv *ret; + + pixbuf=gdk_pixbuf_new_from_file(name, NULL); + if (! pixbuf) + return NULL; + ret=g_new0(struct graphics_image_priv, 1); + ret->pixbuf=pixbuf; + ret->w=gdk_pixbuf_get_width(pixbuf); + ret->h=gdk_pixbuf_get_height(pixbuf); + *w=ret->w; + *h=ret->h; + return ret; +#endif + return NULL; +} + +static void +draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + int i; +/* + if (gr->mode == draw_mode_begin || gr->mode == draw_mode_end) + gdk_draw_lines(gr->drawable, gc->gc, (GdkPoint *)p, count); + if (gr->mode == draw_mode_end || gr->mode == draw_mode_cursor) + gdk_draw_lines(gr->widget->window, gc->gc, (GdkPoint *)p, count); +*/ + /* + if(gr->mode == draw_mode_begin){ + printf("B"); + } else if (gr->mode == draw_mode_end){ + printf("E"); + } else { + printf("x"); + } +*/ + + for (i = 0 ; i < count-1 ; i++) { + +// glEnable( GL_POLYGON_SMOOTH ); +// glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); +// glEnable( GL_BLEND ); + + float dx=p[i+1].x-p[i].x; + float dy=p[i+1].y-p[i].y; + + float cx=(p[i+1].x+p[i].x)/2; + float cy=(p[i+1].y+p[i].y)/2; +// printf("(%lx,%lx) -> (%lx,%lx) : (%lx,%lx)\n",p[i].x,p[i].y,p[i+1].x,p[i+1].y,dx,dy); + + int w=round(sqrt(pow((dx),2)+pow((dy),2))); + + float angle=atan (dy/dx) * 180 / M_PI; + + glPushMatrix(); + glTranslatef(cx,cy,1); + // glColor4f( 0,0,0,1); + // glRasterPos2f( 1,1 ); + glRotatef(angle,0.0,0.0,1.0); + + glColor4f( gc->fr, gc->fg, gc->fb, gc->fa); + + int linewidth=gc->linewidth; + + glBegin( GL_POLYGON ); + glVertex2f( -w/2,-linewidth/2 ); + glVertex2f( -w/2-4,0 ); + glVertex2f( -w/2,+linewidth/2 ); + glVertex2f( +w/2,+linewidth/2 ); + glVertex2f( +w/2+4,0 ); + glVertex2f( +w/2,-linewidth/2 ); + glVertex2f( -w/2,+linewidth/2 ); + glEnd(); + + + // FIXME Roads label can maybe be drawn here, avoid the display_label loop, when playing with Z axis position. + /* + if(attr==1){ + glcRenderStyle(GLC_TEXTURE); + glColor3f(0., 0., 0.); + glScalef(12, 12, 0.); + glcRenderString(">>"); + } else if(attr==-1){ + glcRenderStyle(GLC_TEXTURE); + glColor3f(0., 0., 0.); + glScalef(12, 12, 0.); + glcRenderString("<<"); + } + + */ + glPopMatrix(); + } +// glDisable( GL_BLEND ); +// glDisable( GL_POLYGON_SMOOTH ); + +/* + if(label){ + if((strlen(label)*6)<w){ + SDL_print(label,cx, cy,-angle); + } + } +*/ +} + + +const char* getPrimitiveType(GLenum type) +{ + switch(type) + { + case 0x0000: + return "GL_POINTS"; + break; + case 0x0001: + return "GL_LINES"; + break; + case 0x0002: + return "GL_LINE_LOOP"; + break; + case 0x0003: + return "GL_LINE_STRIP"; + break; + case 0x0004: + return "GL_TRIANGLES"; + break; + case 0x0005: + return "GL_TRIANGLE_STRIP"; + break; + case 0x0006: + return "GL_TRIANGLE_FAN"; + break; + case 0x0007: + return "GL_QUADS"; + break; + case 0x0008: + return "GL_QUAD_STRIP"; + break; + case 0x0009: + return "GL_POLYGON"; + break; + } +} + +void CALLBACK tessBeginCB(GLenum which) +{ + glBegin(which); + + dbg(1,"glBegin( %s );\n",getPrimitiveType(which)); +} + + + +void CALLBACK tessEndCB() +{ + glEnd(); + + dbg(1,"glEnd();\n"); +} + + + +void CALLBACK tessVertexCB(const GLvoid *data) +{ + // cast back to double type + const GLdouble *ptr = (const GLdouble*)data; + + glVertex3dv(ptr); + + dbg(1," glVertex3d();\n"); +} + + +static void +draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + int i; + + GLUtesselator *tess = gluNewTess(); // create a tessellator + if(!tess) return 0; // failed to create tessellation object, return 0 + + GLdouble quad1[count][3]; + for (i = 0 ; i < count ; i++) { + quad1[i][0]=(GLdouble)(p[i].x); + quad1[i][1]=(GLdouble)(p[i].y); + quad1[i][2]=0; + } + + + // register callback functions + gluTessCallback(tess, GLU_TESS_BEGIN, (void (*)(void))tessBeginCB); + gluTessCallback(tess, GLU_TESS_END, (void (*)(void))tessEndCB); + // gluTessCallback(tess, GLU_TESS_ERROR, (void (*)(void))tessErrorCB); + gluTessCallback(tess, GLU_TESS_VERTEX, (void (*)(void))tessVertexCB); + + // tessellate and compile a concave quad into display list + // gluTessVertex() takes 3 params: tess object, pointer to vertex coords, + // and pointer to vertex data to be passed to vertex callback. + // The second param is used only to perform tessellation, and the third + // param is the actual vertex data to draw. It is usually same as the second + // param, but It can be more than vertex coord, for example, color, normal + // and UV coords which are needed for actual drawing. + // Here, we are looking at only vertex coods, so the 2nd and 3rd params are + // pointing same address. + glColor4f( gc->fr, gc->fg, gc->fb, gc->fa); + gluTessBeginPolygon(tess, 0); // with NULL data + gluTessBeginContour(tess); + for (i = 0 ; i < count ; i++) { + gluTessVertex(tess, quad1[i], quad1[i]); + } + gluTessEndContour(tess); + gluTessEndPolygon(tess); + + gluDeleteTess(tess); // delete after tessellation + +} + +static void +draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) +{ +#if 0 + gdk_draw_rectangle(gr->drawable, gc->gc, TRUE, p->x, p->y, w, h); +#endif +} + +static void +draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) +{ +#if 0 + if (gr->mode == draw_mode_begin || gr->mode == draw_mode_end) + gdk_draw_arc(gr->drawable, gc->gc, FALSE, p->x-r/2, p->y-r/2, r, r, 0, 64*360); + if (gr->mode == draw_mode_end || gr->mode == draw_mode_cursor) + gdk_draw_arc(gr->widget->window, gc->gc, FALSE, p->x-r/2, p->y-r/2, r, r, 0, 64*360); +#endif +} + + + +void SDL_print(char * label,int x, int y, double angle) +{ + glPushMatrix(); + glcRenderStyle(GLC_TEXTURE); + glColor4f(0,0,0,1); + glTranslatef(x, y, 1); + glRotatef(180,1,0,0); + glRotatef(angle,0,0,1); + + glScalef(14, 14, 14); + // FIXME : add some error checking : glcGetError() + glcRenderString(label); + glPopMatrix(); + +} + +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) +{ +// dbg(0,"%s : %i,%i, %f\n",text,dx,dy,(180*atan2(dx,dy)/3.14)); + SDL_print(text,p->x,p->y,(180*atan2(dx,dy)/3.14)-90); +} + +static void +draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) +{ +#if 0 + gdk_draw_pixbuf(gr->drawable, fg->gc, img->pixbuf, 0, 0, p->x, p->y, + img->w, img->h, GDK_RGB_DITHER_NONE, 0, 0); +#endif +} + +#ifdef HAVE_IMLIB2 +static void +draw_image_warp(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, int count, char *data) +{ +#if 0 + void *image; + int w,h; + printf("draw_image_warp data=%s\n", data); + image = imlib_load_image(data); + imlib_context_set_display(gdk_x11_drawable_get_xdisplay(gr->widget->window)); + imlib_context_set_colormap(gdk_x11_colormap_get_xcolormap(gtk_widget_get_colormap(gr->widget))); + imlib_context_set_visual(gdk_x11_visual_get_xvisual(gtk_widget_get_visual(gr->widget))); + imlib_context_set_drawable(gdk_x11_drawable_get_xid(gr->drawable)); + imlib_context_set_image(image); + w = imlib_image_get_width(); + h = imlib_image_get_height(); + if (count == 3) { + imlib_render_image_on_drawable_skewed(0, 0, w, h, p[0].x, p[0].y, p[1].x-p[0].x, p[1].y-p[0].y, p[2].x-p[0].x, p[2].y-p[0].y); + } + if (count == 2) { + imlib_render_image_on_drawable_skewed(0, 0, w, h, p[0].x, p[0].y, p[1].x-p[0].x, 0, 0, p[1].y-p[0].y); + } +#endif +} +#endif + +static void +overlay_draw(struct graphics_priv *parent, struct graphics_priv *overlay, int window) +{ +#if 0 + GdkPixbuf *pixbuf,*pixbuf2; + GtkWidget *widget=parent->widget; + guchar *pixels1, *pixels2, *p1, *p2; + int x,y; + int rowstride1,rowstride2; + int n_channels1,n_channels2; + + if (! parent->drawable) + return; + + pixbuf=gdk_pixbuf_get_from_drawable(NULL, overlay->drawable, NULL, 0, 0, 0, 0, overlay->width, overlay->height); + pixbuf2=gdk_pixbuf_new(gdk_pixbuf_get_colorspace(pixbuf), TRUE, gdk_pixbuf_get_bits_per_sample(pixbuf), + gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); + + rowstride1 = gdk_pixbuf_get_rowstride (pixbuf); + rowstride2 = gdk_pixbuf_get_rowstride (pixbuf2); + pixels1=gdk_pixbuf_get_pixels (pixbuf); + pixels2=gdk_pixbuf_get_pixels (pixbuf2); + n_channels1 = gdk_pixbuf_get_n_channels (pixbuf); + n_channels2 = gdk_pixbuf_get_n_channels (pixbuf2); + for (y = 0 ; y < overlay->height ; y++) { + for (x = 0 ; x < overlay->width ; x++) { + p1 = pixels1 + y * rowstride1 + x * n_channels1; + p2 = pixels2 + y * rowstride2 + x * n_channels2; + p2[0]=p1[0]; + p2[1]=p1[1]; + p2[2]=p1[2]; + p2[3]=127; + } + } + if (window) + gdk_draw_pixmap(parent->drawable, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], overlay->background, 0, 0, overlay->p.x, overlay->p.y, overlay->width, overlay->height); + else + gdk_draw_pixmap(overlay->background, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], parent->drawable, overlay->p.x, overlay->p.y, 0, 0, overlay->width, overlay->height); + gdk_draw_pixbuf(parent->drawable, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], pixbuf2, 0, 0, overlay->p.x, overlay->p.y, overlay->width, overlay->height, GDK_RGB_DITHER_NONE, 0, 0); + if (window) + gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], parent->drawable, overlay->p.x, overlay->p.y, overlay->p.x, overlay->p.y, overlay->width, overlay->height); +#if 0 + gdk_draw_pixmap(gr->gra->drawable, + gr->gra->widget->style->fg_gc[GTK_WIDGET_STATE(gr->gra->widget)], + img->gra->drawable, + 0, 0, p->x, p->y, img->gra->width, img->gra->height); +#endif +#endif +} + +static void +draw_restore(struct graphics_priv *gr, struct point *p, int w, int h) +{ +#if 0 + GtkWidget *widget=gr->widget; + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE(widget)], + gr->drawable, + p->x, p->y, p->x, p->y, w, h); +#endif + +} + +static void +background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc) +{ + gr->background_gc=gc; +} + +static void +draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) +{ + if (gr->DLid) { + if (mode == draw_mode_begin) + glNewList(gr->DLid,GL_COMPILE); + if (mode == draw_mode_end) + glEndList(); + } + +#if 0 + struct graphics_priv *overlay; + GtkWidget *widget=gr->widget; + + if (mode == draw_mode_begin) { + if (! gr->parent && gr->background_gc) + gdk_draw_rectangle(gr->drawable, gr->background_gc->gc, TRUE, 0, 0, gr->width, gr->height); + } + if (mode == draw_mode_end && gr->mode == draw_mode_begin) { + if (gr->parent) { + overlay_draw(gr->parent, gr, 1); + } else { + overlay=gr->overlays; + while (overlay) { + overlay_draw(gr, overlay, 0); + overlay=overlay->next; + } + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE(widget)], + gr->drawable, + 0, 0, 0, 0, gr->width, gr->height); + } + } + gr->mode=mode; +#endif +} + +#if 0 +/* Events */ + +static gint +configure(GtkWidget * widget, GdkEventConfigure * event, gpointer user_data) +{ + struct graphics_priv *gra=user_data; + if (! gra->visible) + return TRUE; + if (gra->drawable != NULL) { + gdk_pixmap_unref(gra->drawable); + } + gra->width=widget->allocation.width; + gra->height=widget->allocation.height; + gra->drawable = gdk_pixmap_new(widget->window, gra->width, gra->height, -1); + if (gra->resize_callback) + (*gra->resize_callback)(gra->resize_callback_data, gra->width, gra->height); + return TRUE; +} + +static gint +expose(GtkWidget * widget, GdkEventExpose * event, gpointer user_data) +{ + struct graphics_priv *gra=user_data; + + gra->visible=1; + if (! gra->drawable) + configure(widget, NULL, user_data); + gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], + gra->drawable, event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + + return FALSE; +} + +#if 0 +static gint +button_timeout(gpointer user_data) +{ +#if 0 + struct container *co=user_data; + int x=co->gra->gra->button_event.x; + int y=co->gra->gra->button_event.y; + int button=co->gra->gra->button_event.button; + + co->gra->gra->button_timeout=0; + popup(co, x, y, button); + + return FALSE; +#endif +} +#endif + +static gint +button_press(GtkWidget * widget, GdkEventButton * event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + struct point p; + + p.x=event->x; + p.y=event->y; + if (this->button_callback) + (*this->button_callback)(this->button_callback_data, 1, event->button, &p); + return FALSE; +} + +static gint +button_release(GtkWidget * widget, GdkEventButton * event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + struct point p; + + p.x=event->x; + p.y=event->y; + if (this->button_callback) + (*this->button_callback)(this->button_callback_data, 0, event->button, &p); + return FALSE; +} + +static gint +scroll(GtkWidget * widget, GdkEventScroll * event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + struct point p; + int button; + + p.x=event->x; + p.y=event->y; + if (this->button_callback) { + switch (event->direction) { + case GDK_SCROLL_UP: + button=4; + break; + case GDK_SCROLL_DOWN: + button=5; + break; + default: + button=-1; + break; + } + if (button != -1) { + (*this->button_callback)(this->button_callback_data, 1, button, &p); + (*this->button_callback)(this->button_callback_data, 0, button, &p); + } + } + return FALSE; +} + +static gint +motion_notify(GtkWidget * widget, GdkEventMotion * event, gpointer user_data) +{ + struct graphics_priv *this=user_data; + struct point p; + + p.x=event->x; + p.y=event->y; + if (this->motion_callback) + (*this->motion_callback)(this->motion_callback_data, &p); + return FALSE; +} + +#endif + + +static struct graphics_priv * +overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h) +{ +#if 0 + struct graphics_priv *this=graphics_gtk_drawing_area_new_helper(meth); + this->drawable=gdk_pixmap_new(gr->widget->window, w, h, -1); + this->colormap=gr->colormap; + this->widget=gr->widget; + this->p=*p; + this->width=w; + this->height=h; + this->parent=gr; + this->background=gdk_pixmap_new(gr->widget->window, w, h, -1); + this->next=gr->overlays; + gr->overlays=this; + return this; +#endif + return NULL; +} + +static void * +get_data(struct graphics_priv *this, char *type) +{ + if (strcmp(type,"opengl_displaylist")) + return NULL; +#if 0 + dbg(1,"Creating the DL from driver\n"); + this->DLid = glGenLists(1); +#endif + return &this->DLid; +} + +static void +register_resize_callback(struct graphics_priv *this, void (*callback)(void *data, int w, int h), void *data) +{ + this->resize_callback=callback; + this->resize_callback_data=data; +} + +static void +register_motion_callback(struct graphics_priv *this, void (*callback)(void *data, struct point *p), void *data) +{ + this->motion_callback=callback; + this->motion_callback_data=data; +} + +static void +register_button_callback(struct graphics_priv *this, void (*callback)(void *data, int press, int button, struct point *p), void *data) +{ + this->button_callback=callback; + this->button_callback_data=data; +} + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, +#ifdef HAVE_IMLIB2 + draw_image_warp, +#else + NULL, +#endif + draw_restore, + font_new, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, + register_resize_callback, + register_button_callback, + register_motion_callback, + NULL, // image_free +}; + +static struct graphics_priv * +graphics_opengl_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs) +{ + struct graphics_priv *this=g_new0(struct graphics_priv,1); + *meth=graphics_methods; + +// GtkWidget *draw; + +// draw=gtk_drawnig_area_new(); + + + // Initialize the fonts + int ctx = 0; + int font = 0; + ctx = glcGenContext(); + glcContext(ctx); + font = glcNewFontFromFamily(glcGenFontID(), "Verdana"); + glcFont(font); + glcStringType(GLC_UTF8_QSO); +// glcFontFace(font, "Italic"); + + +/* + this->widget=draw; + + this->colormap=gdk_colormap_new(gdk_visual_get_system(),FALSE); + gtk_widget_set_events(draw, GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_POINTER_MOTION_MASK|GDK_KEY_PRESS_MASK); + g_signal_connect(G_OBJECT(draw), "expose_event", G_CALLBACK(expose), this); + g_signal_connect(G_OBJECT(draw), "configure_event", G_CALLBACK(configure), this); +#if 0 + g_signal_connect(G_OBJECT(draw), "realize_event", G_CALLBACK(realize), co); +#endif + g_signal_connect(G_OBJECT(draw), "button_press_event", G_CALLBACK(button_press), this); + g_signal_connect(G_OBJECT(draw), "button_release_event", G_CALLBACK(button_release), this); + g_signal_connect(G_OBJECT(draw), "scroll_event", G_CALLBACK(scroll), this); + g_signal_connect(G_OBJECT(draw), "motion_notify_event", G_CALLBACK(motion_notify), this); + */ + return this; +} + +void +plugin_init(void) +{ + plugin_register_graphics_type("opengl", graphics_opengl_new); +} diff --git a/graphics/qt_qpainter/Makefile.am b/graphics/qt_qpainter/Makefile.am new file mode 100644 index 00000000..b222328e --- /dev/null +++ b/graphics/qt_qpainter/Makefile.am @@ -0,0 +1,10 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @QT_GUI_CFLAGS@ @NAVIT_CFLAGS@ -I$(top_srcdir) -I$(top_srcdir)/navit -DMODULE=graphics_qt_qpainter +modulegraphics_LTLIBRARIES = libgraphics_qt_qpainter.la +libgraphics_qt_qpainter_la_SOURCES = graphics_qt_qpainter.cpp graphics_qt_qpainter.moc +libgraphics_qt_qpainter_la_LDFLAGS = @QT_GUI_LIBS@ -module -avoid-version +if GRAPHICS_QT_QPAINTER +BUILT_SOURCES = graphics_qt_qpainter.moc +graphics_qt_qpainter.moc: + @MOC@ $(srcdir)/graphics_qt_qpainter.cpp >graphics_qt_qpainter.moc +endif diff --git a/graphics/qt_qpainter/graphics_qt_qpainter.cpp b/graphics/qt_qpainter/graphics_qt_qpainter.cpp new file mode 100644 index 00000000..9c05e8e8 --- /dev/null +++ b/graphics/qt_qpainter/graphics_qt_qpainter.cpp @@ -0,0 +1,1214 @@ +/** + * 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. + */ + +//############################################################################################################## +//# +//# File: graphics_qt_qpainter.cpp +//# Description: Graphics interface for internal GUI using Qt (Trolltech.com) +//# Comment: +//# Authors: Martin Schaller (04/2008), Stefan Klumpp (04/2008) +//# +//############################################################################################################## + + +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include "config.h" +#include "point.h" +#include "item.h" +#include "graphics.h" +#include "color.h" +#include "debug.h" +#include "plugin.h" +#include "callback.h" +#include "event.h" +#include "window.h" +#include "keys.h" +#include "navit/font/freetype/font_freetype.h" + +#include <qglobal.h> +#if QT_VERSION < 0x040000 +#include <qwidget.h> +#include <qapplication.h> +#include <qpainter.h> +#include <qpen.h> +#include <qbrush.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qlistview.h> +#include <qobject.h> +#include <qsocketnotifier.h> +#ifdef HAVE_QPE +#include <qpe/qpeapplication.h> +#endif +#else +#include <QResizeEvent> +#include <QApplication> +#include <QGraphicsScene> +#include <QGraphicsView> +#include <QPainter> +#include <QPen> +#include <QBrush> +#include <QPixmap> +#include <QWidget> +#include <QPolygonF> +#include <QtGui> +#endif + + +//############################################################################################################## +//# Description: RenderArea (QWidget) class for the main window (map, menu, ...) +//# Comment: +//# Authors: Martin Schaller (04/2008), Stefan Klumpp (04/2008) +//############################################################################################################## +class RenderArea : public QWidget + { + Q_OBJECT + public: + RenderArea(struct graphics_priv *priv, QWidget *parent = 0, int w=800, int h=800, int overlay=0); + QPixmap *pixmap; + struct callback_list *cbl; + struct graphics_priv *gra; + +#if QT_VERSION < 0x040000 + GHashTable *timer_type; + GHashTable *timer_callback; + GHashTable *watches; +#endif + protected: + int is_overlay; + QSize sizeHint() const; + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + void mouseEvent(int pressed, QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void wheelEvent(QWheelEvent *event); + void keyPressEvent(QKeyEvent *event); +#if QT_VERSION < 0x040000 + void timerEvent(QTimerEvent *event); +#endif + protected slots: + void watchEvent(int fd); + }; + + +#include "graphics_qt_qpainter.moc" + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +struct graphics_gc_priv { + QPen *pen; + QBrush *brush; + struct color c; +}; + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +struct graphics_priv { +#ifdef HAVE_QPE + QPEApplication *app; +#else + QApplication *app; +#endif + RenderArea *widget; + QPainter *painter; + struct graphics_gc_priv *background_gc; + unsigned char rgba[4]; + enum draw_mode_num mode; + struct graphics_priv *parent,*overlays,*next; + struct point p,pclean; + int cleanup; + int overlay_disable; + int wraparound; + struct font_priv * (*font_freetype_new)(void *meth); + struct font_freetype_methods freetype_methods; + int w,h; +}; + +static void +overlay_rect(struct graphics_priv *parent, struct graphics_priv *overlay, int clean, QRect *r) +{ + struct point p; + int w,h; + if (clean) { + p=overlay->pclean; + } else { + p=overlay->p;; + } + w=overlay->widget->pixmap->width(); + h=overlay->widget->pixmap->height(); + if (overlay->wraparound) { + if (p.x < 0) + p.x+=parent->widget->pixmap->width(); + if (p.y < 0) + p.y+=parent->widget->pixmap->height(); + if (w < 0) + w += parent->widget->pixmap->width(); + if (h < 0) + h += parent->widget->pixmap->height(); + } + r->setRect(p.x, p.y, w, h); +} + +static void +qt_qpainter_draw(struct graphics_priv *gr, const QRect *r, int paintev) +{ + if (!paintev) { + dbg(1,"update %d,%d %d x %d\n", r->x(), r->y(), r->width(), r->height()); + if (r->x() <= -r->width()) + return; + if (r->y() <= -r->height()) + return; + if (r->x() > gr->widget->pixmap->width()) + return; + if (r->y() > gr->widget->pixmap->height()) + return; + dbg(1,"update valid %d,%d %dx%d\n", r->x(), r->y(), r->width(), r->height()); + gr->widget->update(*r); + return; + } + QPixmap pixmap(r->width(),r->height()); + QPainter painter(&pixmap); + struct graphics_priv *overlay=NULL; + if (! gr->overlay_disable) + overlay=gr->overlays; + if ((gr->p.x || gr->p.y) && gr->background_gc) { + painter.setPen(*gr->background_gc->pen); + painter.fillRect(0, 0, gr->widget->pixmap->width(), gr->widget->pixmap->height(), *gr->background_gc->brush); + } + painter.drawPixmap(QPoint(gr->p.x,gr->p.y), *gr->widget->pixmap, *r); + while (overlay) { + QRect ovr; + overlay_rect(gr, overlay, 0, &ovr); + if (!overlay->overlay_disable && r->intersects(ovr)) { + unsigned char *data; + int i,size=ovr.width()*ovr.height(); +#if QT_VERSION < 0x040000 + QImage img=overlay->widget->pixmap->convertToImage(); + img.setAlphaBuffer(1); +#else + QImage img=overlay->widget->pixmap->toImage().convertToFormat(QImage::Format_ARGB32); +#endif + data=img.bits(); + for (i = 0 ; i < size ; i++) { + if (data[0] == overlay->rgba[0] && data[1] == overlay->rgba[1] && data[2] == overlay->rgba[2]) + data[3]=overlay->rgba[3]; + data+=4; + } +#if QT_VERSION < 0x040000 + painter.drawImage(QPoint(ovr.x()-r->x(),ovr.y()-r->y()), img, 0); +#else + painter.drawImage(QPoint(ovr.x()-r->x(),ovr.y()-r->y()), img); +#endif + } + overlay=overlay->next; + } + QPainter painterw(gr->widget); + painterw.drawPixmap(r->x(), r->y(), pixmap); +} + + +//############################################################################################################## +//# Description: Constructor +//# Comment: Using a QPixmap for rendering the graphics +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +RenderArea::RenderArea(struct graphics_priv *priv, QWidget *parent, int w, int h, int overlay) + : QWidget(parent) +{ + pixmap = new QPixmap(w, h); + if (!overlay) { +#if QT_VERSION >= 0x040000 + setWindowTitle("Navit"); +#else + setCaption("Navit"); +#endif + } + is_overlay=overlay; + gra=priv; +#if QT_VERSION < 0x040000 + timer_type=g_hash_table_new(NULL, NULL); + timer_callback=g_hash_table_new(NULL, NULL); + watches=g_hash_table_new(NULL, NULL); + setBackgroundMode(NoBackground); +#endif +} + +//############################################################################################################## +//# Description: QWidget:sizeHint +//# Comment: This property holds the recommended size for the widget +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +QSize RenderArea::sizeHint() const +{ + return QSize(gra->w, gra->h); +} + +//############################################################################################################## +//# Description: QWidget:paintEvent +//# Comment: A paint event is a request to repaint all or part of the widget. +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +void RenderArea::paintEvent(QPaintEvent * event) +{ + qt_qpainter_draw(gra, &event->rect(), 1); +#if 0 + QPainter painter(this); + painter.drawPixmap(0, 0, *pixmap); +#endif +} + + +//############################################################################################################## +//# Description: QWidget::resizeEvent() +//# Comment: When resizeEvent() is called, the widget already has its new geometry. +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +void RenderArea::resizeEvent(QResizeEvent * event) +{ + if (!this->is_overlay) { + QSize size=event->size(); + delete pixmap; + pixmap=new QPixmap(size); + pixmap->fill(); + QPainter painter(pixmap); + QBrush brush; + painter.fillRect(0, 0, size.width(), size.height(), brush); + dbg(0,"size %dx%d\n", size.width(), size.height()); + dbg(0,"pixmap %p %dx%d\n", pixmap, pixmap->width(), pixmap->height()); + callback_list_call_attr_2(this->cbl, attr_resize, (void *)size.width(), (void *)size.height()); + } +} + +//############################################################################################################## +//# Description: Method to handle mouse clicks +//# Comment: Delegate of QWidget::mousePressEvent and QWidget::mouseReleaseEvent (see below) +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +void RenderArea::mouseEvent(int pressed, QMouseEvent *event) +{ + struct point p; + p.x=event->x(); + p.y=event->y(); + switch (event->button()) { + case Qt::LeftButton: + callback_list_call_attr_3(this->cbl, attr_button, (void *)pressed, (void *)1, (void *)&p); + break; + case Qt::MidButton: + callback_list_call_attr_3(this->cbl, attr_button, (void *)pressed, (void *)2, (void *)&p); + break; + case Qt::RightButton: + callback_list_call_attr_3(this->cbl, attr_button, (void *)pressed, (void *)3, (void *)&p); + break; + default: + break; + } +} + +void RenderArea::mousePressEvent(QMouseEvent *event) +{ + mouseEvent(1, event); +} + +void RenderArea::mouseReleaseEvent(QMouseEvent *event) +{ + mouseEvent(0, event); +} + +//############################################################################################################## +//# Description: QWidget::mouseMoveEvent +//# Comment: If mouse tracking is switched on, mouse move events occur even if no mouse button is pressed. +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +void RenderArea::mouseMoveEvent(QMouseEvent *event) +{ + struct point p; + p.x=event->x(); + p.y=event->y(); + callback_list_call_attr_1(this->cbl, attr_motion, (void *)&p); +} + + +//############################################################################################################## +//# Description: Qt Event :: Zoom in/out with the mouse's scrollwheel +//# Comment: +//# Authors: Stefan Klumpp (04/2008) +//############################################################################################################## +void RenderArea::wheelEvent(QWheelEvent *event) +{ + struct point p; + int button; + + p.x=event->x(); // xy-coordinates of the mouse pointer + p.y=event->y(); + + if (event->delta() > 0) // wheel movement away from the person + button=4; + else if (event->delta() < 0) // wheel movement towards the person + button=5; + else + button=-1; + + if (button != -1) { + callback_list_call_attr_3(this->cbl, attr_button, (void *)1, (void *)button, (void *)&p); + callback_list_call_attr_3(this->cbl, attr_button, (void *)0, (void *)button, (void *)&p); + } + + event->accept(); +} + +void RenderArea::keyPressEvent(QKeyEvent *event) +{ +#if QT_VERSION < 0x040000 + QString str=event->text(); + QCString cstr=str.utf8(); + const char *text=cstr; + dbg(0,"enter text='%s' 0x%x (%d) key=%d\n", text, text[0], strlen(text), event->key()); + if (!text || !text[0] || text[0] == 0x7f) { + dbg(0,"special key\n"); + switch (event->key()) { + case 4099: + { + text=(char []){NAVIT_KEY_BACKSPACE,'\0'}; + } + break; + case 4101: + { + text=(char []){NAVIT_KEY_RETURN,'\0'}; + } + break; + case 4114: + { + text=(char []){NAVIT_KEY_LEFT,'\0'}; + } + break; + case 4115: + { + text=(char []){NAVIT_KEY_UP,'\0'}; + } + break; + case 4116: + { + text=(char []){NAVIT_KEY_RIGHT,'\0'}; + } + break; + case 4117: + { + text=(char []){NAVIT_KEY_DOWN,'\0'}; + } + break; + } + } + callback_list_call_attr_1(this->cbl, attr_keypress, (void *)text); + event->accept(); +#endif +} +//############################################################################################################## +// General comments: +// ----------------- +// gr = graphics = draw area +// gc = graphics context = pen to paint on the draw area +//############################################################################################################## + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +struct graphics_font_priv { + QFont *font; +}; + + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +struct graphics_image_priv { + QImage *image; +}; + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void graphics_destroy(struct graphics_priv *gr) +{ +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void font_destroy(struct graphics_font_priv *font) +{ + +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_font_methods font_methods = { + font_destroy +}; + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_font_priv *font_new(struct graphics_priv *gr, struct graphics_font_methods *meth, char *fontfamily, int size, int flags) +{ + struct graphics_font_priv *ret=g_new0(struct graphics_font_priv, 1); + ret->font=new QFont("Arial",size/20); + *meth=font_methods; + return ret; +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void gc_destroy(struct graphics_gc_priv *gc) +{ +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void gc_set_linewidth(struct graphics_gc_priv *gc, int w) +{ + gc->pen->setWidth(w); +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void gc_set_dashes(struct graphics_gc_priv *gc, int w, int offset, unsigned char *dash_list, int n) +{ +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) +{ +#if QT_VERSION >= 0x040000 + QColor col(c->r >> 8, c->g >> 8, c->b >> 8 /* , c->a >> 8 */); +#else + QColor col(c->r >> 8, c->g >> 8, c->b >> 8); +#endif + gc->pen->setColor(col); + gc->brush->setColor(col); + gc->c=*c; +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void gc_set_background(struct graphics_gc_priv *gc, struct color *c) +{ +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_gc_methods gc_methods = { + gc_destroy, + gc_set_linewidth, + gc_set_dashes, + gc_set_foreground, + gc_set_background +}; + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_gc_priv *gc_new(struct graphics_priv *gr, struct graphics_gc_methods *meth) +{ + *meth=gc_methods; + struct graphics_gc_priv *ret=g_new0(struct graphics_gc_priv, 1); + ret->pen=new QPen(); + ret->brush=new QBrush(Qt::SolidPattern); + return ret; +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_image_priv * image_new(struct graphics_priv *gr, struct graphics_image_methods *meth, char *path, int *w, int *h, struct point *hot, int rotation) +{ + struct graphics_image_priv *ret; + + ret=g_new0(struct graphics_image_priv, 1); + + ret->image=new QImage(path); + if (ret->image->isNull()) { + delete(ret->image); + g_free(ret); + return NULL; + } + *w=ret->image->width(); + *h=ret->image->height(); + if (hot) { + hot->x=*w/2; + hot->y=*h/2; + } + return ret; +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + int i; +#if QT_VERSION >= 0x040000 + QPolygon polygon; +#else + QPointArray polygon; +#endif + + for (i = 0 ; i < count ; i++) + polygon.putPoints(i, 1, p[i].x, p[i].y); + gr->painter->setPen(*gc->pen); + gr->painter->drawPolyline(polygon); +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + int i; +#if QT_VERSION >= 0x040000 + QPolygon polygon; +#else + QPointArray polygon; +#endif + + for (i = 0 ; i < count ; i++) + polygon.putPoints(i, 1, p[i].x, p[i].y); + gr->painter->setPen(*gc->pen); + gr->painter->setBrush(*gc->brush); + gr->painter->drawPolygon(polygon); +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) +{ + dbg(1,"gr=%p gc=%p %d,%d,%d,%d\n", gr, gc, p->x, p->y, w, h); + gr->painter->fillRect(p->x,p->y, w, h, *gc->brush); +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) +{ + gr->painter->setPen(*gc->pen); + gr->painter->drawArc(p->x-r/2, p->y-r/2, r, r, 0, 360*16); + +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +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) +{ + QPainter *painter=gr->painter; +#if 0 + QString tmp=QString::fromUtf8(text); +#ifndef QT_NO_TRANSFORMATIONS +#if QT_VERSION >= 0x040000 + QMatrix sav=gr->painter->worldMatrix(); + QMatrix m(dx/65535.0,dy/65535.0,-dy/65535.0,dx/65535.0,p->x,p->y); + painter->setWorldMatrix(m,TRUE); +#else + QWMatrix sav=gr->painter->worldMatrix(); + QWMatrix m(dx/65535.0,dy/65535.0,-dy/65535.0,dx/65535.0,p->x,p->y); + painter->setWorldMatrix(m,TRUE); +#endif + painter->setPen(*fg->pen); + painter->setFont(*font->font); + painter->drawText(0, 0, tmp); + painter->setWorldMatrix(sav); +#else + painter->setPen(*fg->pen); + painter->setFont(*font->font); + painter->drawText(p->x, p->y, tmp); +#endif +#else + struct font_freetype_text *t; + struct font_freetype_glyph *g, **gp; + struct color transparent = {0x0000, 0x0000, 0x0000, 0x0000}; + struct color *fgc=&fg->c, *bgc=&bg->c; + + int i,x,y; + + if (! font) + return; + t=gr->freetype_methods.text_new(text, (struct font_freetype_font *)font, dx, dy); + x=p->x << 6; + y=p->y << 6; + gp=t->glyph; + i=t->glyph_count; + if (bg) { + while (i-- > 0) { + g=*gp++; + if (g->w && g->h) { + unsigned char *data; +#if QT_VERSION < 0x040000 + QImage img(g->w+2, g->h+2, 32); + img.setAlphaBuffer(1); + data=img.bits(); + gr->freetype_methods.get_shadow(g,(unsigned char *)(img.jumpTable()),32,0,bgc,&transparent); +#else + QImage img(g->w+2, g->h+2, QImage::Format_ARGB32); + data=img.bits(); + gr->freetype_methods.get_shadow(g,(unsigned char *)data,32,img.bytesPerLine(),bgc,&transparent); +#endif + + painter->drawImage(((x+g->x)>>6)-1, ((y+g->y)>>6)-1, img); + } + 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++; + if (g->w && g->h) { + unsigned char *data; +#if QT_VERSION < 0x040000 + QImage img(g->w, g->h, 32); + img.setAlphaBuffer(1); + data=img.bits(); + gr->freetype_methods.get_glyph(g,(unsigned char *)(img.jumpTable()),32,0,fgc,bgc,&transparent); +#else + QImage img(g->w, g->h, QImage::Format_ARGB32); + data=img.bits(); + gr->freetype_methods.get_glyph(g,(unsigned char *)data,32,img.bytesPerLine(),fgc,bgc,&transparent); +#endif + painter->drawImage((x+g->x)>>6, (y+g->y)>>6, img); + } + x+=g->dx; + y+=g->dy; + } + gr->freetype_methods.text_destroy(t); +#endif +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) +{ + gr->painter->drawImage(p->x, p->y, *img->image); +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void draw_image_warp(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, int count, char *data) +{ +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void draw_restore(struct graphics_priv *gr, struct point *p, int w, int h) +{ +} + + +static void +draw_drag(struct graphics_priv *gr, struct point *p) +{ + if (!gr->cleanup) { + gr->pclean=gr->p; + gr->cleanup=1; + } + if (p) + gr->p=*p; + else { + gr->p.x=0; + gr->p.y=0; + } +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc) +{ + gr->background_gc=gc; + gr->rgba[2]=gc->c.r >> 8; + gr->rgba[1]=gc->c.g >> 8; + gr->rgba[0]=gc->c.b >> 8; + gr->rgba[3]=gc->c.a >> 8; +} + + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) +{ + dbg(1,"mode for %p %d\n", gr, mode); + QRect r; + if (mode == draw_mode_begin) { + gr->painter->begin(gr->widget->pixmap); +#if 0 + gr->painter->fillRect(QRect(QPoint(0,0), gr->widget->size()), *gr->background_gc->brush); +#endif + } +#if QT_VERSION < 0x040000 + if (mode == draw_mode_cursor) { + gr->painter->begin(gr->widget); + } +#endif + if (mode == draw_mode_end) { +#if 0 + if (gr->mode == draw_mode_begin) { +#endif + gr->painter->end(); + if (gr->parent) { + if (gr->cleanup) { + overlay_rect(gr->parent, gr, 1, &r); + qt_qpainter_draw(gr->parent, &r, 0); + gr->cleanup=0; + } + overlay_rect(gr->parent, gr, 0, &r); + qt_qpainter_draw(gr->parent, &r, 0); + } else { + r.setRect(0, 0, gr->widget->pixmap->width(), gr->widget->pixmap->height()); + qt_qpainter_draw(gr, &r, 0); + } +#if 0 + } else { +#if QT_VERSION < 0x040000 + gr->painter->end(); +#endif + } +#endif + } +#if QT_VERSION >= 0x040000 + if (mode == draw_mode_end_lazy) + gr->painter->end(); +#endif + gr->mode=mode; +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h,int alpha, int wraparound); + +static int argc=1; +static char *argv[]={(char *)"navit",NULL}; + +static int +fullscreen(struct window *win, int on) +{ + struct graphics_priv *this_=(struct graphics_priv *)win->priv; + if (on) + this_->widget->showFullScreen(); + else + this_->widget->showMaximized(); + return 1; +} + +static void +disable_suspend(struct window *win) +{ +#ifdef HAVE_QPE + struct graphics_priv *this_=(struct graphics_priv *)win->priv; + this_->app->setTempScreenSaverMode(QPEApplication::DisableLightOff); +#endif +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void * get_data(struct graphics_priv *this_, char *type) +{ + struct window *win; + if (strcmp(type, "window")) + return NULL; + win=g_new(struct window, 1); + this_->painter=new QPainter; + if (this_->w && this_->h) + this_->widget->show(); + else + this_->widget->showMaximized(); + win->priv=this_; + win->fullscreen=fullscreen; + win->disable_suspend=disable_suspend; + return win; +} + +static void +image_free(struct graphics_priv *gr, struct graphics_image_priv *priv) +{ + delete priv->image; + g_free(priv); +} + +static void +get_text_bbox(struct graphics_priv *gr, struct graphics_font_priv *font, char *text, int dx, int dy, struct point *ret, int estimate) +{ + QPainter *painter=gr->painter; + QString tmp=QString::fromUtf8(text); + painter->setFont(*font->font); + QRect r=painter->boundingRect(0,0,gr->widget->width(),gr->widget->height(),0,tmp); + ret[0].x=0; + ret[0].y=-r.height(); + ret[1].x=0; + ret[1].y=0; + ret[2].x=r.width(); + ret[2].y=0; + ret[3].x=r.width(); + ret[3].y=-r.height(); +} + + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static void overlay_disable(struct graphics_priv *gr, int disable) +{ + gr->overlay_disable=disable; +} + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, + draw_image_warp, + draw_restore, + draw_drag, + font_new, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, + image_free, + get_text_bbox, + overlay_disable, +}; + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h,int alpha,int wraparound) +{ + *meth=graphics_methods; + struct graphics_priv *ret=g_new0(struct graphics_priv, 1); + if (gr->font_freetype_new) { + ret->font_freetype_new=gr->font_freetype_new; + gr->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=(void (*)(struct graphics_priv*, struct graphics_font_priv*, char*, int, int, struct point*, int))ret->freetype_methods.get_text_bbox; + } + ret->widget= new RenderArea(ret,NULL,w,h,1); + ret->wraparound=wraparound; + ret->painter=new QPainter; + ret->p=*p; + ret->parent=gr; + ret->next=gr->overlays; + gr->overlays=ret; + return ret; +} + +#if QT_VERSION < 0x040000 + + +static struct graphics_priv *event_gr; + +static void +event_qt_main_loop_run(void) +{ + event_gr->app->exec(); +} + +static void event_qt_main_loop_quit(void) +{ + dbg(0,"enter\n"); + exit(0); +} + +struct event_watch { + QSocketNotifier *sn; + struct callback *cb; + void *fd; +}; + +static struct event_watch * +event_qt_add_watch(void *fd, enum event_watch_cond cond, struct callback *cb) +{ + dbg(1,"enter fd=%d\n",(int)(long)fd); + struct event_watch *ret=g_new0(struct event_watch, 1); + ret->fd=fd; + ret->cb=cb; + g_hash_table_insert(event_gr->widget->watches, fd, ret); + ret->sn=new QSocketNotifier((int)(long)fd, QSocketNotifier::Read, event_gr->widget); + QObject::connect(ret->sn, SIGNAL(activated(int)), event_gr->widget, SLOT(watchEvent(int))); + return ret; +} + +static void +event_qt_remove_watch(struct event_watch *ev) +{ + g_hash_table_remove(event_gr->widget->watches, ev->fd); + delete(ev->sn); + g_free(ev); + +} + +static struct event_timeout * +event_qt_add_timeout(int timeout, int multi, struct callback *cb) +{ + int id; + id=event_gr->widget->startTimer(timeout); + g_hash_table_insert(event_gr->widget->timer_callback, (void *)id, cb); + g_hash_table_insert(event_gr->widget->timer_type, (void *)id, (void *)!!multi); + return (struct event_timeout *)id; +} + +static void +event_qt_remove_timeout(struct event_timeout *ev) +{ + event_gr->widget->killTimer((int)(long)ev); + g_hash_table_remove(event_gr->widget->timer_callback, ev); + g_hash_table_remove(event_gr->widget->timer_type, ev); +} + +static struct event_idle * +event_qt_add_idle(int priority, struct callback *cb) +{ + dbg(0,"enter\n"); + return (struct event_idle *)event_qt_add_timeout(0, 1, cb); +} + +static void +event_qt_remove_idle(struct event_idle *ev) +{ + dbg(0,"enter\n"); + event_qt_remove_timeout((struct event_timeout *) ev); +} + +static void +event_qt_call_callback(struct callback_list *cb) +{ + dbg(0,"enter\n"); +} + +static struct event_methods event_qt_methods = { + event_qt_main_loop_run, + event_qt_main_loop_quit, + event_qt_add_watch, + event_qt_remove_watch, + event_qt_add_timeout, + event_qt_remove_timeout, + event_qt_add_idle, + event_qt_remove_idle, + event_qt_call_callback, +}; + +struct event_priv { +}; + +struct event_priv * +event_qt_new(struct event_methods *meth) +{ + dbg(0,"enter\n"); + *meth=event_qt_methods; + return NULL; +} + +void RenderArea::timerEvent(QTimerEvent *event) +{ + int id=event->timerId(); + struct callback *cb=(struct callback *)g_hash_table_lookup(timer_callback, (void *)id); + if (cb) + callback_call_0(cb); + if (!g_hash_table_lookup(timer_type, (void *)id)) + event_qt_remove_timeout((struct event_timeout *)id); +} +#endif + +void RenderArea::watchEvent(int fd) +{ +#if QT_VERSION < 0x040000 + struct event_watch *ev=(struct event_watch *)g_hash_table_lookup(watches, (void *)fd); + dbg(1,"fd=%d ev=%p cb=%p\n", fd, ev, ev->cb); + callback_call_0(ev->cb); +#endif +} + + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +static struct graphics_priv * graphics_qt_qpainter_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl) +{ + struct graphics_priv *ret; + struct font_priv * (*font_freetype_new)(void *meth); + struct attr *attr; + + dbg(0,"enter\n"); +#if QT_VERSION < 0x040000 + if (event_gr) + return NULL; + if (! event_request_system("qt","graphics_qt_qpainter_new")) + return NULL; +#else + if (! event_request_system("glib","graphics_qt_qpainter_new")) + return NULL; +#endif +#if 1 + font_freetype_new=(struct font_priv *(*)(void *))plugin_get_font_type("freetype"); + if (!font_freetype_new) + return NULL; +#endif + ret=g_new0(struct graphics_priv, 1); + ret->font_freetype_new=font_freetype_new; + *meth=graphics_methods; +#if 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=(void (*)(struct graphics_priv*, struct graphics_font_priv*, char*, int, int, struct point*, int))ret->freetype_methods.get_text_bbox; +#endif + +#ifdef HAVE_QPE + ret->app = new QPEApplication(argc, argv); +#else + ret->app = new QApplication(argc, argv); +#endif + ret->widget= new RenderArea(ret); + ret->widget->cbl=cbl; +#if QT_VERSION < 0x040000 + event_gr=ret; +#endif + ret->w=800; + ret->h=600; + if ((attr=attr_search(attrs, NULL, attr_w))) + ret->w=attr->u.num; + if ((attr=attr_search(attrs, NULL, attr_h))) + ret->h=attr->u.num; + + dbg(0,"return\n"); + return ret; +} + + +//############################################################################################################## +//# Description: +//# Comment: +//# Authors: Martin Schaller (04/2008) +//############################################################################################################## +void plugin_init(void) +{ + plugin_register_graphics_type("qt_qpainter", graphics_qt_qpainter_new); +#if QT_VERSION < 0x040000 + plugin_register_event_type("qt", event_qt_new); +#endif +} + + + + +// *** EOF *** diff --git a/graphics/sdl/Makefile.am b/graphics/sdl/Makefile.am new file mode 100644 index 00000000..084bf3e9 --- /dev/null +++ b/graphics/sdl/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ @SDL_CFLAGS@ @FREETYPE2_CFLAGS@ @FONTCONFIG_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=graphics_sdl +modulegraphics_LTLIBRARIES = libgraphics_sdl.la +libgraphics_sdl_la_SOURCES = graphics_sdl.c raster.c raster.h +libgraphics_sdl_la_LDFLAGS = @SDL_LIBS@ @SDL_IMAGE_LIBS@ @FREETYPE2_LIBS@ @FONTCONFIG_LIBS@ -module -avoid-version diff --git a/graphics/sdl/graphics_sdl.c b/graphics/sdl/graphics_sdl.c new file mode 100644 index 00000000..c30bc121 --- /dev/null +++ b/graphics/sdl/graphics_sdl.c @@ -0,0 +1,2146 @@ +/* graphics_sdl.c -- barebones sdl graphics layer + + copyright (c) 2008 bryan rittmeyer <bryanr@bryanr.org> + license: GPLv2 + + TODO: + - dashed lines + - ifdef DEBUG -> dbg() + - proper image transparency (libsdl-image xpm does not work) + - valgrind + + revision history: + 2008-06-01 initial + 2008-06-15 SDL_DOUBLEBUF+SDL_Flip for linux fb. fix FT leaks. + 2008-06-18 defer initial resize_callback + 2008-06-21 linux touchscreen + 2008-07-04 custom rastering +*/ + +#include <glib.h> +#include "config.h" +#include "debug.h" +#include "point.h" +#include "graphics.h" +#include "color.h" +#include "plugin.h" +#include "window.h" +#include "navit.h" +#include "keys.h" +#include "item.h" +#include "attr.h" +#include "callback.h" + +#include <SDL/SDL.h> +#include <math.h> + +#define RASTER +#undef SDL_SGE +#undef SDL_GFX +#undef ALPHA + +#undef SDL_TTF +#define SDL_IMAGE +#undef LINUX_TOUCHSCREEN + +#define DISPLAY_W 800 +#define DISPLAY_H 600 + + +#undef DEBUG +#undef PROFILE + +#define OVERLAY_MAX 16 + +#ifdef RASTER +#include "raster.h" +#endif + +#ifdef SDL_SGE +#include <SDL/sge.h> +#endif + +#ifdef SDL_GFX +#include <SDL/SDL_gfxPrimitives.h> +#endif + +#ifdef SDL_TTF +#include <SDL/SDL_ttf.h> +#else +#include <fontconfig/fontconfig.h> +#include <ft2build.h> +#include FT_FREETYPE_H +#include <freetype/ftglyph.h> +#endif +#include <event.h> + +#ifdef SDL_IMAGE +#include <SDL/SDL_image.h> +#endif + +#ifdef LINUX_TOUCHSCREEN +/* we use Linux evdev directly for the touchscreen. */ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <linux/input.h> +#endif + +#include <alloca.h> + +#ifdef PROFILE +#include <sys/time.h> +#include <time.h> +#endif + + +/* TODO: union overlay + non-overlay to reduce size */ +struct graphics_priv; +struct graphics_priv { + SDL_Surface *screen; + int aa; + + /* <overlay> */ + int overlay_mode; + int overlay_x; + int overlay_y; + struct graphics_priv *overlay_parent; + int overlay_idx; + /* </overlay> */ + + /* <main> */ + struct graphics_priv *overlay_array[OVERLAY_MAX]; + int overlay_enable; + enum draw_mode_num draw_mode; + + int resize_callback_initial; + + struct navit *nav; + struct callback_list *cbl; + +#ifdef LINUX_TOUCHSCREEN + int ts_fd; + int32_t ts_hit; + uint32_t ts_x; + uint32_t ts_y; +#endif + +#ifndef SDL_TTF + FT_Library library; +#endif + +#ifdef PROFILE + struct timeval draw_begin_tv; + unsigned long draw_time_peak; +#endif + /* </main> */ +}; + +static int dummy; + + +struct graphics_font_priv { +#ifdef SDL_TTF + TTF_Font *font; +#else + FT_Face face; +#endif +}; + +struct graphics_gc_priv { + struct graphics_priv *gr; + Uint8 fore_r; + Uint8 fore_g; + Uint8 fore_b; + Uint8 fore_a; + Uint8 back_r; + Uint8 back_g; + Uint8 back_b; + Uint8 back_a; + int linewidth; +}; + +struct graphics_image_priv { + SDL_Surface *img; +}; + + +#ifdef LINUX_TOUCHSCREEN +static int input_ts_exit(struct graphics_priv *gr); +#endif + +static void +graphics_destroy(struct graphics_priv *gr) +{ + dbg(0, "graphics_destroy %p %u\n", gr, gr->overlay_mode); + + if(gr->overlay_mode) + { + SDL_FreeSurface(gr->screen); + gr->overlay_parent->overlay_array[gr->overlay_idx] = NULL; + } + else + { +#ifdef SDL_TTF + TTF_Quit(); +#else + FT_Done_FreeType(gr->library); + FcFini(); +#endif +#ifdef LINUX_TOUCHSCREEN + input_ts_exit(gr); +#endif + SDL_Quit(); + } + + g_free(gr); +} + +/* graphics_font */ +static char *fontfamilies[]={ + "Liberation Mono", + "Arial", + "DejaVu Sans", + "NcrBI4nh", + "luximbi", + "FreeSans", + NULL, +}; + +static void font_destroy(struct graphics_font_priv *gf) +{ +#ifdef SDL_TTF +#else + FT_Done_Face(gf->face); +#endif + g_free(gf); +} + +static struct graphics_font_methods font_methods = { + font_destroy +}; + +static struct graphics_font_priv *font_new(struct graphics_priv *gr, struct graphics_font_methods *meth, char *fontfamily, int size, int flags) +{ + struct graphics_font_priv *gf=g_new(struct graphics_font_priv, 1); + +#ifdef SDL_TTF + /* 'size' is in pixels, TTF_OpenFont wants pts. */ + size = size / 15; + +// gf->font = TTF_OpenFont("/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf", size); + if(flags) + { + gf->font = TTF_OpenFont("/usr/share/fonts/truetype/LiberationMono-Bold.ttf", size); + } + else + { + gf->font = TTF_OpenFont("/usr/share/fonts/truetype/LiberationMono-Regular.ttf", size); + } + + if(gf->font == NULL) + { + g_free(gf); + return NULL; + } + + if(flags) + { + /* apparently just means bold right now */ + TTF_SetFontStyle(gf->font, TTF_STYLE_BOLD); + } + else + { + TTF_SetFontStyle(gf->font, TTF_STYLE_NORMAL); + } +#else + /* copy-pasted from graphics_gtk_drawing_area.c + + FIXME: figure out some way to share this b/t gtk and sdl graphics plugin. + new 'font' plugin type that uses an abstracted bitmap fmt to pass to gfx plugin? + */ + + int exact, found; + char **family; + + if(gr->overlay_mode) + { + gr = gr->overlay_parent; + } + + found=0; + for (exact=1;!found && exact>=0;exact--) { + family=fontfamilies; + while (*family && !found) { + dbg(1, "Looking for font family %s. exact=%d\n", *family, exact); + FcPattern *required = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, *family, NULL); + if (flags) + FcPatternAddInteger(required,FC_WEIGHT,FC_WEIGHT_BOLD); + FcConfigSubstitute(FcConfigGetCurrent(), required, FcMatchFont); + FcDefaultSubstitute(required); + FcResult result; + FcPattern *matched = FcFontMatch(FcConfigGetCurrent(), required, &result); + if (matched) { + FcValue v1, v2; + FcChar8 *fontfile; + int fontindex; + FcPatternGet(required, FC_FAMILY, 0, &v1); + FcPatternGet(matched, FC_FAMILY, 0, &v2); + FcResult r1 = FcPatternGetString(matched, FC_FILE, 0, &fontfile); + FcResult r2 = FcPatternGetInteger(matched, FC_INDEX, 0, &fontindex); + if ((r1 == FcResultMatch) && (r2 == FcResultMatch) && (FcValueEqual(v1,v2) || !exact)) { + dbg(2, "About to load font from file %s index %d\n", fontfile, fontindex); + FT_New_Face( gr->library, (char *)fontfile, fontindex, &gf->face ); + found=1; + } + FcPatternDestroy(matched); + } + FcPatternDestroy(required); + family++; + } + } + if (!found) { + g_warning("Failed to load font, no labelling"); + g_free(gf); + return NULL; + } + FT_Set_Char_Size(gf->face, 0, size, 300, 300); + FT_Select_Charmap(gf->face, FT_ENCODING_UNICODE); +#endif + + *meth=font_methods; + + return gf; +} + + +/* graphics_gc */ + +static void +gc_destroy(struct graphics_gc_priv *gc) +{ + g_free(gc); +} + +static void +gc_set_linewidth(struct graphics_gc_priv *gc, int w) +{ +#ifdef DEBUG + printf("gc_set_linewidth %p %d\n", gc, w); +#endif + gc->linewidth = w; +} + +static void +gc_set_dashes(struct graphics_gc_priv *gc, int w, int offset, unsigned char *dash_list, int n) +{ + /* TODO */ +} + +static void +gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) +{ +#ifdef DEBUG + printf("gc_set_foreground: %p %d %d %d %d\n", gc, c->a, c->r, c->g, c->b); +#endif + gc->fore_r = c->r/256; + gc->fore_g = c->g/256; + gc->fore_b = c->b/256; + gc->fore_a = c->a/256; +} + +static void +gc_set_background(struct graphics_gc_priv *gc, struct color *c) +{ +#ifdef DEBUG + printf("gc_set_background: %p %d %d %d %d\n", gc, c->a, c->r, c->g, c->b); +#endif + gc->back_r = c->r/256; + gc->back_g = c->g/256; + gc->back_b = c->b/256; + gc->back_a = c->a/256; +} + +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 *gc=g_new0(struct graphics_gc_priv, 1); + *meth=gc_methods; + gc->gr=gr; + gc->linewidth=1; /* upper layer should override anyway? */ + return gc; +} + + +#if 0 /* unused by core? */ +static void image_destroy(struct graphics_image_priv *gi) +{ +#ifdef SDL_IMAGE + SDL_FreeSurface(gi->img); + g_free(gi); +#endif +} + +static struct graphics_image_methods gi_methods = +{ + image_destroy +}; +#endif + +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) +{ +#ifdef SDL_IMAGE + struct graphics_image_priv *gi; + + /* FIXME: meth is not used yet.. so gi leaks. at least xpm is small */ + + gi = g_new0(struct graphics_image_priv, 1); + gi->img = IMG_Load(name); + if(gi->img) + { + /* TBD: improves blit performance? */ + SDL_SetColorKey(gi->img, SDL_RLEACCEL, gi->img->format->colorkey); + *w=gi->img->w; + *h=gi->img->h; + hot->x=*w/2; + hot->y=*h/2; + } + else + { + /* TODO: debug "colour parse errors" on xpm */ + printf("graphics_sdl: image_new on '%s' failed: %s\n", name, IMG_GetError()); + g_free(gi); + gi = NULL; + } + + return gi; +#else + return NULL; +#endif +} + +static void +image_free(struct graphics_priv *gr, struct graphics_image_priv * gi) +{ +#ifdef SDL_IMAGE + SDL_FreeSurface(gi->img); + g_free(gi); +#endif +} + +static void +get_text_bbox(struct graphics_priv *gr, struct graphics_font_priv *font, char *text, int dx, int dy, struct point *ret, int estimate) +{ + char *p=text; + FT_BBox bbox; + FT_UInt glyph_index; + FT_GlyphSlot slot = font->face->glyph; // a small shortcut + FT_Glyph glyph; + FT_Matrix matrix; + FT_Vector pen; + pen.x = 0 * 64; + pen.y = 0 * 64; + matrix.xx = dx; + matrix.xy = dy; + matrix.yx = -dy; + matrix.yy = dx; + int n,len,x=0,y=0; + + bbox.xMin = bbox.yMin = 32000; + bbox.xMax = bbox.yMax = -32000; + FT_Set_Transform( font->face, &matrix, &pen ); + len=g_utf8_strlen(text, -1); + for ( n = 0; n < len; n++ ) { + FT_BBox glyph_bbox; + glyph_index = FT_Get_Char_Index(font->face, g_utf8_get_char(p)); + p=g_utf8_next_char(p); + FT_Load_Glyph(font->face, glyph_index, FT_LOAD_DEFAULT ); + FT_Get_Glyph(font->face->glyph, &glyph); + FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_pixels, &glyph_bbox ); + FT_Done_Glyph(glyph); + glyph_bbox.xMin += x >> 6; + glyph_bbox.xMax += x >> 6; + glyph_bbox.yMin += y >> 6; + glyph_bbox.yMax += y >> 6; + x += slot->advance.x; + y -= slot->advance.y; + if ( glyph_bbox.xMin < bbox.xMin ) + bbox.xMin = glyph_bbox.xMin; + if ( glyph_bbox.yMin < bbox.yMin ) + bbox.yMin = glyph_bbox.yMin; + if ( glyph_bbox.xMax > bbox.xMax ) + bbox.xMax = glyph_bbox.xMax; + if ( glyph_bbox.yMax > bbox.yMax ) + bbox.yMax = glyph_bbox.yMax; + } + if ( bbox.xMin > bbox.xMax ) { + bbox.xMin = 0; + bbox.yMin = 0; + bbox.xMax = 0; + bbox.yMax = 0; + } + ret[0].x=bbox.xMin; + ret[0].y=-bbox.yMin; + ret[1].x=bbox.xMin; + ret[1].y=-bbox.yMax; + ret[2].x=bbox.xMax; + ret[2].y=-bbox.yMax; + ret[3].x=bbox.xMax; + ret[3].y=-bbox.yMin; +} + +static void +draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + Sint16 *vx, *vy; + Sint16 x, y; + int i; + + vx = alloca(count * sizeof(Sint16)); + vy = alloca(count * sizeof(Sint16)); + + for(i = 0; i < count; i++) + { + x = (Sint16)p[i].x; + y = (Sint16)p[i].y; + +#if 0 + if(x < 0) + { + x = 0; + } + if(y < 0) + { + y = 0; + } +#endif + + vx[i] = x; + vy[i] = y; + +#ifdef DEBUG + printf("draw_polygon: %p %i %d,%d\n", gc, i, p[i].x, p[i].y); +#endif + } + +#ifdef RASTER + if(gr->aa) + { + raster_aapolygon(gr->screen, count, vx, vy, + SDL_MapRGB(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b)); + } + else + { + raster_polygon(gr->screen, count, vx, vy, + SDL_MapRGB(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b)); + } +#else +#ifdef SDL_SGE +#ifdef ALPHA + sge_FilledPolygonAlpha(gr->screen, count, vx, vy, + SDL_MapRGB(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b), + gc->fore_a); +#else +#ifdef ANTI_ALIAS + sge_AAFilledPolygon(gr->screen, count, vx, vy, + SDL_MapRGB(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b)); +#else + sge_FilledPolygon(gr->screen, count, vx, vy, + SDL_MapRGB(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b)); +#endif +#endif +#else + filledPolygonRGBA(gr->screen, vx, vy, count, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#endif +#endif +} + + + +static void +draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) +{ +#ifdef DEBUG + printf("draw_rectangle: %d %d %d %d r=%d g=%d b=%d a=%d\n", p->x, p->y, w, h, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#endif + if(w > gr->screen->w) + { + w = gr->screen->w; + } + if(h > gr->screen->h) + { + h = gr->screen->h; + } + +#ifdef RASTER + raster_rect(gr->screen, p->x, p->y, w, h, + SDL_MapRGB(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b)); +#else +#ifdef SDL_SGE +#ifdef ALPHA + sge_FilledRectAlpha(gr->screen, p->x, p->y, p->x + w, p->y + h, + SDL_MapRGB(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b), + gc->fore_a); +#else + /* no AA -- should use poly instead for that */ + sge_FilledRect(gr->screen, p->x, p->y, p->x + w, p->y + h, + SDL_MapRGB(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b)); +#endif +#else + boxRGBA(gr->screen, p->x, p->y, p->x + w, p->y + h, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#endif +#endif + +} + +static void +draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) +{ +#if 0 + if(gc->fore_a != 0xff) + { + dbg(0, "%d %d %d %u %u:%u:%u:%u\n", p->x, p->y, r, gc->linewidth, + gc->fore_a, gc->fore_r, gc->fore_g, gc->fore_b); + } +#endif + + /* FIXME: does not quite match gtk */ + + /* hack for osd compass.. why is this needed!? */ + if(gr->overlay_mode) + { + r = r / 2; + } + +#ifdef RASTER + if(gr->aa) + { + raster_aacircle(gr->screen, p->x, p->y, r, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b)); + } + else + { + raster_circle(gr->screen, p->x, p->y, r, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b)); + } +#else +#ifdef SDL_SGE +#ifdef ALPHA + sge_FilledCircleAlpha(gr->screen, p->x, p->y, r, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b), + gc->fore_a); +#else +#ifdef ANTI_ALIAS + sge_AAFilledCircle(gr->screen, p->x, p->y, r, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b)); +#else + sge_FilledCircle(gr->screen, p->x, p->y, r, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b)); +#endif +#endif +#else +#ifdef ANTI_ALIAS + aacircleRGBA(gr->screen, p->x, p->y, r, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#else + filledCircleRGBA(gr->screen, p->x, p->y, r, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#endif +#endif +#endif +} + + +static void +draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + /* you might expect lines to be simpler than the other shapes. + but, that would be wrong. 1 line can generate 1 polygon + 2 circles + and even worse, we have to calculate their parameters! + go dust off your trigonometry hat. + */ +#if 0 + int i, l, x_inc, y_inc, lw; + + lw = gc->linewidth; + + for(i = 0; i < count-1; i++) + { +#ifdef DEBUG + printf("draw_lines: %p %d %d,%d->%d,%d %d\n", gc, i, p[i].x, p[i].y, p[i+1].x, p[i+1].y, gc->linewidth); +#endif + for(l = 0; l < lw; l++) + { + /* FIXME: center? */ +#if 1 + if(p[i].x != p[i+1].x) + { + x_inc = l - (lw/2); + } + else + { + x_inc = 0; + } + + if(p[i].y != p[i+1].y) + { + y_inc = l - (lw/2); + } + else + { + y_inc = 0; + } +#else + x_inc = 0; + y_inc = 0; +#endif + +#ifdef ANTI_ALIAS + aalineRGBA(gr->screen, p[i].x + x_inc, p[i].y + y_inc, p[i+1].x + x_inc, p[i+1].y + y_inc, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#else + lineRGBA(gr->screen, p[i].x + x_inc, p[i].y + y_inc, p[i+1].x + x_inc, p[i+1].y + y_inc, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#endif + } + } +#else + /* sort of based on graphics_opengl.c::draw_lines */ + /* FIXME: should honor ./configure flag for no fp. + this could be 100% integer code pretty easily, + except that i am lazy + */ + struct point vert[4]; + int lw = gc->linewidth; + //int lw = 1; + int i; + + for(i = 0; i < count-1; i++) + { + float dx=p[i+1].x-p[i].x; + float dy=p[i+1].y-p[i].y; + +#if 0 + float cx=(p[i+1].x+p[i].x)/2; + float cy=(p[i+1].y+p[i].y)/2; +#endif + + float angle; + + int x_lw_adj, y_lw_adj; + + if(lw == 1) + { +#ifdef RASTER + if(gr->aa) + { + raster_aaline(gr->screen, p[i].x, p[i].y, p[i+1].x, p[i+1].y, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b)); + } + else + { + raster_line(gr->screen, p[i].x, p[i].y, p[i+1].x, p[i+1].y, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b)); + } +#else +#ifdef SDL_SGE +#ifdef ALPHA + sge_LineAlpha(gr->screen, p[i].x, p[i].y, p[i+1].x, p[i+1].y, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b), + gc->fore_a); +#else +#ifdef ANTI_ALIAS + sge_AALine(gr->screen, p[i].x, p[i].y, p[i+1].x, p[i+1].y, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b)); +#else + sge_Line(gr->screen, p[i].x, p[i].y, p[i+1].x, p[i+1].y, + SDL_MapRGB(gr->screen->format, + gc->fore_r, gc->fore_g, gc->fore_b)); +#endif +#endif +#else +#ifdef ANTI_ALIAS + aalineRGBA(gr->screen, p[i].x, p[i].y, p[i+1].x, p[i+1].y, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#else + lineRGBA(gr->screen, p[i].x, p[i].y, p[i+1].x, p[i+1].y, + gc->fore_r, gc->fore_g, gc->fore_b, gc->fore_a); +#endif +#endif +#endif + } + else + { + /* there is probably a much simpler way but this works ok */ + + /* FIXME: float + double mixture */ + /* FIXME: lrint(round())? */ + if(dy == 0.0) + { + angle = 0.0; + x_lw_adj = 0; + y_lw_adj = round((float)lw/2.0); + } + else if(dx == 0.0) + { + angle = 0.0; + x_lw_adj = round((float)lw/2.0); + y_lw_adj = 0; + } + else + { + angle = (M_PI/2.0) - atan(abs(dx)/abs(dy)); + x_lw_adj = round(sin(angle)*(float)lw/2.0); + y_lw_adj = round(cos(angle)*(float)lw/2.0); + if((x_lw_adj < 0) || (y_lw_adj < 0)) + { + printf("i=%d\n", i); + printf(" %d,%d->%d,%d\n", p[i].x, p[i].y, p[i+1].x, p[i+1].y); + printf(" lw=%d angle=%f\n", lw, 180.0 * angle / M_PI); + printf(" x_lw_adj=%d y_lw_adj=%d\n", x_lw_adj, y_lw_adj); + } + } + + if(p[i+1].x > p[i].x) + { + x_lw_adj = -x_lw_adj; + } + if(p[i+1].y > p[i].y) + { + y_lw_adj = -y_lw_adj; + } + +#if 0 + if(((y_lw_adj*y_lw_adj)+(x_lw_adj*x_lw_adj)) != (lw/2)*(lw/2)) + { + printf("i=%d\n", i); + printf(" %d,%d->%d,%d\n", p[i].x, p[i].y, p[i+1].x, p[i+1].y); + printf(" lw=%d angle=%f\n", lw, 180.0 * angle / M_PI); + printf(" x_lw_adj=%d y_lw_adj=%d\n", x_lw_adj, y_lw_adj); + } +#endif + + /* FIXME: draw a circle/square if p[i]==p[i+1]? */ + /* FIXME: clipping, check for neg values. hoping sdl-gfx does this */ + vert[0].x = p[i].x + x_lw_adj; + vert[0].y = p[i].y - y_lw_adj; + vert[1].x = p[i].x - x_lw_adj; + vert[1].y = p[i].y + y_lw_adj; + vert[2].x = p[i+1].x - x_lw_adj; + vert[2].y = p[i+1].y + y_lw_adj; + vert[3].x = p[i+1].x + x_lw_adj; + vert[3].y = p[i+1].y - y_lw_adj; + + draw_polygon(gr, gc, vert, 4); + + /* draw small circles at the ends. this looks better than nothing, and slightly + * better than the triangle used by graphics_opengl, but is more expensive. + * should have an ifdef/xml attr? + */ + + /* FIXME: should just draw a half circle */ + + /* now some circular endcaps, if the width is over 2 */ + if(lw > 2) + { + if(i == 0) + { + draw_circle(gr, gc, &p[i], lw/2); + } + /* we truncate on the divide on purpose, so we don't go outside the line */ + draw_circle(gr, gc, &p[i+1], lw/2); + } + } + } +#endif +} + + +#ifdef SDL_TTF +static void +draw_text(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct graphics_gc_priv *bg, struct graphics_font_priv *gf, char *text, struct point *p, int dx, int dy) +{ + SDL_Surface *ss; + SDL_Color f, b; + SDL_Rect r; + +#if 0 + /* correct? */ + f.r = bg->back_r; + f.g = bg->back_g; + f.b = bg->back_b; + b.r = bg->back_r; + b.g = bg->back_g; + b.b = bg->back_b; +#else + f.r = 0xff; + f.g = 0xff; + f.b = 0xff; + b.r = 0x00; + b.g = 0x00; + b.b = 0x00; +#endif + + /* TODO: dx + dy? */ + + ss = TTF_RenderUTF8_Solid(gf->font, text, b); + if(ss) + { + r.x = p->x; + r.y = p->y; + r.w = ss->w; + r.h = ss->h; + //SDL_SetAlpha(ss, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); + SDL_BlitSurface(ss, NULL, gr->screen, &r); + } +} +#else + +struct text_glyph { + int x,y,w,h; + unsigned char *shadow; + unsigned char pixmap[0]; +}; + +struct text_render { + int x1,y1; + int x2,y2; + int x3,y3; + int x4,y4; + int glyph_count; + struct text_glyph *glyph[0]; +}; + +static unsigned char * +display_text_render_shadow(struct text_glyph *g) +{ + int mask0, mask1, mask2, x, y, w=g->w, h=g->h; + int str=(g->w+9)/8; + unsigned char *shadow; + unsigned char *p, *pm=g->pixmap; + + shadow=malloc(str*(g->h+2)); + memset(shadow, 0, str*(g->h+2)); + for (y = 0 ; y < h ; y++) { + p=shadow+str*y; + mask0=0x4000; + mask1=0xe000; + mask2=0x4000; + for (x = 0 ; x < w ; x++) { + if (pm[x+y*w]) { + p[0]|=(mask0 >> 8); + if (mask0 & 0xff) + p[1]|=mask0; + + p[str]|=(mask1 >> 8); + if (mask1 & 0xff) + p[str+1]|=mask1; + p[str*2]|=(mask2 >> 8); + if (mask2 & 0xff) + p[str*2+1]|=mask2; + } + mask0 >>= 1; + mask1 >>= 1; + mask2 >>= 1; + if (!((mask0 >> 8) | (mask1 >> 8) | (mask2 >> 8))) { + mask0<<=8; + mask1<<=8; + mask2<<=8; + p++; + } + } + } + return shadow; +} + + +static struct text_render * +display_text_render(char *text, struct graphics_font_priv *font, int dx, int dy, int x, int y) +{ + FT_GlyphSlot slot = font->face->glyph; // a small shortcut + FT_Matrix matrix; + FT_Vector pen; + FT_UInt glyph_index; + int n,len; + struct text_render *ret; + struct text_glyph *curr; + char *p=text; + + len=g_utf8_strlen(text, -1); + ret=g_malloc(sizeof(*ret)+len*sizeof(struct text_glyph *)); + ret->glyph_count=len; + + matrix.xx = dx; + matrix.xy = dy; + matrix.yx = -dy; + matrix.yy = dx; + + pen.x = 0 * 64; + pen.y = 0 * 64; + x <<= 6; + y <<= 6; + FT_Set_Transform( font->face, &matrix, &pen ); + + for ( n = 0; n < len; n++ ) + { + + glyph_index = FT_Get_Char_Index(font->face, g_utf8_get_char(p)); + FT_Load_Glyph(font->face, glyph_index, FT_LOAD_DEFAULT ); + FT_Render_Glyph(font->face->glyph, ft_render_mode_normal ); + + curr=g_malloc(sizeof(*curr)+slot->bitmap.rows*slot->bitmap.pitch); + ret->glyph[n]=curr; + + curr->x=(x>>6)+slot->bitmap_left; + curr->y=(y>>6)-slot->bitmap_top; + curr->w=slot->bitmap.width; + curr->h=slot->bitmap.rows; + if (slot->bitmap.width && slot->bitmap.rows) { + memcpy(curr->pixmap, slot->bitmap.buffer, slot->bitmap.rows*slot->bitmap.pitch); + curr->shadow=display_text_render_shadow(curr); + } + else + curr->shadow=NULL; +#if 0 + printf("height=%d\n", slot->metrics.height); + printf("height2=%d\n", face->height); + printf("bbox %d %d %d %d\n", face->bbox.xMin, face->bbox.yMin, face->bbox.xMax, face->bbox.yMax); +#endif +#if 0 + printf("slot->advance x %d y %d\n", + slot->advance.x, + slot->advance.y); +#endif + + x += slot->advance.x; + y -= slot->advance.y; + p=g_utf8_next_char(p); + } + return ret; +} + +#if 0 +static void hexdump(unsigned char *buf, unsigned int w, unsigned int h) +{ + int x, y; + printf("hexdump %u %u\n", w, h); + for(y = 0; y < h; y++) + { + for(x = 0; x < w; x++) + { + printf("%02x ", buf[y*w+x]); + } + printf("\n"); + } +} + +static void bitdump(unsigned char *buf, unsigned int w, unsigned int h) +{ + int x, pos; + printf("bitdump %u %u\n", w, h); + pos = 0; + for(x = 0; x < h * w; x++) + { + if(buf[pos] & (1 << (x&0x7))) + { + printf("00 "); + } + else + { + printf("ff "); + } + + if((x & 0x7) == 0x7) + { + pos++; + } + + if((x % w) == (w-1)) + { + printf("\n"); + } + } + printf("\n"); +} +#endif + +#if 0 +static void sdl_inv_grayscale_pal_set(SDL_Surface *ss) +{ + SDL_Color c; + int i; + + for(i = 0; i < 256; i++) + { + c.r = 255-i; + c.g = 255-i; + c.b = 255-i; + SDL_SetPalette(ss, SDL_LOGPAL, &c, i, 1); + } +} + +static void sdl_scale_pal_set(SDL_Surface *ss, Uint8 r, Uint8 g, Uint8 b) +{ + SDL_Color c; + int i; + + for(i = 0; i < 256; i++) + { + c.r = (i * r) / 256; + c.g = (i * g) / 256; + c.b = (i * b) / 256; + SDL_SetPalette(ss, SDL_LOGPAL, &c, i, 1); + } +} +#endif + +#if 0 +static void sdl_fixed_pal_set(SDL_Surface *ss, Uint8 r, Uint8 g, Uint8 b) +{ + SDL_Color c; + int i; + + c.r = r; + c.g = g; + c.b = b; + for(i = 0; i < 256; i++) + { + SDL_SetPalette(ss, SDL_LOGPAL, &c, i, 1); + } +} +#endif + +static void +display_text_draw(struct text_render *text, struct graphics_priv *gr, struct graphics_gc_priv *fg, struct graphics_gc_priv *bg) +{ + int i, x, y, poff, soff, col_lev; + struct text_glyph *gly, **gp; + Uint32 pix; + Uint8 r, g, b; + +#if 0 + dbg(0,"%u %u %u %u, %u %u %u %u\n", + fg->fore_a, fg->fore_r, fg->fore_g, fg->fore_b, + fg->back_a, fg->back_r, fg->back_g, fg->back_b); + + dbg(0,"%u %u %u %u, %u %u %u %u\n", + bg->fore_a, bg->fore_r, bg->fore_g, bg->fore_b, + bg->back_a, bg->back_r, bg->back_g, bg->back_b); +#endif + + if(bg) + { + col_lev = bg->fore_r + bg->fore_g + bg->fore_b; + } + else + { + col_lev = 0; + } + + /* TODO: lock/unlock in draw_mode() to reduce overhead */ + SDL_LockSurface(gr->screen); + gp=text->glyph; + i=text->glyph_count; + while (i-- > 0) + { + gly=*gp++; + if (gly->w && gly->h) + { +#if 0 + if(gly->shadow && bg) + { + hexdump(gly->pixmap, gly->w, gly->h); + bitdump(gly->shadow, gly->w+2, gly->h+2); + } +#endif + + for(y = 0; y < gly->h + 2; y++) + { + if(gly->y - 1 + y < 0) + { + continue; + } + + if(((gly->y-1) + y) >= gr->screen->h) + { + break; + } + + for(x = 0; x < gly->w + 2; x++) + { + if(gly->x - 1 + x < 0) + { + continue; + } + + if(((gly->x-1) + x) >= gr->screen->w) + { + break; + } + + poff = gr->screen->w * ((gly->y-1) + y) + ((gly->x-1) + x); + poff = poff * gr->screen->format->BytesPerPixel; + + switch(gr->screen->format->BytesPerPixel) + { + case 2: + { + pix = *(Uint16 *)((Uint8*)gr->screen->pixels + poff); + break; + } + case 4: + { + pix = *(Uint32 *)((Uint8*)gr->screen->pixels + poff); + break; + } + default: + { + pix = 0; + break; + } + } + + SDL_GetRGB(pix, + gr->screen->format, + &r, + &g, + &b); + +#ifdef DEBUG + printf("%u %u -> %u off\n", + gly->x, + gly->y, + off); + + printf("%u,%u: %u %u %u in\n", + x, y, + r, g, b, off); +#endif + + + + if(gly->shadow && bg) + { + soff = (8 * ((gly->w+9)/8) * y) + x; + pix = gly->shadow[soff/8]; + + if(pix & (1 << (7-(soff&0x7)))) + { + if(col_lev >= 3*0x80) + { + r |= bg->fore_r; + g |= bg->fore_g; + b |= bg->fore_b; + } + else + { + r &= ~bg->fore_r; + g &= ~bg->fore_g; + b &= ~bg->fore_b; + } + } + } + + /* glyph */ + if((x > 0) && (x <= gly->w) && + (y > 0) && (y <= gly->h)) + { + if(bg && (col_lev >= 3*0x80)) + { + r &= ~gly->pixmap[gly->w * (y-1) + (x-1)]; + g &= ~gly->pixmap[gly->w * (y-1) + (x-1)]; + b &= ~gly->pixmap[gly->w * (y-1) + (x-1)]; + } + else + { + r |= gly->pixmap[gly->w * (y-1) + (x-1)]; + g |= gly->pixmap[gly->w * (y-1) + (x-1)]; + b |= gly->pixmap[gly->w * (y-1) + (x-1)]; + } + } + +#ifdef DEBUG + printf("%u,%u: %u %u %u out\n", + x, y, + r, g, b); +#endif + + pix = SDL_MapRGB(gr->screen->format, + r, + g, + b); + + switch(gr->screen->format->BytesPerPixel) + { + case 2: + { + *(Uint16 *)((Uint8*)gr->screen->pixels + poff) = pix; + break; + } + case 4: + { + *(Uint32 *)((Uint8*)gr->screen->pixels + poff) = pix; + break; + } + default: + { + break; + } + } + } + } + + //dbg(0, "glyph %d %d %d %d\n", g->x, g->y, g->w, g->h); + } + } + SDL_UnlockSurface(gr->screen); +} + +static void +display_text_free(struct text_render *text) +{ + int i; + struct text_glyph **gp; + + gp=text->glyph; + i=text->glyph_count; + while (i-- > 0) { + if ((*gp)->shadow) { + free((*gp)->shadow); + } + g_free(*gp++); + } + g_free(text); +} + +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 text_render *t; + + if (! font) + return; + +#if 0 + if (bg) { + gdk_gc_set_function(fg->gc, GDK_AND_INVERT); + gdk_gc_set_function(bg->gc, GDK_OR); + } +#endif + + t=display_text_render(text, font, dx, dy, p->x, p->y); + display_text_draw(t, gr, fg, bg); + display_text_free(t); +#if 0 + if (bg) { + gdk_gc_set_function(fg->gc, GDK_COPY); + gdk_gc_set_function(bg->gc, GDK_COPY); + } +#endif +} +#endif + + + +static void +draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) +{ +#ifdef SDL_IMAGE + SDL_Rect r; + + r.x = p->x; + r.y = p->y; + r.w = img->img->w; + r.h = img->img->h; + + SDL_BlitSurface(img->img, NULL, gr->screen, &r); +#endif +} + +static void +draw_image_warp(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, int count, char *data) +{ + /* TODO */ +} + +static void +draw_restore(struct graphics_priv *gr, struct point *p, int w, int h) +{ +#ifdef DEBUG + printf("draw_restore\n"); +#endif +} + +static void +background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc) +{ +#ifdef DEBUG + printf("background_gc\n"); +#endif +} + + +static void +draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) +{ +#ifdef PROFILE + struct timeval now; + unsigned long elapsed; +#endif + struct graphics_priv *ov; + SDL_Rect rect; + int i; + + if(gr->overlay_mode) + { + /* will be drawn below */ + } + else + { +#ifdef DEBUG + printf("draw_mode: %d\n", mode); +#endif + +#ifdef PROFILE + if(mode == draw_mode_begin) + { + gettimeofday(&gr->draw_begin_tv, NULL); + } +#endif + + if(mode == draw_mode_end) + { + if((gr->draw_mode == draw_mode_begin) && gr->overlay_enable) + { + for(i = 0; i < OVERLAY_MAX; i++) + { + ov = gr->overlay_array[i]; + if(ov) + { + rect.x = ov->overlay_x; + rect.y = ov->overlay_y; + rect.w = ov->screen->w; + rect.h = ov->screen->h; + SDL_BlitSurface(ov->screen, NULL, + gr->screen, &rect); + } + } + } + + SDL_Flip(gr->screen); + +#ifdef PROFILE + gettimeofday(&now, NULL); + elapsed = 1000000 * (now.tv_sec - gr->draw_begin_tv.tv_sec); + elapsed += (now.tv_usec - gr->draw_begin_tv.tv_usec); + if(elapsed >= gr->draw_time_peak) + { + dbg(0, "draw elapsed %u usec\n", elapsed); + gr->draw_time_peak = elapsed; + } +#endif + } + + gr->draw_mode = mode; + } +} + +static void overlay_disable(struct graphics_priv *gr, int disable) +{ + gr->overlay_enable = !disable; +} + +static struct graphics_priv * +overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int alpha, int wraparound); + +static int window_fullscreen(struct window *win, int on) +{ + /* TODO */ + return 0; +} + +static struct window sdl_win = +{ + NULL, + window_fullscreen + +}; + +static void * +get_data(struct graphics_priv *this, char *type) +{ + printf("get_data: %s\n", type); + + if(strcmp(type, "window") == 0) + { + return &sdl_win; + } + else + { + return &dummy; + } +} + +static void draw_drag(struct graphics_priv *gr, struct point *p) +{ + // FIXME +} + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, + draw_image_warp, + draw_restore, + draw_drag, + font_new, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, +// register_resize_callback, +// register_button_callback, +// register_motion_callback, + image_free, + get_text_bbox, + overlay_disable, +// register_keypress_callback +}; + +static struct graphics_priv * +overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h,int alpha, int wraparound) +{ + struct graphics_priv *ov; + int i, x, y; + + for(i = 0; i < OVERLAY_MAX; i++) + { + if(gr->overlay_array[i] == NULL) + { + break; + } + } + if(i == OVERLAY_MAX) + { + dbg(0, "too many overlays! increase OVERLAY_MAX\n"); + return NULL; + } + dbg(0, "x %d y %d\n", p->x, p->y); + + if(p->x < 0) + { + x = gr->screen->w + p->x; + } + else + { + x = p->x; + } + if(p->y < 0) + { + y = gr->screen->h + p->y; + } + else + { + y = p->y; + } + + dbg(1, "overlay_new %d %d %d %u %u\n", i, + x, + y, + w, + h); + + ov = g_new0(struct graphics_priv, 1); + + ov->screen = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, + w, h, +#if 1 + gr->screen->format->BitsPerPixel, + gr->screen->format->Rmask, + gr->screen->format->Gmask, + gr->screen->format->Bmask, + 0x00000000); +#else + 0x00ff0000, + 0x0000ff00, + 0x000000ff, + 0xff000000); +#endif + SDL_SetAlpha(ov->screen, SDL_SRCALPHA, 128); + + ov->overlay_mode = 1; + ov->overlay_x = x; + ov->overlay_y = y; + ov->overlay_parent = gr; + ov->overlay_idx = i; + + gr->overlay_array[i] = ov; + + *meth=graphics_methods; + + return ov; +} + + +#ifdef LINUX_TOUCHSCREEN + +#define EVFN "/dev/input/eventX" + +static int input_ts_init(struct graphics_priv *gr) +{ + struct input_id ii; + char fn[32]; +#if 0 + char name[64]; +#endif + int n, fd, ret; + + gr->ts_fd = -1; + gr->ts_hit = -1; + gr->ts_x = 0; + gr->ts_y = 0; + + strcpy(fn, EVFN); + n = 0; + while(1) + { + fn[sizeof(EVFN)-2] = '0' + n; + + fd = open(fn, O_RDONLY); + if(fd >= 0) + { +#if 0 + ret = ioctl(fd, EVIOCGNAME(64), (void *)name); + if(ret > 0) + { + printf("input_ts: %s\n", name); + } +#endif + + ret = ioctl(fd, EVIOCGID, (void *)&ii); + if(ret == 0) + { +#if 1 + printf("bustype %04x vendor %04x product %04x version %04x\n", + ii.bustype, + ii.vendor, + ii.product, + ii.version); +#endif + + if((ii.bustype == BUS_USB) && + (ii.vendor == 0x0eef) && + (ii.product == 0x0001)) + { + ret = fcntl(fd, F_SETFL, O_NONBLOCK); + if(ret == 0) + { + gr->ts_fd = fd; + } + else + { + close(fd); + } + + break; + } + } + + close(fd); + } + + n = n + 1; + + /* FIXME: should check all 32 minors */ + if(n == 10) + { + /* not found */ + ret = -1; + break; + } + } + + return ret; +} + + +/* returns 0-based display coordinate for the given ts coord */ +static void input_ts_map(int *disp_x, int *disp_y, + uint32_t ts_x, uint32_t ts_y) +{ + /* Dynamix 7" (eGalax TS) + top left = 1986,103 + top right = 61,114 + bot left = 1986,1897 + bot right = 63,1872 + + calibrate your TS using input_event_dump + and touching all four corners. use the most extreme values. + */ + +#define INPUT_TS_LEFT 1978 +#define INPUT_TS_RIGHT 48 +#define INPUT_TS_TOP 115 +#define INPUT_TS_BOT 1870 + + /* clamp first */ + if(ts_x > INPUT_TS_LEFT) + { + ts_x = INPUT_TS_LEFT; + } + if(ts_x < INPUT_TS_RIGHT) + { + ts_x = INPUT_TS_RIGHT; + } + + ts_x = ts_x - INPUT_TS_RIGHT; + + *disp_x = ((DISPLAY_W-1) * ts_x) / (INPUT_TS_LEFT - INPUT_TS_RIGHT); + *disp_x = (DISPLAY_W-1) - *disp_x; + + + if(ts_y > INPUT_TS_BOT) + { + ts_y = INPUT_TS_BOT; + } + if(ts_y < INPUT_TS_TOP) + { + ts_y = INPUT_TS_TOP; + } + + ts_y = ts_y - INPUT_TS_TOP; + + *disp_y = ((DISPLAY_H-1) * ts_y) / (INPUT_TS_BOT - INPUT_TS_TOP); +/* *disp_y = (DISPLAY_H-1) - *disp_y; */ +} + +#if 0 +static void input_event_dump(struct input_event *ie) +{ + printf("input_event:\n" + "\ttv_sec\t%u\n" + "\ttv_usec\t%lu\n" + "\ttype\t%u\n" + "\tcode\t%u\n" + "\tvalue\t%d\n", + (unsigned int)ie->time.tv_sec, + ie->time.tv_usec, + ie->type, + ie->code, + ie->value); +} +#endif + +static int input_ts_exit(struct graphics_priv *gr) +{ + close(gr->ts_fd); + gr->ts_fd = -1; + + return 0; +} +#endif + + +static gboolean graphics_sdl_idle(void *data) +{ + struct graphics_priv *gr = (struct graphics_priv *)data; + struct point p; + SDL_Event ev; +#ifdef LINUX_TOUCHSCREEN + struct input_event ie; + ssize_t ss; +#endif + int ret, key; + char keybuf[2]; + + /* generate the initial resize callback, so the gui knows W/H + + its unsafe to do this directly inside register_resize_callback; + graphics_gtk does it during Configure, but SDL does not have + an equivalent event, so we use our own flag + */ + if(gr->resize_callback_initial != 0) + { + callback_list_call_attr_2(gr->cbl, attr_resize, (void *)gr->screen->w, (void *)gr->screen->h); + gr->resize_callback_initial = 0; + } + +#ifdef LINUX_TOUCHSCREEN + if(gr->ts_fd >= 0) + { + ss = read(gr->ts_fd, (void *)&ie, sizeof(ie)); + if(ss == sizeof(ie)) + { + /* we (usually) get three events on a touchscreen hit: + 1: type =EV_KEY + code =330 [BTN_TOUCH] + value=1 + + 2: type =EV_ABS + code =0 [X] + value=X pos + + 3: type =EV_ABS + code =1 [Y] + value=Y pos + + 4: type =EV_SYN + + once hit, if the contact point changes, we'll get more + EV_ABS (for 1 or both axes), followed by an EV_SYN. + + and, on a lift: + + 5: type =EV_KEY + code =330 [BTN_TOUCH] + value=0 + + 6: type =EV_SYN + */ + switch(ie.type) + { + case EV_KEY: + { + if(ie.code == BTN_TOUCH) + { + gr->ts_hit = ie.value; + } + + break; + } + + case EV_ABS: + { + if(ie.code == 0) + { + gr->ts_x = ie.value; + } + else if(ie.code == 1) + { + gr->ts_y = ie.value; + } + + break; + } + + case EV_SYN: + { + input_ts_map(&p.x, &p.y, gr->ts_x, gr->ts_y); + + /* always send MOUSE_MOTION (first) */ + callback_list_call_attr_1(gr->cbl, attr_motion, (void *)&p); + if(gr->ts_hit > 0) + { + callback_list_call_attr_3(gr->cbl, attr_button, (void *)1, (void *)SDL_BUTTON_LEFT, (void *)&p); + } + else if(gr->ts_hit == 0) + { + callback_list_call_attr_3(gr->cbl, attr_button, (void *)0, (void *)SDL_BUTTON_LEFT, (void *)&p); + } + + /* reset ts_hit */ + gr->ts_hit = -1; + + break; + } + + default: + { + break; + } + } + } + } +#endif + + while(1) + { + ret = SDL_PollEvent(&ev); + if(ret == 0) + { + break; + } + + switch(ev.type) + { + case SDL_MOUSEMOTION: + { + p.x = ev.motion.x; + p.y = ev.motion.y; + callback_list_call_attr_1(gr->cbl, attr_motion, (void *)&p); + break; + } + + case SDL_KEYDOWN: + { + switch(ev.key.keysym.sym) + { + case SDLK_LEFT: + { + key = NAVIT_KEY_LEFT; + break; + } + case SDLK_RIGHT: + { + key = NAVIT_KEY_RIGHT; + break; + } + case SDLK_BACKSPACE: + { + key = NAVIT_KEY_BACKSPACE; + break; + } + case SDLK_RETURN: + { + key = NAVIT_KEY_RETURN; + break; + } + case SDLK_DOWN: + { + key = NAVIT_KEY_DOWN; + break; + } + case SDLK_PAGEUP: + { + key = NAVIT_KEY_ZOOM_OUT; + break; + } + case SDLK_UP: + { + key = NAVIT_KEY_UP; + break; + } + case SDLK_PAGEDOWN: + { + key = NAVIT_KEY_ZOOM_IN; + break; + } + default: + { + key = 0; + break; + } + } + + keybuf[0] = key; + keybuf[1] = '\0'; + callback_list_call_attr_1(gr->cbl, attr_keypress, (void *)keybuf); + + break; + } + + case SDL_KEYUP: + { + break; + } + + case SDL_MOUSEBUTTONDOWN: + { +#ifdef DEBUG + printf("SDL_MOUSEBUTTONDOWN %d %d %d %d %d\n", + ev.button.which, + ev.button.button, + ev.button.state, + ev.button.x, + ev.button.y); +#endif + + p.x = ev.button.x; + p.y = ev.button.y; + callback_list_call_attr_3(gr->cbl, attr_button, (void *)1, (void *)(int)ev.button.button, (void *)&p); + break; + } + + case SDL_MOUSEBUTTONUP: + { +#ifdef DEBUG + printf("SDL_MOUSEBUTTONUP %d %d %d %d %d\n", + ev.button.which, + ev.button.button, + ev.button.state, + ev.button.x, + ev.button.y); +#endif + + p.x = ev.button.x; + p.y = ev.button.y; + callback_list_call_attr_3(gr->cbl, attr_button, (void *)0, (void *)(int)ev.button.button, (void *)&p); + break; + } + + case SDL_QUIT: + { + navit_destroy(gr->nav); + break; + } + + case SDL_VIDEORESIZE: + { + + gr->screen = SDL_SetVideoMode(ev.resize.w, ev.resize.h, 16, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_RESIZABLE); + if(gr->screen == NULL) + { + navit_destroy(gr->nav); + } + else + { + callback_list_call_attr_2(gr->cbl, attr_resize, (void *)gr->screen->w, (void *)gr->screen->h); + } + + break; + } + + default: + { +#ifdef DEBUG + printf("SDL_Event %d\n", ev.type); +#endif + break; + } + } + } + + return TRUE; +} + + +static struct graphics_priv * +graphics_sdl_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl) +{ + struct graphics_priv *this=g_new0(struct graphics_priv, 1); + struct attr *attr; + int ret; + int w=DISPLAY_W,h=DISPLAY_H; + + this->nav = nav; + this->cbl = cbl; + + ret = SDL_Init(SDL_INIT_VIDEO); + if(ret < 0) + { + g_free(this); + return NULL; + } + +#ifdef SDL_TTF + ret = TTF_Init(); + if(ret < 0) + { + g_free(this); + SDL_Quit(); + return NULL; + } +#else + FT_Init_FreeType( &this->library ); +#endif + + if (! event_request_system("glib","graphics_sdl_new")) + return NULL; + + + /* TODO: xml params for BPP */ + if ((attr=attr_search(attrs, NULL, attr_w))) + w=attr->u.num; + if ((attr=attr_search(attrs, NULL, attr_h))) + h=attr->u.num; + + + this->screen = SDL_SetVideoMode(w, h, 16, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_RESIZABLE); + if(this->screen == NULL) + { + g_free(this); + SDL_Quit(); + return NULL; + } + + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + + SDL_WM_SetCaption("navit", NULL); + +#ifdef LINUX_TOUCHSCREEN + input_ts_init(this); + if(this->ts_fd >= 0) + { + /* mouse cursor does not always display correctly in Linux FB. + anyway, it is unnecessary w/ a touch screen + */ + SDL_ShowCursor(0); + } +#endif + +#ifdef SDL_SGE + sge_Update_OFF(); + sge_Lock_ON(); +#endif + + *meth=graphics_methods; + + g_timeout_add(10, graphics_sdl_idle, this); + + this->overlay_enable = 1; + + this->aa = 1; + if((attr=attr_search(attrs, NULL, attr_antialias))) + this->aa = attr->u.num; + + this->resize_callback_initial=1; + return this; +} + +void +plugin_init(void) +{ + plugin_register_graphics_type("sdl", graphics_sdl_new); +} + diff --git a/graphics/sdl/raster.c b/graphics/sdl/raster.c new file mode 100644 index 00000000..dd54cd4a --- /dev/null +++ b/graphics/sdl/raster.c @@ -0,0 +1,2022 @@ +/* raster.c -- line/rect/circle/poly rasterization + + copyright (c) 2008 bryan rittmeyer <bryanr@bryanr.org> + license: LGPLv2 + + based on SDL_gfx (c) A. Schiffler + and SGE (c)1999-2003 Anders Lindström + + + added filled anti-aliased polygons + + fixed some bugs and improved performance + + revision history + 2008-07-05 initial + 2008-07-06 aacircle +*/ + + +#include <math.h> + +#include "raster.h" + +#undef DEBUG +#undef PARANOID + + +/* raster_rect */ + +static inline void raster_rect_inline(SDL_Surface *dst, int16_t x1, int16_t y1, int16_t w, int16_t h, uint32_t color) +{ + /* sge */ + SDL_Rect rect; + +#if 1 + if((w <= 0) || (h <= 0)) + { + return; + } + +#if 0 + if(x1 < 0) + { + if((x1 + w) < 0) + { + return; + } + else + { + w = w + x1; + x1 = 0; + } + } + if(y1 < 0) + { + if((y1 + h) < 0) + { + return; + } + else + { + h = h + y1; + y1 = 0; + } + } + + if(x1 + w >= dst->w) + { + w = dst->w - x1; + } + if(y1 + h >= dst->h) + { + h = dst->h - y1; + } +#endif + + rect.x = x1; + rect.y = y1; + rect.w = w; + rect.h = h; + + /* will clip internally */ + SDL_FillRect(dst, &rect, color); + +#else + + x2 = x1 + w; + y2 = y1 + h; + + /* + * Order coordinates to ensure that + * x1<=x2 and y1<=y2 + */ + if (x1 > x2) { + tmp = x1; + x1 = x2; + x2 = tmp; + } + if (y1 > y2) { + tmp = y1; + y1 = y2; + y2 = tmp; + } + + /* + * Get clipping boundary and + * check visibility + */ + left = dst->clip_rect.x; + if (x2<left) { + return(0); + } + right = dst->clip_rect.x + dst->clip_rect.w - 1; + if (x1>right) { + return(0); + } + top = dst->clip_rect.y; + if (y2<top) { + return(0); + } + bottom = dst->clip_rect.y + dst->clip_rect.h - 1; + if (y1>bottom) { + return(0); + } + + /* Clip all points */ + if (x1<left) { + x1=left; + } else if (x1>right) { + x1=right; + } + if (x2<left) { + x2=left; + } else if (x2>right) { + x2=right; + } + if (y1<top) { + y1=top; + } else if (y1>bottom) { + y1=bottom; + } + if (y2<top) { + y2=top; + } else if (y2>bottom) { + y2=bottom; + } + +#if 0 + /* + * Test for special cases of straight line or single point + */ + if (x1 == x2) { + if (y1 == y2) { + return (pixelColor(dst, x1, y1, color)); + } else { + return (vlineColor(dst, x1, y1, y2, color)); + } + } + if (y1 == y2) { + return (hlineColor(dst, x1, x2, y1, color)); + } +#endif + + /* + * Calculate width&height + */ + w = x2 - x1; + h = y2 - y1; + + /* + * No alpha-blending required + */ + +#if 0 + /* + * Setup color + */ + colorptr = (Uint8 *) & color; + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + color = SDL_MapRGBA(dst->format, colorptr[0], colorptr[1], colorptr[2], colorptr[3]); + } else { + color = SDL_MapRGBA(dst->format, colorptr[3], colorptr[2], colorptr[1], colorptr[0]); + } +#endif + + /* + * Lock surface + */ + SDL_LockSurface(dst); + + /* + * More variable setup + */ + dx = w; + dy = h; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8 *) dst->pixels) + pixx * (int) x1 + pixy * (int) y1; + pixellast = pixel + pixx * dx + pixy * dy; + dx++; + + /* + * Draw + */ + switch (dst->format->BytesPerPixel) { + case 1: + for (; pixel <= pixellast; pixel += pixy) { + memset(pixel, (Uint8) color, dx); + } + break; + case 2: + pixy -= (pixx * dx); + for (; pixel <= pixellast; pixel += pixy) { + for (x = 0; x < dx; x++) { + *(Uint16*) pixel = color; + pixel += pixx; + } + } + break; + case 3: + pixy -= (pixx * dx); + for (; pixel <= pixellast; pixel += pixy) { + for (x = 0; x < dx; x++) { + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + pixel += pixx; + } + } + break; + default: /* case 4 */ + pixy -= (pixx * dx); + for (; pixel <= pixellast; pixel += pixy) { + for (x = 0; x < dx; x++) { + *(Uint32 *) pixel = color; + pixel += pixx; + } + } + break; + } + + /* + * Unlock surface + */ + SDL_UnlockSurface(dst); + +#endif +} + +void raster_rect(SDL_Surface *s, int16_t x, int16_t y, int16_t w, int16_t h, uint32_t col) +{ + raster_rect_inline(s, x, y, w, h, col); +} + +#define raster_rect_inline raster_rect + + +/* raster :: pixel */ + +#define MODIFIED_ALPHA_PIXEL_ROUTINE + + +#define clip_xmin(surface) surface->clip_rect.x +#define clip_xmax(surface) surface->clip_rect.x+surface->clip_rect.w-1 +#define clip_ymin(surface) surface->clip_rect.y +#define clip_ymax(surface) surface->clip_rect.y+surface->clip_rect.h-1 + + +static void raster_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color) +{ + if(x>=clip_xmin(surface) && x<=clip_xmax(surface) && y>=clip_ymin(surface) && y<=clip_ymax(surface)){ + switch (surface->format->BytesPerPixel) { + case 1: { /* Assuming 8-bpp */ + *((Uint8 *)surface->pixels + y*surface->pitch + x) = color; + } + break; + + case 2: { /* Probably 15-bpp or 16-bpp */ + *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color; + } + break; + + case 3: { /* Slow 24-bpp mode, usually not used */ + Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3; + + /* Gack - slow, but endian correct */ + *(pix+surface->format->Rshift/8) = color>>surface->format->Rshift; + *(pix+surface->format->Gshift/8) = color>>surface->format->Gshift; + *(pix+surface->format->Bshift/8) = color>>surface->format->Bshift; + *(pix+surface->format->Ashift/8) = color>>surface->format->Ashift; + } + break; + + case 4: { /* Probably 32-bpp */ + *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color; + } + break; + } + } +} + +/* PutPixel routine with alpha blending, input color in destination format */ + +/* New, faster routine - default blending pixel */ + +static void raster_PutPixelAlpha(SDL_Surface * surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha) +{ + /* sdl-gfx */ + Uint32 Rmask = surface->format->Rmask, Gmask = + surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask; + Uint32 R = 0, G = 0, B = 0, A = 0; + + if (x >= clip_xmin(surface) && x <= clip_xmax(surface) + && y >= clip_ymin(surface) && y <= clip_ymax(surface)) { + + switch (surface->format->BytesPerPixel) { + case 1:{ /* Assuming 8-bpp */ + if (alpha == 255) { + *((Uint8 *) surface->pixels + y * surface->pitch + x) = color; + } else { + Uint8 *pixel = (Uint8 *) surface->pixels + y * surface->pitch + x; + + Uint8 dR = surface->format->palette->colors[*pixel].r; + Uint8 dG = surface->format->palette->colors[*pixel].g; + Uint8 dB = surface->format->palette->colors[*pixel].b; + Uint8 sR = surface->format->palette->colors[color].r; + Uint8 sG = surface->format->palette->colors[color].g; + Uint8 sB = surface->format->palette->colors[color].b; + + dR = dR + ((sR - dR) * alpha >> 8); + dG = dG + ((sG - dG) * alpha >> 8); + dB = dB + ((sB - dB) * alpha >> 8); + + *pixel = SDL_MapRGB(surface->format, dR, dG, dB); + } + } + break; + + case 2:{ /* Probably 15-bpp or 16-bpp */ + if (alpha == 255) { + *((Uint16 *) surface->pixels + y * surface->pitch / 2 + x) = color; + } else { + Uint16 *pixel = (Uint16 *) surface->pixels + y * surface->pitch / 2 + x; + Uint32 dc = *pixel; + + R = ((dc & Rmask) + (((color & Rmask) - (dc & Rmask)) * alpha >> 8)) & Rmask; + G = ((dc & Gmask) + (((color & Gmask) - (dc & Gmask)) * alpha >> 8)) & Gmask; + B = ((dc & Bmask) + (((color & Bmask) - (dc & Bmask)) * alpha >> 8)) & Bmask; + if (Amask) + A = ((dc & Amask) + (((color & Amask) - (dc & Amask)) * alpha >> 8)) & Amask; + + *pixel = R | G | B | A; + } + } + break; + + case 3:{ /* Slow 24-bpp mode, usually not used */ + Uint8 *pix = (Uint8 *) surface->pixels + y * surface->pitch + x * 3; + Uint8 rshift8 = surface->format->Rshift / 8; + Uint8 gshift8 = surface->format->Gshift / 8; + Uint8 bshift8 = surface->format->Bshift / 8; + Uint8 ashift8 = surface->format->Ashift / 8; + + + if (alpha == 255) { + *(pix + rshift8) = color >> surface->format->Rshift; + *(pix + gshift8) = color >> surface->format->Gshift; + *(pix + bshift8) = color >> surface->format->Bshift; + *(pix + ashift8) = color >> surface->format->Ashift; + } else { + Uint8 dR, dG, dB, dA = 0; + Uint8 sR, sG, sB, sA = 0; + + pix = (Uint8 *) surface->pixels + y * surface->pitch + x * 3; + + dR = *((pix) + rshift8); + dG = *((pix) + gshift8); + dB = *((pix) + bshift8); + dA = *((pix) + ashift8); + + sR = (color >> surface->format->Rshift) & 0xff; + sG = (color >> surface->format->Gshift) & 0xff; + sB = (color >> surface->format->Bshift) & 0xff; + sA = (color >> surface->format->Ashift) & 0xff; + + dR = dR + ((sR - dR) * alpha >> 8); + dG = dG + ((sG - dG) * alpha >> 8); + dB = dB + ((sB - dB) * alpha >> 8); + dA = dA + ((sA - dA) * alpha >> 8); + + *((pix) + rshift8) = dR; + *((pix) + gshift8) = dG; + *((pix) + bshift8) = dB; + *((pix) + ashift8) = dA; + } + } + break; + +#ifdef ORIGINAL_ALPHA_PIXEL_ROUTINE + + case 4:{ /* Probably :-) 32-bpp */ + if (alpha == 255) { + *((Uint32 *) surface->pixels + y * surface->pitch / 4 + x) = color; + } else { + Uint32 Rshift, Gshift, Bshift, Ashift; + Uint32 *pixel = (Uint32 *) surface->pixels + y * surface->pitch / 4 + x; + Uint32 dc = *pixel; + + Rshift = surface->format->Rshift; + Gshift = surface->format->Gshift; + Bshift = surface->format->Bshift; + Ashift = surface->format->Ashift; + + R = ((dc & Rmask) + (((((color & Rmask) - (dc & Rmask)) >> Rshift) * alpha >> 8) << Rshift)) & Rmask; + G = ((dc & Gmask) + (((((color & Gmask) - (dc & Gmask)) >> Gshift) * alpha >> 8) << Gshift)) & Gmask; + B = ((dc & Bmask) + (((((color & Bmask) - (dc & Bmask)) >> Bshift) * alpha >> 8) << Bshift)) & Bmask; + if (Amask) + A = ((dc & Amask) + (((((color & Amask) - (dc & Amask)) >> Ashift) * alpha >> 8) << Ashift)) & Amask; + + *pixel = R | G | B | A; + } + } + break; +#endif + +#ifdef MODIFIED_ALPHA_PIXEL_ROUTINE + + case 4:{ /* Probably :-) 32-bpp */ + if (alpha == 255) { + *((Uint32 *) surface->pixels + y * surface->pitch / 4 + x) = color; + } else { + Uint32 Rshift, Gshift, Bshift, Ashift; + Uint32 *pixel = (Uint32 *) surface->pixels + y * surface->pitch / 4 + x; + Uint32 dc = *pixel; + Uint32 dR = (color & Rmask), dG = (color & Gmask), dB = (color & Bmask); + Uint32 surfaceAlpha, preMultR, preMultG, preMultB; + Uint32 aTmp; + + Rshift = surface->format->Rshift; + Gshift = surface->format->Gshift; + Bshift = surface->format->Bshift; + Ashift = surface->format->Ashift; + + preMultR = (alpha * (dR>>Rshift)); + preMultG = (alpha * (dG>>Gshift)); + preMultB = (alpha * (dB>>Bshift)); + + surfaceAlpha = ((dc & Amask) >> Ashift); + aTmp = (255 - alpha); + if ((A = 255 - ((aTmp * (255 - surfaceAlpha)) >> 8 ))) { + aTmp *= surfaceAlpha; + R = (preMultR + ((aTmp * ((dc & Rmask) >> Rshift)) >> 8)) / A << Rshift & Rmask; + G = (preMultG + ((aTmp * ((dc & Gmask) >> Gshift)) >> 8)) / A << Gshift & Gmask; + B = (preMultB + ((aTmp * ((dc & Bmask) >> Bshift)) >> 8)) / A << Bshift & Bmask; + } + *pixel = R | G | B | (A << Ashift & Amask); + + } + } + break; +#endif + } + } +} + + +/* FIXME: eliminate these 2 functions */ + +static int raster_pixelColorNolock(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color) +{ + int result = 0; + +#if 0 + /* + * Setup color + */ + alpha = color & 0x000000ff; + mcolor = + SDL_MapRGBA(dst->format, (color & 0xff000000) >> 24, + (color & 0x00ff0000) >> 16, (color & 0x0000ff00) >> 8, alpha); +#endif + + /* + * Draw + */ + raster_PutPixel(dst, x, y, color); + + return (result); +} + + +/* Pixel - using alpha weight on color for AA-drawing - no locking */ + +static int raster_pixelColorWeightNolock(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color, Uint32 weight) +{ +#if 0 + Uint32 a; + + /* + * Get alpha + */ + a = (color & (Uint32) 0x000000ff); + + /* + * Modify Alpha by weight + */ + a = ((a * weight) >> 8); +#endif + + raster_PutPixelAlpha(dst, x, y, color, weight); + + return 0; +} + + +/* raster :: line */ + + + +static inline void raster_hline(SDL_Surface * dst, Sint16 x1, Sint16 x2, Sint16 y, Uint32 color) +{ +#if 1 + SDL_Rect l; + + /* sge */ + if(x1>x2){Sint16 tmp=x1; x1=x2; x2=tmp;} + + l.x=x1; l.y=y; l.w=x2-x1+1; l.h=1; + + SDL_FillRect(dst, &l, color); + +#else + /* sdl_gfx */ + Sint16 left, right, top, bottom; + Uint8 *pixel, *pixellast; + int dx; + int pixx, pixy; + Sint16 w; + Sint16 xtmp; + int result = -1; +#if 0 + int i; + union + { + double d; + uint16_t col[4]; + } doub; + + for(i = 0; i < 4; i++) + { + doub.col[i] = color; + } +#endif + + /* + * Check visibility of clipping rectangle + */ + if ((dst->clip_rect.w==0) || (dst->clip_rect.h==0)) { + return(0); + } + + /* + * Swap x1, x2 if required to ensure x1<=x2 + */ + if (x1 > x2) { + xtmp = x1; + x1 = x2; + x2 = xtmp; + } + + /* + * Get clipping boundary and + * check visibility of hline + */ + left = dst->clip_rect.x; + if (x2<left) { + return(0); + } + right = dst->clip_rect.x + dst->clip_rect.w - 1; + if (x1>right) { + return(0); + } + top = dst->clip_rect.y; + bottom = dst->clip_rect.y + dst->clip_rect.h - 1; + if ((y<top) || (y>bottom)) { + return (0); + } + + /* + * Clip x + */ + if (x1 < left) { + x1 = left; + } + if (x2 > right) { + x2 = right; + } + + /* + * Calculate width + */ + w = x2 - x1; + +#if 0 + printf("raster_hline %d %d %d %d\n", x1, x2, y, w); +#endif + + /* + * Lock surface + */ + if (SDL_MUSTLOCK(dst)) { + SDL_LockSurface(dst); + } + + /* + * More variable setup + */ + dx = w; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8 *) dst->pixels) + pixx * (int) x1 + pixy * (int) y; + + /* + * Draw + */ + switch (dst->format->BytesPerPixel) { + case 1: + memset(pixel, color, dx); + break; + case 2: + pixellast = pixel + dx + dx; +#if 0 + for (; (pixel+3) <= pixellast; pixel += 4*pixx) + { + *(double *)pixel = doub.d; + } +#endif + for (; pixel <= pixellast; pixel += pixx) { + *(Uint16 *) pixel = color; + } + break; + case 3: + pixellast = pixel + dx + dx + dx; + for (; pixel <= pixellast; pixel += pixx) { + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + } + break; + default: /* case 4 */ + dx = dx + dx; + pixellast = pixel + dx + dx; + for (; pixel <= pixellast; pixel += pixx) { + *(Uint32 *) pixel = color; + } + break; + } + + /* + * Unlock surface + */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + /* + * Set result code + */ + result = 0; + + return (result); +#endif +} + +static void raster_vline(SDL_Surface *dst, Sint16 x, Sint16 y1, Sint16 y2, Uint32 color) +{ + SDL_Rect l; + + if(y1>y2){Sint16 tmp=y1; y1=y2; y2=tmp;} + + l.x=x; l.y=y1; l.w=1; l.h=y2-y1+1; + + SDL_FillRect(dst, &l, color); +} + + + +/* raster_line */ +#define CLIP_LEFT_EDGE 0x1 +#define CLIP_RIGHT_EDGE 0x2 +#define CLIP_BOTTOM_EDGE 0x4 +#define CLIP_TOP_EDGE 0x8 +#define CLIP_INSIDE(a) (!a) +#define CLIP_REJECT(a,b) (a&b) +#define CLIP_ACCEPT(a,b) (!(a|b)) + +static int clipEncode(Sint16 x, Sint16 y, Sint16 left, Sint16 top, Sint16 right, Sint16 bottom) +{ + int code = 0; + + if (x < left) { + code |= CLIP_LEFT_EDGE; + } else if (x > right) { + code |= CLIP_RIGHT_EDGE; + } + if (y < top) { + code |= CLIP_TOP_EDGE; + } else if (y > bottom) { + code |= CLIP_BOTTOM_EDGE; + } + return code; +} + +static int clipLine(SDL_Surface * dst, Sint16 * x1, Sint16 * y1, Sint16 * x2, Sint16 * y2) +{ + Sint16 left, right, top, bottom; + int code1, code2; + int draw = 0; + Sint16 swaptmp; + float m; + + /* + * Get clipping boundary + */ + left = dst->clip_rect.x; + right = dst->clip_rect.x + dst->clip_rect.w - 1; + top = dst->clip_rect.y; + bottom = dst->clip_rect.y + dst->clip_rect.h - 1; + + while (1) { + code1 = clipEncode(*x1, *y1, left, top, right, bottom); + code2 = clipEncode(*x2, *y2, left, top, right, bottom); + if (CLIP_ACCEPT(code1, code2)) { + draw = 1; + break; + } else if (CLIP_REJECT(code1, code2)) + break; + else { + if (CLIP_INSIDE(code1)) { + swaptmp = *x2; + *x2 = *x1; + *x1 = swaptmp; + swaptmp = *y2; + *y2 = *y1; + *y1 = swaptmp; + swaptmp = code2; + code2 = code1; + code1 = swaptmp; + } + if (*x2 != *x1) { + m = (*y2 - *y1) / (float) (*x2 - *x1); + } else { + m = 1.0f; + } + if (code1 & CLIP_LEFT_EDGE) { + *y1 += (Sint16) ((left - *x1) * m); + *x1 = left; + } else if (code1 & CLIP_RIGHT_EDGE) { + *y1 += (Sint16) ((right - *x1) * m); + *x1 = right; + } else if (code1 & CLIP_BOTTOM_EDGE) { + if (*x2 != *x1) { + *x1 += (Sint16) ((bottom - *y1) / m); + } + *y1 = bottom; + } else if (code1 & CLIP_TOP_EDGE) { + if (*x2 != *x1) { + *x1 += (Sint16) ((top - *y1) / m); + } + *y1 = top; + } + } + } + + return draw; +} + + +void raster_line(SDL_Surface *dst, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t color) +{ + /* sdl-gfx */ + int pixx, pixy; + int x, y; + int dx, dy; + int sx, sy; + int swaptmp; + Uint8 *pixel; + + /* + * Clip line and test if we have to draw + */ + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return; + } + + /* + * Test for special cases of straight lines or single point + */ + if (x1 == x2) { + if (y1 < y2) { + raster_vline(dst, x1, y1, y2, color); + return; + } else if (y1 > y2) { + raster_vline(dst, x1, y2, y1, color); + return; + } else { + raster_PutPixel(dst, x1, y1, color); + return; + } + } + if (y1 == y2) { + if (x1 < x2) { + raster_hline(dst, x1, x2, y1, color); + return; + } else if (x1 > x2) { + raster_hline(dst, x2, x1, y1, color); + return; + } + } + + + /* + * Variable setup + */ + dx = x2 - x1; + dy = y2 - y1; + sx = (dx >= 0) ? 1 : -1; + sy = (dy >= 0) ? 1 : -1; + + /* Lock surface */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return; + } + } + + /* + * No alpha blending - use fast pixel routines + */ + +#if 0 + /* + * Setup color + */ + colorptr = (Uint8 *) & color; + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + color = SDL_MapRGBA(dst->format, colorptr[0], colorptr[1], colorptr[2], colorptr[3]); + } else { + color = SDL_MapRGBA(dst->format, colorptr[3], colorptr[2], colorptr[1], colorptr[0]); + } +#endif + + /* + * More variable setup + */ + dx = sx * dx + 1; + dy = sy * dy + 1; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8 *) dst->pixels) + pixx * (int) x1 + pixy * (int) y1; + pixx *= sx; + pixy *= sy; + if (dx < dy) { + swaptmp = dx; + dx = dy; + dy = swaptmp; + swaptmp = pixx; + pixx = pixy; + pixy = swaptmp; + } + + /* + * Draw + */ + x = 0; + y = 0; + switch (dst->format->BytesPerPixel) { + case 1: + for (; x < dx; x++, pixel += pixx) { + *pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 2: + for (; x < dx; x++, pixel += pixx) { + *(Uint16 *) pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 3: + for (; x < dx; x++, pixel += pixx) { + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + default: /* case 4 */ + for (; x < dx; x++, pixel += pixx) { + *(Uint32 *) pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + } + + /* Unlock surface */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return; +} + + +#define AAlevels 256 +#define AAbits 8 + +static void raster_aalineColorInt(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color, int draw_endpoint) +{ + Sint32 xx0, yy0, xx1, yy1; + int result; + Uint32 intshift, erracc, erradj; + Uint32 erracctmp, wgt, wgtcompmask; + int dx, dy, tmp, xdir, y0p1, x0pxdir; + + /* + * Check visibility of clipping rectangle + */ + if ((dst->clip_rect.w==0) || (dst->clip_rect.h==0)) { + return; + } + + /* + * Clip line and test if we have to draw + */ + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return; + } + + /* + * Keep on working with 32bit numbers + */ + xx0 = x1; + yy0 = y1; + xx1 = x2; + yy1 = y2; + + /* + * Reorder points if required + */ + if (yy0 > yy1) { + tmp = yy0; + yy0 = yy1; + yy1 = tmp; + tmp = xx0; + xx0 = xx1; + xx1 = tmp; + } + + /* + * Calculate distance + */ + dx = xx1 - xx0; + dy = yy1 - yy0; + + /* + * Adjust for negative dx and set xdir + */ + if (dx >= 0) { + xdir = 1; + } else { + xdir = -1; + dx = (-dx); + } + + /* + * Check for special cases + */ + if (dx == 0) { + /* + * Vertical line + */ + raster_vline(dst, x1, y1, y2, color); + return; + } else if (dy == 0) { + /* + * Horizontal line + */ + raster_hline(dst, x1, x2, y1, color); + return; + } else if (dx == dy) { + /* + * Diagonal line + */ + raster_line(dst, x1, y1, x2, y2, color); + return; + } + + /* + * Line is not horizontal, vertical or diagonal + */ + result = 0; + + /* + * Zero accumulator + */ + erracc = 0; + + /* + * # of bits by which to shift erracc to get intensity level + */ + intshift = 32 - AAbits; + /* + * Mask used to flip all bits in an intensity weighting + */ + wgtcompmask = AAlevels - 1; + + /* Lock surface */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return; + } + } + + /* + * Draw the initial pixel in the foreground color + */ + raster_pixelColorNolock(dst, x1, y1, color); + + /* + * x-major or y-major? + */ + if (dy > dx) { + + /* + * y-major. Calculate 16-bit fixed point fractional part of a pixel that + * X advances every time Y advances 1 pixel, truncating the result so that + * we won't overrun the endpoint along the X axis + */ + /* + * Not-so-portable version: erradj = ((Uint64)dx << 32) / (Uint64)dy; + */ + erradj = ((dx << 16) / dy) << 16; + + /* + * draw all pixels other than the first and last + */ + x0pxdir = xx0 + xdir; + while (--dy) { + erracctmp = erracc; + erracc += erradj; + if (erracc <= erracctmp) { + /* + * rollover in error accumulator, x coord advances + */ + xx0 = x0pxdir; + x0pxdir += xdir; + } + yy0++; /* y-major so always advance Y */ + + /* + * the AAbits most significant bits of erracc give us the intensity + * weighting for this pixel, and the complement of the weighting for + * the paired pixel. + */ + wgt = (erracc >> intshift) & 255; + raster_pixelColorWeightNolock (dst, xx0, yy0, color, 255 - wgt); + raster_pixelColorWeightNolock (dst, x0pxdir, yy0, color, wgt); + } + + } else { + + /* + * x-major line. Calculate 16-bit fixed-point fractional part of a pixel + * that Y advances each time X advances 1 pixel, truncating the result so + * that we won't overrun the endpoint along the X axis. + */ + /* + * Not-so-portable version: erradj = ((Uint64)dy << 32) / (Uint64)dx; + */ + erradj = ((dy << 16) / dx) << 16; + + /* + * draw all pixels other than the first and last + */ + y0p1 = yy0 + 1; + while (--dx) { + + erracctmp = erracc; + erracc += erradj; + if (erracc <= erracctmp) { + /* + * Accumulator turned over, advance y + */ + yy0 = y0p1; + y0p1++; + } + xx0 += xdir; /* x-major so always advance X */ + /* + * the AAbits most significant bits of erracc give us the intensity + * weighting for this pixel, and the complement of the weighting for + * the paired pixel. + */ + wgt = (erracc >> intshift) & 255; + raster_pixelColorWeightNolock (dst, xx0, yy0, color, 255 - wgt); + raster_pixelColorWeightNolock (dst, xx0, y0p1, color, wgt); + } + } + + /* + * Do we have to draw the endpoint + */ + if (draw_endpoint) { + /* + * Draw final pixel, always exactly intersected by the line and doesn't + * need to be weighted. + */ + raster_pixelColorNolock (dst, x2, y2, color); + } + + /* Unlock surface */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } +} + +void raster_aaline(SDL_Surface *s, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t col) +{ + raster_aalineColorInt(s, x1, y1, x2, y2, col, 1); +} + + + +/* raster :: circle */ + +void raster_circle(SDL_Surface *dst, int16_t x, int16_t y, int16_t r, uint32_t color) +{ + /* sdl-gfx */ + Sint16 left, right, top, bottom; + int result; + Sint16 x1, y1, x2, y2; + Sint16 cx = 0; + Sint16 cy = r; + Sint16 ocx = (Sint16) 0xffff; + Sint16 ocy = (Sint16) 0xffff; + Sint16 df = 1 - r; + Sint16 d_e = 3; + Sint16 d_se = -2 * r + 5; + Sint16 xpcx, xmcx, xpcy, xmcy; + Sint16 ypcy, ymcy, ypcx, ymcx; + + /* + * Check visibility of clipping rectangle + */ + if ((dst->clip_rect.w==0) || (dst->clip_rect.h==0)) { + return; + } + + /* + * Sanity check radius + */ + if (r < 0) { + return; + } + + /* + * Special case for r=0 - draw a point + */ + if (r == 0) { + return (raster_PutPixel(dst, x, y, color)); + } + + /* + * Get circle and clipping boundary and + * test if bounding box of circle is visible + */ + x2 = x + r; + left = dst->clip_rect.x; + if (x2<left) { + return; + } + x1 = x - r; + right = dst->clip_rect.x + dst->clip_rect.w - 1; + if (x1>right) { + return; + } + y2 = y + r; + top = dst->clip_rect.y; + if (y2<top) { + return; + } + y1 = y - r; + bottom = dst->clip_rect.y + dst->clip_rect.h - 1; + if (y1>bottom) { + return; + } + + /* + * Draw + */ + result = 0; + do { + xpcx = x + cx; + xmcx = x - cx; + xpcy = x + cy; + xmcy = x - cy; + if (ocy != cy) { + if (cy > 0) { + ypcy = y + cy; + ymcy = y - cy; + raster_hline(dst, xmcx, xpcx, ypcy, color); + raster_hline(dst, xmcx, xpcx, ymcy, color); +// raster_rect_inline(dst, xmcx, ypcy, 2*cx, 1, color); +// raster_rect_inline(dst, xmcx, ymcy, 2*cx, 1, color); + + } else { + raster_hline(dst, xmcx, xpcx, y, color); +// raster_rect_inline(dst, xmcx, y, 2*cx, 1, color); + } + ocy = cy; + } + if (ocx != cx) { + if (cx != cy) { + if (cx > 0) { + ypcx = y + cx; + ymcx = y - cx; + raster_hline(dst, xmcy, xpcy, ymcx, color); + raster_hline(dst, xmcy, xpcy, ypcx, color); + //raster_rect_inline(dst, xmcy, ymcx, 2*cy, 1, color); + //raster_rect_inline(dst, xmcy, ypcx, 2*cy, 1, color); + } else { + raster_hline(dst, xmcy, xpcy, y, color); + //raster_rect_inline(dst, xmcy, y, 2*cy, 1, color); + } + } + ocx = cx; + } + /* + * Update + */ + if (df < 0) { + df += d_e; + d_e += 2; + d_se += 2; + } else { + df += d_se; + d_e += 2; + d_se += 4; + cy--; + } + cx++; + } while (cx <= cy); +} + + +/* FIXME: convert to fixed pt */ +static void raster_AAFilledEllipse(SDL_Surface *surface, Sint16 xc, Sint16 yc, Sint16 rx, Sint16 ry, Uint32 color) +{ + /* sge */ + /* Sanity check */ + if (rx < 1) + rx = 1; + if (ry < 1) + ry = 1; + + int a2 = rx * rx; + int b2 = ry * ry; + + int ds = 2 * a2; + int dt = 2 * b2; + + int dxt = (int)(a2 / sqrt(a2 + b2)); + + int t = 0; + int s = -2 * a2 * ry; + int d = 0; + + Sint16 x = xc; + Sint16 y = yc - ry; + + Sint16 xs, ys, dyt; + float cp, is, ip, imax = 1.0; + + /* Lock surface */ + if ( SDL_MUSTLOCK(surface) ) + if ( SDL_LockSurface(surface) < 0 ) + return; + + /* "End points" */ + raster_PutPixel(surface, x, y, color); + raster_PutPixel(surface, 2*xc-x, y, color); + + raster_PutPixel(surface, x, 2*yc-y, color); + raster_PutPixel(surface, 2*xc-x, 2*yc-y, color); + + /* unlock surface */ + if (SDL_MUSTLOCK(surface) ) + SDL_UnlockSurface(surface); + + raster_vline(surface, x, y+1, 2*yc-y-1, color); + + int i; + + for (i = 1; i <= dxt; i++) + { + x--; + d += t - b2; + + if (d >= 0) + ys = y - 1; + else if ((d - s - a2) > 0) + { + if ((2 * d - s - a2) >= 0) + ys = y + 1; + else + { + ys = y; + y++; + d -= s + a2; + s += ds; + } + } + else + { + y++; + ys = y + 1; + d -= s + a2; + s += ds; + } + + t -= dt; + + /* Calculate alpha */ + cp = (float) abs(d) / abs(s); + is = cp * imax; + ip = imax - is; + + + /* Lock surface */ + if ( SDL_MUSTLOCK(surface) ) + if ( SDL_LockSurface(surface) < 0 ) + return; + + /* Upper half */ + raster_PutPixelAlpha(surface, x, y, color, (Uint8)(ip*255)); + raster_PutPixelAlpha(surface, 2*xc-x, y, color, (Uint8)(ip*255)); + + raster_PutPixelAlpha(surface, x, ys, color, (Uint8)(is*255)); + raster_PutPixelAlpha(surface, 2*xc-x, ys, color, (Uint8)(is*255)); + + + /* Lower half */ + raster_PutPixelAlpha(surface, x, 2*yc-y, color, (Uint8)(ip*255)); + raster_PutPixelAlpha(surface, 2*xc-x, 2*yc-y, color, (Uint8)(ip*255)); + + raster_PutPixelAlpha(surface, x, 2*yc-ys, color, (Uint8)(is*255)); + raster_PutPixelAlpha(surface, 2*xc-x, 2*yc-ys, color, (Uint8)(is*255)); + + /* unlock surface */ + if (SDL_MUSTLOCK(surface) ) + SDL_UnlockSurface(surface); + + + /* Fill */ + raster_vline(surface, x, y+1, 2*yc-y-1, color); + raster_vline(surface, 2*xc-x, y+1, 2*yc-y-1, color); + raster_vline(surface, x, ys+1, 2*yc-ys-1, color); + raster_vline(surface, 2*xc-x, ys+1, 2*yc-ys-1, color); + } + + dyt = abs(y - yc); + + for (i = 1; i <= dyt; i++) + { + y++; + d -= s + a2; + + if (d <= 0) + xs = x + 1; + else if ((d + t - b2) < 0) + { + if ((2 * d + t - b2) <= 0) + xs = x - 1; + else + { + xs = x; + x--; + d += t - b2; + t -= dt; + } + } + else + { + x--; + xs = x - 1; + d += t - b2; + t -= dt; + } + + s += ds; + + /* Calculate alpha */ + cp = (float) abs(d) / abs(t); + is = cp * imax; + ip = imax - is; + + + /* Lock surface */ + if ( SDL_MUSTLOCK(surface) ) + if ( SDL_LockSurface(surface) < 0 ) + return; + + /* Upper half */ + raster_PutPixelAlpha(surface, x, y, color, (Uint8)(ip*255)); + raster_PutPixelAlpha(surface, 2*xc-x, y, color, (Uint8)(ip*255)); + + raster_PutPixelAlpha(surface, xs, y, color, (Uint8)(is*255)); + raster_PutPixelAlpha(surface, 2*xc-xs, y, color, (Uint8)(is*255)); + + + /* Lower half*/ + raster_PutPixelAlpha(surface, x, 2*yc-y, color, (Uint8)(ip*255)); + raster_PutPixelAlpha(surface, 2*xc-x, 2*yc-y, color, (Uint8)(ip*255)); + + raster_PutPixelAlpha(surface, xs, 2*yc-y, color, (Uint8)(is*255)); + raster_PutPixelAlpha(surface, 2*xc-xs, 2*yc-y, color, (Uint8)(is*255)); + + /* unlock surface */ + if (SDL_MUSTLOCK(surface) ) + SDL_UnlockSurface(surface); + + /* Fill */ + raster_hline(surface, x+1, 2*xc-x-1, y, color); + raster_hline(surface, xs+1, 2*xc-xs-1, y, color); + raster_hline(surface, x+1, 2*xc-x-1, 2*yc-y, color); + raster_hline(surface, xs+1, 2*xc-xs-1, 2*yc-y, color); + } +} + +void raster_aacircle(SDL_Surface *s, int16_t x, int16_t y, int16_t r, uint32_t col) +{ + raster_AAFilledEllipse(s, x, y, r, r, col); +} + +#if 0 +void raster_aacircle(SDL_Surface *s, int16_t x, int16_t y, int16_t r, uint32_t col) +{ + /* sdl-gfx */ + Sint16 left, right, top, bottom; + int result; + Sint16 x1, y1, x2, y2; + Sint16 cx = 0; + Sint16 cy = r; + Sint16 ocx = (Sint16) 0xffff; + Sint16 ocy = (Sint16) 0xffff; + Sint16 df = 1 - r; + Sint16 d_e = 3; + Sint16 d_se = -2 * r + 5; + Sint16 xpcx, xmcx, xpcy, xmcy; + Sint16 ypcy, ymcy, ypcx, ymcx; + + /* + * Check visibility of clipping rectangle + */ + if ((dst->clip_rect.w==0) || (dst->clip_rect.h==0)) { + return; + } + + /* + * Sanity check radius + */ + if (r < 0) { + return; + } + +#if 0 + /* + * Special case for r=0 - draw a point + */ + if (r == 0) { + return (pixelColor(dst, x, y, color)); + } +#endif + + /* + * Get circle and clipping boundary and + * test if bounding box of circle is visible + */ + x2 = x + r; + left = dst->clip_rect.x; + if (x2<left) { + return; + } + x1 = x - r; + right = dst->clip_rect.x + dst->clip_rect.w - 1; + if (x1>right) { + return; + } + y2 = y + r; + top = dst->clip_rect.y; + if (y2<top) { + return; + } + y1 = y - r; + bottom = dst->clip_rect.y + dst->clip_rect.h - 1; + if (y1>bottom) { + return; + } + + /* + * Draw + */ + result = 0; + do { + xpcx = x + cx; + xmcx = x - cx; + xpcy = x + cy; + xmcy = x - cy; + if (ocy != cy) { + if (cy > 0) { + ypcy = y + cy; + ymcy = y - cy; + raster_hlineColor(dst, xmcx, xpcx, ypcy, color); + raster_hlineColor(dst, xmcx, xpcx, ymcy, color); +// raster_rect_inline(dst, xmcx, ypcy, 2*cx, 1, color); +// raster_rect_inline(dst, xmcx, ymcy, 2*cx, 1, color); + + } else { + raster_hlineColor(dst, xmcx, xpcx, y, color); +// raster_rect_inline(dst, xmcx, y, 2*cx, 1, color); + } + ocy = cy; + } + if (ocx != cx) { + if (cx != cy) { + if (cx > 0) { + ypcx = y + cx; + ymcx = y - cx; + raster_hlineColor(dst, xmcy, xpcy, ymcx, color); + raster_hlineColor(dst, xmcy, xpcy, ypcx, color); + //raster_rect_inline(dst, xmcy, ymcx, 2*cy, 1, color); + //raster_rect_inline(dst, xmcy, ypcx, 2*cy, 1, color); + } else { + raster_hlineColor(dst, xmcy, xpcy, y, color); + //raster_rect_inline(dst, xmcy, y, 2*cy, 1, color); + } + } + ocx = cx; + } + /* + * Update + */ + if (df < 0) { + df += d_e; + d_e += 2; + d_se += 2; + } else { + df += d_se; + d_e += 2; + d_se += 4; + cy--; + } + cx++; + } while (cx <= cy); +#if 0 + /* sge */ + Sint16 cx = 0; + Sint16 cy = r; + int draw=1; + Sint16 df = 1 - r; + Sint16 d_e = 3; + Sint16 d_se = -2 * r + 5; + +#ifdef DEBUG + printf("raster_circle %d %d %d\n", x, y, r); +#endif + + if(r < 0) + { + return; + } + + do { + if(draw) + { + raster_rect_inline(s, x-cx, y+cy, 2*cx, 1, col); + raster_rect_inline(s, x-cx, y-cy, 2*cx, 1, col); + draw=0; + } + if(cx!=cy) + { + if(cx) + { + raster_rect_inline(s, x-cy, y-cx, 2*cy, 1, col); + raster_rect_inline(s, x-cy, y+cx, 2*cy, 1, col); + } + else + { + raster_rect_inline(s, x-cy, y, 2*cy, 1, col); + } + } + + if (df < 0) + { + df += d_e; + d_e += 2; + d_se += 2; + } + else + { + df += d_se; + d_e += 2; + d_se += 4; + cy--; + draw=1; + } + cx++; + } while(cx <= cy); +#endif +} +#endif + + + +/* raster :: poly */ + + +/* ---- Filled Polygon */ + +/* Helper qsort callback for polygon drawing */ + +static int gfxPrimitivesCompareInt(const void *a, const void *b) +{ + return (*(const int *) a) - (*(const int *) b); +} + + +/* Global vertex array to use if optional parameters are not given in polygon calls. */ +static int *gfxPrimitivesPolyIntsGlobal = NULL; +static int gfxPrimitivesPolyAllocatedGlobal = 0; + +/* (Note: The last two parameters are optional; but required for multithreaded operation.) */ + +static inline int raster_filledPolygonColorMT(SDL_Surface * dst, const Sint16 * vx, const Sint16 * vy, int n, Uint32 color, int **polyInts, int *polyAllocated) +{ + /* sdl-gfx */ + int result; + int i; + int y, xa, xb; + int miny, maxy; + int x1, y1; + int x2, y2; + int ind1, ind2; + int ints; + int *gfxPrimitivesPolyInts = NULL; + int gfxPrimitivesPolyAllocated = 0; + + /* + * Check visibility of clipping rectangle + */ + if ((dst->clip_rect.w==0) || (dst->clip_rect.h==0)) { + return(0); + } + + /* + * Sanity check number of edges + */ + if (n < 3) { + return -1; + } + + /* + * Map polygon cache + */ + if ((polyInts==NULL) || (polyAllocated==NULL)) { + /* Use global cache */ + gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsGlobal; + gfxPrimitivesPolyAllocated = gfxPrimitivesPolyAllocatedGlobal; + } else { + /* Use local cache */ + gfxPrimitivesPolyInts = *polyInts; + gfxPrimitivesPolyAllocated = *polyAllocated; + } + + /* + * Allocate temp array, only grow array + */ + if (!gfxPrimitivesPolyAllocated) { + gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n); + gfxPrimitivesPolyAllocated = n; + } else { + if (gfxPrimitivesPolyAllocated < n) { + gfxPrimitivesPolyInts = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n); + gfxPrimitivesPolyAllocated = n; + } + } + + /* + * Check temp array + */ + if (gfxPrimitivesPolyInts==NULL) { + gfxPrimitivesPolyAllocated = 0; + } + + /* + * Update cache variables + */ + if ((polyInts==NULL) || (polyAllocated==NULL)) { + gfxPrimitivesPolyIntsGlobal = gfxPrimitivesPolyInts; + gfxPrimitivesPolyAllocatedGlobal = gfxPrimitivesPolyAllocated; + } else { + *polyInts = gfxPrimitivesPolyInts; + *polyAllocated = gfxPrimitivesPolyAllocated; + } + + /* + * Check temp array again + */ + if (gfxPrimitivesPolyInts==NULL) { + return(-1); + } + + /* + * Determine Y maxima + */ + miny = vy[0]; + maxy = vy[0]; + for (i = 1; (i < n); i++) { + if (vy[i] < miny) { + miny = vy[i]; + } else if (vy[i] > maxy) { + maxy = vy[i]; + } + } + + /* + * Draw, scanning y + */ + result = 0; + for (y = miny; (y <= maxy); y++) { + ints = 0; + for (i = 0; (i < n); i++) { + if (!i) { + ind1 = n - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + y1 = vy[ind1]; + y2 = vy[ind2]; + if (y1 < y2) { + x1 = vx[ind1]; + x2 = vx[ind2]; + } else if (y1 > y2) { + y2 = vy[ind1]; + y1 = vy[ind2]; + x2 = vx[ind1]; + x1 = vx[ind2]; + } else { + continue; + } + if ( ((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2)) ) { + gfxPrimitivesPolyInts[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1); + } + } + + qsort(gfxPrimitivesPolyInts, ints, sizeof(int), gfxPrimitivesCompareInt); + + for (i = 0; (i < ints); i += 2) { + xa = gfxPrimitivesPolyInts[i] + 1; + xa = (xa >> 16) + ((xa & 32768) >> 15); + xb = gfxPrimitivesPolyInts[i+1] - 1; + xb = (xb >> 16) + ((xb & 32768) >> 15); + raster_hline(dst, xa, xb, y, color); +// raster_rect_inline(dst, xa, y, xb - xa, 1, color); + } + } + + return (result); +} + +void raster_polygon(SDL_Surface *s, int16_t n, int16_t *vx, int16_t *vy, uint32_t col) +{ + raster_filledPolygonColorMT(s, vx, vy, n, col, NULL, NULL); +} + + +void raster_aapolygon(SDL_Surface *dst, int16_t n, int16_t *vx, int16_t *vy, uint32_t color) +{ + /* sdl-gfx + sge w/ rphlx changes: basically, draw aaline border, + then fill. + + the output is not perfect yet but usually looks better than aliasing + */ + int result; + int i; + int y, xa, xb; + int miny, maxy; + int x1, y1; + int x2, y2; + int ind1, ind2; + int ints; + int *gfxPrimitivesPolyInts = NULL; + int gfxPrimitivesPolyAllocated = 0; + const Sint16 *px1, *py1, *px2, *py2; + int **polyInts; + int *polyAllocated; + + polyInts = NULL; + polyAllocated = NULL; + + /* + * Check visibility of clipping rectangle + */ + if ((dst->clip_rect.w==0) || (dst->clip_rect.h==0)) { + return; + } + + /* + * Sanity check number of edges + */ + if (n < 3) { + return; + } + + + /* + * Pointer setup + */ + px1 = px2 = vx; + py1 = py2 = vy; + px2++; + py2++; + + /* + * Draw + */ + result = 0; + for (i = 1; i < n; i++) { + raster_aalineColorInt(dst, *px1, *py1, *px2, *py2, color, 0); + px1 = px2; + py1 = py2; + px2++; + py2++; + } + raster_aalineColorInt(dst, *px1, *py1, *vx, *vy, color, 0); + + /* + * Map polygon cache + */ + if ((polyInts==NULL) || (polyAllocated==NULL)) { + /* Use global cache */ + gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsGlobal; + gfxPrimitivesPolyAllocated = gfxPrimitivesPolyAllocatedGlobal; + } else { + /* Use local cache */ + gfxPrimitivesPolyInts = *polyInts; + gfxPrimitivesPolyAllocated = *polyAllocated; + } + + /* + * Allocate temp array, only grow array + */ + if (!gfxPrimitivesPolyAllocated) { + gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n); + gfxPrimitivesPolyAllocated = n; + } else { + if (gfxPrimitivesPolyAllocated < n) { + gfxPrimitivesPolyInts = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n); + gfxPrimitivesPolyAllocated = n; + } + } + + /* + * Check temp array + */ + if (gfxPrimitivesPolyInts==NULL) { + gfxPrimitivesPolyAllocated = 0; + } + + /* + * Update cache variables + */ + if ((polyInts==NULL) || (polyAllocated==NULL)) { + gfxPrimitivesPolyIntsGlobal = gfxPrimitivesPolyInts; + gfxPrimitivesPolyAllocatedGlobal = gfxPrimitivesPolyAllocated; + } else { + *polyInts = gfxPrimitivesPolyInts; + *polyAllocated = gfxPrimitivesPolyAllocated; + } + + /* + * Check temp array again + */ + if (gfxPrimitivesPolyInts==NULL) { + return; + } + + /* + * Determine Y maxima + */ + miny = vy[0]; + maxy = vy[0]; + for (i = 1; (i < n); i++) { + if (vy[i] < miny) { + miny = vy[i]; + } else if (vy[i] > maxy) { + maxy = vy[i]; + } + } + + /* + * Draw, scanning y + */ + result = 0; + for (y = miny; (y <= maxy); y++) { + ints = 0; + for (i = 0; (i < n); i++) { + if (!i) { + ind1 = n - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + y1 = vy[ind1]; + y2 = vy[ind2]; + if (y1 < y2) { + x1 = vx[ind1]; + x2 = vx[ind2]; + } else if (y1 > y2) { + y2 = vy[ind1]; + y1 = vy[ind2]; + x2 = vx[ind1]; + x1 = vx[ind2]; + } else { + continue; + } + if ( ((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2)) ) { + gfxPrimitivesPolyInts[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1); + } + } + + qsort(gfxPrimitivesPolyInts, ints, sizeof(int), gfxPrimitivesCompareInt); + +// o = p = -1; + for (i = 0; (i < ints); i +=2) { +#if 0 + xa = gfxPrimitivesPolyInts[i] + 1; + xa = (xa >> 16) + ((xa & 32768) >> 15); + xb = gfxPrimitivesPolyInts[i+1] - 1; + xb = (xb >> 16) + ((xb & 32768) >> 15); +#else + xa = (gfxPrimitivesPolyInts[i] >> 16); + xb = (gfxPrimitivesPolyInts[i+1] >> 16); +#endif + +#if 0 + if(o < 0) + { + o = xa+1; + } + else if(p < 0) + { + p = xa; + } + + if( (o >= 0) && (p >= 0)) + { + if(p-o < 0) + { + o = p = -1; + continue; + } + + raster_hlineColor(dst, o, p, y, color); + + o = p = -1; + } +#else + + raster_hline(dst, xa+1, xb, y, color); +#endif + +// raster_rect_inline(dst, xa, y, xb - xa, 1, color); + } + } +} + diff --git a/graphics/sdl/raster.h b/graphics/sdl/raster.h new file mode 100644 index 00000000..8f50b271 --- /dev/null +++ b/graphics/sdl/raster.h @@ -0,0 +1,25 @@ +/* raster.h -- line/rect/circle/poly rasterization + + copyright (c) 2008 bryan rittmeyer <bryanr@bryanr.org> + + license: LGPLv2 +*/ + +#ifndef __RASTER_H +#define __RASTER_H + +#include <stdint.h> +#include <SDL/SDL.h> + +void raster_rect(SDL_Surface *s, int16_t x, int16_t y, int16_t w, int16_t h, uint32_t col); + +void raster_line(SDL_Surface *s, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t col); +void raster_circle(SDL_Surface *s, int16_t x, int16_t y, int16_t r, uint32_t col); +void raster_polygon(SDL_Surface *s, int16_t n, int16_t *vx, int16_t *vy, uint32_t col); + +void raster_aaline(SDL_Surface *s, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t col); +void raster_aacircle(SDL_Surface *s, int16_t x, int16_t y, int16_t r, uint32_t col); +void raster_aapolygon(SDL_Surface *s, int16_t n, int16_t *vx, int16_t *vy, uint32_t col); + +#endif /* __RASTER_H */ + diff --git a/graphics/win32/Makefile.am b/graphics/win32/Makefile.am new file mode 100644 index 00000000..a6fb52f9 --- /dev/null +++ b/graphics/win32/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=graphics_win32 +if PLUGINS +modulegraphics_LTLIBRARIES = libgraphics_win32.la +else +noinst_LTLIBRARIES = libgraphics_win32.la +endif +libgraphics_win32_la_SOURCES = graphics_win32.c xpm2bmp.c +libgraphics_win32_la_LDFLAGS = -module -avoid-version diff --git a/graphics/win32/graphics_win32.c b/graphics/win32/graphics_win32.c new file mode 100644 index 00000000..b740b8d8 --- /dev/null +++ b/graphics/win32/graphics_win32.c @@ -0,0 +1,1074 @@ +#include <windows.h> +#include <wingdi.h> +#include <glib.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +#include "config.h" +#include "debug.h" +#include "point.h" +#include "graphics.h" +#include "color.h" +#include "callback.h" +#include "plugin.h" +#include "item.h" +#include "window.h" +#include "graphics_win32.h" +#include "xpm2bmp.h" +#include "support/win32/ConvertUTF.h" + +struct graphics_priv { + struct navit *nav; + struct window window; + struct point p; + int width; + int height; + int library_init; + int visible; + HANDLE wnd_parent_handle; + HANDLE wnd_handle; + COLORREF bg_color; + struct callback_list *cbl; + enum draw_mode_num mode; +}; + +static HWND g_hwnd; + +#ifndef GET_WHEEL_DELTA_WPARAM + #define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam)) +#endif + + +static GHashTable *image_cache_hash = NULL; + + +HFONT EzCreateFont (HDC hdc, TCHAR * szFaceName, int iDeciPtHeight, + int iDeciPtWidth, int iAttributes, BOOL fLogRes) ; + +#define EZ_ATTR_BOLD 1 +#define EZ_ATTR_ITALIC 2 +#define EZ_ATTR_UNDERLINE 4 +#define EZ_ATTR_STRIKEOUT 8 + +HFONT EzCreateFont (HDC hdc, TCHAR * szFaceName, int iDeciPtHeight, + int iDeciPtWidth, int iAttributes, BOOL fLogRes) +{ + FLOAT cxDpi, cyDpi ; + HFONT hFont ; + LOGFONT lf ; + POINT pt ; + TEXTMETRIC tm ; + + SaveDC (hdc) ; + +#ifndef HAVE_API_WIN32_CE + SetGraphicsMode (hdc, GM_ADVANCED) ; + ModifyWorldTransform (hdc, NULL, MWT_IDENTITY) ; +#endif + SetViewportOrgEx (hdc, 0, 0, NULL) ; +#ifndef HAVE_API_WIN32_CE + SetWindowOrgEx (hdc, 0, 0, NULL) ; +#endif + + if (fLogRes) + { + cxDpi = (FLOAT) GetDeviceCaps (hdc, LOGPIXELSX) ; + cyDpi = (FLOAT) GetDeviceCaps (hdc, LOGPIXELSY) ; + } + else + { + cxDpi = (FLOAT) (25.4 * GetDeviceCaps (hdc, HORZRES) / + GetDeviceCaps (hdc, HORZSIZE)) ; + + cyDpi = (FLOAT) (25.4 * GetDeviceCaps (hdc, VERTRES) / + GetDeviceCaps (hdc, VERTSIZE)) ; + } + + pt.x = (int) (iDeciPtWidth * cxDpi / 72) ; + pt.y = (int) (iDeciPtHeight * cyDpi / 72) ; + +#ifndef HAVE_API_WIN32_CE + DPtoLP (hdc, &pt, 1) ; +#endif + lf.lfHeight = - (int) (fabs (pt.y) / 10.0 + 0.5) ; + lf.lfWidth = 0 ; + lf.lfEscapement = 0 ; + lf.lfOrientation = 0 ; + lf.lfWeight = iAttributes & EZ_ATTR_BOLD ? 700 : 0 ; + lf.lfItalic = iAttributes & EZ_ATTR_ITALIC ? 1 : 0 ; + lf.lfUnderline = iAttributes & EZ_ATTR_UNDERLINE ? 1 : 0 ; + lf.lfStrikeOut = iAttributes & EZ_ATTR_STRIKEOUT ? 1 : 0 ; + lf.lfCharSet = DEFAULT_CHARSET ; + lf.lfOutPrecision = 0 ; + lf.lfClipPrecision = 0 ; + lf.lfQuality = 0 ; + lf.lfPitchAndFamily = 0 ; + + lstrcpy (lf.lfFaceName, szFaceName) ; + + hFont = CreateFontIndirect (&lf) ; + + if (iDeciPtWidth != 0) + { + hFont = (HFONT) SelectObject (hdc, hFont) ; + + GetTextMetrics (hdc, &tm) ; + + DeleteObject (SelectObject (hdc, hFont)) ; + + lf.lfWidth = (int) (tm.tmAveCharWidth * + fabs (pt.x) / fabs (pt.y) + 0.5) ; + + hFont = CreateFontIndirect (&lf) ; + } + + RestoreDC (hdc, -1) ; + return hFont ; +} + +struct graphics_image_priv { + PXPM2BMP pxpm; +}; + + +static void ErrorExit(LPTSTR lpszFunction) +{ + // Retrieve the system error message for the last-error code + + LPVOID lpMsgBuf; + LPVOID lpDisplayBuf; + DWORD dw = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL ); + + lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, + (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); + wprintf((LPTSTR)lpDisplayBuf, TEXT("%s failed with error %d: %s"), lpszFunction, dw, lpMsgBuf); + + dbg(0, "%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf); + MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); + + LocalFree(lpMsgBuf); + LocalFree(lpDisplayBuf); + ExitProcess(dw); +} + + + +struct graphics_gc_priv { + HWND hwnd; + int line_width; + COLORREF fg_color; + COLORREF bg_color; + struct graphics_priv *gr; +}; + + +//struct graphics_priv *g_gra; + +static HDC hMemDC; +static HBITMAP hBitmap; +static HBITMAP hOldBitmap; + +// Fills the region 'rgn' in graded colours +static void MakeMemoryDC(HANDLE hWnd, HDC hdc ) +{ + if ( hMemDC ) + { + if ( hOldBitmap ) + { + SelectObject( hMemDC, hOldBitmap ); + DeleteObject( hBitmap ); + hBitmap = NULL; + hOldBitmap = NULL; + } + } + + // Creates memory DC + hMemDC = CreateCompatibleDC(hdc); + if ( hMemDC ) + { + RECT rectRgn; + GetClientRect( hWnd, &rectRgn ); + + int Width = rectRgn.right - rectRgn.left; + int Height = rectRgn.bottom - rectRgn.top; + dbg(0, "resize memDC to: %d %d \n", Width, Height ); + + hBitmap = CreateCompatibleBitmap(hdc, Width, Height ); + + if ( hBitmap ) + { + hOldBitmap = (HBITMAP) SelectObject( hMemDC, hBitmap); + } + } +} + +static void HandleButtonClick( struct graphics_priv *gra_priv, int updown, int button, long lParam ) +{ + POINTS p = MAKEPOINTS(lParam); + struct point pt; + pt.x = p.x; + pt.y = p.y; + callback_list_call_attr_3(gra_priv->cbl, attr_button, (void *)updown, (void *)button, (void *)&pt); +} + +static LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) +{ + +//if ( Message != 15 ) +//printf( "CHILD %d %d %d \n", Message, wParam, lParam ); + + struct graphics_priv* gra_priv = (struct graphics_priv*)GetWindowLongPtr( hwnd , DWLP_USER ); + + switch(Message) + { + case WM_CREATE: + { + HDC hdc; + hdc = GetDC( hwnd ); + MakeMemoryDC(hwnd, hdc ); + ReleaseDC( hwnd, hdc ); + } + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case WM_USER + 1: + break; + } + break; + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_USER+1: + if ( gra_priv ) + { + RECT rc ; + HDC hdc; + + GetClientRect( hwnd, &rc ); + gra_priv->width = rc.right; + gra_priv->height = rc.bottom; + + hdc = GetDC( hwnd ); + MakeMemoryDC(hwnd, hdc ); + ReleaseDC( hwnd, hdc ); + callback_list_call_attr_2(gra_priv->cbl, attr_resize, (void *)gra_priv->width, (void *)gra_priv->height); + } + break; + case WM_USER+2: + { + struct callback_list *cbl = wParam; +#ifdef HAVE_API_WIN32_CE + /* FIXME: Reset the idle timer need a better place */ + SystemIdleTimerReset(); +#endif + callback_list_call_0(cbl); + } + break; + + case WM_SIZE: + /* + if ( gra_priv ) + { + //graphics = GetWindowLong( hwnd, DWL_USER, 0 ); + + + { + HDC hdc; + hdc = GetDC( hwnd ); + MakeMemoryDC(hwnd, hdc ); + ReleaseDC( hwnd, hdc ); + } + (*gra_priv->resize_callback)(gra_priv->resize_callback_data, gra_priv->width, gra_priv->height); + + + } + */ + if ( gra_priv ) + { + gra_priv->width = LOWORD( lParam ); + gra_priv->height = HIWORD( lParam ); + dbg(0, "resize gfx to: %d %d \n", gra_priv->width, gra_priv->height ); + } + break; + case WM_DESTROY: + PostQuitMessage(0); + //exit( 0 ); + break; + case WM_PAINT: + if ( gra_priv ) + { + PAINTSTRUCT ps = { 0 }; + HDC hdc = BeginPaint(hwnd, &ps); + if ( hMemDC ) + { + BitBlt( hdc, 0, 0, gra_priv->width , gra_priv->height, hMemDC, 0, 0, SRCCOPY ); + } + EndPaint(hwnd, &ps); + } + break; + case WM_MOUSEMOVE: + { + POINTS p = MAKEPOINTS(lParam); + struct point pt; + pt.x = p.x; + pt.y = p.y; + + //dbg(1, "WM_MOUSEMOVE: %d %d \n", p.x, p.y ); + callback_list_call_attr_1(gra_priv->cbl, attr_motion, (void *)&pt); + } + break; + + case WM_LBUTTONDOWN: + { + dbg(1, "LBUTTONDOWN\n"); + HandleButtonClick( gra_priv, 1, 1, lParam); + } + break; + case WM_LBUTTONUP: + { + dbg(1, "LBUTTONUP\n"); + HandleButtonClick( gra_priv, 0, 1, lParam); + } + break; + case WM_RBUTTONDOWN: + HandleButtonClick( gra_priv, 1, 3,lParam ); + break; + case WM_RBUTTONUP: + HandleButtonClick( gra_priv, 0, 3,lParam ); + break; + case WM_LBUTTONDBLCLK: + dbg(1, "LBUTTONDBLCLK\n"); + HandleButtonClick( gra_priv, 1, 6,lParam ); + break; + default: + return DefWindowProc(hwnd, Message, wParam, lParam); + } + return 0; +} + + +static const wchar_t g_szClassName[] = {'N','A','V','G','R','A','\0'}; + +static HANDLE CreateGraphicsWindows( struct graphics_priv* gr ) +{ +#ifdef HAVE_API_WIN32_CE + WNDCLASS wc; +#else + WNDCLASSEX wc; + wc.cbSize = sizeof(WNDCLASSEX); + wc.hIconSm = NULL; +#endif + HWND hwnd; + RECT rcParent; + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 64; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL; + wc.lpszClassName = g_szClassName; + + GetClientRect( gr->wnd_parent_handle,&rcParent); + +#ifdef HAVE_API_WIN32_CE + if(!RegisterClass(&wc)) +#else + if(!RegisterClassEx(&wc)) +#endif + { + ErrorExit( TEXT("Window Registration Failed!") ); + return NULL; + } + + gr->width = rcParent.right - rcParent.left; + gr->height = rcParent.bottom - rcParent.top; + callback_list_call_attr_2(gr->cbl, attr_resize, (void *)gr->width, (void *)gr->height); + + g_hwnd = hwnd = CreateWindow(g_szClassName, + TEXT(""), +#ifdef HAVE_API_WIN32_CE + WS_VISIBLE, +#elif 1 + WS_CHILD, +#else + WS_OVERLAPPEDWINDOW, +#endif + 0, + 0, + gr->width, + gr->height, +#if 1 + gr->wnd_parent_handle, +#else + NULL, +#endif +#if 1 + (HMENU)ID_CHILD_GFX, +#else + NULL, +#endif + GetModuleHandle(NULL), + NULL); + + if(hwnd == NULL) + { + ErrorExit( TEXT("Window Creation Failed!") ); + return NULL; + } + gr->wnd_handle = hwnd; + + SetWindowLongPtr( hwnd , DWLP_USER, gr ); + + ShowWindow( hwnd, TRUE ); + UpdateWindow( hwnd ); + + + PostMessage( gr->wnd_parent_handle, WM_USER + 1, 0, 0 ); + + return hwnd; +} + + + +static void graphics_destroy(struct graphics_priv *gr) +{ + g_free( gr ); +} + + +static void gc_destroy(struct graphics_gc_priv *gc) +{ + g_free( gc ); +} + +static void gc_set_linewidth(struct graphics_gc_priv *gc, int w) +{ + gc->line_width = w; +} + +static void gc_set_dashes(struct graphics_gc_priv *gc, int width, int offset, unsigned char dash_list[], int n) +{ +// gdk_gc_set_dashes(gc->gc, 0, (gint8 *)dash_list, n); +// gdk_gc_set_line_attributes(gc->gc, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND); +} + + + +static void gc_set_color(struct graphics_gc_priv *gc, struct color *c, int fg) +{ + + gc->fg_color = RGB( c->r, c->g, c->b ); +} + +static void gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) +{ + gc->fg_color = RGB( c->r, c->g, c->b ); +} + +static void gc_set_background(struct graphics_gc_priv *gc, struct color *c) +{ + gc->bg_color = RGB( c->r, c->g, c->b ); + if ( hMemDC ) + SetBkColor( hMemDC, gc->bg_color ); + +} + +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 *gc=g_new(struct graphics_gc_priv, 1); + *meth=gc_methods; + gc->hwnd = gr->wnd_handle; + gc->line_width = 1; + gc->fg_color = RGB( 0,0,0 ); + gc->bg_color = RGB( 255,255,255 ); + return gc; +} + + +static void draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + int i; + HPEN holdpen; + HPEN hpen; + + hpen = CreatePen( PS_SOLID, gc->line_width, gc->fg_color ); + holdpen = SelectObject( hMemDC, hpen ); + + SetBkColor( hMemDC, gc->bg_color ); + + int first = 1; + for ( i = 0; i< count; i++ ) + { + if ( first ) + { + first = 0; + MoveToEx( hMemDC, p[0].x, p[0].y, NULL ); + } + else + { + LineTo( hMemDC, p[i].x, p[i].y ); + } + } + + SelectObject( hMemDC, holdpen ); + DeleteObject( hpen ); +} + +static void draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + + //if (gr->mode == draw_mode_begin || gr->mode == draw_mode_end) + { + int i; + POINT points[ count ]; + for ( i=0;i< count; i++ ) + { + points[i].x = p[i].x; + points[i].y = p[i].y; + } + HPEN hpen; + HBRUSH holdbrush; + HBRUSH hbrush; + + SetBkColor( hMemDC, gc->bg_color ); + + hpen = CreatePen( PS_NULL, gc->line_width, gc->fg_color ); + SelectObject( hMemDC, hpen ); + hbrush = CreateSolidBrush( gc->fg_color ); + holdbrush = SelectObject( hMemDC, hbrush ); + + Polygon( hMemDC, points,count ); + + SelectObject( hMemDC, holdbrush ); + DeleteObject( hbrush ); + DeleteObject( hbrush ); + } +} + + +static void draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) +{ +// gdk_draw_rectangle(gr->drawable, gc->gc, TRUE, p->x, p->y, w, h); +} + +static void draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) +{ + HDC dc = hMemDC; + + HPEN holdpen; + HPEN hpen; + + hpen = CreatePen( PS_SOLID, gc->line_width, gc->fg_color ); + holdpen = SelectObject( dc, hpen ); + + SetBkColor( hMemDC, gc->bg_color ); + + Ellipse( dc, p->x - r, p->y -r, p->x + r, p->y + r ); + + SelectObject( dc, holdpen ); + DeleteObject( hpen ); + +// if (gr->mode == draw_mode_begin || gr->mode == draw_mode_end) +// gdk_draw_arc(gr->drawable, gc->gc, FALSE, p->x-r/2, p->y-r/2, r, r, 0, 64*360); +// if (gr->mode == draw_mode_end || gr->mode == draw_mode_cursor) +// gdk_draw_arc(gr->widget->window, gc->gc, FALSE, p->x-r/2, p->y-r/2, r, r, 0, 64*360); +} + + + +static void draw_restore(struct graphics_priv *gr, struct point *p, int w, int h) +{ + InvalidateRect( gr->wnd_handle, NULL, FALSE ); +} + +static void draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) +{ + // printf( "set draw_mode to %d\n", (int)mode ); + + if ( mode == draw_mode_begin ) + { + if ( gr->wnd_handle == NULL ) + { + CreateGraphicsWindows( gr ); + } + if ( gr->mode != draw_mode_begin ) + { + if ( hMemDC ) + { + RECT rcClient; + HBRUSH bgBrush = CreateSolidBrush( gr->bg_color ); + GetClientRect( gr->wnd_handle, &rcClient ); + FillRect( hMemDC, &rcClient, bgBrush ); + DeleteObject( bgBrush ); + } + } + } + + // force paint + if (mode == draw_mode_end && gr->mode == draw_mode_begin) + { + InvalidateRect( gr->wnd_handle, NULL, FALSE ); + } + + gr->mode=mode; + +} + + +static void * get_data(struct graphics_priv *this_, char *type) +{ + if ( strcmp( "wnd_parent_handle_ptr", type ) == 0 ) { + return &( this_->wnd_parent_handle ); + } + if ( strcmp( "START_CLIENT", type ) == 0 ) { + CreateGraphicsWindows( this_ ); + return NULL; + } + if (!strcmp(type, "window")) { +#ifdef HAVE_API_WIN32_CE + WNDCLASS wc; +#else + WNDCLASSEX wc; + wc.cbSize = sizeof(WNDCLASSEX); + wc.hIconSm = NULL; +#endif + HWND hwnd; + RECT rcParent; + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 64; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL; + wc.lpszClassName = g_szClassName; + + GetClientRect( this_->wnd_parent_handle,&rcParent); + +#ifdef HAVE_API_WIN32_CE + if(!RegisterClass(&wc)) +#else + if(!RegisterClassEx(&wc)) +#endif + { + ErrorExit( TEXT("Window Registration Failed!") ); + return NULL; + } + + callback_list_call_attr_2(this_->cbl, attr_resize, (void *)this_->width, (void *)this_->height); + + g_hwnd = hwnd = CreateWindow(g_szClassName, + TEXT(""), +#ifdef HAVE_API_WIN32_CE + WS_VISIBLE, +#else + WS_OVERLAPPEDWINDOW, +#endif + 0, + 0, + this_->width, + this_->height, + this_->wnd_parent_handle, + NULL, + GetModuleHandle(NULL), + NULL); + + if(hwnd == NULL) + { + ErrorExit( TEXT("Window Creation Failed!") ); + return NULL; + } + this_->wnd_handle = hwnd; + + SetWindowLongPtr( hwnd , DWLP_USER, this_ ); + + ShowWindow( hwnd, TRUE ); + UpdateWindow( hwnd ); + + + PostMessage( this_->wnd_parent_handle, WM_USER + 1, 0, 0 ); + + this_->window.priv=this_; + return &this_->window; + } + return NULL; +} + + +static void background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc) +{ + RECT rcClient; + HBRUSH bgBrush = CreateSolidBrush( gc->bg_color ); + GetClientRect( gr->wnd_handle, &rcClient ); + FillRect( hMemDC, &rcClient, bgBrush ); + DeleteObject( bgBrush ); + gr->bg_color = gc->bg_color; +} + +struct graphics_font_priv { + LOGFONT lf; + HFONT hfont; + int size; +}; + +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) +{ + RECT rcClient; + GetClientRect( gr->wnd_handle, &rcClient ); + + int prevBkMode = SetBkMode( hMemDC, TRANSPARENT ); + + if ( NULL == font->hfont ) + { +#ifdef WIN_USE_SYSFONT + font->hfont = (HFONT) GetStockObject (SYSTEM_FONT); + GetObject (font->hfont, sizeof (LOGFONT), &font->lf); +#else + font->hfont = EzCreateFont (hMemDC, TEXT ("Arial"), font->size/2, 0, 0, TRUE); + GetObject ( font->hfont, sizeof (LOGFONT), &font->lf) ; +#endif + } + + + double angle = -atan2( dy, dx ) * 180 / 3.14159 ; + if (angle < 0) + angle += 360; + + SetTextAlign (hMemDC, TA_BASELINE) ; + SetViewportOrgEx (hMemDC, p->x, p->y, NULL) ; + font->lf.lfEscapement = font->lf.lfOrientation = ( angle * 10 ) ; + DeleteObject (font->hfont) ; + + font->hfont = CreateFontIndirect (&font->lf); + HFONT hOldFont = SelectObject(hMemDC, font->hfont ); + + { + wchar_t utf16[1024]; + const UTF8 *utf8 = (UTF8 *)text; + UTF16 *utf16p = (UTF16 *) utf16; + SetBkMode (hMemDC, TRANSPARENT); + if (ConvertUTF8toUTF16(&utf8, utf8+strlen(text), + &utf16p, utf16p+sizeof(utf16), + lenientConversion) == conversionOK) { +#ifdef _WIN32_WCE + ExtTextOut (hMemDC, 0, 0, 0, NULL, + utf16, (wchar_t*) utf16p - utf16, NULL); +#else + ExtTextOutW(hMemDC, 0, 0, 0, NULL, + utf16, (wchar_t*) utf16p - utf16, NULL); +#endif + } + } + + + SelectObject(hMemDC, hOldFont); + DeleteObject (font->hfont) ; + + SetBkMode( hMemDC, prevBkMode ); + + SetViewportOrgEx (hMemDC, 0, 0, NULL) ; +} + +static void font_destroy(struct graphics_font_priv *font) +{ + if ( font->hfont ) + { + DeleteObject(font->hfont); + } + g_free(font); +} + +static struct graphics_font_methods font_methods = { + font_destroy +}; + +static struct graphics_font_priv *font_new(struct graphics_priv *gr, struct graphics_font_methods *meth, char *name, int size, int flags) +{ + struct graphics_font_priv *font=g_new(struct graphics_font_priv, 1); + *meth = font_methods; + + font->hfont = NULL; + font->size = size; + // FontFamily fontFamily( "Liberation Mono"); +//font( &fontFamily, size, FontStyleRegular, UnitPoint ); + return font; +} + +static void image_cache_hash_add( const char* key, struct graphics_image_priv* val_ptr) +{ + if ( image_cache_hash == NULL ) { + image_cache_hash = g_hash_table_new(g_str_hash, g_str_equal); + } + + if ( g_hash_table_lookup(image_cache_hash, key ) == NULL ) + { + g_hash_table_insert(image_cache_hash, g_strdup( key ), (gpointer)val_ptr ); + } + +} + +static struct graphics_image_priv* image_cache_hash_lookup( const char* key ) +{ + struct graphics_image_priv* val_ptr = NULL; + + if ( image_cache_hash != NULL ) + { + val_ptr = g_hash_table_lookup(image_cache_hash, key ); + } + return val_ptr; +} + + + +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) +{ + struct graphics_image_priv* ret; + + if ( NULL == ( ret = image_cache_hash_lookup( name ) ) ) + { + ret = g_new( struct graphics_image_priv, 1 ); + dbg(2, "loading image '%s'\n", name ); + ret->pxpm = Xpm2bmp_new(); + Xpm2bmp_load( ret->pxpm, name ); + image_cache_hash_add( name, ret ); + } + + return ret; +} + +static void draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) +{ + Xpm2bmp_paint( img->pxpm , hMemDC, p->x, p->y ); +} + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, +#ifdef HAVE_IMLIB2 + NULL, // draw_image_warp, +#else + NULL, +#endif + draw_restore, + NULL, // draw_drag + font_new, + gc_new, + background_gc, + NULL, // overlay_new, + image_new, + get_data, +}; + +static struct graphics_priv * +graphics_win32_new_helper(struct graphics_methods *meth) +{ + struct graphics_priv *this_=g_new0(struct graphics_priv,1); + *meth=graphics_methods; + this_->mode = -1; + return this_; +} + +static struct graphics_priv* +graphics_win32_new( struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl) +{ + struct attr *attr; + + struct graphics_priv* this_; + if (!event_request_system("win32","graphics_win32")) + return NULL; + this_=graphics_win32_new_helper(meth); + this_->nav=nav; + this_->width=792; + if ((attr=attr_search(attrs, NULL, attr_w))) + this_->width=attr->u.num; + this_->height=547; + if ((attr=attr_search(attrs, NULL, attr_h))) + this_->height=attr->u.num; + + this_->cbl=cbl; + return this_; +} + +static void +event_win32_main_loop_run(void) +{ + MSG msg; + + dbg(0,"enter\n"); + while (GetMessage(&msg, 0, 0, 0)) { + TranslateMessage(&msg); /* Keyboard input. */ + DispatchMessage(&msg); + } + +} + +static void event_win32_main_loop_quit(void) +{ + dbg(0,"enter\n"); + PostQuitMessage(0); +} + +static struct event_watch * +event_win32_add_watch(void *h, enum event_watch_cond cond, struct callback *cb) +{ + dbg(0,"enter\n"); + return NULL; +} + +static void +event_win32_remove_watch(struct event_watch *ev) +{ + dbg(0,"enter\n"); +} + +static GList *timers; +struct event_timeout { + UINT_PTR timer_id; + int multi; + struct callback *cb; + struct event_timeout *next; +}; + +static void run_timer(UINT_PTR idEvent) +{ + GList *l; + struct event_timeout *t; + l = timers; + while (l) { + t = l->data; + if (t->timer_id == idEvent) { + dbg(1, "run timer:%d\n", idEvent); + callback_call_0(t->cb); + if (!t->multi) { + KillTimer(NULL, t->timer_id); + timers = g_list_remove(timers, t); + free(t); + } + return; + } + l = g_list_next(l); + } + dbg(0, "timer %d not found\n", idEvent); +} + +static VOID CALLBACK win32_timer_cb(HWND hwnd, UINT uMsg, + UINT_PTR idEvent, + DWORD dwTime) +{ + run_timer(idEvent); +} + +static struct event_timeout * +event_win32_add_timeout(int timeout, int multi, struct callback *cb) +{ + struct event_timeout *t; + t = calloc(1, sizeof(*t)); + if (!t) + return t; + t->multi = multi; + timers = g_list_prepend(timers, t); + t->cb = cb; + t->timer_id = SetTimer(NULL, 0, timeout, win32_timer_cb); + dbg(1, "Started timer %d for %d\n", t->timer_id, timeout); + return t; +} + +static void +event_win32_remove_timeout(struct event_timeout *to) +{ + GList *l; + struct event_timeout *t=NULL; + dbg(1,"enter:%d\n", t->timer_id); + l = timers; + while (l) { + t = l->data; + /* Use the pointer not the ID, IDs are reused */ + if (t == to) { + KillTimer(NULL, t->timer_id); + timers = g_list_remove(timers, t); + free(t); + return; + } + l = g_list_next(l); + } + dbg(0, "Timer %d not found\n", to->timer_id); + free(to); +} + +static struct event_idle * +event_win32_add_idle(struct callback *cb) +{ + dbg(0,"enter\n"); + return NULL; +} + +static void +event_win32_remove_idle(struct event_idle *ev) +{ + dbg(0,"enter\n"); +} + +static void +event_win32_call_callback(struct callback_list *cb) +{ + PostMessage(g_hwnd, WM_USER+2, (WPARAM)cb , (LPARAM)0); +} + +static struct event_methods event_win32_methods = { + event_win32_main_loop_run, + event_win32_main_loop_quit, + event_win32_add_watch, + event_win32_remove_watch, + event_win32_add_timeout, + event_win32_remove_timeout, + event_win32_add_idle, + event_win32_remove_idle, + event_win32_call_callback, +}; + +static struct event_priv * +event_win32_new(struct event_methods *meth) +{ + *meth=event_win32_methods; + return NULL; +} + +void +plugin_init(void) +{ + plugin_register_graphics_type("win32", graphics_win32_new); + plugin_register_event_type("win32", event_win32_new); +} diff --git a/graphics/win32/graphics_win32.h b/graphics/win32/graphics_win32.h new file mode 100644 index 00000000..9aab62ae --- /dev/null +++ b/graphics/win32/graphics_win32.h @@ -0,0 +1,59 @@ +#ifndef WIN32_GUI_INCLUDED +#define WIN32_GUI_INCLUDED + +#include "resources/resource.h" +#include "coord.h" +#include "point.h" +#include "graphics.h" +#include "event.h" + +#define ID_CHILD_GFX 100 +#define ID_CHILD_TOOLBAR (ID_CHILD_GFX + 1) +#define ID_CHILD_1 (ID_CHILD_TOOLBAR + 1) +#define ID_CHILD_2 (ID_CHILD_1 + 1) +#define ID_CHILD_3 (ID_CHILD_2 + 1) +#define ID_CHILD_4 (ID_CHILD_4 + 1) + +#define ID_DISPLAY_ZOOMIN 200 +#define ID_DISPLAY_ZOOMOUT 201 +#define ID_DISPLAY_REFRESH 202 +#define ID_DISPLAY_CURSOR 203 +#define ID_DISPLAY_ORIENT 204 + +#define ID_FILE_EXIT 9001 +#define ID_STUFF_GO 9002 + +//#define _(text) gettext(text) + +#define POPUP_MENU_OFFSET 4000 + +struct statusbar_methods; +struct menu_methods; +struct datawindow_methods; +struct navit; +struct callback; + + +struct menu_priv { + HWND wnd_handle; + HMENU hMenu; + struct callback* cb; +}; + +struct gui_priv { + struct navit *nav; + HANDLE hwnd; +}; + + +struct graphics_priv; + +struct menu_priv *gui_gtk_menubar_new(struct gui_priv *gui, struct menu_methods *meth); +struct menu_priv *gui_gtk_toolbar_new(struct gui_priv *gui, struct menu_methods *meth); +struct statusbar_priv *gui_gtk_statusbar_new(struct gui_priv *gui, struct statusbar_methods *meth); +struct menu_priv *gui_gtk_popup_new(struct gui_priv *gui, struct menu_methods *meth); +struct datawindow_priv *gui_gtk_datawindow_new(struct gui_priv *gui, char *name, struct callback *click, struct callback *close, struct datawindow_methods *meth); + +struct graphics_priv* win32_graphics_new( struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl); + +#endif diff --git a/graphics/win32/resources/resource.h b/graphics/win32/resources/resource.h new file mode 100644 index 00000000..55bcc2d7 --- /dev/null +++ b/graphics/win32/resources/resource.h @@ -0,0 +1,4 @@ +#include <windows.h> + +#define IDI_NAVIT 1100 +#define IDB_NAVITTOOLBAR 1101 diff --git a/graphics/win32/xpm2bmp.c b/graphics/win32/xpm2bmp.c new file mode 100644 index 00000000..95214d9c --- /dev/null +++ b/graphics/win32/xpm2bmp.c @@ -0,0 +1,588 @@ +#include <stdio.h> +#include <stdlib.h> +#include <windows.h> +#include <wingdi.h> +#include "xpm2bmp.h" + +/* #define _DBG */ + +// function prototypes +static int CreateBitmapFromXpm( const char* filename, PXPM2BMP pXpm2bmp ); + +// typedefs +static XPMCOLORENTRY theRGBRecords[] = +{ + {"ALICEBLUE", 240, 248, 255}, + {"ANTIQUEWHITE", 250, 235, 215}, + {"AQUAMARINE", 50, 191, 193}, + {"AZURE", 240, 255, 255}, + {"BEIGE", 245, 245, 220}, + {"BISQUE", 255, 228, 196}, + {"BLACK", 0, 0, 0}, + {"BLANCHEDALMOND", 255, 235, 205}, + {"BLUE", 0, 0, 255}, + {"BLUEVIOLET", 138, 43, 226}, + {"BROWN", 165, 42, 42}, + {"BURLYWOOD", 222, 184, 135}, + {"CADETBLUE", 95, 146, 158}, + {"CHARTREUSE", 127, 255, 0}, + {"CHOCOLATE", 210, 105, 30}, + {"CORAL", 255, 114, 86}, + {"CORNFLOWERBLUE", 34, 34, 152}, + {"CORNSILK", 255, 248, 220}, + {"CYAN", 0, 255, 255}, + {"DARKGOLDENROD", 184, 134, 11}, + {"DARKGREEN", 0, 86, 45}, + {"DARKKHAKI", 189, 183, 107}, + {"DARKOLIVEGREEN", 85, 86, 47}, + {"DARKORANGE", 255, 140, 0}, + {"DARKORCHID", 139, 32, 139}, + {"DARKSALMON", 233, 150, 122}, + {"DARKSEAGREEN", 143, 188, 143}, + {"DARKSLATEBLUE", 56, 75, 102}, + {"DARKSLATEGRAY", 47, 79, 79}, + {"DARKTURQUOISE", 0, 166, 166}, + {"DARKVIOLET", 148, 0, 211}, + {"DEEPPINK", 255, 20, 147}, + {"DEEPSKYBLUE", 0, 191, 255}, + {"DIMGRAY", 84, 84, 84}, + {"DODGERBLUE", 30, 144, 255}, + {"FIREBRICK", 142, 35, 35}, + {"FLORALWHITE", 255, 250, 240}, + {"FORESTGREEN", 80, 159, 105}, + {"GAINSBORO", 220, 220, 220}, + {"GHOSTWHITE", 248, 248, 255}, + {"GOLD", 218, 170, 0}, + {"GOLDENROD", 239, 223, 132}, + {"GRAY", 126, 126, 126}, + {"GRAY0", 0, 0, 0}, + {"GRAY1", 3, 3, 3}, + {"GRAY10", 26, 26, 26}, + {"GRAY100", 255, 255, 255}, + {"GRAY11", 28, 28, 28}, + {"GRAY12", 31, 31, 31}, + {"GRAY13", 33, 33, 33}, + {"GRAY14", 36, 36, 36}, + {"GRAY15", 38, 38, 38}, + {"GRAY16", 41, 41, 41}, + {"GRAY17", 43, 43, 43}, + {"GRAY18", 46, 46, 46}, + {"GRAY19", 48, 48, 48}, + {"GRAY2", 5, 5, 5}, + {"GRAY20", 51, 51, 51}, + {"GRAY21", 54, 54, 54}, + {"GRAY22", 56, 56, 56}, + {"GRAY23", 59, 59, 59}, + {"GRAY24", 61, 61, 61}, + {"GRAY25", 64, 64, 64}, + {"GRAY26", 66, 66, 66}, + {"GRAY27", 69, 69, 69}, + {"GRAY28", 71, 71, 71}, + {"GRAY29", 74, 74, 74}, + {"GRAY3", 8, 8, 8}, + {"GRAY30", 77, 77, 77}, + {"GRAY31", 79, 79, 79}, + {"GRAY32", 82, 82, 82}, + {"GRAY33", 84, 84, 84}, + {"GRAY34", 87, 87, 87}, + {"GRAY35", 89, 89, 89}, + {"GRAY36", 92, 92, 92}, + {"GRAY37", 94, 94, 94}, + {"GRAY38", 97, 97, 97}, + {"GRAY39", 99, 99, 99}, + {"GRAY4", 10, 10, 10}, + {"GRAY40", 102, 102, 102}, + {"GRAY41", 105, 105, 105}, + {"GRAY42", 107, 107, 107}, + {"GRAY43", 110, 110, 110}, + {"GRAY44", 112, 112, 112}, + {"GRAY45", 115, 115, 115}, + {"GRAY46", 117, 117, 117}, + {"GRAY47", 120, 120, 120}, + {"GRAY48", 122, 122, 122}, + {"GRAY49", 125, 125, 125}, + {"GRAY5", 13, 13, 13}, + {"GRAY50", 127, 127, 127}, + {"GRAY51", 130, 130, 130}, + {"GRAY52", 133, 133, 133}, + {"GRAY53", 135, 135, 135}, + {"GRAY54", 138, 138, 138}, + {"GRAY55", 140, 140, 140}, + {"GRAY56", 143, 143, 143}, + {"GRAY57", 145, 145, 145}, + {"GRAY58", 148, 148, 148}, + {"GRAY59", 150, 150, 150}, + {"GRAY6", 15, 15, 15}, + {"GRAY60", 153, 153, 153}, + {"GRAY61", 156, 156, 156}, + {"GRAY62", 158, 158, 158}, + {"GRAY63", 161, 161, 161}, + {"GRAY64", 163, 163, 163}, + {"GRAY65", 166, 166, 166}, + {"GRAY66", 168, 168, 168}, + {"GRAY67", 171, 171, 171}, + {"GRAY68", 173, 173, 173}, + {"GRAY69", 176, 176, 176}, + {"GRAY7", 18, 18, 18}, + {"GRAY70", 179, 179, 179}, + {"GRAY71", 181, 181, 181}, + {"GRAY72", 184, 184, 184}, + {"GRAY73", 186, 186, 186}, + {"GRAY74", 189, 189, 189}, + {"GRAY75", 191, 191, 191}, + {"GRAY76", 194, 194, 194}, + {"GRAY77", 196, 196, 196}, + {"GRAY78", 199, 199, 199}, + {"GRAY79", 201, 201, 201}, + {"GRAY8", 20, 20, 20}, + {"GRAY80", 204, 204, 204}, + {"GRAY81", 207, 207, 207}, + {"GRAY82", 209, 209, 209}, + {"GRAY83", 212, 212, 212}, + {"GRAY84", 214, 214, 214}, + {"GRAY85", 217, 217, 217}, + {"GRAY86", 219, 219, 219}, + {"GRAY87", 222, 222, 222}, + {"GRAY88", 224, 224, 224}, + {"GRAY89", 227, 227, 227}, + {"GRAY9", 23, 23, 23}, + {"GRAY90", 229, 229, 229}, + {"GRAY91", 232, 232, 232}, + {"GRAY92", 235, 235, 235}, + {"GRAY93", 237, 237, 237}, + {"GRAY94", 240, 240, 240}, + {"GRAY95", 242, 242, 242}, + {"GRAY96", 245, 245, 245}, + {"GRAY97", 247, 247, 247}, + {"GRAY98", 250, 250, 250}, + {"GRAY99", 252, 252, 252}, + {"GREEN", 0, 255, 0}, + {"GREENYELLOW", 173, 255, 47}, + {"HONEYDEW", 240, 255, 240}, + {"HOTPINK", 255, 105, 180}, + {"INDIANRED", 107, 57, 57}, + {"IVORY", 255, 255, 240}, + {"KHAKI", 179, 179, 126}, + {"LAVENDER", 230, 230, 250}, + {"LAVENDERBLUSH", 255, 240, 245}, + {"LAWNGREEN", 124, 252, 0}, + {"LEMONCHIFFON", 255, 250, 205}, + {"LIGHTBLUE", 176, 226, 255}, + {"LIGHTCORAL", 240, 128, 128}, + {"LIGHTCYAN", 224, 255, 255}, + {"LIGHTGOLDENROD", 238, 221, 130}, + {"LIGHTGOLDENRODYELLOW", 250, 250, 210}, + {"LIGHTGRAY", 168, 168, 168}, + {"LIGHTPINK", 255, 182, 193}, + {"LIGHTSALMON", 255, 160, 122}, + {"LIGHTSEAGREEN", 32, 178, 170}, + {"LIGHTSKYBLUE", 135, 206, 250}, + {"LIGHTSLATEBLUE", 132, 112, 255}, + {"LIGHTSLATEGRAY", 119, 136, 153}, + {"LIGHTSTEELBLUE", 124, 152, 211}, + {"LIGHTYELLOW", 255, 255, 224}, + {"LIMEGREEN", 0, 175, 20}, + {"LINEN", 250, 240, 230}, + {"MAGENTA", 255, 0, 255}, + {"MAROON", 143, 0, 82}, + {"MEDIUMAQUAMARINE", 0, 147, 143}, + {"MEDIUMBLUE", 50, 50, 204}, + {"MEDIUMFORESTGREEN", 50, 129, 75}, + {"MEDIUMGOLDENROD", 209, 193, 102}, + {"MEDIUMORCHID", 189, 82, 189}, + {"MEDIUMPURPLE", 147, 112, 219}, + {"MEDIUMSEAGREEN", 52, 119, 102}, + {"MEDIUMSLATEBLUE", 106, 106, 141}, + {"MEDIUMSPRINGGREEN", 35, 142, 35}, + {"MEDIUMTURQUOISE", 0, 210, 210}, + {"MEDIUMVIOLETRED", 213, 32, 121}, + {"MIDNIGHTBLUE", 47, 47, 100}, + {"MINTCREAM", 245, 255, 250}, + {"MISTYROSE", 255, 228, 225}, + {"MOCCASIN", 255, 228, 181}, + {"NAVAJOWHITE", 255, 222, 173}, + {"NAVY", 35, 35, 117}, + {"NAVYBLUE", 35, 35, 117}, + {"OLDLACE", 253, 245, 230}, + {"OLIVEDRAB", 107, 142, 35}, + {"ORANGE", 255, 135, 0}, + {"ORANGERED", 255, 69, 0}, + {"ORCHID", 239, 132, 239}, + {"PALEGOLDENROD", 238, 232, 170}, + {"PALEGREEN", 115, 222, 120}, + {"PALETURQUOISE", 175, 238, 238}, + {"PALEVIOLETRED", 219, 112, 147}, + {"PAPAYAWHIP", 255, 239, 213}, + {"PEACHPUFF", 255, 218, 185}, + {"PERU", 205, 133, 63}, + {"PINK", 255, 181, 197}, + {"PLUM", 197, 72, 155}, + {"POWDERBLUE", 176, 224, 230}, + {"PURPLE", 160, 32, 240}, + {"RED", 255, 0, 0}, + {"ROSYBROWN", 188, 143, 143}, + {"ROYALBLUE", 65, 105, 225}, + {"SADDLEBROWN", 139, 69, 19}, + {"SALMON", 233, 150, 122}, + {"SANDYBROWN", 244, 164, 96}, + {"SEAGREEN", 82, 149, 132}, + {"SEASHELL", 255, 245, 238}, + {"SIENNA", 150, 82, 45}, + {"SKYBLUE", 114, 159, 255}, + {"SLATEBLUE", 126, 136, 171}, + {"SLATEGRAY", 112, 128, 144}, + {"SNOW", 255, 250, 250}, + {"SPRINGGREEN", 65, 172, 65}, + {"STEELBLUE", 84, 112, 170}, + {"TAN", 222, 184, 135}, + {"THISTLE", 216, 191, 216}, + {"TOMATO", 255, 99, 71}, + {"TRANSPARENT", 0, 0, 1}, + {"TURQUOISE", 25, 204, 223}, + {"VIOLET", 156, 62, 206}, + {"VIOLETRED", 243, 62, 150}, + {"WHEAT", 245, 222, 179}, + {"WHITE", 255, 255, 255}, + {"WHITESMOKE", 245, 245, 245}, + {"YELLOW", 255, 255, 0}, + {"YELLOWGREEN", 50, 216, 56} +}; + + +PXPM2BMP Xpm2bmp_new() +{ + PXPM2BMP preturn = (PXPM2BMP)calloc( sizeof( XPM2BMP ) , 1 ); + return preturn; +} + + +int Xpm2bmp_load( PXPM2BMP pXpm2bmp, const char* filename ) +{ + return CreateBitmapFromXpm( filename, pXpm2bmp ); +} + +int Xpm2bmp_paint( PXPM2BMP pXpm2bmp, HDC hdc, int x1,int y1 ) +{ + StretchDIBits(hdc, + x1, y1, pXpm2bmp->size_x, pXpm2bmp->size_y, + 0, 0, pXpm2bmp->size_x, pXpm2bmp->size_y, + pXpm2bmp->wimage_data_trans, + (BITMAPINFO *)pXpm2bmp->bmih_trans, + DIB_RGB_COLORS, + SRCAND ); + + StretchDIBits(hdc, + x1, y1, pXpm2bmp->size_x, pXpm2bmp->size_y, + 0, 0, pXpm2bmp->size_x, pXpm2bmp->size_y, + pXpm2bmp->wimage_data, + (BITMAPINFO *)pXpm2bmp->bmih, + DIB_RGB_COLORS, + SRCPAINT ); + + return 0; +} + +static int parse_line_values( const char* line, PXPM2BMP pXpm2bmp ) +{ + int return_value = 0; + char* parse_line = (char*)line; + char* tok; + int value_pos = 0; + + parse_line = strchr( parse_line, '"' ); + parse_line++; + + tok = strtok( parse_line, " \t\n" ); + + while ( tok != 0 ) + { + int val = atoi( tok ); + switch ( value_pos ) + { + case 0: pXpm2bmp->size_x = val; break; + case 1: pXpm2bmp->size_y = val; break; + case 2: pXpm2bmp->colors = val; break; + case 3: pXpm2bmp->chars_per_pixel = val; break; + case 4: pXpm2bmp->hotspot_x = val; break; + case 5: pXpm2bmp->hotspot_y = val; break; + } + tok = strtok( NULL, " \t" ); + value_pos ++; + + } + + return return_value; +} + +static int hex2int( char c ) +{ + if ((c >= '0') && (c <='9' )) return c - '0'; + if ((c >= 'A') && (c <= 'F')) return c - 'A' + 10; + if ((c >= 'a') && (c <= 'f')) return c - 'a' + 10; + return -1; +} + +static DWORD string2hex16( const char* str ) +{ + int i1 = hex2int( str[0] ); + int i2 = hex2int( str[1] ); + if ( ( i1 >= 0 ) && ( i2 >= 0 ) ) + return i1*16+i2; + return -1; +} + +static int parse_color_values( const char* line, PXPM2BMP pXpm2bmp ) +{ + int return_value = 0; + char* cq = strchr( line, '"' ); + char* cchar = strchr( cq+pXpm2bmp->chars_per_pixel+1, 'c' ); + char* chash = strchr( cq+pXpm2bmp->chars_per_pixel+1, '#' ); + char* qe = strchr( cq+pXpm2bmp->chars_per_pixel+1, '"' ); + + cq++; + + if ( cq ) + { + memcpy( pXpm2bmp->color_entires[ pXpm2bmp-> color_entires_size].color_str, cq, pXpm2bmp->chars_per_pixel + 1 ); + pXpm2bmp->color_entires[ pXpm2bmp-> color_entires_size].color_str[ pXpm2bmp->chars_per_pixel ] = '\0'; + + + if ( cchar && chash && qe) + { + chash++; + *qe = 0; + int len = strlen( chash ); + + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].r = string2hex16( &chash[0] ); + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].g = string2hex16( &chash[len / 3] ); + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].b = string2hex16( &chash[len * 2 / 3] ); +#ifdef _DBG +printf( "adding color %s => %d RGB %x %x %x to index %d\n", + line, + pXpm2bmp->color_entires_size, + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].r, + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].g, + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].b, + pXpm2bmp->color_entires_size ); +#endif + } + else + { + int q; + char *start = cchar + 1; + char *end = start; + + while ( *start != 0 ) + { + if ( ( *start != '\t' ) && ( *start != ' ' ) ) + { + break; + } + start++; + } + + end = start; + while ( *end != 0 ) + { + if ( ( *end == '\t' ) || ( *end == ' ' ) || ( *end == '"' )) + { + *end = 0; + } + end++; + } + + start = _strupr( start ); + + for ( q=0; q < sizeof( theRGBRecords ) / sizeof( theRGBRecords[0] ); q++ ) + { + + if ( 0 == strcmp( start, theRGBRecords[q].color_str ) ) + { + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].r = theRGBRecords[q].r; + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].g = theRGBRecords[q].g; + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].b = theRGBRecords[q].b; + } + } + if ( 0 == strcmp( start, "NONE" ) ) + { + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].r = 255; + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].g = 0; + pXpm2bmp->color_entires[ pXpm2bmp->color_entires_size].b = 255; + } + } + } + pXpm2bmp->color_entires_size++; + + return return_value; +} + +static int vv = 0; + +static int parse_pixel_line_values( const char* line, PXPM2BMP pXpm2bmp, unsigned char* pixel_data, unsigned char* pixel_data_trans ) +{ + int return_value = 0; + int i,j; + + + char* cq = strchr( line, '"' ); + int pix_idx = 0; + int size_x = pXpm2bmp->size_x; + int len = strlen( cq ); + + cq++; + + if ( len > pXpm2bmp->chars_per_pixel * size_x ) + { + for ( i=0; i< size_x; i++ ) + { + int found = 0; + char* cmp = &cq[ i * pXpm2bmp->chars_per_pixel]; + + for ( j=0; j< pXpm2bmp-> color_entires_size; j++ ) + { + if ( strncmp( cmp, pXpm2bmp->color_entires[ j ].color_str, pXpm2bmp->chars_per_pixel ) == 0 ) + { + int r = pXpm2bmp->color_entires[ j ].r; + int g = pXpm2bmp->color_entires[ j ].g; + int b = pXpm2bmp->color_entires[ j ].b; + + if ( ( r == 255 ) && ( g == 0 ) && ( r == 255 ) ) + { + r=g=b=0; + pixel_data_trans[ pix_idx ] = 255; + pixel_data_trans[ pix_idx+1 ] = 255; + pixel_data_trans[ pix_idx+2 ] = 255; + } + else + { + pixel_data_trans[ pix_idx ] = 0; + pixel_data_trans[ pix_idx+1 ] = 0; + pixel_data_trans[ pix_idx+2 ] = 0; + } + + // pixel_data[ pix_idx++ ] = pXpm2bmp->color_entires[ j ].r; + // pixel_data[ pix_idx++ ] = pXpm2bmp->color_entires[ j ].g; + // pixel_data[ pix_idx++ ] = pXpm2bmp->color_entires[ j ].b; + pixel_data[ pix_idx++ ] = b; + pixel_data[ pix_idx++ ] = g; + pixel_data[ pix_idx++ ] = r; + found = 1; + vv++; + break; + } + } + if ( !found ) + { + fprintf( stderr, "XPMLIB: error color not found\n" ); + } + + } + } + else + { + return_value = -1; + fprintf( stderr, "XPMLIB: invalid line length\n" ); + } + return return_value; +} + + +static int CreateBitmapFromXpm( const char* filename, PXPM2BMP pXpm2bmp ) +{ + int return_val = 0; + unsigned char i, row; + char line[ 1024 ]; + int nbytes ; + FILE* file_xpm = fopen( filename, "r" ); + + int phase = 0; + row = 0; + + if ( file_xpm ) + { + while ( fgets(line, sizeof( line ), file_xpm ) ) + { +#ifdef _DBG + printf( "PARSING: %s\n", line ); +#endif + if ( line[ 0 ] != '"' ) + continue; + + switch ( phase ) + { + case 0: + parse_line_values( line, pXpm2bmp ); +#ifdef _DBG + printf( "size_x %d\n", pXpm2bmp->size_x ); + printf( "size_y %d\n", pXpm2bmp->size_y ); +#endif + phase = 1; + + pXpm2bmp->color_entires_size = 0; + nbytes = ( pXpm2bmp->chars_per_pixel + 1 ) * pXpm2bmp->colors; + + pXpm2bmp->color_entires = calloc( sizeof( XPMCOLORENTRY ), pXpm2bmp->colors + 100 ); + pXpm2bmp->color_entires[0].color_str = calloc( nbytes, pXpm2bmp->colors ); + for ( i = 1; i< pXpm2bmp->colors; i++ ) + { + pXpm2bmp->color_entires[i].color_str = pXpm2bmp->color_entires[0].color_str + ( pXpm2bmp->chars_per_pixel + 1 ) * i; + } + + if (!(pXpm2bmp->dib = (unsigned char *)malloc(sizeof(BITMAPINFOHEADER) + pXpm2bmp->size_x * pXpm2bmp->size_y * 3))) + { + return 4; + } + if (!(pXpm2bmp->dib_trans = (unsigned char *)calloc(sizeof(BITMAPINFOHEADER) + pXpm2bmp->size_x * pXpm2bmp->size_y * 3,1))) + { + return 4; + } + + memset(pXpm2bmp->dib, 0, sizeof(BITMAPINFOHEADER)); + pXpm2bmp->bmih = (BITMAPINFOHEADER *)pXpm2bmp->dib; + pXpm2bmp->bmih->biSize = sizeof(BITMAPINFOHEADER); + pXpm2bmp->bmih->biWidth = pXpm2bmp->size_x; + pXpm2bmp->bmih->biHeight = -((long)pXpm2bmp->size_y); + pXpm2bmp->bmih->biPlanes = 1; + pXpm2bmp->bmih->biBitCount = 24; + pXpm2bmp->bmih->biCompression = 0; + pXpm2bmp->wimage_data = pXpm2bmp->dib + sizeof(BITMAPINFOHEADER); + + + pXpm2bmp->bmih_trans = (BITMAPINFOHEADER *)pXpm2bmp->dib_trans; + pXpm2bmp->bmih_trans->biSize = sizeof(BITMAPINFOHEADER); + pXpm2bmp->bmih_trans->biWidth = pXpm2bmp->size_x; + pXpm2bmp->bmih_trans->biHeight = -((long)pXpm2bmp->size_y); + pXpm2bmp->bmih_trans->biPlanes = 1; + pXpm2bmp->bmih_trans->biBitCount = 24; + pXpm2bmp->bmih_trans->biCompression = 0; + pXpm2bmp->wimage_data_trans = pXpm2bmp->dib_trans + sizeof(BITMAPINFOHEADER); +// memset( pXpm2bmp->wimage_data_trans, 255, 5* 22 * 3 ); + + break; + case 1: + parse_color_values( line, pXpm2bmp ); + if ( pXpm2bmp->color_entires_size >= pXpm2bmp->colors ) + { + phase = 2; + } + + break; + case 2: + parse_pixel_line_values( line, pXpm2bmp, + pXpm2bmp->wimage_data + row * pXpm2bmp->size_x * 3, + pXpm2bmp->wimage_data_trans + row * pXpm2bmp->size_x * 3 ); + + row++; + if ( row >= pXpm2bmp->size_y ) + { + phase = 3; + } + break; + } + + } + + fclose( file_xpm ); + } + return return_val; +} diff --git a/graphics/win32/xpm2bmp.h b/graphics/win32/xpm2bmp.h new file mode 100644 index 00000000..2fa028f1 --- /dev/null +++ b/graphics/win32/xpm2bmp.h @@ -0,0 +1,44 @@ +#ifndef Xpm2BMP_H_INCLUDED +#define Xpm2BMP_H_INCLUDED + +#include <windows.h> +#include "wingdi.h" + +typedef struct XPMCOLORENTRY_TAG +{ + char* color_str; + unsigned long r; + unsigned long g; + unsigned long b; +} XPMCOLORENTRY, *PXPMCOLORENTRY; + +typedef struct XPM2BMP_TAG +{ + unsigned short size_x; + unsigned short size_y; + unsigned short colors; + unsigned short pixels; + unsigned short chars_per_pixel; + unsigned short hotspot_x; + unsigned short hotspot_y; + + int color_entires_size; + PXPMCOLORENTRY color_entires; + + unsigned char *dib; + unsigned char *wimage_data; + BITMAPINFOHEADER *bmih; + + unsigned char *dib_trans; + unsigned char *wimage_data_trans; + BITMAPINFOHEADER *bmih_trans; + +} XPM2BMP, *PXPM2BMP; + + +PXPM2BMP Xpm2bmp_new(); +int Xpm2bmp_load( PXPM2BMP pXpm2bmp, const char* filename ); +int Xpm2bmp_paint( PXPM2BMP pXpm2bmp, HDC hdc, int x1,int y1 ); + + +#endif // Xpm2BMP_H_INCLUDED |