diff options
Diffstat (limited to 'navit/graphics')
| -rw-r--r-- | navit/graphics/android/graphics_android.c | 67 | ||||
| -rw-r--r-- | navit/graphics/qt5/graphics_qt5.cpp | 28 | ||||
| -rw-r--r-- | navit/graphics/sdl/graphics_sdl.c | 60 | ||||
| -rw-r--r-- | navit/graphics/sdl/raster.c | 199 | ||||
| -rw-r--r-- | navit/graphics/sdl/raster.h | 6 | ||||
| -rw-r--r-- | navit/graphics/win32/graphics_win32.c | 175 |
6 files changed, 447 insertions, 88 deletions
diff --git a/navit/graphics/android/graphics_android.c b/navit/graphics/android/graphics_android.c index e119cf3cc..c6fd37488 100644 --- a/navit/graphics/android/graphics_android.c +++ b/navit/graphics/android/graphics_android.c @@ -483,8 +483,9 @@ static void resize_callback(struct graphics_priv *gra, int w, int h) { callback_list_call_attr_2(gra->cbl, attr_resize, (void *)w, (void *)h); } -static void padding_callback(struct graphics_priv *gra, int left, int top, int right, int bottom) { - dbg(lvl_debug, "win.padding left=%d top=%d right=%d bottom=%d ok", left, top, right, bottom); +static void padding_changed_callback(struct graphics_priv *gra, int left, int top, int right, + int bottom) { + dbg(lvl_error, "win.padding left=%d top=%d right=%d bottom=%d", left, top, right, bottom); gra->padding->left = left; gra->padding->top = top; gra->padding->right = right; @@ -627,45 +628,45 @@ static int graphics_android_init(struct graphics_priv *ret, struct graphics_priv if (ret->Paint) ret->Paint = (*jnienv)->NewGlobalRef(jnienv, ret->Paint); - cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setSizeChangedCallback", "(I)V"); + cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setSizeChangedCallback", "(J)V"); if (cid == NULL) { - dbg(lvl_error,"no SetResizeCallback method found"); + dbg(lvl_error,"no setResizeCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(resize_callback), ret); - (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (int)cb); + (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); - cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setPaddingChangedCallback", "(I)V"); + cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setPaddingChangedCallback", "(J)V"); if (cid == NULL) { - dbg(lvl_error,"no SetPaddingCallback method found"); + dbg(lvl_error,"no setPaddingCallback method found"); return 0; /* exception thrown */ } - cb=callback_new_1(callback_cast(padding_callback), ret); - (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (int)cb); + cb=callback_new_1(callback_cast(padding_changed_callback), ret); + (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); - cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setButtonCallback", "(I)V"); + cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setButtonCallback", "(J)V"); if (cid == NULL) { - dbg(lvl_error,"no SetButtonCallback method found"); + dbg(lvl_error,"no setButtonCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(button_callback), ret); - (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (int)cb); + (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); - cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setMotionCallback", "(I)V"); + cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setMotionCallback", "(J)V"); if (cid == NULL) { - dbg(lvl_error,"no SetMotionCallback method found"); + dbg(lvl_error,"no setMotionCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(motion_callback), ret); - (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (int)cb); + (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); - cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setKeypressCallback", "(I)V"); + cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setKeypressCallback", "(J)V"); if (cid == NULL) { - dbg(lvl_error,"no SetKeypressCallback method found"); + dbg(lvl_error,"no setKeypressCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(keypress_callback), ret); - (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (int)cb); + (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); if (!find_method(ret->NavitGraphicsClass, "draw_polyline", "(Landroid/graphics/Paint;[I)V", &ret->NavitGraphics_draw_polyline)) @@ -696,7 +697,7 @@ static int graphics_android_init(struct graphics_priv *ret, struct graphics_priv return 0; if (!find_method(ret->NavitGraphicsClass, "overlay_resize", "(IIIII)V", &ret->NavitGraphics_overlay_resize)) return 0; - if (!find_method(ret->NavitGraphicsClass, "SetCamera", "(I)V", &ret->NavitGraphics_SetCamera)) + if (!find_method(ret->NavitGraphicsClass, "setCamera", "(I)V", &ret->NavitGraphics_SetCamera)) return 0; #if 0 set_activity(ret->NavitGraphics); @@ -732,7 +733,7 @@ static void graphics_android_disable_suspend(struct window *win) { static void graphics_android_cmd_runMenuItem(struct graphics_priv *this, char *function, struct attr **in, struct attr ***out, int *valid) { int ncmd=0; - dbg(0,"Running %s",function); + dbg(lvl_debug,"Running %s",function); if(!strcmp(function,"map_download_dialog")) { ncmd=3; } else if(!strcmp(function,"backup_restore_dialog")) { @@ -911,7 +912,7 @@ static jmethodID NavitWatch_remove; static void do_poll(JNIEnv *env, int fd, int cond) { struct pollfd pfd; pfd.fd=fd; - dbg(lvl_debug,"%p poll called for %d %d", fd, cond); + dbg(lvl_debug,"poll called for %d %d", fd, cond); switch ((enum event_watch_cond)cond) { case event_watch_cond_read: pfd.events=POLLIN; @@ -931,8 +932,8 @@ static void do_poll(JNIEnv *env, int fd, int cond) { static struct event_watch *event_android_add_watch(int h, enum event_watch_cond cond, struct callback *cb) { jobject ret; - ret=(*jnienv)->NewObject(jnienv, NavitWatchClass, NavitWatch_init, (int)do_poll, h, (int) cond, (int)cb); - dbg(lvl_debug,"result for %d,%d,%p=%p",h,cond,cb,ret); + ret=(*jnienv)->NewObject(jnienv, NavitWatchClass, NavitWatch_init, (jlong)do_poll, h, (jint) cond, (jlong)cb); + dbg(lvl_debug,"result for %d,%d,%p = %p",h,cond,cb,ret); if (ret) ret = (*jnienv)->NewGlobalRef(jnienv, ret); return (struct event_watch *)ret; @@ -973,7 +974,8 @@ static struct event_timeout *event_android_add_timeout(int timeout, int multi, s ret->cb = cb; ret->multi = multi; ret->handle_timeout = event_android_handle_timeout; - ret->jni_timeout = (*jnienv)->NewObject(jnienv, NavitTimeoutClass, NavitTimeout_init, timeout, multi, (int)ret); + ret->jni_timeout = (*jnienv)->NewObject(jnienv, NavitTimeoutClass, NavitTimeout_init, timeout, multi, (jlong)(ret)); + dbg(lvl_debug,"result for %d,%d,%p = %p",timeout,multi,cb,ret); if (ret->jni_timeout) ret->jni_timeout = (*jnienv)->NewGlobalRef(jnienv, ret->jni_timeout); return ret; @@ -1025,7 +1027,7 @@ static struct event_priv *event_android_new(struct event_methods *meth) { dbg(lvl_debug,"enter"); if (!find_class_global("org/navitproject/navit/NavitTimeout", &NavitTimeoutClass)) return NULL; - NavitTimeout_init = (*jnienv)->GetMethodID(jnienv, NavitTimeoutClass, "<init>", "(IZI)V"); + NavitTimeout_init = (*jnienv)->GetMethodID(jnienv, NavitTimeoutClass, "<init>", "(IZJ)V"); if (NavitTimeout_init == NULL) return NULL; NavitTimeout_remove = (*jnienv)->GetMethodID(jnienv, NavitTimeoutClass, "remove", "()V"); @@ -1044,7 +1046,7 @@ static struct event_priv *event_android_new(struct event_methods *meth) { if (!find_class_global("org/navitproject/navit/NavitWatch", &NavitWatchClass)) return NULL; - NavitWatch_init = (*jnienv)->GetMethodID(jnienv, NavitWatchClass, "<init>", "(IIII)V"); + NavitWatch_init = (*jnienv)->GetMethodID(jnienv, NavitWatchClass, "<init>", "(JIIJ)V"); if (NavitWatch_init == NULL) return NULL; NavitWatch_remove = (*jnienv)->GetMethodID(jnienv, NavitWatchClass, "remove", "()V"); @@ -1056,7 +1058,7 @@ static struct event_priv *event_android_new(struct event_methods *meth) { Navit_disableSuspend = (*jnienv)->GetMethodID(jnienv, NavitClass, "disableSuspend", "()V"); if (Navit_disableSuspend == NULL) return NULL; - Navit_exit = (*jnienv)->GetMethodID(jnienv, NavitClass, "exit", "()V"); + Navit_exit = (*jnienv)->GetMethodID(jnienv, NavitClass, "onDestroy", "()V"); if (Navit_exit == NULL) return NULL; Navit_fullscreen = (*jnienv)->GetMethodID(jnienv, NavitClass, "fullscreen", "(I)V"); @@ -1076,7 +1078,12 @@ static struct event_priv *event_android_new(struct event_methods *meth) { return NULL; } - +/* below needs review, android resizes the view and the actual height of the keyboard is not + * passed down here and is irrelevant, only wether it is onscreen matters, so + * android returns a height of 1 px in the case of an onscreen keyboard just + * to keep the logic to remove the keyboard from the screen afterwards working untill + * the logic in native code is reviewed. + * / /** * @brief Displays the native input method. * @@ -1096,14 +1103,12 @@ static struct event_priv *event_android_new(struct event_methods *meth) { * @return True if the input method is going to be displayed, false if not. */ int show_native_keyboard (struct graphics_keyboard *kbd) { - kbd->w = -1; if (Navit_showNativeKeyboard == NULL) { dbg(lvl_error, "method Navit.showNativeKeyboard() not found, cannot display keyboard"); return 0; } kbd->h = (*jnienv)->CallIntMethod(jnienv, android_activity, Navit_showNativeKeyboard); - dbg(lvl_error, "keyboard size is %d x %d px", kbd->w, kbd->h); - dbg(lvl_error, "return"); + dbg(lvl_error, "keyboard height is %d px is a lie", kbd->h); /* zero height means we're not showing a keyboard, therefore normalize height to boolean */ return !!(kbd->h); } diff --git a/navit/graphics/qt5/graphics_qt5.cpp b/navit/graphics/qt5/graphics_qt5.cpp index 10784d6b3..cb9b4c456 100644 --- a/navit/graphics/qt5/graphics_qt5.cpp +++ b/navit/graphics/qt5/graphics_qt5.cpp @@ -408,13 +408,7 @@ static void draw_polygon(struct graphics_priv* gr, struct graphics_gc_priv* gc, polygon.putPoints(i, 1, p[i].x, p[i].y); gr->painter->setPen(*gc->pen); gr->painter->setBrush(*gc->brush); - /* if the polygon is transparent, we need to clear it first */ - if (!gc->brush->isOpaque()) { - QPainter::CompositionMode mode = gr->painter->compositionMode(); - gr->painter->setCompositionMode(QPainter::CompositionMode_Clear); - gr->painter->drawPolygon(polygon); - gr->painter->setCompositionMode(mode); - } + gr->painter->drawPolygon(polygon); } @@ -446,13 +440,6 @@ static void draw_polygon_with_holes (struct graphics_priv *gr, struct graphics_g if(hole_count > 0) path = path.subtracted(inner); - /* if the polygon is transparent, we need to clear it first */ - if (!gc->brush->isOpaque()) { - QPainter::CompositionMode mode = gr->painter->compositionMode(); - gr->painter->setCompositionMode(QPainter::CompositionMode_Clear); - gr->painter->drawPath(path); - gr->painter->setCompositionMode(mode); - } gr->painter->drawPath(path); } @@ -460,13 +447,6 @@ static void draw_rectangle(struct graphics_priv* gr, struct graphics_gc_priv* gc // dbg(lvl_debug,"gr=%p gc=%p %d,%d,%d,%d", gr, gc, p->x, p->y, w, h); if (gr->painter == NULL) return; - /* if the rectangle is transparent, we need to clear it first */ - if (!gc->brush->isOpaque()) { - QPainter::CompositionMode mode = gr->painter->compositionMode(); - gr->painter->setCompositionMode(QPainter::CompositionMode_Clear); - gr->painter->fillRect(p->x, p->y, w, h, *gc->brush); - gr->painter->setCompositionMode(mode); - } gr->painter->fillRect(p->x, p->y, w, h, *gc->brush); } @@ -647,9 +627,11 @@ static void draw_mode(struct graphics_priv* gr, enum draw_mode_num mode) { case draw_mode_begin: dbg(lvl_debug, "Begin drawing on context %p (use == %d)", gr, gr->use_count); gr->use_count++; - if (gr->painter == NULL) + if (gr->painter == NULL) { + if(gr->parent != NULL) + gr->pixmap->fill(QColor(0,0,0,0)); gr->painter = new QPainter(gr->pixmap); - else + } else dbg(lvl_debug, "drawing on %p already active", gr); break; case draw_mode_end: diff --git a/navit/graphics/sdl/graphics_sdl.c b/navit/graphics/sdl/graphics_sdl.c index 4187d4ed6..d92699307 100644 --- a/navit/graphics/sdl/graphics_sdl.c +++ b/navit/graphics/sdl/graphics_sdl.c @@ -284,45 +284,47 @@ static void image_free(struct graphics_priv *gr, struct graphics_image_priv * gi g_free(gi); } -static void draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) { +static void draw_polygon_with_holes (struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count, + int hole_count, int* ccount, struct point **holes) { + + dbg(lvl_debug, "draw_polygon_with_holes: %p ", gc); if ((gr->overlay_parent && !gr->overlay_parent->overlay_enable) || (gr->overlay_parent && gr->overlay_parent->overlay_enable && !gr->overlay_enable) ) { return; } - 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; - vx[i] = x; - vy[i] = y; - - dbg(lvl_debug, "draw_polygon: %p %i %d,%d", gc, i, p[i].x, p[i].y); - } + /* SDL library (SDL_gfx) uses array of X and array of Y instead of array of points + * as the rest of navit does. This requires translating the coordinates from one struct + * into another. As we have our own version of SDL_gfx anyway, I step aside from this + * mechanic and continue using points. This breaks (pseudo= compatibility with stock + * sdl_graphics. Since we need to raytrace the polygons anyway, we can prepare the + * coordinates for SDL primitives there. + */ if(gr->aa) { - raster_aapolygon(gr->screen, count, vx, vy, - SDL_MapRGBA(gr->screen->format, - gc->fore_r, - gc->fore_g, - gc->fore_b, - gc->fore_a)); + raster_aapolygon_with_holes(gr->screen, p, count, hole_count, ccount, holes, + SDL_MapRGBA(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b, + gc->fore_a)); } else { - raster_polygon(gr->screen, count, vx, vy, - SDL_MapRGBA(gr->screen->format, - gc->fore_r, - gc->fore_g, - gc->fore_b, - gc->fore_a)); + raster_polygon_with_holes(gr->screen, p, count, hole_count, ccount, holes, + SDL_MapRGBA(gr->screen->format, + gc->fore_r, + gc->fore_g, + gc->fore_b, + gc->fore_a)); } } +static void draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) { + dbg(lvl_debug, "draw_polygon: %p ", gc); + /* Use polygon with holes primitive as this seems to be better performing than the + * traditional SDL_gfx like ones */ + draw_polygon_with_holes(gr, gc, p, count, 0, NULL, NULL); +} + static void draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) { if ((gr->overlay_parent && !gr->overlay_parent->overlay_enable) || (gr->overlay_parent && gr->overlay_parent->overlay_enable && !gr->overlay_enable) ) { @@ -819,6 +821,8 @@ static struct graphics_methods graphics_methods = { NULL, /* set_attr */ NULL, /* show_native_keyboard */ NULL, /* hide_native_keyboard */ + NULL, /* get_dpi */ + draw_polygon_with_holes }; static struct graphics_priv *overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, diff --git a/navit/graphics/sdl/raster.c b/navit/graphics/sdl/raster.c index 6c713844b..8da7cd9a1 100644 --- a/navit/graphics/sdl/raster.c +++ b/navit/graphics/sdl/raster.c @@ -16,6 +16,7 @@ #include <math.h> +#include <glib.h> #include "raster.h" @@ -1953,8 +1954,8 @@ void raster_aapolygon(SDL_Surface *dst, int16_t n, int16_t *vx, int16_t *vy, uin o = p = -1; } #else - raster_hline(dst, xa+1, xb, y, color); + #endif // raster_rect_inline(dst, xa, y, xb - xa, 1, color); @@ -1962,3 +1963,199 @@ void raster_aapolygon(SDL_Surface *dst, int16_t n, int16_t *vx, int16_t *vy, uin } } +/** + * @brief render filled polygon with holes by raycasting along the y axis + * + * This function renders a filled polygon that can have holes by SDL primitive + * graphic functions by raycasting along the y axis. This works basically the same + * as for complex polygons. Only difference is the "holes" are individual + * polygon loops not connected to the outer loop. + * FIXME: This draws well as long as the "hole" does not intersect with the + * outer polygon. However such multipolygons are seen a mapping error in OSM + * and therefore the rendering err may even help in detecting them. + * But this could be fixed by never starting a line on a vertex that came from a + * hole intersection. + * + * @param s SDL surface to draw on + * @param p Array of points containing the outer polygon + * @param count Number of points in outer polygon + * @param hole_count Number of hole polygons + * @param ccount number of points per hole polygon + * @oaram holes array of point arrays. One for each "hole" + * @param col Color to draw this. + */ +void raster_aapolygon_with_holes (SDL_Surface *s, struct point *p, int count, int hole_count, int* ccount, + struct point **holes, uint32_t col) { + int i; + struct point * p1; + struct point * p2; + /* Check visibility of clipping rectangle */ + if ((s->clip_rect.w==0) || (s->clip_rect.h==0)) { + return; + } + + /* Sanity check number of edges */ + if (count < 3) { + return; + } + /* + * Draw antialiased outline + */ + p1 = p2 = p; + p2++; + for (i = 1; i < count; i++) { + raster_aalineColorInt(s, p1->x, p1->y, p2->x, p2->y, col, 0); + p1 = p2; + p2++; + } + raster_aalineColorInt(s, p1->x, p1->y, p->x, p->y, col, 0); + raster_polygon_with_holes(s, p, count, hole_count, ccount, holes, col); +} + +/** + * @brief render filled polygon with holes by raycasting along the y axis + * + * This function renders a filled polygon that can have holes by SDL primitive + * graphic functions by raycasting along the y axis. This works basically the same + * as for complex polygons. Only difference is the "holes" are individual + * polygon loops not connected to the outer loop. + * FIXME: This draws well as long as the "hole" does not intersect with the + * outer polygon. However such multipolygons are seen a mapping error in OSM + * and therefore the rendering err may even help in detecting them. + * But this could be fixed by never starting a line on a vertex that came from a + * hole intersection. + * + * @param s SDL surface to draw on + * @param p Array of points containing the outer polygon + * @param count Number of points in outer polygon + * @param hole_count Number of hole polygons + * @param ccount number of points per hole polygon + * @oaram holes array of point arrays. One for each "hole" + * @param col Color to draw this. + */ +void raster_polygon_with_holes (SDL_Surface *s, struct point *p, int count, int hole_count, int* ccount, + struct point **holes, uint32_t col) { + int vertex_max; + int vertex_count; + int * vertexes; + int miny, maxy; + int i; + int y; + + /* Check visibility of clipping rectangle */ + if ((s->clip_rect.w==0) || (s->clip_rect.h==0)) { + return; + } + + /* Sanity check number of edges */ + if (count < 3) { + return; + } + + /* + * Prepare a buffer for vertexes. Maximum number of vertexes is the number of points + * of polygon and holes + */ + vertex_max = count; + for(i =0; i < hole_count; i ++) { + vertex_max += ccount[i]; + } + vertexes = g_malloc(sizeof(int) * vertex_max); + if(vertexes == NULL) { + return; + } + + /* calculate y min and max coordinate. We can ignore the holes, as we won't render hole + * parts "bigger" than the surrounding polygon.*/ + miny = p[0].y; + maxy = p[0].y; + for (i = 1; (i < count); i++) { + if (p[i].y < miny) { + miny = p[i].y; + } else if (p[i].y > maxy) { + maxy = p[i].y; + } + } + + /* scan y coordinates from miny to maxy */ + for(y = miny; y <= maxy ; y ++) { + int h; + vertex_count=0; + /* calculate the intersecting points of the polygon with current y and add to vertexes array*/ + for (i = 0; (i < count); i++) { + int ind1; + int ind2; + struct point p1; + struct point p2; + + if (!i) { + ind1 = count - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + p1.y = p[ind1].y; + p2.y = p[ind2].y; + if (p1.y < p2.y) { + p1.x = p[ind1].x; + p2.x = p[ind2].x; + } else if (p1.y > p2.y) { + p2.y = p[ind1].y; + p1.y = p[ind2].y; + p2.x = p[ind1].x; + p1.x = p[ind2].x; + } else { + continue; + } + if ( ((y >= p1.y) && (y < p2.y)) || ((y == maxy) && (y > p1.y) && (y <= p2.y)) ) { + vertexes[vertex_count++] = ((65536 * (y - p1.y)) / (p2.y - p1.y)) * (p2.x - p1.x) + (65536 * p1.x); + } + } + for(h= 0; h < hole_count; h ++) { + /* add the intersecting points from the holes as well */ + for (i = 0; (i < ccount[h]); i++) { + int ind1; + int ind2; + struct point p1; + struct point p2; + + if (!i) { + ind1 = ccount[h] - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + p1.y = holes[h][ind1].y; + p2.y = holes[h][ind2].y; + if (p1.y < p2.y) { + p1.x = holes[h][ind1].x; + p2.x = holes[h][ind2].x; + } else if (p1.y > p2.y) { + p2.y = holes[h][ind1].y; + p1.y = holes[h][ind2].y; + p2.x = holes[h][ind1].x; + p1.x = holes[h][ind2].x; + } else { + continue; + } + if ( ((y >= p1.y) && (y < p2.y)) || ((y == maxy) && (y > p1.y) && (y <= p2.y)) ) { + vertexes[vertex_count++] = ((65536 * (y - p1.y)) / (p2.y - p1.y)) * (p2.x - p1.x) + (65536 * p1.x); + } + } + } + + /* sort the vertexes */ + qsort(vertexes, vertex_count, sizeof(int), gfxPrimitivesCompareInt); + /* draw the lines between every second vertex */ + for (i = 0; (i < vertex_count); i +=2) { + Sint16 xa; + Sint16 xb; + xa = (vertexes[i] >> 16); + xb = (vertexes[i+1] >> 16); + raster_hline(s, xa+1, xb, y, col); + } + } + g_free(vertexes); +} diff --git a/navit/graphics/sdl/raster.h b/navit/graphics/sdl/raster.h index 2e68ea05f..e295f23fb 100644 --- a/navit/graphics/sdl/raster.h +++ b/navit/graphics/sdl/raster.h @@ -10,15 +10,21 @@ #include <stdint.h> #include "SDL.h" +#include "point.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_polygon_with_holes (SDL_Surface *s, struct point *p, int count, int hole_count, int* ccount, + struct point **holes, 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); +void raster_aapolygon_with_holes (SDL_Surface *s, struct point *p, int count, int hole_count, int* ccount, + struct point **holes, uint32_t col); + #endif /* __RASTER_H */ diff --git a/navit/graphics/win32/graphics_win32.c b/navit/graphics/win32/graphics_win32.c index 6bcad3be6..6346e59f7 100644 --- a/navit/graphics/win32/graphics_win32.c +++ b/navit/graphics/win32/graphics_win32.c @@ -829,8 +829,177 @@ static void draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, #if HAVE_API_WIN32_CE /* - * Windows CE doesn't support PaintPath used for other versions. No polygon with holes support for CE yet. + * Windows CE doesn't feature GraphicsPath, so in order to draw filled polygons + * with holes, we need to resort on manual raycasting. The following functions + * have been inspired from SDL backend that does need to raycast all polygons. */ + +/* Helper qsort callback for polygon drawing */ +static int gfxPrimitivesCompareInt(const void *a, const void *b) { + return (*(const int *) a) - (*(const int *) b); +} + +/** + * @brief render filled polygon with holes by raycasting along the y axis + * + * This function renders a filled polygon that can have holes by SDL primitive + * graphic functions by raycasting along the y axis. This works basically the same + * as for complex polygons. Only difference is the "holes" are individual + * polygon loops not connected to the outer loop. + * FIXME: This draws well as long as the "hole" does not intersect with the + * outer polygon. However such multipolygons are seen a mapping error in OSM + * and therefore the rendering err may even help in detecting them. + * But this could be fixed by never starting a line on a vertex that came from a + * hole intersection. + * + * @param gr graphics instance + * @param gc graphics context + * @param p Array of points for the outer polygon + * @param count Number of points in outer polygon + * @param hole_count Number of hole polygons + * @param ccount number of points per hole polygon + * @oaram holes array of point arrays. One for each "hole" + */ +static void draw_polygon_with_holes (struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count, + int hole_count, int* ccount, struct point **holes) { + int vertex_max; + int vertex_count; + int * vertexes; + int miny, maxy; + int i; + int y; + HPEN holdpen; + HBRUSH holdbrush; + HPEN linepen; + + /* Sanity check number of edges */ + if (count < 3) { + return; + } + + /* + * Prepare a buffer for vertexes. Maximum number of vertexes is the number of points + * of polygon and holes + */ + vertex_max = count; + for(i =0; i < hole_count; i ++) { + vertex_max += ccount[i]; + } + vertexes = g_malloc(sizeof(int) * vertex_max); + if(vertexes == NULL) { + return; + } + + /* create pen to draw the lines */ + linepen = CreatePen( PS_SOLID, 1, gc->fg_color ); + + /* remeber pen and brush */ + holdpen = SelectObject( gr->hMemDC, linepen ); + holdbrush = SelectObject( gr->hMemDC, gc->hbrush ); + + /* calculate y min and max coordinate. We can ignore the holes, as we won't render hole + * parts "bigger" than the surrounding polygon.*/ + miny = p[0].y; + maxy = p[0].y; + for (i = 1; (i < count); i++) { + if (p[i].y < miny) { + miny = p[i].y; + } else if (p[i].y > maxy) { + maxy = p[i].y; + } + } + + /* scan y coordinates from miny to maxy */ + for(y = miny; y <= maxy ; y ++) { + int h; + vertex_count=0; + /* calculate the intersecting points of the polygon with current y and add to vertexes array*/ + for (i = 0; (i < count); i++) { + int ind1; + int ind2; + struct point p1; + struct point p2; + + if (!i) { + ind1 = count - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + p1.y = p[ind1].y; + p2.y = p[ind2].y; + if (p1.y < p2.y) { + p1.x = p[ind1].x; + p2.x = p[ind2].x; + } else if (p1.y > p2.y) { + p2.y = p[ind1].y; + p1.y = p[ind2].y; + p2.x = p[ind1].x; + p1.x = p[ind2].x; + } else { + continue; + } + if ( ((y >= p1.y) && (y < p2.y)) || ((y == maxy) && (y > p1.y) && (y <= p2.y)) ) { + vertexes[vertex_count++] = ((65536 * (y - p1.y)) / (p2.y - p1.y)) * (p2.x - p1.x) + (65536 * p1.x); + } + } + for(h= 0; h < hole_count; h ++) { + /* add the intersecting points from the holes as well */ + for (i = 0; (i < ccount[h]); i++) { + int ind1; + int ind2; + struct point p1; + struct point p2; + + if (!i) { + ind1 = ccount[h] - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + p1.y = holes[h][ind1].y; + p2.y = holes[h][ind2].y; + if (p1.y < p2.y) { + p1.x = holes[h][ind1].x; + p2.x = holes[h][ind2].x; + } else if (p1.y > p2.y) { + p2.y = holes[h][ind1].y; + p1.y = holes[h][ind2].y; + p2.x = holes[h][ind1].x; + p1.x = holes[h][ind2].x; + } else { + continue; + } + if ( ((y >= p1.y) && (y < p2.y)) || ((y == maxy) && (y > p1.y) && (y <= p2.y)) ) { + vertexes[vertex_count++] = ((65536 * (y - p1.y)) / (p2.y - p1.y)) * (p2.x - p1.x) + (65536 * p1.x); + } + } + } + + /* sort the vertexes */ + qsort(vertexes, vertex_count, sizeof(int), gfxPrimitivesCompareInt); + /* draw the lines between every second vertex */ + for (i = 0; (i < vertex_count); i +=2) { + int xa; + int xb; + xa = (vertexes[i] >> 16); + xb = (vertexes[i+1] >> 16); + MoveToEx( gr->hMemDC, xa+1, y, NULL ); + LineTo( gr->hMemDC, xb, y ); + } + } + /* free vertex buffer */ + g_free(vertexes); + + /* restore pen and brush */ + SelectObject( gr->hMemDC, holdbrush); + SelectObject( gr->hMemDC, holdpen); + + /* delete linepen */ + DeleteObject(linepen); +} #else static void draw_polygon_with_holes (struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count, int hole_count, int* ccount, struct point **holes) { @@ -1522,11 +1691,7 @@ static struct graphics_methods graphics_methods = { NULL, /* show_native_keyboard */ NULL, /* hide_native_keyboard */ NULL, /* get dpi */ -#if HAVE_API_WIN32_CE - NULL, /* draw_polygon_with_holes */ -#else draw_polygon_with_holes -#endif }; |
