gimp/app/core/gimpdrawable-preview.c
Michael Natterer 3c33140fc1 themes/Default/images/stock-delete-16.png
2003-03-06  Michael Natterer  <mitch@gimp.org>

	* themes/Default/images/stock-delete-16.png
	* themes/Default/images/stock-lower-16.png
	* themes/Default/images/stock-new-16.png
	* themes/Default/images/stock-paste-16.png
	* themes/Default/images/stock-raise-16.png
	* themes/Default/images/stock-refresh-16.png: removed these files
	since we use the icons provided by GTK+ now.

	* themes/Default/gtkrc
	* themes/Default/images/Makefile.am: removed them here hoo.

	* libgimpwidgets/gimpstock.[ch]: reordered stuff to be consistent
	in the header and the .c file. Added GIMP_STOCK_ERROR and
	GIMP_STOCK_QUESTION which are available in all sizes (unlike
	GTK_STOCK_DIALOG_ERROR and GTK_STOCK_DIALOG_QUESTION).

	* app/core/gimpviewable.c
	* app/display/gimpdisplayshell.c
	* app/gui/file-commands.c
	* app/gui/file-new-dialog.c
	* app/gui/file-save-dialog.c
	* app/widgets/gimpwidgets-utils.c
	* app/widgets/gimpdatafactoryview.c: use the new stock IDs.

	* app/config/gimpcoreconfig.[ch]: renamed "preview_size" to
	"layer_preview_size" and added "gboolean layer_previews" which
	switches layer previews on/off independent of their size.

	* app/config/gimprc-blurbs.h: added/changed their blurbs.

	* app/core/core-enums.[ch]: removed GIMP_PREVIEW_SIZE_NONE.

	* app/core/gimpdrawable-preview.c
	* app/core/gimpdrawable.c
	* app/core/gimpimage.c: return NULL previews if
	core_config->layer_previews is FALSE. Invalidate all layer/channel
	previews whenever "layer_previews" changes.

	* app/widgets/gimppreviewrendererdrawable.c
	* app/widgets/gimppreviewrendererimage.c: render the stock_id
	if the drawable/image returns a NULL preview. Fixes bug #107242.

	* app/display/gimpdisplayshell-handlers.c: don't set the
	sensitivity of the navigation button because it can no longer be
	disabled.

	* app/display/gimpdisplayshell-layer-select.c
	* app/gui/dialogs-constructors.c
	* app/gui/dialogs.c
	* app/gui/paths-dialog.c: changed accordingly.

	* app/gui/preferences-dialog.c: added a toggle button for the new
	"layer_previews" boolean.

	* app/widgets/gimpcontainergridview.c
	* app/widgets/gimpcontainerlistview.c: chain up unconditionally
	in GimpContainerView::clear_items().

	* app/widgets/gimpcontainertreeview.c: ditto. Made the reorder()
	implementation lengthy and eeky (but working) again... Stop signal
	emission on double clicks so GtkTreeView doesn't re-select the
	item we are about change.

	* app/widgets/gimpcontainerview.c
	(gimp_container_view_real_clear_items): need to use
	g_hash_table_new_full() here too or everything will b0rk.

	* app/widgets/gimppreviewrenderer.c
	(gimp_preview_renderer_default_render_stock): use
	gtk_widget_render_icon() instead of gtk_icon_set_render_icon().

	* tools/pdbgen/enums.pl: this file wanted to be regenerated...
2003-03-06 16:47:34 +00:00

463 lines
12 KiB
C

