mirror of https://github.com/GNOME/gimp.git
Implemented an image pyramid for the GimpProjection. An image pyramid
2007-06-06 Martin Nordholts <martinn@svn.gnome.org> Implemented an image pyramid for the GimpProjection. An image pyramid caches a projection at several sizes, causing the rendering code not to have to swap in all tiles of a (potentially) large image; it can use small versions of the projection if the user is zoomed out. The image pyramid also imroves visual quality, especially at zoom levels where there is a pyramid level that matches perfectly (i.e. at e.g. 50%, 25%, and 12.5% zoom). A step on the right track for bug #76096. * app/core/gimpprojection.[ch]: Adjusted to make use of an image pyramid. GimpProjection now keeps an array of TileManager:s, one per pyramid level. Renamed _alloc_tiles to _alloc_levels. * app/display/gimpdisplayshell-draw.c: (gimp_display_shell_draw_area): Use the right GimpProjection level when drawing * app/display/gimpdisplayshell-render.c: (render_image_init_info_full): Setup RenderInfo with level in mind * app/base/tile-manager.[ch]: Extended API a bit, nothing complicated. * app/base/tile-manager-private.h (struct _TileManager): Keep a pointer to the level below for use in an image pyramid. svn path=/trunk/; revision=22727
This commit is contained in:
parent
67ec846b73
commit
7917611e53
26
ChangeLog
26
ChangeLog
|
@ -1,3 +1,29 @@
|
|||
2007-06-06 Martin Nordholts <martinn@svn.gnome.org>
|
||||
|
||||
Implemented an image pyramid for the GimpProjection. An image pyramid
|
||||
caches a projection at several sizes, causing the rendering code not to
|
||||
have to swap in all tiles of a (potentially) large image; it can use
|
||||
small versions of the projection if the user is zoomed out.
|
||||
|
||||
The image pyramid also imroves visual quality, especially at zoom levels
|
||||
where there is a pyramid level that matches perfectly (i.e. at e.g. 50%,
|
||||
25%, and 12.5% zoom). A step on the right track for bug #76096.
|
||||
|
||||
* app/core/gimpprojection.[ch]: Adjusted to make use of an image
|
||||
pyramid. GimpProjection now keeps an array of TileManager:s, one per
|
||||
pyramid level. Renamed _alloc_tiles to _alloc_levels.
|
||||
|
||||
* app/display/gimpdisplayshell-draw.c: (gimp_display_shell_draw_area):
|
||||
Use the right GimpProjection level when drawing
|
||||
|
||||
* app/display/gimpdisplayshell-render.c: (render_image_init_info_full):
|
||||
Setup RenderInfo with level in mind
|
||||
|
||||
* app/base/tile-manager.[ch]: Extended API a bit, nothing complicated.
|
||||
|
||||
* app/base/tile-manager-private.h (struct _TileManager): Keep a pointer
|
||||
to the level below for use in an image pyramid.
|
||||
|
||||
2007-06-06 Sven Neumann <sven@gimp.org>
|
||||
|
||||
* app/core/gimpimage-preview.c (gimp_image_get_new_preview): cleanup.
|
||||
|
|
|
@ -40,6 +40,10 @@ struct _TileManager
|
|||
gint cached_num; /* number of cached tile */
|
||||
Tile *cached_tile; /* the actual cached tile */
|
||||
|
||||
TileManager *level_below; /* The TileManager containing the level
|
||||
* below in an image pyramid.
|
||||
*/
|
||||
|
||||
gpointer user_data; /* hook for hanging data off of */
|
||||
};
|
||||
|
||||
|
|
|
@ -57,13 +57,14 @@ tile_manager_new (gint width,
|
|||
|
||||
tm = g_slice_new0 (TileManager);
|
||||
|
||||
tm->ref_count = 1;
|
||||
tm->width = width;
|
||||
tm->height = height;
|
||||
tm->bpp = bpp;
|
||||
tm->ntile_rows = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
|
||||
tm->ntile_cols = (width + TILE_WIDTH - 1) / TILE_WIDTH;
|
||||
tm->cached_num = -1;
|
||||
tm->ref_count = 1;
|
||||
tm->width = width;
|
||||
tm->height = height;
|
||||
tm->bpp = bpp;
|
||||
tm->ntile_rows = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
|
||||
tm->ntile_cols = (width + TILE_WIDTH - 1) / TILE_WIDTH;
|
||||
tm->cached_num = -1;
|
||||
tm->level_below = NULL;
|
||||
|
||||
return tm;
|
||||
}
|
||||
|
@ -251,6 +252,22 @@ tile_manager_get (TileManager *tm,
|
|||
return *tile_ptr;
|
||||
}
|
||||
|
||||
Tile *
|
||||
tile_manager_get_at (TileManager *tm,
|
||||
gint tile_col,
|
||||
gint tile_row,
|
||||
gint wantread,
|
||||
gint wantwrite)
|
||||
{
|
||||
g_return_val_if_fail (tm != NULL, NULL);
|
||||
|
||||
if (tile_col < 0 || tile_col >= tm->ntile_cols ||
|
||||
tile_row < 0 || tile_row >= tm->ntile_rows)
|
||||
return NULL;
|
||||
|
||||
return tile_manager_get(tm, tile_row * tm->ntile_cols + tile_col, wantread, wantwrite);
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_validate (TileManager *tm,
|
||||
Tile *tile)
|
||||
|
@ -491,6 +508,26 @@ tile_manager_map (TileManager *tm,
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_invalidate_area (TileManager *tm,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h)
|
||||
{
|
||||
gint i;
|
||||
gint j;
|
||||
|
||||
for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT)))
|
||||
for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH)))
|
||||
{
|
||||
Tile *tile = tile_manager_get_tile (tm, j, i, FALSE, FALSE);
|
||||
|
||||
if (tile != NULL)
|
||||
tile_invalidate_tile (&tile, tm, j, i);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_set_user_data (TileManager *tm,
|
||||
gpointer user_data)
|
||||
|
@ -532,6 +569,39 @@ tile_manager_bpp (const TileManager *tm)
|
|||
return tm->bpp;
|
||||
}
|
||||
|
||||
gint
|
||||
tile_manager_tiles_per_col (const TileManager *tm)
|
||||
{
|
||||
g_return_val_if_fail (tm != NULL, 0);
|
||||
|
||||
return tm->ntile_cols;
|
||||
}
|
||||
|
||||
gint
|
||||
tile_manager_tiles_per_row (const TileManager *tm)
|
||||
{
|
||||
g_return_val_if_fail (tm != NULL, 0);
|
||||
|
||||
return tm->ntile_rows;
|
||||
}
|
||||
|
||||
TileManager *
|
||||
tile_manager_get_level_below (const TileManager *tm)
|
||||
{
|
||||
g_return_val_if_fail (tm != NULL, NULL);
|
||||
|
||||
return tm->level_below;
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_set_level_below (TileManager *tm,
|
||||
TileManager *level_below)
|
||||
{
|
||||
g_return_if_fail (tm != NULL);
|
||||
|
||||
tm->level_below = level_below;
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_get_offsets (const TileManager *tm,
|
||||
gint *x,
|
||||
|
@ -622,6 +692,23 @@ tile_manager_get_tile_coordinates (TileManager *tm,
|
|||
*y = TILE_HEIGHT * (tl->tile_num / tm->ntile_cols);
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_get_tile_col_row (TileManager *tm,
|
||||
Tile *tile,
|
||||
gint *tile_col,
|
||||
gint *tile_row)
|
||||
{
|
||||
gint tile_x;
|
||||
gint tile_y;
|
||||
|
||||
g_return_if_fail (tm && tile && tile_col && tile_row);
|
||||
|
||||
tile_manager_get_tile_coordinates (tm, tile, &tile_x, &tile_y);
|
||||
|
||||
*tile_col = tile_x / TILE_WIDTH;
|
||||
*tile_row = tile_y / TILE_HEIGHT;
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_map_over_tile (TileManager *tm,
|
||||
Tile *tile,
|
||||
|
|
|
@ -61,6 +61,12 @@ Tile * tile_manager_get (TileManager *tm,
|
|||
gint wantread,
|
||||
gint wantwrite);
|
||||
|
||||
Tile * tile_manager_get_at (TileManager *tm,
|
||||
gint tile_col,
|
||||
gint tile_row,
|
||||
gint wantread,
|
||||
gint wantwrite);
|
||||
|
||||
void tile_manager_map_tile (TileManager *tm,
|
||||
gint xpixel,
|
||||
gint ypixel,
|
||||
|
@ -89,6 +95,12 @@ void tile_invalidate_tile (Tile **tile_ptr,
|
|||
void tile_manager_invalidate_tiles (TileManager *tm,
|
||||
Tile *toplevel_tile);
|
||||
|
||||
void tile_manager_invalidate_area (TileManager *tm,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h);
|
||||
|
||||
void tile_manager_set_user_data (TileManager *tm,
|
||||
gpointer user_data);
|
||||
gpointer tile_manager_get_user_data (const TileManager *tm);
|
||||
|
@ -96,6 +108,11 @@ gpointer tile_manager_get_user_data (const TileManager *tm);
|
|||
gint tile_manager_width (const TileManager *tm);
|
||||
gint tile_manager_height (const TileManager *tm);
|
||||
gint tile_manager_bpp (const TileManager *tm);
|
||||
gint tile_manager_tiles_per_col (const TileManager *tm);
|
||||
gint tile_manager_tiles_per_row (const TileManager *tm);
|
||||
TileManager *tile_manager_get_level_below (const TileManager *tm);
|
||||
void tile_manager_set_level_below (TileManager *tm,
|
||||
TileManager *level_below);
|
||||
|
||||
void tile_manager_get_offsets (const TileManager *tm,
|
||||
gint *x,
|
||||
|
@ -111,6 +128,10 @@ void tile_manager_get_tile_coordinates (TileManager *tm,
|
|||
Tile *tile,
|
||||
gint *x,
|
||||
gint *y);
|
||||
void tile_manager_get_tile_col_row (TileManager *tm,
|
||||
Tile *tile,
|
||||
gint *tile_col,
|
||||
gint *tile_row);
|
||||
|
||||
void tile_manager_map_over_tile (TileManager *tm,
|
||||
Tile *tile,
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "libgimpmath/gimpmath.h"
|
||||
|
||||
#include "core-types.h"
|
||||
|
||||
#include "base/tile.h"
|
||||
|
@ -59,7 +61,7 @@ static gint gimp_projection_get_opacity_at (GimpPickable *pickabl
|
|||
gint x,
|
||||
gint y);
|
||||
|
||||
static void gimp_projection_alloc_tiles (GimpProjection *proj);
|
||||
static void gimp_projection_alloc_levels (GimpProjection *proj);
|
||||
static void gimp_projection_add_update_area (GimpProjection *proj,
|
||||
gint x,
|
||||
gint y,
|
||||
|
@ -83,6 +85,12 @@ static void gimp_projection_invalidate (GimpProjection *proj,
|
|||
gint h);
|
||||
static void gimp_projection_validate_tile (TileManager *tm,
|
||||
Tile *tile);
|
||||
static void gimp_projection_write_quarter (Tile *dest,
|
||||
Tile *source,
|
||||
gint i,
|
||||
gint j);
|
||||
static void gimp_projection_validate_pyramid_tile (TileManager *tm,
|
||||
Tile *tile);
|
||||
|
||||
static void gimp_projection_image_update (GimpImage *image,
|
||||
gint x,
|
||||
|
@ -135,11 +143,17 @@ gimp_projection_class_init (GimpProjectionClass *klass)
|
|||
static void
|
||||
gimp_projection_init (GimpProjection *proj)
|
||||
{
|
||||
gint level;
|
||||
|
||||
proj->image = NULL;
|
||||
|
||||
proj->type = -1;
|
||||
proj->bytes = 0;
|
||||
proj->tiles = NULL;
|
||||
|
||||
for (level = 0; level < PYRAMID_MAX_LEVELS; level++)
|
||||
proj->pyramid[level] = NULL;
|
||||
|
||||
proj->top_level = PYRAMID_BASE_LEVEL;
|
||||
|
||||
proj->update_areas = NULL;
|
||||
|
||||
|
@ -167,6 +181,7 @@ static void
|
|||
gimp_projection_finalize (GObject *object)
|
||||
{
|
||||
GimpProjection *proj = GIMP_PROJECTION (object);
|
||||
gint level;
|
||||
|
||||
if (proj->idle_render.idle_id)
|
||||
{
|
||||
|
@ -180,11 +195,13 @@ gimp_projection_finalize (GObject *object)
|
|||
gimp_area_list_free (proj->idle_render.update_areas);
|
||||
proj->idle_render.update_areas = NULL;
|
||||
|
||||
if (proj->tiles)
|
||||
{
|
||||
tile_manager_unref (proj->tiles);
|
||||
proj->tiles = NULL;
|
||||
}
|
||||
for (level = 0; level <= proj->top_level; level++)
|
||||
if (proj->pyramid[level])
|
||||
{
|
||||
tile_manager_unref (proj->pyramid[level]);
|
||||
proj->pyramid[level] = NULL;
|
||||
}
|
||||
proj->top_level = PYRAMID_BASE_LEVEL;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
@ -195,9 +212,11 @@ gimp_projection_get_memsize (GimpObject *object,
|
|||
{
|
||||
GimpProjection *projection = GIMP_PROJECTION (object);
|
||||
gint64 memsize = 0;
|
||||
gint level;
|
||||
|
||||
if (projection->tiles)
|
||||
memsize += tile_manager_get_memsize (projection->tiles, FALSE);
|
||||
for (level = 0; level <= projection->top_level; level++)
|
||||
if (projection->pyramid[level])
|
||||
memsize += tile_manager_get_memsize (projection->pyramid[level], FALSE);
|
||||
|
||||
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
|
||||
gui_size);
|
||||
|
@ -233,7 +252,8 @@ gimp_projection_estimate_memsize (GimpImageBaseType type,
|
|||
break;
|
||||
}
|
||||
|
||||
return bytes * (gint64) width * (gint64) height;
|
||||
/* The levels constitute a geometric sum with a ratio of 1/4. */
|
||||
return bytes * (gint64) width * (gint64) height * 1.33;
|
||||
}
|
||||
|
||||
|
||||
|
@ -300,16 +320,82 @@ gimp_projection_new (GimpImage *image)
|
|||
TileManager *
|
||||
gimp_projection_get_tiles (GimpProjection *proj)
|
||||
{
|
||||
return gimp_projection_get_tiles_at_level (proj, PYRAMID_BASE_LEVEL);
|
||||
}
|
||||
|
||||
TileManager *
|
||||
gimp_projection_get_tiles_at_level (GimpProjection *proj,
|
||||
gint level)
|
||||
{
|
||||
TileManager *base_level;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_PROJECTION (proj), NULL);
|
||||
|
||||
if (proj->tiles == NULL ||
|
||||
tile_manager_width (proj->tiles) != proj->image->width ||
|
||||
tile_manager_height (proj->tiles) != proj->image->height)
|
||||
{
|
||||
gimp_projection_alloc_tiles (proj);
|
||||
}
|
||||
base_level = proj->pyramid[PYRAMID_BASE_LEVEL];
|
||||
|
||||
return proj->tiles;
|
||||
if (base_level == NULL ||
|
||||
proj->image->width != tile_manager_width (base_level) ||
|
||||
proj->image->height != tile_manager_height (base_level))
|
||||
gimp_projection_alloc_levels (proj);
|
||||
|
||||
g_return_val_if_fail (level >= PYRAMID_BASE_LEVEL &&
|
||||
level <= proj->top_level,
|
||||
NULL);
|
||||
|
||||
return proj->pyramid[level];
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_projection_level_size_from_scale:
|
||||
* @proj: pointer to a GimpProjection
|
||||
* @scalex: scale to use
|
||||
* @level: where to store level used
|
||||
* @width: where to store width of level
|
||||
* @height: where to store height of level
|
||||
*
|
||||
* Calculates what level and the width and height of that level that should be
|
||||
* used for scale @scalex.
|
||||
**/
|
||||
void
|
||||
gimp_projection_level_size_from_scale (GimpProjection *proj,
|
||||
gdouble scalex,
|
||||
gint *level,
|
||||
gint *width,
|
||||
gint *height)
|
||||
{
|
||||
TileManager *src_tiles;
|
||||
|
||||
g_return_if_fail (GIMP_IS_PROJECTION (proj) &&
|
||||
level &&
|
||||
width &&
|
||||
height);
|
||||
|
||||
/* We must make sure the pyramid is up to date. */
|
||||
gimp_projection_alloc_levels (proj);
|
||||
|
||||
*level = gimp_projection_scale_to_level (proj, scalex);
|
||||
src_tiles = gimp_projection_get_tiles_at_level (proj, *level);
|
||||
|
||||
*width = tile_manager_width (src_tiles);
|
||||
*height = tile_manager_height (src_tiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_projection_scale_to_level:
|
||||
* @proj: pointer to a GimpProjection
|
||||
* @scalex: scale to use
|
||||
*
|
||||
* Return value: Returns the level (base level = 0) that should be used for scale @scalex.
|
||||
**/
|
||||
gint
|
||||
gimp_projection_scale_to_level (GimpProjection *proj,
|
||||
gdouble scalex)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_PROJECTION (proj), PYRAMID_BASE_LEVEL);
|
||||
|
||||
return CLAMP ((gint) -(log (scalex) / log(2)),
|
||||
PYRAMID_BASE_LEVEL,
|
||||
proj->top_level);
|
||||
}
|
||||
|
||||
GimpImage *
|
||||
|
@ -384,7 +470,7 @@ gimp_projection_finish_draw (GimpProjection *proj)
|
|||
/* private functions */
|
||||
|
||||
static void
|
||||
gimp_projection_alloc_tiles (GimpProjection *proj)
|
||||
gimp_projection_alloc_levels (GimpProjection *proj)
|
||||
{
|
||||
GimpImageType proj_type = 0;
|
||||
gint proj_bytes = 0;
|
||||
|
@ -412,29 +498,73 @@ gimp_projection_alloc_tiles (GimpProjection *proj)
|
|||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (proj->tiles)
|
||||
if (proj->pyramid[PYRAMID_BASE_LEVEL])
|
||||
{
|
||||
if (proj_type != proj->type ||
|
||||
proj_bytes != proj->bytes ||
|
||||
proj->image->width != tile_manager_width (proj->tiles) ||
|
||||
proj->image->height != tile_manager_height (proj->tiles))
|
||||
gint current_width = tile_manager_width (proj->pyramid[PYRAMID_BASE_LEVEL]);
|
||||
gint current_height = tile_manager_height (proj->pyramid[PYRAMID_BASE_LEVEL]);
|
||||
|
||||
if (proj_type != proj->type ||
|
||||
proj_bytes != proj->bytes ||
|
||||
proj->image->width != current_width ||
|
||||
proj->image->height != current_height)
|
||||
{
|
||||
tile_manager_unref (proj->tiles);
|
||||
proj->tiles = NULL;
|
||||
gint level;
|
||||
|
||||
for (level = 0; level <= proj->top_level; level++)
|
||||
{
|
||||
tile_manager_unref (proj->pyramid[level]);
|
||||
proj->pyramid[level] = NULL;
|
||||
}
|
||||
|
||||
proj->top_level = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (! proj->tiles)
|
||||
if (! proj->pyramid[PYRAMID_BASE_LEVEL])
|
||||
{
|
||||
gint level;
|
||||
|
||||
proj->type = proj_type;
|
||||
proj->bytes = proj_bytes;
|
||||
|
||||
proj->tiles = tile_manager_new (proj->image->width,
|
||||
proj->image->height,
|
||||
proj->bytes);
|
||||
tile_manager_set_user_data (proj->tiles, proj);
|
||||
tile_manager_set_validate_proc (proj->tiles,
|
||||
gimp_projection_validate_tile);
|
||||
for (level = 0; level < PYRAMID_MAX_LEVELS; level++)
|
||||
{
|
||||
gint level_width = proj->image->width / (1 << level);
|
||||
gint level_height = proj->image->height / (1 << level);
|
||||
|
||||
/* There is no use having levels that have the same number of
|
||||
* tiles as the parent level.
|
||||
*/
|
||||
if (level != PYRAMID_BASE_LEVEL &&
|
||||
level_width <= TILE_WIDTH / 2 &&
|
||||
level_height <= TILE_HEIGHT / 2 ||
|
||||
level_width == 0 ||
|
||||
level_height == 0)
|
||||
break;
|
||||
|
||||
proj->top_level = level;
|
||||
proj->pyramid[level] = tile_manager_new (level_width,
|
||||
level_height,
|
||||
proj->bytes);
|
||||
|
||||
tile_manager_set_user_data (proj->pyramid[level], proj);
|
||||
|
||||
if (level == PYRAMID_BASE_LEVEL)
|
||||
{
|
||||
/* Validate tiles by building from the layers of the image. */
|
||||
tile_manager_set_validate_proc (proj->pyramid[level],
|
||||
gimp_projection_validate_tile);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use the level below to validate tiles. */
|
||||
tile_manager_set_validate_proc (proj->pyramid[level],
|
||||
gimp_projection_validate_pyramid_tile);
|
||||
|
||||
tile_manager_set_level_below (proj->pyramid[level],
|
||||
proj->pyramid[level - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -665,19 +795,28 @@ gimp_projection_invalidate (GimpProjection *proj,
|
|||
gint w,
|
||||
gint h)
|
||||
{
|
||||
Tile *tile;
|
||||
TileManager *tm;
|
||||
gint i, j;
|
||||
gint level;
|
||||
|
||||
tm = gimp_projection_get_tiles (proj);
|
||||
for (level = 0; level <= proj->top_level; level++)
|
||||
{
|
||||
gint c = (1 << level);
|
||||
|
||||
for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT)))
|
||||
for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH)))
|
||||
{
|
||||
tile = tile_manager_get_tile (tm, j, i, FALSE, FALSE);
|
||||
/* Tile invalidation must propagate all the way up in the pyramid, so keep
|
||||
* width and height > 0.
|
||||
*/
|
||||
gint invalidation_width = MAX (w / c, 1);
|
||||
gint invalidation_height = MAX (h / c, 1);
|
||||
|
||||
tile_invalidate_tile (&tile, tm, j, i);
|
||||
}
|
||||
|
||||
tm = gimp_projection_get_tiles_at_level (proj, level);
|
||||
|
||||
tile_manager_invalidate_area (tm,
|
||||
x / c,
|
||||
y / c,
|
||||
invalidation_width,
|
||||
invalidation_height);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -696,6 +835,77 @@ gimp_projection_validate_tile (TileManager *tm,
|
|||
gimp_projection_construct (proj, x, y, w, h);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_projection_write_quarter (Tile *dest,
|
||||
Tile *source,
|
||||
gint i,
|
||||
gint j)
|
||||
{
|
||||
const guchar *source_data = tile_data_pointer (source, 0, 0);
|
||||
guchar *dest_data = tile_data_pointer (dest, 0, 0);
|
||||
|
||||
gint source_ewidth = tile_ewidth (source);
|
||||
gint source_eheight = tile_eheight (source);
|
||||
gint dest_ewidth = tile_ewidth (dest);
|
||||
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 < source_eheight / 2; y++)
|
||||
{
|
||||
gint x;
|
||||
guchar *dst = dest_data + y * dest_ewidth * bpp;
|
||||
const guchar *src = source_data + y * 2 * source_ewidth * bpp;
|
||||
|
||||
for (x = 0; x < source_ewidth / 2; x++)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < bpp; i++)
|
||||
dst[i] = (src[i] +
|
||||
src[i + bpp] +
|
||||
src[i + source_ewidth * bpp] +
|
||||
src[i + source_ewidth * bpp + bpp]) / 4;
|
||||
|
||||
|
||||
dst += bpp;
|
||||
src += bpp * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_projection_validate_pyramid_tile (TileManager *tm,
|
||||
Tile *tile)
|
||||
{
|
||||
gint tile_col;
|
||||
gint tile_row;
|
||||
gint i;
|
||||
gint j;
|
||||
TileManager *level_below = tile_manager_get_level_below (tm);
|
||||
Tile *source[2][2] = { { NULL, NULL },
|
||||
{ NULL, NULL } };
|
||||
|
||||
tile_manager_get_tile_col_row (tm, tile, &tile_col, &tile_row);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
for (j = 0; j < 2; j++)
|
||||
source[i][j] = tile_manager_get_at (level_below,
|
||||
tile_col * 2 + i,
|
||||
tile_row * 2 + j,
|
||||
TRUE,
|
||||
FALSE);
|
||||
for (i = 0; i < 2; i++)
|
||||
for (j = 0; j < 2; j++)
|
||||
if (source[i][j])
|
||||
{
|
||||
gimp_projection_write_quarter (tile, source[i][j], i, j);
|
||||
|
||||
tile_release (source[i][j], FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/* image callbacks */
|
||||
|
||||
|
@ -714,7 +924,7 @@ static void
|
|||
gimp_projection_image_size_changed (GimpImage *image,
|
||||
GimpProjection *proj)
|
||||
{
|
||||
gimp_projection_alloc_tiles (proj);
|
||||
gimp_projection_alloc_levels (proj);
|
||||
gimp_projection_add_update_area (proj, 0, 0, image->width, image->height);
|
||||
}
|
||||
|
||||
|
@ -722,7 +932,7 @@ static void
|
|||
gimp_projection_image_mode_changed (GimpImage *image,
|
||||
GimpProjection *proj)
|
||||
{
|
||||
gimp_projection_alloc_tiles (proj);
|
||||
gimp_projection_alloc_levels (proj);
|
||||
gimp_projection_add_update_area (proj, 0, 0, image->width, image->height);
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,9 @@ struct _GimpProjectionIdleRender
|
|||
#define GIMP_IS_PROJECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PROJECTION))
|
||||
#define GIMP_PROJECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PROJECTION, GimpProjectionClass))
|
||||
|
||||
#define PYRAMID_MAX_LEVELS 10
|
||||
#define PYRAMID_BASE_LEVEL 0
|
||||
|
||||
|
||||
typedef struct _GimpProjectionClass GimpProjectionClass;
|
||||
|
||||
|
@ -56,7 +59,10 @@ struct _GimpProjection
|
|||
|
||||
GimpImageType type;
|
||||
gint bytes;
|
||||
TileManager *tiles;
|
||||
|
||||
/* An image pyramid. Level n + 1 has half the width and height of level n. */
|
||||
TileManager *pyramid[PYRAMID_MAX_LEVELS];
|
||||
gint top_level;
|
||||
|
||||
GSList *update_areas;
|
||||
GimpProjectionIdleRender idle_render;
|
||||
|
@ -82,6 +88,20 @@ GType gimp_projection_get_type (void) G_GNUC_CONST;
|
|||
GimpProjection * gimp_projection_new (GimpImage *image);
|
||||
|
||||
TileManager * gimp_projection_get_tiles (GimpProjection *proj);
|
||||
|
||||
TileManager * gimp_projection_get_tiles_at_level
|
||||
(GimpProjection *proj,
|
||||
gint level);
|
||||
void gimp_projection_level_size_from_scale
|
||||
(GimpProjection *proj,
|
||||
gdouble scalex,
|
||||
gint *level,
|
||||
gint *width,
|
||||
gint *height);
|
||||
|
||||
gint gimp_projection_scale_to_level (GimpProjection *proj,
|
||||
gdouble scalex);
|
||||
|
||||
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);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "core/gimpguide.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimplist.h"
|
||||
#include "core/gimpprojection.h"
|
||||
#include "core/gimpsamplepoint.h"
|
||||
|
||||
#include "vectors/gimpstroke.h"
|
||||
|
@ -504,16 +505,28 @@ gimp_display_shell_draw_area (GimpDisplayShell *shell,
|
|||
gint w,
|
||||
gint h)
|
||||
{
|
||||
gint sx, sy;
|
||||
gint sw, sh;
|
||||
GimpProjection *proj = shell->display->image->projection;
|
||||
gint level_used;
|
||||
gint width_of_level;
|
||||
gint height_of_level;
|
||||
gint sx, sy;
|
||||
gint sw, sh;
|
||||
|
||||
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
|
||||
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell) &&
|
||||
GIMP_IS_PROJECTION (proj));
|
||||
|
||||
gimp_projection_level_size_from_scale (proj,
|
||||
shell->scale_x,
|
||||
&level_used,
|
||||
&width_of_level,
|
||||
&height_of_level);
|
||||
|
||||
/* the image's size in display coordinates */
|
||||
sx = shell->disp_xoffset - shell->offset_x;
|
||||
sy = shell->disp_yoffset - shell->offset_y;
|
||||
sw = SCALEX (shell, shell->display->image->width);
|
||||
sh = SCALEY (shell, shell->display->image->height);
|
||||
/* SCALE[XY] with pyramid level taken into account. */
|
||||
sw = PROJ_ROUND (width_of_level * (shell->scale_x * (1 << level_used)));
|
||||
sh = PROJ_ROUND (height_of_level * (shell->scale_y * (1 << level_used)));
|
||||
|
||||
/* check if the passed in area intersects with
|
||||
* both the display and the image
|
||||
|
|
|
@ -831,19 +831,22 @@ render_image_init_info_full (RenderInfo *info,
|
|||
gint h,
|
||||
GimpProjection *projection)
|
||||
{
|
||||
info->shell = shell;
|
||||
info->w = w;
|
||||
info->h = h;
|
||||
info->scalex = shell->scale_x;
|
||||
info->scaley = shell->scale_y;
|
||||
info->dest_bpp = 3;
|
||||
info->dest_bpl = info->dest_bpp * GIMP_RENDER_BUF_WIDTH;
|
||||
info->dest_width = info->dest_bpp * info->w;
|
||||
gint level = gimp_projection_scale_to_level (projection, shell->scale_x);
|
||||
info->shell = shell;
|
||||
info->dest_bpp = 3;
|
||||
info->dest_bpl = info->dest_bpp * GIMP_RENDER_BUF_WIDTH;
|
||||
info->scalex = shell->scale_x * (1 << level);
|
||||
info->scaley = shell->scale_y * (1 << level);
|
||||
|
||||
render_image_init_info (info, shell, x, y,
|
||||
gimp_projection_get_tiles (projection));
|
||||
|
||||
info->scale = render_image_accelerate_scaling (w, info->x, info->scalex);
|
||||
gimp_projection_get_tiles_at_level (projection,
|
||||
level));
|
||||
info->w = w;
|
||||
info->h = h;
|
||||
info->dest_width = info->dest_bpp * info->w;
|
||||
info->scale = render_image_accelerate_scaling (info->w,
|
||||
info->x,
|
||||
info->scalex);
|
||||
|
||||
if (GIMP_IMAGE_TYPE_HAS_ALPHA (gimp_projection_get_image_type (projection)))
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue