diff --git a/ChangeLog b/ChangeLog index 85f45c3f0d..6a34a79298 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2007-12-13 Sven Neumann + + Speed up our display rendering code paths by keeping data with + pre-multiplied alpha where possible. The projection tile-manager + at 100 % zoom is not affected. So we can still always get the + non-pre-multiplied pixel data. + + * app/core/gimpprojection.[ch]: removed + gimp_projection_get_opacity(), the projection is always opaque. + + * app/base/tile-pyramid.[ch]: use pre-multiplied alpha for the + upper levels of the pyramid. This speeds up validation of the + upper levels significantly. + + * app/base/temp-buf.[ch]: added temp_buf_demultiply(). + + * app/core/gimpimage-preview.c: demultiply the preview temp-buf + obtained from the projection's tile tyramid. + + * app/display/gimpdisplayshell-draw.c + * app/display/gimpdisplayshell-render.c: added code to deal with + pre-multiplied data. In fact all data returned by + render_image_tile_fault() is now pre-multiplied so that + render_image_rgb_a() and render_image_gray_a() don't need to use + the large lookup tables from gimprender.[ch] any longer. + 2007-12-13 Sven Neumann * app/widgets/gimprender.[ch]: export the light and dark check diff --git a/app/base/temp-buf.c b/app/base/temp-buf.c index 221086d563..6d7d05218e 100644 --- a/app/base/temp-buf.c +++ b/app/base/temp-buf.c @@ -54,11 +54,11 @@ temp_buf_new (gint width, temp = g_slice_new (TempBuf); - temp->width = width; - temp->height = height; - temp->bytes = bytes; - temp->x = x; - temp->y = y; + temp->bytes = bytes; + temp->width = width; + temp->height = height; + temp->x = x; + temp->y = y; temp->data = g_new (guchar, width * height * bytes); @@ -240,13 +240,13 @@ temp_buf_scale (TempBuf *src, gint new_width, gint new_height) { - TempBuf *dest; - guchar *src_data; - guchar *dest_data; - gdouble x_ratio; - gdouble y_ratio; - gint loop1; - gint loop2; + TempBuf *dest; + const guchar *src_data; + guchar *dest_data; + gdouble x_ratio; + gdouble y_ratio; + gint loop1; + gint loop2; g_return_val_if_fail (src != NULL, NULL); g_return_val_if_fail (new_width > 0 && new_height > 0, NULL); @@ -266,9 +266,9 @@ temp_buf_scale (TempBuf *src, { for (loop2 = 0 ; loop2 < new_width ; loop2++) { - guchar *src_pixel; - guchar *dest_pixel; - gint i; + const guchar *src_pixel; + guchar *dest_pixel; + gint i; src_pixel = src_data + (gint) (loop2 * x_ratio) * src->bytes + @@ -344,38 +344,93 @@ temp_buf_copy_area (TempBuf *src, return new; } +/** + * temp_buf_demultiply: + * @buf: + * + * Converts a TempBuf with pre-multiplied alpha to a 'normal' TempBuf. + */ void -temp_buf_free (TempBuf *temp_buf) +temp_buf_demultiply (TempBuf *buf) { - g_return_if_fail (temp_buf != NULL); + guchar *data; + gint pixels; + gint x, y; - if (temp_buf->data) - g_free (temp_buf->data); + g_return_if_fail (buf != NULL); - g_slice_free (TempBuf, temp_buf); + switch (buf->bytes) + { + case 1: + break; + + case 2: + data = temp_buf_data (buf); + pixels = buf->width * buf->height; + while (pixels--) + { + if (data[1]) + data[0] = (data[0] << 8) / data[1]; + + data += 2; + } + break; + + case 3: + break; + + case 4: + data = temp_buf_data (buf); + pixels = buf->width * buf->height; + while (pixels--) + { + if (data[3]) + { + data[0] = (data[0] << 8) / data[3]; + data[1] = (data[1] << 8) / data[3]; + data[2] = (data[2] << 8) / data[3]; + } + + data += 4; + } + break; + + default: + g_return_if_reached (); + break; + } +} + +void +temp_buf_free (TempBuf *buf) +{ + g_return_if_fail (buf != NULL); + + if (buf->data) + g_free (buf->data); + + g_slice_free (TempBuf, buf); } guchar * -temp_buf_data (TempBuf *temp_buf) +temp_buf_data (TempBuf *buf) { - return temp_buf->data; + return buf->data; } guchar * -temp_buf_data_clear (TempBuf *temp_buf) +temp_buf_data_clear (TempBuf *buf) { - memset (temp_buf->data, 0, - temp_buf->height * temp_buf->width * temp_buf->bytes); + memset (buf->data, 0, buf->height * buf->width * buf->bytes); - return temp_buf->data; + return buf->data; } gsize -temp_buf_get_memsize (TempBuf *temp_buf) +temp_buf_get_memsize (TempBuf *buf) { - if (temp_buf) - return (sizeof (TempBuf) + - (gsize) temp_buf->bytes * temp_buf->width * temp_buf->height); + if (buf) + return (sizeof (TempBuf) + buf->bytes * buf->width * buf->height); return 0; } diff --git a/app/base/temp-buf.h b/app/base/temp-buf.h index 64c9517f70..b87c7d4928 100644 --- a/app/base/temp-buf.h +++ b/app/base/temp-buf.h @@ -22,12 +22,12 @@ struct _TempBuf { - gint bytes; /* the necessary info */ - gint width; - gint height; - gint x, y; /* origin of data source */ - guchar *data; /* The data buffer. Do never access this field - directly, use temp_buf_data() instead !! */ + gint bytes; /* number of bytes per pixel (1,2,3 or 4) */ + gint width; + gint height; + gint x, y; /* origin of data source */ + guchar *data; /* The data buffer. Do never access this field + directly, use temp_buf_data() instead !! */ }; @@ -62,6 +62,9 @@ TempBuf * temp_buf_copy_area (TempBuf *src, gint height, gint dest_x, gint dest_y); + +void temp_buf_demultiply (TempBuf *buf); + void temp_buf_free (TempBuf *buf); guchar * temp_buf_data (TempBuf *buf); guchar * temp_buf_data_clear (TempBuf *buf); diff --git a/app/base/tile-pyramid.c b/app/base/tile-pyramid.c index 26fb69c9ca..84d1fb0764 100644 --- a/app/base/tile-pyramid.c +++ b/app/base/tile-pyramid.c @@ -41,15 +41,23 @@ struct _TilePyramid }; -static gint tile_pyramid_alloc_levels (TilePyramid *pyramid, - gint top_level); -static void tile_pyramid_validate_tile (TileManager *tm, - Tile *tile, - TileManager *tm_below); -static void tile_pyramid_write_quarter (Tile *dest, - Tile *src, - gint i, - gint j); +static gint tile_pyramid_alloc_levels (TilePyramid *pyramid, + gint top_level); +static void tile_pyramid_validate_tile (TileManager *tm, + Tile *tile, + TileManager *tm_below); +static void tile_pyramid_validate_upper_tile (TileManager *tm, + Tile *tile, + TileManager *tm_below); + +static void tile_pyramid_write_quarter (Tile *dest, + Tile *src, + gint i, + gint j); +static void tile_pyramid_write_upper_quarter (Tile *dest, + Tile *src, + gint i, + gint j); /** * tile_pyramid_new: @@ -178,8 +186,10 @@ tile_pyramid_get_level (gint width, /** * tile_pyramid_get_tiles: - * @pyramid: a #TilePyramid - * @level: level, typically obtained using tile_pyramid_get_level() + * @pyramid: a #TilePyramid + * @level: level, typically obtained using tile_pyramid_get_level() + * @is_premult: location to store whether the pixel data has the alpha + * channel pre-multiplied or not * * Gives access to the #TileManager at @level of the @pyramid. * @@ -187,7 +197,8 @@ tile_pyramid_get_level (gint width, **/ TileManager * tile_pyramid_get_tiles (TilePyramid *pyramid, - gint level) + gint level, + gboolean *is_premult) { g_return_val_if_fail (pyramid != NULL, NULL); @@ -195,6 +206,9 @@ tile_pyramid_get_tiles (TilePyramid *pyramid, g_return_val_if_fail (pyramid->tiles[level] != NULL, NULL); + if (is_premult) + *is_premult = (level > 0); + return pyramid->tiles[level]; } @@ -338,8 +352,9 @@ tile_pyramid_alloc_levels (TilePyramid *pyramid, for (level = pyramid->top_level + 1; level <= top_level; level++) { - gint width = pyramid->width >> level; - gint height = pyramid->height >> level; + TileValidateProc proc; + gint width = pyramid->width >> level; + gint height = pyramid->height >> level; if (width == 0 || height == 0) return pyramid->top_level; @@ -354,14 +369,23 @@ tile_pyramid_alloc_levels (TilePyramid *pyramid, pyramid->tiles[level] = tile_manager_new (width, height, pyramid->bytes); /* Use the level below to validate tiles. */ + if (level == 1) + proc = (TileValidateProc) tile_pyramid_validate_tile; + else + proc = (TileValidateProc) tile_pyramid_validate_upper_tile; + tile_manager_set_validate_proc (pyramid->tiles[level], - (TileValidateProc) tile_pyramid_validate_tile, + proc, pyramid->tiles[level - 1]); } return pyramid->top_level; } +/* This method is used to validate a pyramid tile from the base level. + * It needs to pre-multiply the alpha channel because upper levels are + * pre-multiplied. + */ static void tile_pyramid_validate_tile (TileManager *tm, Tile *tile, @@ -388,6 +412,39 @@ tile_pyramid_validate_tile (TileManager *tm, } } +/* This method is used to validate tiles in the upper pyramid levels. + * Here all data has the alpha channel pre-multiplied. + */ +static void +tile_pyramid_validate_upper_tile (TileManager *tm, + Tile *tile, + TileManager *tm_below) +{ + gint tile_col; + gint tile_row; + gint i, j; + + tile_manager_get_tile_col_row (tm, tile, &tile_col, &tile_row); + + for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) + { + Tile *source = tile_manager_get_at (tm_below, + tile_col * 2 + i, + tile_row * 2 + j, + TRUE, FALSE); + if (source) + { + tile_pyramid_write_upper_quarter (tile, source, i, j); + tile_release (source, FALSE); + } + } +} + +/* Average the src tile to one quarter of the destination tile. + * The source tile doesn't have pre-multiplied alpha, but the + * destination tile does. + */ static void tile_pyramid_write_quarter (Tile *dest, Tile *src, @@ -451,7 +508,7 @@ tile_pyramid_write_quarter (Tile *dest, dst[0] = ((src0[0] * src0[1] + src1[0] * src1[1] + src2[0] * src2[1] + - src3[0] * src3[1]) / a); + src3[0] * src3[1]) >> 10); dst[1] = (a + 2) >> 2; break; } @@ -503,15 +560,15 @@ tile_pyramid_write_quarter (Tile *dest, dst[0] = ((src0[0] * src0[3] + src1[0] * src1[3] + src2[0] * src2[3] + - src3[0] * src3[3]) / a); + src3[0] * src3[3]) >> 10); dst[1] = ((src0[1] * src0[3] + src1[1] * src1[3] + src2[1] * src2[3] + - src3[1] * src3[3]) / a); + src3[1] * src3[3]) >> 10); dst[2] = ((src0[2] * src0[3] + src1[2] * src1[3] + src2[2] * src2[3] + - src3[2] * src3[3]) / a); + src3[2] * src3[3]) >> 10); dst[3] = (a + 2) >> 2; break; } @@ -530,3 +587,103 @@ tile_pyramid_write_quarter (Tile *dest, src_data += src_ewidth * bpp * 2; } } + +/* Average the src tile to one quarter of the destination tile. + * The source and destination tiles have pre-multiplied alpha. + */ +static void +tile_pyramid_write_upper_quarter (Tile *dest, + Tile *src, + gint i, + gint j) +{ + const guchar *src_data = tile_data_pointer (src, 0, 0); + guchar *dest_data = tile_data_pointer (dest, 0, 0); + const gint src_ewidth = tile_ewidth (src); + const gint src_eheight = tile_eheight (src); + const gint dest_ewidth = tile_ewidth (dest); + const gint bpp = tile_bpp (dest); + gint y; + + /* Adjust dest pointer to the right quadrant. */ + dest_data += i * bpp * (TILE_WIDTH / 2) + + j * bpp * dest_ewidth * (TILE_HEIGHT / 2); + + for (y = 0; y < src_eheight / 2; y++) + { + const guchar *src0 = src_data; + const guchar *src1 = src_data + bpp; + const guchar *src2 = src0 + bpp * src_ewidth; + const guchar *src3 = src1 + bpp * src_ewidth; + guchar *dst = dest_data; + gint x; + + switch (bpp) + { + case 1: + for (x = 0; x < src_ewidth / 2; x++) + { + dst[0] = (src0[0] + src1[0] + src2[0] + src3[0]) >> 2; + + dst += 1; + + src0 += 2; + src1 += 2; + src2 += 2; + src3 += 2; + } + break; + + case 2: + for (x = 0; x < src_ewidth / 2; x++) + { + dst[0] = (src0[0] + src1[0] + src2[0] + src3[0]) >> 2; + dst[1] = (src0[1] + src1[1] + src2[1] + src3[1]) >> 2; + + dst += 2; + + src0 += 4; + src1 += 4; + src2 += 4; + src3 += 4; + } + break; + + case 3: + for (x = 0; x < src_ewidth / 2; x++) + { + dst[0] = (src0[0] + src1[0] + src2[0] + src3[0]) >> 2; + dst[1] = (src0[1] + src1[1] + src2[1] + src3[1]) >> 2; + dst[2] = (src0[2] + src1[2] + src2[2] + src3[2]) >> 2; + + dst += 3; + + src0 += 6; + src1 += 6; + src2 += 6; + src3 += 6; + } + break; + + case 4: + for (x = 0; x < src_ewidth / 2; x++) + { + dst[0] = (src0[0] + src1[0] + src2[0] + src3[0]) >> 2; + dst[1] = (src0[1] + src1[1] + src2[1] + src3[1]) >> 2; + dst[2] = (src0[2] + src1[2] + src2[2] + src3[2]) >> 2; + dst[3] = (src0[3] + src1[3] + src2[3] + src3[3]) >> 2; + + dst += 4; + + src0 += 8; + src1 += 8; + src2 += 8; + src3 += 8; + } + break; + } + + dest_data += dest_ewidth * bpp; + src_data += src_ewidth * bpp * 2; + } +} diff --git a/app/base/tile-pyramid.h b/app/base/tile-pyramid.h index 78a08ed8cc..8264c0da8e 100644 --- a/app/base/tile-pyramid.h +++ b/app/base/tile-pyramid.h @@ -30,7 +30,8 @@ gint tile_pyramid_get_level (gint width, gdouble scale); TileManager * tile_pyramid_get_tiles (TilePyramid *pyramid, - gint level); + gint level, + gboolean *is_premult); void tile_pyramid_invalidate_area (TilePyramid *pyramid, gint x, diff --git a/app/core/gimpimage-preview.c b/app/core/gimpimage-preview.c index f2cca978fc..a9888330b2 100644 --- a/app/core/gimpimage-preview.c +++ b/app/core/gimpimage-preview.c @@ -109,7 +109,7 @@ gimp_image_get_preview (GimpViewable *viewable, temp_buf_free (image->preview); image->preview = gimp_image_get_new_preview (viewable, context, - width, height); + width, height); return image->preview; } @@ -122,16 +122,28 @@ gimp_image_get_new_preview (GimpViewable *viewable, gint height) { GimpImage *image = GIMP_IMAGE (viewable); + TempBuf *buf; TileManager *tiles; gdouble scale_x; gdouble scale_y; gint level; + gboolean is_premult; scale_x = (gdouble) width / (gdouble) image->width; scale_y = (gdouble) height / (gdouble) image->height; level = gimp_projection_get_level (image->projection, scale_x, scale_y); - tiles = gimp_projection_get_tiles_at_level (image->projection, level); - return tile_manager_get_preview (tiles, width, height); + tiles = gimp_projection_get_tiles_at_level (image->projection, level, + &is_premult); + + buf = tile_manager_get_preview (tiles, width, height); + + /* FIXME: We could avoid this if the view renderer and all other + * preview code would know how to deal with pre-multiply alpha. + */ + if (is_premult) + temp_buf_demultiply (buf); + + return buf; } diff --git a/app/core/gimpprojection.c b/app/core/gimpprojection.c index 386f20d4a0..50b3ff6946 100644 --- a/app/core/gimpprojection.c +++ b/app/core/gimpprojection.c @@ -306,13 +306,14 @@ gimp_projection_new (GimpImage *image) TileManager * gimp_projection_get_tiles (GimpProjection *proj) { - return gimp_projection_get_tiles_at_level (proj, 0); + return gimp_projection_get_tiles_at_level (proj, 0, NULL); } TileManager * gimp_projection_get_tiles_at_level (GimpProjection *proj, - gint level) + gint level, + gboolean *is_premult) { g_return_val_if_fail (GIMP_IS_PROJECTION (proj), NULL); @@ -327,7 +328,7 @@ gimp_projection_get_tiles_at_level (GimpProjection *proj, proj); } - return tile_pyramid_get_tiles (proj->pyramid, level); + return tile_pyramid_get_tiles (proj->pyramid, level, is_premult); } /** @@ -384,14 +385,6 @@ gimp_projection_get_bytes (const GimpProjection *proj) return GIMP_IMAGE_TYPE_BYTES (gimp_projection_get_image_type (proj)); } -gdouble -gimp_projection_get_opacity (const GimpProjection *proj) -{ - g_return_val_if_fail (GIMP_IS_PROJECTION (proj), GIMP_OPACITY_OPAQUE); - - return GIMP_OPACITY_OPAQUE; -} - void gimp_projection_flush (GimpProjection *proj) { diff --git a/app/core/gimpprojection.h b/app/core/gimpprojection.h index 2c6d244246..a223b10be4 100644 --- a/app/core/gimpprojection.h +++ b/app/core/gimpprojection.h @@ -85,7 +85,8 @@ TileManager * gimp_projection_get_tiles (GimpProjection *proj); TileManager * gimp_projection_get_tiles_at_level (GimpProjection *proj, - gint level); + gint level, + gboolean *is_premult); gint gimp_projection_get_level (GimpProjection *proj, gdouble scale_x, gdouble scale_y); @@ -93,7 +94,6 @@ gint gimp_projection_get_level (GimpProjection *proj, GimpImage * gimp_projection_get_image (const GimpProjection *proj); GimpImageType gimp_projection_get_image_type (const GimpProjection *proj); gint gimp_projection_get_bytes (const GimpProjection *proj); -gdouble gimp_projection_get_opacity (const GimpProjection *proj); void gimp_projection_flush (GimpProjection *proj); void gimp_projection_flush_now (GimpProjection *proj); diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c index 4f40747b47..39a38000ce 100644 --- a/app/display/gimpdisplayshell-draw.c +++ b/app/display/gimpdisplayshell-draw.c @@ -520,7 +520,7 @@ gimp_display_shell_draw_area (GimpDisplayShell *shell, level = gimp_projection_get_level (proj, shell->scale_x, shell->scale_y); - tiles = gimp_projection_get_tiles_at_level (proj, level); + tiles = gimp_projection_get_tiles_at_level (proj, level, NULL); level_width = tile_manager_width (tiles); level_height = tile_manager_height (tiles); diff --git a/app/display/gimpdisplayshell-render.c b/app/display/gimpdisplayshell-render.c index 3f797ca541..28d16ec455 100644 --- a/app/display/gimpdisplayshell-render.c +++ b/app/display/gimpdisplayshell-render.c @@ -39,8 +39,6 @@ #include "core/gimpimage-colormap.h" #include "core/gimpprojection.h" -#include "widgets/gimprender.h" - #include "gimpcanvas.h" #include "gimpdisplay.h" #include "gimpdisplayshell.h" @@ -56,6 +54,9 @@ 100% and 200% zoom) */ +#define GIMP_DISPLAY_RENDER_BUF_WIDTH 256 + + typedef struct _RenderInfo RenderInfo; typedef void (* RenderFunc) (RenderInfo *info); @@ -64,9 +65,9 @@ struct _RenderInfo { GimpDisplayShell *shell; TileManager *src_tiles; - const guint *alpha; const guchar *src; guchar *dest; + gboolean src_is_premult; gint x, y; gint w, h; gdouble scalex; @@ -99,7 +100,8 @@ struct _RenderInfo static void gimp_display_shell_render_info_scale (RenderInfo *info, GimpDisplayShell *shell, TileManager *tiles, - gint level); + gint level, + gboolean is_premult); static void gimp_display_shell_render_setup_notify (GObject *config, GParamSpec *param_spec, @@ -110,6 +112,8 @@ static guchar *tile_buf = NULL; static guint check_mod = 0; static guint check_shift = 0; +static guchar check_dark = 0; +static guchar check_light = 0; void @@ -126,7 +130,7 @@ gimp_display_shell_render_init (Gimp *gimp) gimp); /* allocate a buffer for arranging information from a row of tiles */ - tile_buf = g_new (guchar, GIMP_RENDER_BUF_WIDTH * MAX_CHANNELS); + tile_buf = g_new (guchar, GIMP_DISPLAY_RENDER_BUF_WIDTH * MAX_CHANNELS); gimp_display_shell_render_setup_notify (G_OBJECT (gimp->config), NULL, gimp); } @@ -153,11 +157,15 @@ gimp_display_shell_render_setup_notify (GObject *config, Gimp *gimp) { GimpCheckSize check_size; + GimpCheckType check_type; g_object_get (config, "transparency-size", &check_size, + "transparency-type", &check_type, NULL); + gimp_checks_get_shades (check_type, &check_light, &check_dark); + switch (check_size) { case GIMP_CHECK_SIZE_SMALL_CHECKS: @@ -183,8 +191,6 @@ gimp_display_shell_render_setup_notify (GObject *config, static void render_image_rgb_a (RenderInfo *info); static void render_image_gray_a (RenderInfo *info); -static const guint * render_image_init_alpha (gint mult); - static const guchar * render_image_tile_fault (RenderInfo *info); @@ -235,7 +241,7 @@ gimp_display_shell_render (GimpDisplayShell *shell, info.h = h; info.dest_bpp = 3; - info.dest_bpl = info.dest_bpp * GIMP_RENDER_BUF_WIDTH; + info.dest_bpl = info.dest_bpp * GIMP_DISPLAY_RENDER_BUF_WIDTH; info.dest_width = info.dest_bpp * info.w; switch (GIMP_DISPLAY_CONFIG (image->gimp->config)->zoom_quality) @@ -249,25 +255,19 @@ gimp_display_shell_render (GimpDisplayShell *shell, break; } - if (GIMP_IMAGE_TYPE_HAS_ALPHA (gimp_projection_get_image_type (projection))) - { - gdouble opacity = gimp_projection_get_opacity (projection); - - info.alpha = render_image_init_alpha (opacity * 255.999); - } - /* Setup RenderInfo for rendering a GimpProjection level. */ { - TileManager *src_tiles; + TileManager *tiles; gint level; + gboolean premult; level = gimp_projection_get_level (projection, shell->scale_x, shell->scale_y); - src_tiles = gimp_projection_get_tiles_at_level (projection, level); + tiles = gimp_projection_get_tiles_at_level (projection, level, &premult); - gimp_display_shell_render_info_scale (&info, shell, src_tiles, level); + gimp_display_shell_render_info_scale (&info, shell, tiles, level, premult); } /* Currently, only RGBA and GRAYA projection types are used. */ @@ -292,7 +292,7 @@ gimp_display_shell_render (GimpDisplayShell *shell, shell->render_buf, w, h, 3, - 3 * GIMP_RENDER_BUF_WIDTH); + 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH); /* dim pixels outside the highlighted rectangle */ if (highlight) @@ -301,10 +301,10 @@ gimp_display_shell_render (GimpDisplayShell *shell, } else if (shell->mask) { - TileManager *src_tiles = gimp_drawable_get_tiles (shell->mask); + TileManager *tiles = gimp_drawable_get_tiles (shell->mask); /* The mask does not (yet) have an image pyramid, use 0 as level, */ - gimp_display_shell_render_info_scale (&info, shell, src_tiles, 0); + gimp_display_shell_render_info_scale (&info, shell, tiles, 0, FALSE); gimp_display_shell_render_mask (shell, &info); } @@ -314,7 +314,7 @@ gimp_display_shell_render (GimpDisplayShell *shell, x + shell->disp_xoffset, y + shell->disp_yoffset, w, h, shell->render_buf, - 3 * GIMP_RENDER_BUF_WIDTH, + 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH, shell->offset_x, shell->offset_y); } @@ -354,7 +354,7 @@ gimp_display_shell_render_highlight (GimpDisplayShell *shell, for (x = 0; x < w; x++) GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x) - buf += 3 * GIMP_RENDER_BUF_WIDTH; + buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH; } for ( ; y < rect.y + rect.height; y++) @@ -365,7 +365,7 @@ gimp_display_shell_render_highlight (GimpDisplayShell *shell, for (x += rect.width; x < w; x++) GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x) - buf += 3 * GIMP_RENDER_BUF_WIDTH; + buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH; } for ( ; y < h; y++) @@ -373,7 +373,7 @@ gimp_display_shell_render_highlight (GimpDisplayShell *shell, for (x = 0; x < w; x++) GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x) - buf += 3 * GIMP_RENDER_BUF_WIDTH; + buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH; } } else @@ -383,7 +383,7 @@ gimp_display_shell_render_highlight (GimpDisplayShell *shell, for (x = 0; x < w; x++) GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x) - buf += 3 * GIMP_RENDER_BUF_WIDTH; + buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH; } } } @@ -467,9 +467,8 @@ gimp_display_shell_render_mask (GimpDisplayShell *shell, static void render_image_gray_a (RenderInfo *info) { - const guint *alpha = info->alpha; - gint y, ye; - gint x, xe; + gint y, ye; + gint x, xe; y = info->y; ye = info->y + info->h; @@ -486,28 +485,21 @@ render_image_gray_a (RenderInfo *info) dark_light = (y >> check_shift) + (info->x >> check_shift); - for (x = info->x; x < xe; x++) + for (x = info->x; x < xe; x++, src += 2, dest += 3) { - const guint a = alpha[src[ALPHA_G_PIX]]; - guchar val; + guint v; if (dark_light & 0x1) - val = gimp_render_blend_dark_check[(a | src[GRAY_PIX])]; + v = ((src[0] << 8) + check_dark * (256 - src[1])) >> 8; else - val = gimp_render_blend_light_check[(a | src[GRAY_PIX])]; + v = ((src[0] << 8) + check_light * (256 - src[1])) >> 8; - src += 2; - - dest[0] = val; - dest[1] = val; - dest[2] = val; - dest += 3; + dest[0] = dest[1] = dest[2] = v; if (((x + 1) & check_mod) == 0) dark_light += 1; } - if (++y == ye) break; @@ -524,9 +516,8 @@ render_image_gray_a (RenderInfo *info) static void render_image_rgb_a (RenderInfo *info) { - const guint *alpha = info->alpha; - gint y, ye; - gint x, xe; + gint y, ye; + gint x, xe; y = info->y; ye = info->y + info->h; @@ -543,25 +534,26 @@ render_image_rgb_a (RenderInfo *info) dark_light = (y >> check_shift) + (info->x >> check_shift); - for (x = info->x; x < xe; x++) + for (x = info->x; x < xe; x++, src += 4, dest += 3) { - const guint a = alpha[src[ALPHA_PIX]]; + guint r, g, b; if (dark_light & 0x1) { - dest[0] = gimp_render_blend_dark_check[(a | src[RED_PIX])]; - dest[1] = gimp_render_blend_dark_check[(a | src[GREEN_PIX])]; - dest[2] = gimp_render_blend_dark_check[(a | src[BLUE_PIX])]; + r = ((src[0] << 8) + check_dark * (256 - src[3])) >> 8; + g = ((src[1] << 8) + check_dark * (256 - src[3])) >> 8; + b = ((src[2] << 8) + check_dark * (256 - src[3])) >> 8; } else { - dest[0] = gimp_render_blend_light_check[(a | src[RED_PIX])]; - dest[1] = gimp_render_blend_light_check[(a | src[GREEN_PIX])]; - dest[2] = gimp_render_blend_light_check[(a | src[BLUE_PIX])]; + r = ((src[0] << 8) + check_light * (256 - src[3])) >> 8; + g = ((src[1] << 8) + check_light * (256 - src[3])) >> 8; + b = ((src[2] << 8) + check_light * (256 - src[3])) >> 8; } - src += 4; - dest += 3; + dest[0] = r; + dest[1] = g; + dest[2] = b; if (((x + 1) & check_mod) == 0) dark_light += 1; @@ -585,9 +577,11 @@ static void gimp_display_shell_render_info_scale (RenderInfo *info, GimpDisplayShell *shell, TileManager *tiles, - gint level) + gint level, + gboolean is_premult) { - info->src_tiles = tiles; + info->src_tiles = tiles; + info->src_is_premult = is_premult; /* We must reset info->dest because this member is modified in render * functions. @@ -643,27 +637,7 @@ gimp_display_shell_render_info_scale (RenderInfo *info, } } -static const guint * -render_image_init_alpha (gint mult) -{ - static guint *alpha_mult = NULL; - static gint alpha_val = -1; - - if (alpha_val != mult) - { - gint i; - - if (!alpha_mult) - alpha_mult = g_new (guint, 256); - - alpha_val = mult; - for (i = 0; i < 256; i++) - alpha_mult[i] = ((mult * i) / 255) << 8; - } - - return alpha_mult; -} - +/* This version assumes that the src data is already pre-multiplied. */ static inline void box_filter (const guint left_weight, const guint center_weight, @@ -674,6 +648,38 @@ box_filter (const guint left_weight, const guchar **src, /* the 9 surrounding source pixels */ guchar *dest, const gint bpp) +{ + const guint sum = ((left_weight + center_weight + right_weight) * + (top_weight + middle_weight + bottom_weight)); + gint i; + + for (i = 0; i < bpp; i++) + { + dest[i] = ( left_weight * ((src[0][i] * top_weight) + + (src[3][i] * middle_weight) + + (src[6][i] * bottom_weight)) + + center_weight * ((src[1][i] * top_weight) + + (src[4][i] * middle_weight) + + (src[7][i] * bottom_weight)) + + right_weight * ((src[2][i] * top_weight) + + (src[5][i] * middle_weight) + + (src[8][i] * bottom_weight))) / sum; + } +} + +/* This version assumes that the src data is not pre-multipled. + * It creates pre-multiplied output though. + */ +static inline void +box_filter_premult (const guint left_weight, + const guint center_weight, + const guint right_weight, + const guint top_weight, + const guint middle_weight, + const guint bottom_weight, + const guchar **src, /* the 9 surrounding source pixels */ + guchar *dest, + const gint bpp) { const guint sum = ((left_weight + center_weight + right_weight) * (top_weight + middle_weight + bottom_weight)) >> 4; @@ -699,33 +705,24 @@ box_filter (const guint left_weight, guint a = (center_weight * (factors[0] + factors[1] + factors[2]) + right_weight * (factors[3] + factors[4] + factors[5]) + left_weight * (factors[6] + factors[7] + factors[8])); + guint i; - if (a) + for (i = 0; i < ALPHA; i++) { - gint i; + dest[i] = ((center_weight * (factors[0] * src[1][i] + + factors[1] * src[4][i] + + factors[2] * src[7][i]) + - for (i = 0; i < ALPHA; i++) - { - dest[i] = ((center_weight * (factors[0] * src[1][i] + - factors[1] * src[4][i] + - factors[2] * src[7][i]) + + right_weight * (factors[3] * src[2][i] + + factors[4] * src[5][i] + + factors[5] * src[8][i]) + - right_weight * (factors[3] * src[2][i] + - factors[4] * src[5][i] + - factors[5] * src[8][i]) + - - left_weight * (factors[6] * src[0][i] + - factors[7] * src[3][i] + - factors[8] * src[6][i]) - ) / a) & 0xff; - } - - dest[ALPHA] = (a + (sum >> 1)) / sum; - } - else - { - dest[ALPHA] = 0; + left_weight * (factors[6] * src[0][i] + + factors[7] * src[3][i] + + factors[8] * src[6][i])) / sum) >> 8; } + + dest[ALPHA] = (a + (sum >> 1)) / sum; } #undef ALPHA break; @@ -753,33 +750,24 @@ box_filter (const guint left_weight, guint a = (center_weight * (factors[0] + factors[1] + factors[2]) + right_weight * (factors[3] + factors[4] + factors[5]) + left_weight * (factors[6] + factors[7] + factors[8])); + guint i; - if (a) + for (i = 0; i < ALPHA; i++) { - gint i; + dest[i] = ((center_weight * (factors[0] * src[1][i] + + factors[1] * src[4][i] + + factors[2] * src[7][i]) + - for (i = 0; i < ALPHA; i++) - { - dest[i] = ((center_weight * (factors[0] * src[1][i] + - factors[1] * src[4][i] + - factors[2] * src[7][i]) + + right_weight * (factors[3] * src[2][i] + + factors[4] * src[5][i] + + factors[5] * src[8][i]) + - right_weight * (factors[3] * src[2][i] + - factors[4] * src[5][i] + - factors[5] * src[8][i]) + - - left_weight * (factors[6] * src[0][i] + - factors[7] * src[3][i] + - factors[8] * src[6][i]) - ) / a) & 0xff; - } - - dest[ALPHA] = (a + (sum >> 1)) / sum; - } - else - { - dest[ALPHA] = 0; + left_weight * (factors[6] * src[0][i] + + factors[7] * src[3][i] + + factors[8] * src[6][i])) / sum) >> 8; } + + dest[ALPHA] = (a + (sum >> 1)) / sum; } #undef ALPHA break; @@ -799,7 +787,9 @@ static const guchar * render_image_tile_fault_nearest (RenderInfo *info); * 678 */ -/* function to render a horizontal line of view data */ +/* Function to render a horizontal line of view data. The data + * returned from this function has the alpha channel pre-multiplied. + */ static const guchar * render_image_tile_fault (RenderInfo *info) { @@ -1032,9 +1022,14 @@ render_image_tile_fault (RenderInfo *info) src[8] = src[5]; } - box_filter (left_weight, center_weight, right_weight, - top_weight, middle_weight, bottom_weight, - src, dest, bpp); + if (info->src_is_premult) + box_filter (left_weight, center_weight, right_weight, + top_weight, middle_weight, bottom_weight, + src, dest, bpp); + else + box_filter_premult (left_weight, center_weight, right_weight, + top_weight, middle_weight, bottom_weight, + src, dest, bpp); dest += bpp; @@ -1253,83 +1248,6 @@ done: return tile_buf; } -/* function to render a horizontal line of view data */ -static const guchar * -render_image_tile_fault_nearest (RenderInfo *info) -{ - Tile *tile; - const guchar *src; - guchar *dest; - gint width; - gint tilex; - gint bpp; - gint src_x; - gint64 dx; - - tile = tile_manager_get_tile (info->src_tiles, - info->src_x, info->src_y, TRUE, FALSE); - - g_return_val_if_fail (tile != NULL, tile_buf); - - src = tile_data_pointer (tile, info->src_x, info->src_y); - - bpp = tile_manager_bpp (info->src_tiles); - dest = tile_buf; - - dx = info->dx_start; - - width = info->w; - - src_x = info->src_x; - tilex = info->src_x / TILE_WIDTH; - - do - { - const guchar *s = src; - gint skipped; - - switch (bpp) - { - case 4: - *dest++ = *s++; - case 3: - *dest++ = *s++; - case 2: - *dest++ = *s++; - case 1: - *dest++ = *s++; - } - - dx += info->x_dest_inc; - skipped = dx / info->x_src_dec; - - if (skipped) - { - src += skipped * bpp; - src_x += skipped; - dx -= skipped * info->x_src_dec; - - if ((src_x / TILE_WIDTH) != tilex) - { - tile_release (tile, FALSE); - tilex += 1; - - tile = tile_manager_get_tile (info->src_tiles, - src_x, info->src_y, TRUE, FALSE); - if (! tile) - return tile_buf; - - src = tile_data_pointer (tile, src_x, info->src_y); - } - } - } - while (--width); - - tile_release (tile, FALSE); - - return tile_buf; -} - static const guchar * render_image_tile_fault_one_row (RenderInfo *info) { @@ -1476,9 +1394,14 @@ render_image_tile_fault_one_row (RenderInfo *info) src[8] = src[7]; } - box_filter (left_weight, center_weight, right_weight, - top_weight, middle_weight, bottom_weight, - src, dest, bpp); + if (info->src_is_premult) + box_filter (left_weight, center_weight, right_weight, + top_weight, middle_weight, bottom_weight, + src, dest, bpp); + else + box_filter_premult (left_weight, center_weight, right_weight, + top_weight, middle_weight, bottom_weight, + src, dest, bpp); dest += bpp; @@ -1611,9 +1534,111 @@ render_image_tile_fault_one_row (RenderInfo *info) while (--width); done: - for (dx=0; dx<3; dx++) + for (dx = 0; dx < 3; dx++) if (tile[dx]) tile_release (tile[dx], FALSE); return tile_buf; } + +/* function to render a horizontal line of view data */ +static const guchar * +render_image_tile_fault_nearest (RenderInfo *info) +{ + Tile *tile; + const guchar *src; + guchar *d; + gint width; + gint tilex; + gint bpp; + gint src_x; + gint64 dx; + + tile = tile_manager_get_tile (info->src_tiles, + info->src_x, info->src_y, TRUE, FALSE); + + g_return_val_if_fail (tile != NULL, tile_buf); + + src = tile_data_pointer (tile, info->src_x, info->src_y); + + bpp = tile_manager_bpp (info->src_tiles); + + dx = info->dx_start; + + width = info->w; + + src_x = info->src_x; + tilex = info->src_x / TILE_WIDTH; + + d = tile_buf; + + do + { + const guchar *s = src; + gint skipped; + + if (info->src_is_premult) + { + switch (bpp) + { + case 4: + *d++ = *s++; + *d++ = *s++; + case 2: + *d++ = *s++; + *d++ = *s++; + } + } + else /* pre-multiply */ + { + switch (bpp) + { + case 4: + d[0] = (s[0] * s[3]) >> 8; + d[1] = (s[1] * s[3]) >> 8; + d[2] = (s[2] * s[3]) >> 8; + d[3] = s[3]; + + d += 4; + s += 4; + break; + + case 2: + d[0] = (s[0] * s[1]) >> 8; + d[1] = s[1]; + + d += 2; + s += 2; + break; + } + } + + dx += info->x_dest_inc; + skipped = dx / info->x_src_dec; + + if (skipped) + { + src += skipped * bpp; + src_x += skipped; + dx -= skipped * info->x_src_dec; + + if ((src_x / TILE_WIDTH) != tilex) + { + tile_release (tile, FALSE); + tilex += 1; + + tile = tile_manager_get_tile (info->src_tiles, + src_x, info->src_y, TRUE, FALSE); + if (! tile) + return tile_buf; + + src = tile_data_pointer (tile, src_x, info->src_y); + } + } + } + while (--width); + + tile_release (tile, FALSE); + + return tile_buf; +}