From 4b76d2096d4622fed8641b24f9dfe4ce782e307e Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 26 Apr 2017 12:33:57 +0100 Subject: GTK: Optimise tiled bitmap plotting, and scaled bitmap rendering. We now let Cairo deal with the tiling. Also we don't keep cached scaled bitmaps any more. The speedup for small bitmap tile sizes is huge. --- frontends/gtk/plotters.c | 228 ++++++++++++++++------------------------------- 1 file changed, 79 insertions(+), 149 deletions(-) (limited to 'frontends/gtk') diff --git a/frontends/gtk/plotters.c b/frontends/gtk/plotters.c index 2ed1d066a..c57b03294 100644 --- a/frontends/gtk/plotters.c +++ b/frontends/gtk/plotters.c @@ -484,132 +484,6 @@ nsgtk_plot_path(const struct redraw_context *ctx, } -/** - * plot a pixbuf - * - * \param x x coordinate to put pixmap - * \param y y coordinate to put pixmap - * \param width width of pixmap - * \param height height of pixmap - * \param bitmap The bitmap to plot - * \param bg the background colour - */ -static nserror -nsgtk_plot_pixbuf(int x, int y, int width, int height, - struct bitmap *bitmap, colour bg) -{ - int x0, y0, x1, y1; - int dsrcx, dsrcy, dwidth, dheight; - int bmwidth, bmheight; - - cairo_surface_t *bmsurface = bitmap->surface; - - /* Bail early if we can */ - if (width == 0 || height == 0) - /* Nothing to plot */ - return NSERROR_OK; - if ((x > (cliprect.x + cliprect.width)) || - ((x + width) < cliprect.x) || - (y > (cliprect.y + cliprect.height)) || - ((y + height) < cliprect.y)) { - /* Image completely outside clip region */ - return NSERROR_OK; - } - - /* Get clip rectangle / image rectangle edge differences */ - x0 = cliprect.x - x; - y0 = cliprect.y - y; - x1 = (x + width) - (cliprect.x + cliprect.width); - y1 = (y + height) - (cliprect.y + cliprect.height); - - /* Set initial draw geometry */ - dsrcx = x; - dsrcy = y; - dwidth = width; - dheight = height; - - /* Manually clip draw coordinates to area of image to be rendered */ - if (x0 > 0) { - /* Clip left */ - dsrcx += x0; - dwidth -= x0; - } - if (y0 > 0) { - /* Clip top */ - dsrcy += y0; - dheight -= y0; - } - if (x1 > 0) { - /* Clip right */ - dwidth -= x1; - } - if (y1 > 0) { - /* Clip bottom */ - dheight -= y1; - } - - if (dwidth == 0 || dheight == 0) - /* Nothing to plot */ - return NSERROR_OK; - - bmwidth = cairo_image_surface_get_width(bmsurface); - bmheight = cairo_image_surface_get_height(bmsurface); - - /* Render the bitmap */ - if ((bmwidth == width) && (bmheight == height)) { - /* Bitmap is not scaled */ - /* Plot the bitmap */ - cairo_set_source_surface(current_cr, bmsurface, x, y); - cairo_rectangle(current_cr, dsrcx, dsrcy, dwidth, dheight); - cairo_fill(current_cr); - - } else { - /* Bitmap is scaled */ - if ((bitmap->scsurface != NULL) && - ((cairo_image_surface_get_width(bitmap->scsurface) != width) || - (cairo_image_surface_get_height(bitmap->scsurface) != height))){ - cairo_surface_destroy(bitmap->scsurface); - bitmap->scsurface = NULL; - } - - if (bitmap->scsurface == NULL) { - bitmap->scsurface = cairo_surface_create_similar(bmsurface,CAIRO_CONTENT_COLOR_ALPHA, width, height); - cairo_t *cr = cairo_create(bitmap->scsurface); - - /* Scale *before* setting the source surface (1) */ - cairo_scale(cr, (double)width / bmwidth, (double)height / bmheight); - cairo_set_source_surface(cr, bmsurface, 0, 0); - - /* To avoid getting the edge pixels blended with 0 - * alpha, which would occur with the default - * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer (2) - */ - cairo_pattern_set_extend(cairo_get_source(cr), - CAIRO_EXTEND_REFLECT); - - /* Replace the destination with the source instead of - * overlaying - */ - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - - /* Do the actual drawing */ - cairo_paint(cr); - - cairo_destroy(cr); - - } - /* Plot the scaled bitmap */ - cairo_set_source_surface(current_cr, bitmap->scsurface, x, y); - cairo_rectangle(current_cr, dsrcx, dsrcy, dwidth, dheight); - cairo_fill(current_cr); - - - } - - return NSERROR_OK; -} - - /** * Plot a bitmap * @@ -643,44 +517,100 @@ nsgtk_plot_bitmap(const struct redraw_context *ctx, colour bg, bitmap_flags_t flags) { - int doneheight = 0, donewidth = 0; bool repeat_x = (flags & BITMAPF_REPEAT_X); bool repeat_y = (flags & BITMAPF_REPEAT_Y); + GdkRectangle cliprect_bitmap; + cairo_surface_t *img_surface; + int img_width, img_height; /* Bail early if we can */ - if (width == 0 || height == 0) + if (width <= 0 || height <= 0) { /* Nothing to plot */ return NSERROR_OK; + } - if (!(repeat_x || repeat_y)) { - /* Not repeating at all, so just pass it on */ - return nsgtk_plot_pixbuf(x, y, width, height, bitmap, bg); + /* Copy the clip rectangle into bitmap plot clip rectangle */ + cliprect_bitmap = cliprect; + + /* Constrain bitmap plot rectangle for any lack of tiling */ + if (!repeat_x) { + if (cliprect_bitmap.width > width) { + cliprect_bitmap.width = width; + } + if (cliprect_bitmap.x < x) { + cliprect_bitmap.x = x; + cliprect_bitmap.width -= x - cliprect_bitmap.x; + } + } + if (!repeat_y) { + if (cliprect_bitmap.height > height) { + cliprect_bitmap.height = height; + } + if (cliprect_bitmap.y < y) { + cliprect_bitmap.y = y; + cliprect_bitmap.height -= y - cliprect_bitmap.y; + } } - if (y > cliprect.y) { - doneheight = (cliprect.y - height) + ((y - cliprect.y) % height); - } else { - doneheight = y; + /* Bail early if we can */ + if (cliprect_bitmap.width <= 0 || cliprect_bitmap.height <= 0) { + /* Nothing to plot */ + return NSERROR_OK; } - while (doneheight < (cliprect.y + cliprect.height)) { - if (x > cliprect.x) { - donewidth = (cliprect.x - width) + ((x - cliprect.x) % width); - } else { - donewidth = x; + /* Get the image's surface and intrinsic dimensions */ + img_surface = bitmap->surface; + img_width = cairo_image_surface_get_width(img_surface); + img_height = cairo_image_surface_get_height(img_surface); + + /* Set the source surface */ + if ((img_width == width) && (img_height == height)) { + /* Non-scaled rendering */ + cairo_set_source_surface(current_cr, img_surface, x, y); + + /* Enable tiling if we're repeating */ + if (repeat_x || repeat_y) { + cairo_pattern_set_extend( + cairo_get_source(current_cr), + CAIRO_EXTEND_REPEAT); } - while (donewidth < (cliprect.x + cliprect.width)) { - nsgtk_plot_pixbuf(donewidth, doneheight, - width, height, bitmap, bg); - donewidth += width; - if (!repeat_x) - break; + /* Render the bitmap */ + cairo_rectangle(current_cr, + cliprect_bitmap.x, + cliprect_bitmap.y, + cliprect_bitmap.width, + cliprect_bitmap.height); + cairo_fill(current_cr); + } else { + /* Scaled rendering */ + double scale_x = (double)width / img_width; + double scale_y = (double)height / img_height; + + /* Save cairo rendering context state before scaling */ + cairo_save(current_cr); + cairo_scale(current_cr, scale_x, scale_y); + + cairo_set_source_surface(current_cr, img_surface, + x / scale_x, y / scale_y); + + /* Enable tiling if we're repeating */ + if (repeat_x || repeat_y) { + cairo_pattern_set_extend( + cairo_get_source(current_cr), + CAIRO_EXTEND_REPEAT); } - doneheight += height; - if (!repeat_y) - break; + /* Render the bitmap */ + cairo_rectangle(current_cr, + cliprect_bitmap.x / scale_x, + cliprect_bitmap.y / scale_y, + cliprect_bitmap.width / scale_x, + cliprect_bitmap.height / scale_y); + cairo_fill(current_cr); + + /* Restore pre-scaling cairo rendering state */ + cairo_restore(current_cr); } return NSERROR_OK; -- cgit v1.2.3