Speed up our display rendering code paths by keeping data with

2007-12-13  Sven Neumann  <sven@gimp.org>

	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.

svn path=/trunk/; revision=24352
This commit is contained in:
Sven Neumann 2007-12-13 17:27:07 +00:00 committed by Sven Neumann
parent daf03994b0
commit aaa16d3b87
10 changed files with 551 additions and 279 deletions

View File

@ -1,3 +1,29 @@
2007-12-13 Sven Neumann <sven@gimp.org>
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 <sven@gimp.org>
* app/widgets/gimprender.[ch]: export the light and dark check

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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;
}