gimp/app/core/gimpimage-preview.c
Michael Natterer 09454fb2a4 some general cleanup.
2005-09-03  Michael Natterer  <mitch@gimp.org>

	* app/base/pixel-region.[ch]: some general cleanup.

	(pixel_region_init_temp_buf)
	(pixel_region_init_data): new functions which initialize pixel
	regions on TempBufs and on raw contiguous arrays of pixel data.

	(pixel_region_configure): fixed a bug that has probably been there
	forever: when processing contiguous (non-tiled) data, interpret
	the original x and y coordinates of the region as offsets into
	the data. Before this fix, the initial x and y were simply ignored
	(by using them in a broken way), thus always forcing the upper
	left corner of the region being the beginning of the passed data.

	Lots of code was working around this problem by setting the
	pixel_region's data pointer to the proper starting pixel of the
	region in the middle the buffer.

	* libgimp/gimppixelrgn.c: some general cleanup.

	(gimp_pixel_rgn_configure): same fix as above. Fortunately, nobody
	seems to know that libgimp pixel regions can be used on arrays of
	data, just as core ones. Only two plug-ins were using this
	feature, and they are antique and written by spencer and federico,
	respectively. They both don't use offsets into the buffers and are
	not affected by this change. It's highly unlikely that anybody out
	there knows/uses this feature, so it can IMHO be safely changed.

	* app/base/temp-buf.c
	* app/core/gimpbuffer.c
	* app/core/gimpdrawable-combine.c
	* app/core/gimpdrawable-preview.c
	* app/core/gimpimage-preview.c
	* app/core/gimplayer.c
	* app/paint/gimpbrushcore.c
	* app/paint/gimpclone.c
	* app/paint/gimpconvolve.c
	* app/paint/gimpdodgeburn.c
	* app/paint/gimppaintcore.c
	* app/paint/gimpsmudge.c
	* app/tools/gimpiscissorstool.c
	* app/tools/gimppainttool.c: use the pixel_region_init_foo()
	functions instead of initializing regions of TempBufs and raw data
	manually. Removed lots of workarounds for the broken offset
	handling. The changed places of code are much more readable now.
2005-09-03 17:16:58 +00:00

