From f71506a5e01d921404c2c50fad4b26dea57b5341 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 19 Oct 2010 11:02:48 +0200 Subject: e-map: Make cache a server-side surface Doing performance optimizations on the client is not a good idea... --- widgets/misc/e-map.c | 187 +++++++++++++++++++++++++-------------------------- 1 file changed, 92 insertions(+), 95 deletions(-) (limited to 'widgets/misc/e-map.c') diff --git a/widgets/misc/e-map.c b/widgets/misc/e-map.c index eb73174f1c..f39bdf7c8b 100644 --- a/widgets/misc/e-map.c +++ b/widgets/misc/e-map.c @@ -60,7 +60,8 @@ EMapZoomState; struct _EMapPrivate { /* Pointer to map image */ - GdkPixbuf *map_pixbuf, *map_render_pixbuf; + GdkPixbuf *map_pixbuf; + cairo_surface_t *map_render_surface; /* Settings */ gboolean frozen, smooth_zoom; @@ -84,6 +85,7 @@ struct _EMapPrivate { static void e_map_finalize (GObject *object); static void e_map_realize (GtkWidget *widget); +static void e_map_unrealize (GtkWidget *widget); static void e_map_size_request (GtkWidget *widget, GtkRequisition *requisition); static void e_map_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gint e_map_button_press (GtkWidget *widget, GdkEventButton *event); @@ -93,7 +95,7 @@ static gint e_map_expose (GtkWidget *widget, GdkEventExpose *event); static gint e_map_key_press (GtkWidget *widget, GdkEventKey *event); static void e_map_set_scroll_adjustments (GtkWidget *widget, GtkAdjustment *hadj, GtkAdjustment *vadj); -static void update_render_pixbuf (EMap *map, GdkInterpType interp, gboolean render_overlays); +static void update_render_surface (EMap *map, gboolean render_overlays); static void set_scroll_area (EMap *view, int width, int height); static void center_at (EMap *map, gint x, gint y); static void smooth_center_at (EMap *map, gint x, gint y); @@ -139,6 +141,7 @@ e_map_class_init (EMapClass *class) GTK_TYPE_ADJUSTMENT); widget_class->realize = e_map_realize; + widget_class->unrealize = e_map_unrealize; widget_class->size_request = e_map_size_request; widget_class->size_allocate = e_map_size_allocate; widget_class->button_press_event = e_map_button_press; @@ -202,11 +205,8 @@ e_map_finalize (GObject *object) priv->map_pixbuf = NULL; } - if (priv->map_render_pixbuf) - { - g_object_unref (priv->map_render_pixbuf); - priv->map_render_pixbuf = NULL; - } + /* gone in unrealize */ + g_assert (priv->map_render_surface == NULL); g_free (priv); view->priv = NULL; @@ -256,7 +256,20 @@ e_map_realize (GtkWidget *widget) gtk_widget_set_style (widget, style); gdk_window_set_back_pixmap (window, NULL, FALSE); - update_render_pixbuf (E_MAP (widget), GDK_INTERP_BILINEAR, TRUE); + update_render_surface (E_MAP (widget), TRUE); +} + +static void +e_map_unrealize (GtkWidget *widget) +{ + EMap *view = E_MAP (widget); + EMapPrivate *priv = view->priv; + + cairo_surface_destroy (priv->map_render_surface); + priv->map_render_surface = NULL; + + if (GTK_WIDGET_CLASS (e_map_parent_class)->unrealize) + (*GTK_WIDGET_CLASS (e_map_parent_class)->unrealize) (widget); } /* Size_request handler for the map view */ @@ -309,7 +322,7 @@ e_map_size_allocate (GtkWidget *widget, GtkAllocation *allocation) gtk_widget_queue_draw (widget); } - update_render_pixbuf (view, GDK_INTERP_BILINEAR, TRUE); + update_render_surface (view, TRUE); } /* Button press handler for the map view */ @@ -366,16 +379,15 @@ e_map_expose (GtkWidget *widget, GdkEventExpose *event) view = E_MAP (widget); priv = view->priv; - if (!priv->map_render_pixbuf) return FALSE; cr = gdk_cairo_create (event->window); gdk_cairo_region (cr, event->region); cairo_clip (cr); - gdk_cairo_set_source_pixbuf (cr, - priv->map_render_pixbuf, - - priv->xofs, - - priv->yofs); + cairo_set_source_surface (cr, + priv->map_render_surface, + - priv->xofs, + - priv->yofs); cairo_paint (cr); cairo_destroy (cr); @@ -559,8 +571,8 @@ e_map_window_to_world (EMap *map, gdouble win_x, gdouble win_y, gdouble *world_l priv = map->priv; g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (map))); - width = gdk_pixbuf_get_width (priv->map_render_pixbuf); - height = gdk_pixbuf_get_height (priv->map_render_pixbuf); + width = E_MAP_GET_WIDTH (map); + height = E_MAP_GET_HEIGHT (map); *world_longitude = (win_x + priv->xofs - (gdouble) width / 2.0) / ((gdouble) width / 2.0) * 180.0; @@ -577,12 +589,12 @@ e_map_world_to_window (EMap *map, gdouble world_longitude, gdouble world_latitud g_return_if_fail (map); priv = map->priv; - g_return_if_fail (priv->map_render_pixbuf); + g_return_if_fail (priv->map_render_surface); g_return_if_fail (world_longitude >= -180.0 && world_longitude <= 180.0); g_return_if_fail (world_latitude >= -90.0 && world_latitude <= 90.0); - width = gdk_pixbuf_get_width (priv->map_render_pixbuf); - height = gdk_pixbuf_get_height (priv->map_render_pixbuf); + width = E_MAP_GET_WIDTH (map); + height = E_MAP_GET_HEIGHT (map); *win_x = (width / 2.0 + (width / 2.0) * world_longitude / 180.0) - priv->xofs; *win_y = (height / 2.0 - (height / 2.0) * world_latitude / 90.0) - priv->yofs; @@ -706,7 +718,7 @@ e_map_remove_point (EMap *map, EMapPoint *point) /* FIXME: Re-scaling the whole pixbuf is more than a little * overkill when just one point is removed */ - update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE); + update_render_surface (map, TRUE); repaint_point (map, point); } @@ -766,7 +778,7 @@ e_map_point_is_in_view (EMap *map, EMapPoint *point) gdouble x, y; priv = map->priv; - if (!priv->map_render_pixbuf) return FALSE; + if (!priv->map_render_surface) return FALSE; e_map_world_to_window (map, point->longitude, point->latitude, &x, &y); gtk_widget_get_allocation (GTK_WIDGET (map), &allocation); @@ -815,7 +827,7 @@ e_map_get_closest_point (EMap *map, gdouble longitude, gdouble latitude, gboolea static void update_and_paint (EMap *map) { - update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE); + update_render_surface (map, TRUE); gtk_widget_queue_draw (GTK_WIDGET (map)); } @@ -833,15 +845,14 @@ load_map_background (EMap *view, gchar *name) if (priv->map_pixbuf) g_object_unref (priv->map_pixbuf); priv->map_pixbuf = pb0; - update_render_pixbuf (view, GDK_INTERP_BILINEAR, TRUE); + update_render_surface (view, TRUE); return TRUE; } static void -update_render_pixbuf (EMap *map, - GdkInterpType interp, - gboolean render_overlays) +update_render_surface (EMap *map, + gboolean render_overlays) { EMapPrivate *priv; EMapPoint *point; @@ -877,20 +888,26 @@ update_render_pixbuf (EMap *map, /* Reallocate the pixbuf */ - if (priv->map_render_pixbuf) g_object_unref (priv->map_render_pixbuf); - priv->map_render_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, /* No alpha */ - 8, width, height); + if (priv->map_render_surface) cairo_surface_destroy (priv->map_render_surface); + priv->map_render_surface = gdk_window_create_similar_surface (gtk_widget_get_window (GTK_WIDGET (map)), + CAIRO_CONTENT_COLOR, + width, height); /* Scale the original map into the rendering pixbuf */ if (width > 1 && height > 1) { - gdk_pixbuf_scale (priv->map_pixbuf, priv->map_render_pixbuf, 0, 0, /* Dest (x, y) */ - width, height, 0, 0, /* Offset (x, y) */ - zoom, zoom, /* Scale (x, y) */ - interp); + cairo_t *cr = cairo_create (priv->map_render_surface); + cairo_scale (cr, (double) width / orig_width, (double) height / orig_height); + gdk_cairo_set_source_pixbuf (cr, priv->map_pixbuf, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); } + /* Compute image offsets with respect to window */ + + set_scroll_area (map, width, height); + if (render_overlays) { /* Add points */ @@ -901,73 +918,53 @@ update_render_pixbuf (EMap *map, update_render_point (map, point); } } - - /* Compute image offsets with respect to window */ - - set_scroll_area (map, width, height); -} - -/* Queues a repaint of the specified area in window coordinates */ - -static void -put_pixel_with_clipping (GdkPixbuf *pixbuf, gint x, gint y, guint rgba) -{ - gint width, height; - gint rowstride, n_channels; - guchar *pixels, *pixel; - - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); - rowstride = gdk_pixbuf_get_rowstride (pixbuf); - n_channels = gdk_pixbuf_get_n_channels (pixbuf); - pixels = gdk_pixbuf_get_pixels (pixbuf); - - if (x < 0 || x >= width || y < 0 || y >= height) - return; - - pixel = pixels + (y * rowstride) + (x * n_channels); - - *pixel = (rgba >> 24); - *(pixel + 1) = (rgba >> 16) & 0x000000ff; - *(pixel + 2) = (rgba >> 8) & 0x000000ff; - - if (n_channels > 3) - { - *(pixel + 3) = rgba & 0x000000ff; - } } -/* Redraw point in client pixbuf */ +/* Redraw point in client surface */ static void update_render_point (EMap *map, EMapPoint *point) { EMapPrivate *priv; - GdkPixbuf *pb; + cairo_t *cr; gdouble px, py; + static guchar mask1[] = { 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static guchar mask2[] = { 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00 }; + cairo_surface_t *mask; priv = map->priv; - pb = priv->map_render_pixbuf; - if (!pb) return; + + if (priv->map_render_surface == NULL) + return; + + cr = cairo_create (priv->map_render_surface); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); e_map_world_to_window (map, point->longitude, point->latitude, &px, &py); - px += priv->xofs; - py += priv->yofs; - - put_pixel_with_clipping (pb, px, py, point->rgba); - put_pixel_with_clipping (pb, px - 1, py, point->rgba); - put_pixel_with_clipping (pb, px + 1, py, point->rgba); - put_pixel_with_clipping (pb, px, py - 1, point->rgba); - put_pixel_with_clipping (pb, px, py + 1, point->rgba); - - put_pixel_with_clipping (pb, px - 2, py, 0x000000ff); - put_pixel_with_clipping (pb, px + 2, py, 0x000000ff); - put_pixel_with_clipping (pb, px, py - 2, 0x000000ff); - put_pixel_with_clipping (pb, px, py + 2, 0x000000ff); - put_pixel_with_clipping (pb, px - 1, py - 1, 0x000000ff); - put_pixel_with_clipping (pb, px - 1, py + 1, 0x000000ff); - put_pixel_with_clipping (pb, px + 1, py - 1, 0x000000ff); - put_pixel_with_clipping (pb, px + 1, py + 1, 0x000000ff); + px = floor (px + priv->xofs); + py = floor (py + priv->yofs); + + cairo_set_source_rgb (cr, 0, 0, 0); + mask = cairo_image_surface_create_for_data (mask1, CAIRO_FORMAT_A8, 5, 5, 8); + cairo_mask_surface (cr, mask, px - 2, py - 2); + cairo_surface_destroy (mask); + + cairo_set_source_rgba (cr, + ((point->rgba >> 24) & 0xff) / 255.0, + ((point->rgba >> 16) & 0xff) / 255.0, + ((point->rgba >> 8) & 0xff) / 255.0, + ( point->rgba & 0xff) / 255.0); + mask = cairo_image_surface_create_for_data (mask2, CAIRO_FORMAT_A8, 3, 3, 4); + cairo_mask_surface (cr, mask, px - 1, py - 1); + cairo_surface_destroy (mask); + + cairo_destroy (cr); } /* Repaint point on X server */ @@ -1295,8 +1292,8 @@ zoom_in_smooth (EMap *map) priv = map->priv; window = gtk_widget_get_window (GTK_WIDGET (map)); - width = gdk_pixbuf_get_width (priv->map_render_pixbuf); - height = gdk_pixbuf_get_height (priv->map_render_pixbuf); + width = E_MAP_GET_WIDTH (map); + height = E_MAP_GET_HEIGHT (map); /* Center the target point as much as possible */ @@ -1306,7 +1303,7 @@ zoom_in_smooth (EMap *map) /* Render and paint a temporary map without overlays, so they don't get in * the way (look ugly) while zooming */ - update_render_pixbuf (map, GDK_INTERP_BILINEAR, FALSE); + update_render_surface (map, FALSE); gtk_widget_draw (GTK_WIDGET (map), NULL); /* Find out where in the area we're going to zoom to */ @@ -1317,7 +1314,7 @@ zoom_in_smooth (EMap *map) * blowup sequence ends */ priv->zoom_state = E_MAP_ZOOMED_IN; - update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE); + update_render_surface (map, TRUE); /* Do the blowup */ @@ -1347,7 +1344,7 @@ zoom_in (EMap *map) priv->zoom_state = E_MAP_ZOOMED_IN; - update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE); + update_render_surface (map, TRUE); e_map_world_to_window ( map, priv->zoom_target_long, @@ -1380,14 +1377,14 @@ zoom_out (EMap *map) area.width = allocation.width; area.height = allocation.height; - /* Must be done before update_render_pixbuf() */ + /* Must be done before update_render_surface() */ e_map_window_to_world ( map, area.width / 2, area.height / 2, &longitude, &latitude); priv->zoom_state = E_MAP_ZOOMED_OUT; - update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE); + update_render_surface (map, TRUE); e_map_world_to_window (map, longitude, latitude, &x, &y); center_at (map, x + priv->xofs, y + priv->yofs); -- cgit v1.2.3