/* The GIMP -- an 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 <string.h>
#include <glib-object.h>
#include "libgimpmath/gimpmath.h"
#include "core-types.h"
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "base/tile-manager.h"
#include "base/tile.h"
#include "paint-funcs/paint-funcs.h"
#include "config/gimpcoreconfig.h"
#include "gimp.h"
#include "gimpchannel.h"
#include "gimpimage.h"
#include "gimpdrawable.h"
#include "gimplayer.h"
#include "gimppreviewcache.h"
/* local function prototypes */
static TempBuf * gimp_drawable_preview_private (GimpDrawable *drawable,
gint width,
gint height);
static void gimp_drawable_preview_scale (GimpImageBaseType type,
guchar *cmap,
PixelRegion *srcPR,
PixelRegion *destPR,
gint subsample);
/* public functions */
void
gimp_drawable_get_preview_size (GimpViewable *viewable,
gint size,
gboolean is_popup,
gboolean dot_for_dot,
gint *width,
gint *height)
{
GimpDrawable *drawable;
GimpImage *gimage;
drawable = GIMP_DRAWABLE (viewable);
gimage = gimp_item_get_image (GIMP_ITEM (drawable));
if (gimage && ! is_popup)
{
gimp_viewable_calc_preview_size (viewable,
gimage->width,
gimage->height,
size,
size,
dot_for_dot,
gimage->xresolution,
gimage->yresolution,
width,
height,
NULL);
}
else
{
gimp_viewable_calc_preview_size (viewable,
drawable->width,
drawable->height,
size,
size,
dot_for_dot,
1.0,
1.0,
width,
height,
NULL);
}
}
gboolean
gimp_drawable_get_popup_size (GimpViewable *viewable,
gint width,
gint height,
gboolean dot_for_dot,
gint *popup_width,
gint *popup_height)
{
GimpDrawable *drawable;
GimpImage *gimage;
drawable = GIMP_DRAWABLE (viewable);
gimage = gimp_item_get_image (GIMP_ITEM (drawable));
if (drawable->width > width || drawable->height > height)
{
gboolean scaling_up;
gimp_viewable_calc_preview_size (viewable,
drawable->width,
drawable->height,
MIN (width * 2,
GIMP_VIEWABLE_MAX_POPUP_SIZE),
MIN (height * 2,
GIMP_VIEWABLE_MAX_POPUP_SIZE),
dot_for_dot,
gimage ? gimage->xresolution : 1.0,
gimage ? gimage->yresolution : 1.0,
popup_width,
popup_height,
&scaling_up);
if (scaling_up)
{
*popup_width = drawable->width;
*popup_width = drawable->height;
}
return TRUE;
}
return FALSE;
}
TempBuf *
gimp_drawable_get_preview (GimpViewable *viewable,
gint width,
gint height)
{
GimpDrawable *drawable;
GimpImage *gimage;
drawable = GIMP_DRAWABLE (viewable);
gimage = gimp_item_get_image (GIMP_ITEM (drawable));
if (! gimage || ! gimage->gimp->config->layer_previews)
return NULL;
/* Ok prime the cache with a large preview if the cache is invalid */
if (! drawable->preview_valid &&
width <= PREVIEW_CACHE_PRIME_WIDTH &&
height <= PREVIEW_CACHE_PRIME_HEIGHT &&
gimage &&
gimage->width > PREVIEW_CACHE_PRIME_WIDTH &&
gimage->height > PREVIEW_CACHE_PRIME_HEIGHT)
{
TempBuf *tb = gimp_drawable_preview_private (drawable,
PREVIEW_CACHE_PRIME_WIDTH,
PREVIEW_CACHE_PRIME_HEIGHT);
/* Save the 2nd call */
if (width == PREVIEW_CACHE_PRIME_WIDTH &&
height == PREVIEW_CACHE_PRIME_HEIGHT)
return tb;
}
/* Second call - should NOT visit the tile cache...*/
return gimp_drawable_preview_private (drawable, width, height);
}
/* private functions */
static TempBuf *
gimp_drawable_preview_private (GimpDrawable *drawable,
gint width,
gint height)
{
TempBuf *preview_buf;
PixelRegion srcPR;
PixelRegion destPR;
GimpImageBaseType type;
gint bytes;
gint subsample;
TempBuf *ret_buf;
type = GIMP_RGB;
bytes = 0;
/* The easy way */
if (drawable->preview_valid &&
(ret_buf = gimp_preview_cache_get (&drawable->preview_cache,
width, height)))
{
return ret_buf;
}
/* The hard way */
else
{
type = GIMP_IMAGE_TYPE_BASE_TYPE (gimp_drawable_type (drawable));
switch (type)
{
case GIMP_RGB:
case GIMP_GRAY:
bytes = gimp_drawable_bytes (drawable);
break;
case GIMP_INDEXED:
bytes = (gimp_drawable_type (drawable) == GIMP_INDEXED_IMAGE) ? 3 : 4;
break;
}
/* calculate 'acceptable' subsample */
subsample = 1;
/* handle some truncation errors */
if (width < 1) width = 1;
if (height < 1) height = 1;
while ((width * (subsample + 1) * 2 < gimp_drawable_width (drawable)) &&
(height * (subsample + 1) * 2 < gimp_drawable_height (drawable)))
subsample += 1;
pixel_region_init (&srcPR, gimp_drawable_data (drawable),
0, 0,
gimp_drawable_width (drawable),
gimp_drawable_height (drawable),
FALSE);
preview_buf = temp_buf_new (width, height, bytes, 0, 0, NULL);
destPR.bytes = preview_buf->bytes;
destPR.x = 0;
destPR.y = 0;
destPR.w = width;
destPR.h = height;
destPR.rowstride = width * destPR.bytes;
destPR.data = temp_buf_data (preview_buf);
if (GIMP_IS_LAYER (drawable))
{
GimpImage *gimage;
gimage = gimp_item_get_image (GIMP_ITEM (drawable));
gimp_drawable_preview_scale (type, gimage->cmap,
&srcPR, &destPR,
subsample);
}
else if (GIMP_IS_CHANNEL (drawable))
{
subsample_region (&srcPR, &destPR, subsample);
}
if (! drawable->preview_valid)
gimp_preview_cache_invalidate (&drawable->preview_cache);
drawable->preview_valid = TRUE;
gimp_preview_cache_add (&drawable->preview_cache, preview_buf);
return preview_buf;
}
}
static void
gimp_drawable_preview_scale (GimpImageBaseType type,
guchar *cmap,
PixelRegion *srcPR,
PixelRegion *destPR,
gint subsample)
{
#define EPSILON 0.000001
guchar *src, *s;
guchar *dest, *d;
gdouble *row, *r;
gint destwidth;
gint src_row, src_col;
gint bytes, b;
gint width, height;
gint orig_width, orig_height;
gdouble x_rat, y_rat;
gdouble x_cum, y_cum;
gdouble x_last, y_last;
gdouble *x_frac, y_frac, tot_frac;
gint i, j;
gint frac;
gboolean advance_dest;
guchar rgb[MAX_CHANNELS];
orig_width = srcPR->w / subsample;
orig_height = srcPR->h / subsample;
width = destPR->w;
height = destPR->h;
/* Some calculations... */
bytes = destPR->bytes;
destwidth = destPR->rowstride;
/* the data pointers... */
src = g_new (guchar, orig_width * bytes);
dest = destPR->data;
/* find the ratios of old x to new x and old y to new y */
x_rat = (gdouble) orig_width / (gdouble) width;
y_rat = (gdouble) orig_height / (gdouble) height;
/* allocate an array to help with the calculations */
row = g_new0 (gdouble, width * bytes);
x_frac = g_new (gdouble, width + orig_width);
/* initialize the pre-calculated pixel fraction array */
src_col = 0;
x_cum = (gdouble) src_col;
x_last = x_cum;
for (i = 0; i < width + orig_width; i++)
{
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
{
x_cum += x_rat;
x_frac[i] = x_cum - x_last;
}
else
{
src_col ++;
x_frac[i] = src_col - x_last;
}
x_last += x_frac[i];
}
/* counters... */
src_row = 0;
y_cum = (double) src_row;
y_last = y_cum;
pixel_region_get_row (srcPR,
0,
src_row * subsample,
orig_width * subsample,
src,
subsample);
/* Scale the selected region */
for (i = 0; i < height; )
{
src_col = 0;
x_cum = (gdouble) src_col;
/* determine the fraction of the src pixel we are using for y */
if (y_cum + y_rat <= (src_row + 1 + EPSILON))
{
y_cum += y_rat;
y_frac = y_cum - y_last;
advance_dest = TRUE;
}
else
{
src_row ++;
y_frac = src_row - y_last;
advance_dest = FALSE;
}
y_last += y_frac;
s = src;
r = row;
frac = 0;
j = width;
while (j)
{
tot_frac = x_frac[frac++] * y_frac;
/* If indexed, transform the color to RGB */
if (type == GIMP_INDEXED)
{
map_to_color (2, cmap, s, rgb);
r[RED_PIX] += rgb[RED_PIX] * tot_frac;
r[GREEN_PIX] += rgb[GREEN_PIX] * tot_frac;
r[BLUE_PIX] += rgb[BLUE_PIX] * tot_frac;
if (bytes == 4)
r[ALPHA_PIX] += s[ALPHA_I_PIX] * tot_frac;
}
else
{
for (b = 0; b < bytes; b++)
r[b] += s[b] * tot_frac;
}
/* increment the destination */
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
{
r += bytes;
x_cum += x_rat;
j--;
}
/* increment the source */
else
{
s += srcPR->bytes;
src_col++;
}
}
if (advance_dest)
{
tot_frac = 1.0 / (x_rat * y_rat);
/* copy "row" to "dest" */
d = dest;
r = row;
j = width;
while (j--)
{
b = bytes;
while (b--)
*d++ = (guchar) ((*r++ * tot_frac) + 0.5);
}
dest += destwidth;
/* clear the "row" array */
memset (row, 0, sizeof (gdouble) * destwidth);
i++;
}
else
{
pixel_region_get_row (srcPR,
0,
src_row * subsample,
orig_width * subsample,
src,
subsample);
}
}
/* free up temporary arrays */
g_free (row);
g_free (x_frac);
g_free (src);
}