383 lines
11 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 <glib-object.h>
#include "core-types.h"
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "paint-funcs/paint-funcs.h"
#include "config/gimpcoreconfig.h"
#include "gimp.h"
#include "gimp-utils.h"
#include "gimpdrawable-preview.h"
#include "gimpimage.h"
#include "gimpimage-preview.h"
#include "gimplayer.h"
#include "gimplayermask.h"
#include "gimplist.h"
void
gimp_image_get_preview_size (GimpViewable *viewable,
gint size,
gboolean is_popup,
gboolean dot_for_dot,
gint *width,
gint *height)
{
GimpImage *gimage = GIMP_IMAGE (viewable);
if (! gimage->gimp->config->layer_previews && ! is_popup)
{
*width = size;
*height = size;
return;
}
gimp_viewable_calc_preview_size (gimage->width,
gimage->height,
size,
size,
dot_for_dot,
gimage->xresolution,
gimage->yresolution,
width,
height,
NULL);
}
gboolean
gimp_image_get_popup_size (GimpViewable *viewable,
gint width,
gint height,
gboolean dot_for_dot,
gint *popup_width,
gint *popup_height)
{
GimpImage *gimage = GIMP_IMAGE (viewable);
if (! gimage->gimp->config->layer_previews)
return FALSE;
if (gimage->width > width || gimage->height > height)
{
gboolean scaling_up;
gimp_viewable_calc_preview_size (gimage->width,
gimage->height,
width * 2,
height * 2,
dot_for_dot, 1.0, 1.0,
popup_width,
popup_height,
&scaling_up);
if (scaling_up)
{
*popup_width = gimage->width;
*popup_height = gimage->height;
}
return TRUE;
}
return FALSE;
}
TempBuf *
gimp_image_get_preview (GimpViewable *viewable,
gint width,
gint height)
{
GimpImage *gimage = GIMP_IMAGE (viewable);
if (! gimage->gimp->config->layer_previews)
return NULL;
if (gimage->comp_preview_valid &&
gimage->comp_preview->width == width &&
gimage->comp_preview->height == height)
{
/* The easy way */
return gimage->comp_preview;
}
else
{
/* The hard way */
if (gimage->comp_preview)
temp_buf_free (gimage->comp_preview);
/* Actually construct the composite preview from the layer previews!
* This might seem ridiculous, but it's actually the best way, given
* a number of unsavory alternatives.
*/
gimage->comp_preview = gimp_image_get_new_preview (viewable,
width, height);
gimage->comp_preview_valid = TRUE;
return gimage->comp_preview;
}
}
TempBuf *
gimp_image_get_new_preview (GimpViewable *viewable,
gint width,
gint height)
{
GimpImage *gimage;
GimpLayer *layer;
GimpLayer *floating_sel;
PixelRegion src1PR, src2PR, maskPR;
PixelRegion *mask;
TempBuf *comp;
TempBuf *layer_buf;
TempBuf *mask_buf;
GList *list;
GSList *reverse_list = NULL;
gdouble ratio;
gint x, y, w, h;
gint x1, y1, x2, y2;
gint bytes;
gboolean construct_flag;
gboolean visible_components[MAX_CHANNELS] = { TRUE, TRUE, TRUE, TRUE };
gint off_x, off_y;
gimage = GIMP_IMAGE (viewable);
if (! gimage->gimp->config->layer_previews)
return NULL;
ratio = (gdouble) width / (gdouble) gimage->width;
switch (gimp_image_base_type (gimage))
{
case GIMP_RGB:
case GIMP_INDEXED:
bytes = 4;
break;
case GIMP_GRAY:
bytes = 2;
break;
default:
bytes = 0;
g_assert_not_reached ();
break;
}
/* The construction buffer */
comp = temp_buf_new (width, height, bytes, 0, 0, NULL);
temp_buf_data_clear (comp);
floating_sel = NULL;
for (list = GIMP_LIST (gimage->layers)->list;
list;
list = g_list_next (list))
{
layer = (GimpLayer *) list->data;
/* only add layers that are visible to the list */
if (gimp_item_get_visible (GIMP_ITEM (layer)))
{
/* floating selections are added right above the layer
* they are attached to
*/
if (gimp_layer_is_floating_sel (layer))
{
floating_sel = layer;
}
else
{
if (floating_sel &&
floating_sel->fs.drawable == GIMP_DRAWABLE (layer))
{
reverse_list = g_slist_prepend (reverse_list, floating_sel);
}
reverse_list = g_slist_prepend (reverse_list, layer);
}
}
}
construct_flag = FALSE;
for (; reverse_list; reverse_list = g_slist_next (reverse_list))
{
gint src_x, src_y;
gint src_width, src_height;
gboolean use_sub_preview = FALSE;
layer = (GimpLayer *) reverse_list->data;
gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y);
if (! gimp_rectangle_intersect (0, 0,
gimp_item_width (GIMP_ITEM (layer)),
gimp_item_height (GIMP_ITEM (layer)),
-off_x, -off_y,
gimage->width, gimage->height,
&src_x, &src_y,
&src_width, &src_height))
{
continue;
}
x = (gint) RINT (ratio * off_x);
y = (gint) RINT (ratio * off_y);
w = (gint) RINT (ratio * gimp_item_width (GIMP_ITEM (layer)));
h = (gint) RINT (ratio * gimp_item_height (GIMP_ITEM (layer)));
if (w < 1 || h < 1)
continue;
if ((w * h) > (width * height * 4))
use_sub_preview = TRUE;
x1 = CLAMP (x, 0, width);
y1 = CLAMP (y, 0, height);
x2 = CLAMP (x + w, 0, width);
y2 = CLAMP (y + h, 0, height);
if (x2 == x1 || y2 == y1)
continue;
pixel_region_init_temp_buf (&src1PR, comp,
x1, y1, x2 - x1, y2 - y1);
if (use_sub_preview)
{
layer_buf = gimp_drawable_get_sub_preview (GIMP_DRAWABLE (layer),
src_x, src_y,
src_width, src_height,
(x2 - x1),
(y2 - y1));
g_assert (layer_buf);
g_assert (layer_buf->bytes <= comp->bytes);
pixel_region_init_temp_buf (&src2PR, layer_buf,
0, 0, src1PR.w, src1PR.h);
}
else
{
layer_buf = gimp_viewable_get_preview (GIMP_VIEWABLE (layer), w, h);
g_assert (layer_buf);
g_assert (layer_buf->bytes <= comp->bytes);
pixel_region_init_temp_buf (&src2PR, layer_buf,
x1 - x, y1 - y, src1PR.w, src1PR.h);
}
if (layer->mask && layer->mask->apply_mask)
{
if (use_sub_preview)
{
mask_buf =
gimp_drawable_get_sub_preview (GIMP_DRAWABLE (layer->mask),
src_x, src_y,
src_width, src_height,
x2 - x1,
y2 - y1);
pixel_region_init_temp_buf (&maskPR, mask_buf,
0, 0, src1PR.w, maskPR.h);
}
else
{
mask_buf = gimp_viewable_get_preview (GIMP_VIEWABLE (layer->mask),
w, h);
pixel_region_init_temp_buf (&maskPR, mask_buf,
x1 - x, y1 - y, src1PR.w, maskPR.h);
}
mask = &maskPR;
}
else
{
mask_buf = NULL;
mask = NULL;
}
/* Based on the type of the layer, project the layer onto the
* composite preview...
* Indexed images are actually already converted to RGB and RGBA,
* so just project them as if they were type "intensity"
* Send in all TRUE for visible since that info doesn't matter
* for previews
*/
if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
{
if (! construct_flag)
initial_region (&src2PR, &src1PR,
mask, NULL,
layer->opacity * 255.999,
layer->mode,
visible_components,
INITIAL_INTENSITY_ALPHA);
else
combine_regions (&src1PR, &src2PR, &src1PR,
mask, NULL,
layer->opacity * 255.999,
layer->mode,
visible_components,
COMBINE_INTEN_A_INTEN_A);
}
else
{
if (! construct_flag)
initial_region (&src2PR, &src1PR,
mask, NULL,
layer->opacity * 255.999,
layer->mode,
visible_components,
INITIAL_INTENSITY);
else
combine_regions (&src1PR, &src2PR, &src1PR,
mask, NULL,
layer->opacity * 255.999,
layer->mode,
visible_components,
COMBINE_INTEN_A_INTEN);
}
if (use_sub_preview)
{
temp_buf_free (layer_buf);
if (mask_buf)
temp_buf_free (mask_buf);
}
construct_flag = TRUE;
}
g_slist_free (reverse_list);
return comp;
}