mirror of https://github.com/GNOME/gimp.git
app/base/Makefile.am app/base/base-types.h image pyramid code split out of
2007-06-20 Sven Neumann <sven@gimp.org> * app/base/Makefile.am * app/base/base-types.h * app/base/tile-pyramid.[ch]: image pyramid code split out of GimpProjection. * app/core/gimpprojection.[ch]: use the new TilePyramid. svn path=/trunk/; revision=22807
This commit is contained in:
parent
923b06bd0e
commit
3038dc0e80
|
@ -1,3 +1,12 @@
|
|||
2007-06-20 Sven Neumann <sven@gimp.org>
|
||||
|
||||
* app/base/Makefile.am
|
||||
* app/base/base-types.h
|
||||
* app/base/tile-pyramid.[ch]: image pyramid code split out of
|
||||
GimpProjection.
|
||||
|
||||
* app/core/gimpprojection.[ch]: use TilePyramid.
|
||||
|
||||
2007-06-20 Sven Neumann <sven@gimp.org>
|
||||
|
||||
* app/base/tile.[ch]: removed unused function tile_mark_valid().
|
||||
|
|
|
@ -50,6 +50,8 @@ libappbase_a_SOURCES = \
|
|||
tile-manager-private.h \
|
||||
tile-manager-crop.c \
|
||||
tile-manager-crop.h \
|
||||
tile-pyramid.c \
|
||||
tile-pyramid.h \
|
||||
tile-swap.c \
|
||||
tile-swap.h
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ typedef struct _TempBuf TempBuf;
|
|||
|
||||
typedef struct _Tile Tile;
|
||||
typedef struct _TileManager TileManager;
|
||||
typedef struct _TilePyramid TilePyramid;
|
||||
|
||||
|
||||
/* functions */
|
||||
|
|
|
@ -0,0 +1,404 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "base-types.h"
|
||||
|
||||
#include "tile.h"
|
||||
#include "tile-manager.h"
|
||||
#include "tile-pyramid.h"
|
||||
|
||||
|
||||
#define PYRAMID_MAX_LEVELS 10
|
||||
|
||||
|
||||
struct _TilePyramid
|
||||
{
|
||||
GimpImageType type;
|
||||
guint width;
|
||||
guint height;
|
||||
gint bytes;
|
||||
TileManager *tiles[PYRAMID_MAX_LEVELS];
|
||||
gint top_level;
|
||||
};
|
||||
|
||||
|
||||
static gint tile_pyramid_alloc_levels (TilePyramid *pyramid,
|
||||
gint top_level);
|
||||
static void tile_pyramid_validate_tile (TileManager *tm,
|
||||
Tile *tile);
|
||||
static void tile_pyramid_write_quarter (Tile *dest,
|
||||
Tile *src,
|
||||
gint i,
|
||||
gint j);
|
||||
|
||||
|
||||
TilePyramid *
|
||||
tile_pyramid_new (GimpImageType type,
|
||||
gint width,
|
||||
gint height)
|
||||
{
|
||||
TilePyramid *pyramid;
|
||||
|
||||
g_return_val_if_fail (width > 0, NULL);
|
||||
g_return_val_if_fail (height > 0, NULL);
|
||||
|
||||
pyramid = g_slice_new0 (TilePyramid);
|
||||
|
||||
pyramid->type = type;
|
||||
pyramid->width = width;
|
||||
pyramid->height = height;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GIMP_GRAY_IMAGE:
|
||||
pyramid->bytes = 1;
|
||||
break;
|
||||
|
||||
case GIMP_GRAYA_IMAGE:
|
||||
pyramid->bytes = 2;
|
||||
break;
|
||||
|
||||
case GIMP_RGB_IMAGE:
|
||||
pyramid->bytes = 3;
|
||||
break;
|
||||
|
||||
case GIMP_RGBA_IMAGE:
|
||||
pyramid->bytes = 4;
|
||||
break;
|
||||
|
||||
case GIMP_INDEXED_IMAGE:
|
||||
case GIMP_INDEXEDA_IMAGE:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
pyramid->tiles[0] = tile_manager_new (width, height, pyramid->bytes);
|
||||
|
||||
return pyramid;
|
||||
}
|
||||
|
||||
void
|
||||
tile_pyramid_destroy (TilePyramid *pyramid)
|
||||
{
|
||||
gint level;
|
||||
|
||||
g_return_if_fail (pyramid != NULL);
|
||||
|
||||
for (level = 0; level <= pyramid->top_level; level++)
|
||||
tile_manager_unref (pyramid->tiles[level]);
|
||||
|
||||
g_slice_free (TilePyramid, pyramid);
|
||||
}
|
||||
|
||||
gint
|
||||
tile_pyramid_get_level (gint width,
|
||||
gint height,
|
||||
gdouble scale)
|
||||
{
|
||||
gdouble next = 1.0;
|
||||
guint w = (guint) width;
|
||||
guint h = (guint) height;
|
||||
gint level;
|
||||
|
||||
for (level = 0; level < PYRAMID_MAX_LEVELS; level++)
|
||||
{
|
||||
w >>= 1;
|
||||
h >>= 1;
|
||||
|
||||
if (w == 0 || h == 0)
|
||||
break;
|
||||
|
||||
if (w <= TILE_WIDTH && h <= TILE_HEIGHT)
|
||||
break;
|
||||
|
||||
next /= 2;
|
||||
|
||||
if (next < scale)
|
||||
break;
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
TileManager *
|
||||
tile_pyramid_get_tiles (TilePyramid *pyramid,
|
||||
gint level)
|
||||
{
|
||||
g_return_val_if_fail (pyramid != NULL, NULL);
|
||||
|
||||
level = tile_pyramid_alloc_levels (pyramid, level);
|
||||
|
||||
g_return_val_if_fail (pyramid->tiles[level] != NULL, NULL);
|
||||
|
||||
return pyramid->tiles[level];
|
||||
}
|
||||
|
||||
void
|
||||
tile_pyramid_invalidate_area (TilePyramid *pyramid,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h)
|
||||
{
|
||||
gint level;
|
||||
|
||||
g_return_if_fail (pyramid != NULL);
|
||||
|
||||
for (level = 0; level <= pyramid->top_level; level++)
|
||||
{
|
||||
/* Tile invalidation must propagate all the way up in the pyramid,
|
||||
* so keep width and height > 0.
|
||||
*/
|
||||
tile_manager_invalidate_area (pyramid->tiles[level],
|
||||
x >> level,
|
||||
y >> level,
|
||||
MAX (w >> level, 1),
|
||||
MAX (h >> level, 1));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tile_pyramid_set_validate_proc (TilePyramid *pyramid,
|
||||
TileValidateProc proc,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_return_if_fail (pyramid != NULL);
|
||||
|
||||
tile_manager_set_validate_proc (pyramid->tiles[0], proc);
|
||||
tile_manager_set_user_data (pyramid->tiles[0], user_data);
|
||||
}
|
||||
|
||||
gint
|
||||
tile_pyramid_get_width (const TilePyramid *pyramid)
|
||||
{
|
||||
g_return_val_if_fail (pyramid != NULL, 0);
|
||||
|
||||
return pyramid->width;
|
||||
}
|
||||
|
||||
gint
|
||||
tile_pyramid_get_height (const TilePyramid *pyramid)
|
||||
{
|
||||
g_return_val_if_fail (pyramid != NULL, 0);
|
||||
|
||||
return pyramid->height;
|
||||
}
|
||||
|
||||
gint
|
||||
tile_pyramid_get_bpp (const TilePyramid *pyramid)
|
||||
{
|
||||
g_return_val_if_fail (pyramid != NULL, 0);
|
||||
|
||||
return pyramid->bytes;
|
||||
}
|
||||
|
||||
gint64
|
||||
tile_pyramid_get_memsize (const TilePyramid *pyramid)
|
||||
{
|
||||
gint64 memsize = sizeof (TilePyramid);
|
||||
gint level;
|
||||
|
||||
g_return_val_if_fail (pyramid != NULL, 0);
|
||||
|
||||
for (level = 0; level <= pyramid->top_level; level++)
|
||||
memsize += tile_manager_get_memsize (pyramid->tiles[level], FALSE);
|
||||
|
||||
return memsize;
|
||||
}
|
||||
|
||||
/* private functions */
|
||||
|
||||
/* This function make sure that levels are allocated up to the level
|
||||
* it returns. The return value may be smaller than the level that
|
||||
* was actually requested.
|
||||
*/
|
||||
static gint
|
||||
tile_pyramid_alloc_levels (TilePyramid *pyramid,
|
||||
gint top_level)
|
||||
{
|
||||
gint level;
|
||||
|
||||
top_level = MIN (top_level, PYRAMID_MAX_LEVELS - 1);
|
||||
|
||||
if (top_level <= pyramid->top_level)
|
||||
return top_level;
|
||||
|
||||
for (level = pyramid->top_level + 1; level <= top_level; level++)
|
||||
{
|
||||
gint width = pyramid->width >> level;
|
||||
gint height = pyramid->height >> level;
|
||||
|
||||
if (width == 0 || height == 0)
|
||||
return pyramid->top_level;
|
||||
|
||||
/* There is no use having levels that have the same number of
|
||||
* tiles as the parent level.
|
||||
*/
|
||||
if (width <= TILE_WIDTH / 2 && height <= TILE_HEIGHT / 2)
|
||||
return pyramid->top_level;
|
||||
|
||||
pyramid->top_level = level;
|
||||
pyramid->tiles[level] = tile_manager_new (width, height, pyramid->bytes);
|
||||
|
||||
tile_manager_set_user_data (pyramid->tiles[level], pyramid);
|
||||
|
||||
/* Use the level below to validate tiles. */
|
||||
tile_manager_set_validate_proc (pyramid->tiles[level],
|
||||
tile_pyramid_validate_tile);
|
||||
|
||||
tile_manager_set_level_below (pyramid->tiles[level],
|
||||
pyramid->tiles[level - 1]);
|
||||
}
|
||||
|
||||
return pyramid->top_level;
|
||||
}
|
||||
|
||||
static void
|
||||
tile_pyramid_validate_tile (TileManager *tm,
|
||||
Tile *tile)
|
||||
{
|
||||
TileManager *level_below = tile_manager_get_level_below (tm);
|
||||
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 (level_below,
|
||||
tile_col * 2 + i,
|
||||
tile_row * 2 + j,
|
||||
TRUE, FALSE);
|
||||
if (source)
|
||||
{
|
||||
tile_pyramid_write_quarter (tile, source, i, j);
|
||||
tile_release (source, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tile_pyramid_write_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:
|
||||
g_warning ("not yet implemented");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
for (x = 0; x < src_ewidth / 2; x++)
|
||||
{
|
||||
gint a = src0[1] + src1[1] + src2[1] + src3[1];
|
||||
|
||||
if (a)
|
||||
{
|
||||
dst[0] = ((src0[0] * src0[1] +
|
||||
src1[0] * src1[1] +
|
||||
src2[0] * src2[1] +
|
||||
src3[0] * src3[1]) / a);
|
||||
dst[1] = a / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = dst[1] = 0;
|
||||
}
|
||||
|
||||
dst += 2;
|
||||
|
||||
src0 += 4;
|
||||
src1 += 4;
|
||||
src2 += 4;
|
||||
src3 += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
g_warning ("not yet implemented");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
for (x = 0; x < src_ewidth / 2; x++)
|
||||
{
|
||||
gint a = src0[3] + src1[3] + src2[3] + src3[3];
|
||||
|
||||
if (a)
|
||||
{
|
||||
dst[0] = ((src0[0] * src0[3] +
|
||||
src1[0] * src1[3] +
|
||||
src2[0] * src2[3] +
|
||||
src3[0] * src3[3]) / a);
|
||||
dst[1] = ((src0[1] * src0[3] +
|
||||
src1[1] * src1[3] +
|
||||
src2[1] * src2[3] +
|
||||
src3[1] * src3[3]) / a);
|
||||
dst[2] = ((src0[2] * src0[3] +
|
||||
src1[2] * src1[3] +
|
||||
src2[2] * src2[3] +
|
||||
src3[2] * src3[3]) / a);
|
||||
dst[3] = a / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = dst[1] = dst[2] = dst[3] = 0;
|
||||
}
|
||||
|
||||
dst += 4;
|
||||
|
||||
src0 += 8;
|
||||
src1 += 8;
|
||||
src2 += 8;
|
||||
src3 += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dest_data += dest_ewidth * bpp;
|
||||
src_data += src_ewidth * bpp * 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __TILE_PYRAMID_H__
|
||||
#define __TILE_PYRAMID_H__
|
||||
|
||||
|
||||
TilePyramid * tile_pyramid_new (GimpImageType type,
|
||||
gint width,
|
||||
gint height);
|
||||
void tile_pyramid_destroy (TilePyramid *pyramid);
|
||||
|
||||
gint tile_pyramid_get_level (gint width,
|
||||
gint height,
|
||||
gdouble scale);
|
||||
|
||||
TileManager * tile_pyramid_get_tiles (TilePyramid *pyramid,
|
||||
gint level);
|
||||
|
||||
void tile_pyramid_invalidate_area (TilePyramid *pyramid,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h);
|
||||
|
||||
void tile_pyramid_set_validate_proc (TilePyramid *pyramid,
|
||||
TileValidateProc proc,
|
||||
gpointer user_data);
|
||||
|
||||
gint tile_pyramid_get_width (const TilePyramid *pyramid);
|
||||
gint tile_pyramid_get_height (const TilePyramid *pyramid);
|
||||
gint tile_pyramid_get_bpp (const TilePyramid *pyramid);
|
||||
|
||||
gint64 tile_pyramid_get_memsize (const TilePyramid *pyramid);
|
||||
|
||||
|
||||
#endif /* __TILE_PYRAMID_H__ */
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "base/tile.h"
|
||||
#include "base/tile-manager.h"
|
||||
#include "base/tile-pyramid.h"
|
||||
|
||||
#include "gimp.h"
|
||||
#include "gimparea.h"
|
||||
|
@ -59,9 +60,8 @@ static gint gimp_projection_get_opacity_at (GimpPickable *pickabl
|
|||
gint x,
|
||||
gint y);
|
||||
|
||||
static gint gimp_projection_alloc_levels (GimpProjection *proj,
|
||||
gint top_level);
|
||||
static void gimp_projection_release_levels (GimpProjection *proj);
|
||||
static TilePyramid * gimp_projection_create_pyramid (GimpProjection *proj);
|
||||
|
||||
static void gimp_projection_add_update_area (GimpProjection *proj,
|
||||
gint x,
|
||||
gint y,
|
||||
|
@ -85,13 +85,6 @@ static void gimp_projection_invalidate (GimpProjection *proj,
|
|||
guint 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,
|
||||
gint y,
|
||||
|
@ -143,23 +136,11 @@ gimp_projection_class_init (GimpProjectionClass *klass)
|
|||
static void
|
||||
gimp_projection_init (GimpProjection *proj)
|
||||
{
|
||||
gint level;
|
||||
|
||||
proj->image = NULL;
|
||||
|
||||
proj->type = -1;
|
||||
proj->bytes = 0;
|
||||
|
||||
for (level = 0; level < PYRAMID_MAX_LEVELS; level++)
|
||||
proj->pyramid[level] = NULL;
|
||||
|
||||
proj->top_level = PYRAMID_BASE_LEVEL;
|
||||
|
||||
proj->pyramid = NULL;
|
||||
proj->update_areas = NULL;
|
||||
|
||||
proj->idle_render.idle_id = 0;
|
||||
proj->idle_render.update_areas = NULL;
|
||||
|
||||
proj->construct_flag = FALSE;
|
||||
}
|
||||
|
||||
|
@ -181,7 +162,6 @@ static void
|
|||
gimp_projection_finalize (GObject *object)
|
||||
{
|
||||
GimpProjection *proj = GIMP_PROJECTION (object);
|
||||
gint level;
|
||||
|
||||
if (proj->idle_render.idle_id)
|
||||
{
|
||||
|
@ -195,14 +175,11 @@ gimp_projection_finalize (GObject *object)
|
|||
gimp_area_list_free (proj->idle_render.update_areas);
|
||||
proj->idle_render.update_areas = 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;
|
||||
if (proj->pyramid)
|
||||
{
|
||||
tile_pyramid_destroy (proj->pyramid);
|
||||
proj->pyramid = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
@ -212,12 +189,10 @@ gimp_projection_get_memsize (GimpObject *object,
|
|||
gint64 *gui_size)
|
||||
{
|
||||
GimpProjection *projection = GIMP_PROJECTION (object);
|
||||
gint64 memsize = 0;
|
||||
gint level;
|
||||
gint64 memsize = 0;
|
||||
|
||||
for (level = 0; level <= projection->top_level; level++)
|
||||
if (projection->pyramid[level])
|
||||
memsize += tile_manager_get_memsize (projection->pyramid[level], FALSE);
|
||||
if (projection->pyramid)
|
||||
memsize = tile_pyramid_get_memsize (projection->pyramid);
|
||||
|
||||
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
|
||||
gui_size);
|
||||
|
@ -253,7 +228,7 @@ gimp_projection_estimate_memsize (GimpImageBaseType type,
|
|||
break;
|
||||
}
|
||||
|
||||
/* The levels constitute a geometric sum with a ratio of 1/4. */
|
||||
/* The pyramid levels constitute a geometric sum with a ratio of 1/4. */
|
||||
return bytes * (gint64) width * (gint64) height * 1.33;
|
||||
}
|
||||
|
||||
|
@ -321,7 +296,8 @@ gimp_projection_new (GimpImage *image)
|
|||
TileManager *
|
||||
gimp_projection_get_tiles (GimpProjection *proj)
|
||||
{
|
||||
return gimp_projection_get_tiles_at_level (proj, PYRAMID_BASE_LEVEL);
|
||||
return gimp_projection_get_tiles_at_level (proj, 0);
|
||||
|
||||
}
|
||||
|
||||
TileManager *
|
||||
|
@ -330,11 +306,10 @@ gimp_projection_get_tiles_at_level (GimpProjection *proj,
|
|||
{
|
||||
g_return_val_if_fail (GIMP_IS_PROJECTION (proj), NULL);
|
||||
|
||||
level = gimp_projection_alloc_levels (proj, level);
|
||||
if (! proj->pyramid)
|
||||
proj->pyramid = gimp_projection_create_pyramid (proj);
|
||||
|
||||
g_return_val_if_fail (proj->pyramid[level] != NULL, NULL);
|
||||
|
||||
return proj->pyramid[level];
|
||||
return tile_pyramid_get_tiles (proj->pyramid, level);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -352,34 +327,9 @@ gimp_projection_get_level (GimpProjection *proj,
|
|||
gdouble scale_x,
|
||||
gdouble scale_y)
|
||||
{
|
||||
gdouble scale;
|
||||
gdouble next = 1.0;
|
||||
guint width = proj->image->width >> 1;
|
||||
guint height = proj->image->height >> 1;
|
||||
gint level;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_PROJECTION (proj), PYRAMID_BASE_LEVEL);
|
||||
|
||||
scale = MAX (scale_x, scale_y);
|
||||
|
||||
for (level = PYRAMID_BASE_LEVEL; level < PYRAMID_MAX_LEVELS; level++)
|
||||
{
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
|
||||
if (width == 0 || height == 0)
|
||||
break;
|
||||
|
||||
if (width <= TILE_WIDTH && height <= TILE_HEIGHT)
|
||||
break;
|
||||
|
||||
next /= 2;
|
||||
|
||||
if (next < scale)
|
||||
break;
|
||||
}
|
||||
|
||||
return level;
|
||||
return tile_pyramid_get_level (proj->image->width,
|
||||
proj->image->height,
|
||||
MAX (scale_x, scale_y));
|
||||
}
|
||||
|
||||
GimpImage *
|
||||
|
@ -395,7 +345,17 @@ gimp_projection_get_image_type (const GimpProjection *proj)
|
|||
{
|
||||
g_return_val_if_fail (GIMP_IS_PROJECTION (proj), -1);
|
||||
|
||||
return proj->type;
|
||||
switch (gimp_image_base_type (proj->image))
|
||||
{
|
||||
case GIMP_RGB:
|
||||
case GIMP_INDEXED:
|
||||
return GIMP_RGBA_IMAGE;
|
||||
|
||||
case GIMP_GRAY:
|
||||
return GIMP_GRAYA_IMAGE;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
gint
|
||||
|
@ -403,7 +363,7 @@ gimp_projection_get_bytes (const GimpProjection *proj)
|
|||
{
|
||||
g_return_val_if_fail (GIMP_IS_PROJECTION (proj), 0);
|
||||
|
||||
return proj->bytes;
|
||||
return GIMP_IMAGE_TYPE_BYTES (gimp_projection_get_image_type (proj));
|
||||
}
|
||||
|
||||
gdouble
|
||||
|
@ -453,102 +413,19 @@ gimp_projection_finish_draw (GimpProjection *proj)
|
|||
|
||||
/* private functions */
|
||||
|
||||
/* This function make sure that levels are allocated up to the level
|
||||
* it returns. The return value may be smaller than the level that
|
||||
* was actually requested.
|
||||
*/
|
||||
static gint
|
||||
gimp_projection_alloc_levels (GimpProjection *proj,
|
||||
gint top_level)
|
||||
|
||||
static TilePyramid *
|
||||
gimp_projection_create_pyramid (GimpProjection *proj)
|
||||
{
|
||||
gint level;
|
||||
TilePyramid *pyramid;
|
||||
|
||||
top_level = MIN (top_level, PYRAMID_MAX_LEVELS - 1);
|
||||
pyramid = tile_pyramid_new (gimp_projection_get_image_type (proj),
|
||||
proj->image->width,
|
||||
proj->image->height);
|
||||
|
||||
if (! proj->pyramid[PYRAMID_BASE_LEVEL])
|
||||
{
|
||||
gint width = proj->image->width;
|
||||
gint height = proj->image->height;
|
||||
tile_pyramid_set_validate_proc (pyramid, gimp_projection_validate_tile, proj);
|
||||
|
||||
/* Find the number of bytes required for the projection.
|
||||
* This includes the intensity channels and an alpha channel
|
||||
* if one doesn't exist.
|
||||
*/
|
||||
switch (gimp_image_base_type (proj->image))
|
||||
{
|
||||
case GIMP_RGB:
|
||||
case GIMP_INDEXED:
|
||||
proj->type = GIMP_RGBA_IMAGE;
|
||||
proj->bytes = 4;
|
||||
break;
|
||||
|
||||
case GIMP_GRAY:
|
||||
proj->type = GIMP_GRAYA_IMAGE;
|
||||
proj->bytes = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
proj->pyramid[PYRAMID_BASE_LEVEL] = tile_manager_new (width, height,
|
||||
proj->bytes);
|
||||
|
||||
tile_manager_set_user_data (proj->pyramid[PYRAMID_BASE_LEVEL], proj);
|
||||
|
||||
/* Validate tiles by building from the layers of the image. */
|
||||
tile_manager_set_validate_proc (proj->pyramid[PYRAMID_BASE_LEVEL],
|
||||
gimp_projection_validate_tile);
|
||||
|
||||
proj->top_level = PYRAMID_BASE_LEVEL;
|
||||
}
|
||||
|
||||
if (top_level <= proj->top_level)
|
||||
return top_level;
|
||||
|
||||
for (level = proj->top_level + 1; level <= top_level; level++)
|
||||
{
|
||||
gint width = (guint) proj->image->width >> level;
|
||||
gint height = (guint) proj->image->height >> level;
|
||||
|
||||
if (width == 0 || height == 0)
|
||||
return proj->top_level;
|
||||
|
||||
/* There is no use having levels that have the same number of
|
||||
* tiles as the parent level.
|
||||
*/
|
||||
if (width <= TILE_WIDTH / 2 && height <= TILE_HEIGHT / 2)
|
||||
return proj->top_level;
|
||||
|
||||
proj->top_level = level;
|
||||
proj->pyramid[level] = tile_manager_new (width, height, proj->bytes);
|
||||
|
||||
tile_manager_set_user_data (proj->pyramid[level], proj);
|
||||
|
||||
/* 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]);
|
||||
}
|
||||
|
||||
return proj->top_level;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_projection_release_levels (GimpProjection *proj)
|
||||
{
|
||||
gint level;
|
||||
|
||||
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 = 0;
|
||||
return pyramid;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -772,21 +649,8 @@ gimp_projection_invalidate (GimpProjection *proj,
|
|||
guint w,
|
||||
guint h)
|
||||
{
|
||||
gint level;
|
||||
|
||||
for (level = 0; level <= proj->top_level; level++)
|
||||
{
|
||||
TileManager *tm = gimp_projection_get_tiles_at_level (proj, level);
|
||||
|
||||
/* Tile invalidation must propagate all the way up in the pyramid,
|
||||
* so keep width and height > 0.
|
||||
*/
|
||||
tile_manager_invalidate_area (tm,
|
||||
x >> level,
|
||||
y >> level,
|
||||
MAX (w >> level, 1),
|
||||
MAX (h >> level, 1));
|
||||
}
|
||||
if (proj->pyramid)
|
||||
tile_pyramid_invalidate_area (proj->pyramid, x, y, w, h);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -795,138 +659,13 @@ gimp_projection_validate_tile (TileManager *tm,
|
|||
{
|
||||
GimpProjection *proj = tile_manager_get_user_data (tm);
|
||||
gint x, y;
|
||||
gint w, h;
|
||||
|
||||
/* Find the coordinates of this tile */
|
||||
tile_manager_get_tile_coordinates (tm, tile, &x, &y);
|
||||
|
||||
w = tile_ewidth (tile);
|
||||
h = tile_eheight (tile);
|
||||
|
||||
gimp_projection_construct (proj, x, y, w, h);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_projection_write_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 2:
|
||||
for (x = 0; x < src_ewidth / 2; x++)
|
||||
{
|
||||
gint a = src0[1] + src1[1] + src2[1] + src3[1];
|
||||
|
||||
if (a)
|
||||
{
|
||||
dst[0] = ((src0[0] * src0[1] +
|
||||
src1[0] * src1[1] +
|
||||
src2[0] * src2[1] +
|
||||
src3[0] * src3[1]) / a);
|
||||
dst[1] = a / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = dst[1] = 0;
|
||||
}
|
||||
|
||||
dst += 2;
|
||||
|
||||
src0 += 4;
|
||||
src1 += 4;
|
||||
src2 += 4;
|
||||
src3 += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
for (x = 0; x < src_ewidth / 2; x++)
|
||||
{
|
||||
gint a = src0[3] + src1[3] + src2[3] + src3[3];
|
||||
|
||||
if (a)
|
||||
{
|
||||
dst[0] = ((src0[0] * src0[3] +
|
||||
src1[0] * src1[3] +
|
||||
src2[0] * src2[3] +
|
||||
src3[0] * src3[3]) / a);
|
||||
dst[1] = ((src0[1] * src0[3] +
|
||||
src1[1] * src1[3] +
|
||||
src2[1] * src2[3] +
|
||||
src3[1] * src3[3]) / a);
|
||||
dst[2] = ((src0[2] * src0[3] +
|
||||
src1[2] * src1[3] +
|
||||
src2[2] * src2[3] +
|
||||
src3[2] * src3[3]) / a);
|
||||
dst[3] = a / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = dst[1] = dst[2] = dst[3] = 0;
|
||||
}
|
||||
|
||||
dst += 4;
|
||||
|
||||
src0 += 8;
|
||||
src1 += 8;
|
||||
src2 += 8;
|
||||
src3 += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dest_data += dest_ewidth * bpp;
|
||||
src_data += src_ewidth * bpp * 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_projection_validate_pyramid_tile (TileManager *tm,
|
||||
Tile *tile)
|
||||
{
|
||||
TileManager *level_below = tile_manager_get_level_below (tm);
|
||||
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 (level_below,
|
||||
tile_col * 2 + i,
|
||||
tile_row * 2 + j,
|
||||
TRUE, FALSE);
|
||||
if (source)
|
||||
{
|
||||
gimp_projection_write_quarter (tile, source, i, j);
|
||||
tile_release (source, FALSE);
|
||||
}
|
||||
}
|
||||
gimp_projection_construct (proj,
|
||||
x, y,
|
||||
tile_ewidth (tile), tile_eheight (tile));
|
||||
}
|
||||
|
||||
/* image callbacks */
|
||||
|
@ -946,7 +685,12 @@ static void
|
|||
gimp_projection_image_size_changed (GimpImage *image,
|
||||
GimpProjection *proj)
|
||||
{
|
||||
gimp_projection_release_levels (proj);
|
||||
if (proj->pyramid)
|
||||
{
|
||||
tile_pyramid_destroy (proj->pyramid);
|
||||
proj->pyramid = NULL;
|
||||
}
|
||||
|
||||
gimp_projection_add_update_area (proj, 0, 0, image->width, image->height);
|
||||
}
|
||||
|
||||
|
@ -954,7 +698,12 @@ static void
|
|||
gimp_projection_image_mode_changed (GimpImage *image,
|
||||
GimpProjection *proj)
|
||||
{
|
||||
gimp_projection_release_levels (proj);
|
||||
if (proj->pyramid)
|
||||
{
|
||||
tile_pyramid_destroy (proj->pyramid);
|
||||
proj->pyramid = NULL;
|
||||
}
|
||||
|
||||
gimp_projection_add_update_area (proj, 0, 0, image->width, image->height);
|
||||
}
|
||||
|
||||
|
|
|
@ -45,8 +45,6 @@ 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;
|
||||
|
@ -57,12 +55,7 @@ struct _GimpProjection
|
|||
|
||||
GimpImage *image;
|
||||
|
||||
GimpImageType type;
|
||||
gint bytes;
|
||||
|
||||
/* An image pyramid. Level n + 1 has half the width and height of level n. */
|
||||
TileManager *pyramid[PYRAMID_MAX_LEVELS];
|
||||
gint top_level;
|
||||
TilePyramid *pyramid;
|
||||
|
||||
GSList *update_areas;
|
||||
GimpProjectionIdleRender idle_render;
|
||||
|
|
Loading…
Reference in New Issue