app: gimp_image_crop_auto_shrink() -> gimp_pickable_auto_shrink()

It was in gimpimag-crop only for historic reasons. Clean up API
and code to do simpler and more reasonable stuff.
This commit is contained in:
Michael Natterer 2012-09-22 14:56:52 +02:00
parent 07107fe214
commit c1811d6bdf
6 changed files with 364 additions and 317 deletions

View File

@ -315,6 +315,8 @@ libappcore_a_sources = \
gimppdbprogress.h \
gimppickable.c \
gimppickable.h \
gimppickable-auto-shrink.c \
gimppickable-auto-shrink.h \
gimpprogress.c \
gimpprogress.h \
gimpprojectable.c \

View File

@ -17,8 +17,6 @@
#include "config.h"
#include <string.h>
#include <gegl.h>
#include "core-types.h"
@ -33,38 +31,11 @@
#include "gimpimage-undo.h"
#include "gimpimage-undo-push.h"
#include "gimplayer.h"
#include "gimppickable.h"
#include "gimpsamplepoint.h"
#include "gimp-intl.h"
typedef enum
{
AUTO_CROP_NOTHING = 0,
AUTO_CROP_ALPHA = 1,
AUTO_CROP_COLOR = 2
} AutoCropType;
typedef AutoCropType (* ColorsEqualFunc) (guchar *col1,
guchar *col2);
/* local function prototypes */
static AutoCropType gimp_image_crop_guess_bgcolor (GimpPickable *pickable,
guchar *color,
gint x1,
gint x2,
gint y1,
gint y2);
static gint gimp_image_crop_colors_equal (guchar *col1,
guchar *col2);
static gint gimp_image_crop_colors_alpha (guchar *col1,
guchar *col2);
/* public functions */
void
@ -286,252 +257,3 @@ gimp_image_crop (GimpImage *image,
gimp_unset_busy (image->gimp);
}
gboolean
gimp_image_crop_auto_shrink (GimpImage *image,
gint x1,
gint y1,
gint x2,
gint y2,
gboolean active_drawable_only,
gint *shrunk_x1,
gint *shrunk_y1,
gint *shrunk_x2,
gint *shrunk_y2)
{
GimpDrawable *active_drawable = NULL;
GimpPickable *pickable;
GeglBuffer *buffer;
GeglRectangle rect;
ColorsEqualFunc colors_equal_func;
guchar bgcolor[MAX_CHANNELS] = { 0, 0, 0, 0 };
guchar *buf = NULL;
gint width, height;
const Babl *format;
gint x, y, abort;
gboolean retval = FALSE;
g_return_val_if_fail (image != NULL, FALSE);
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
g_return_val_if_fail (shrunk_x1 != NULL, FALSE);
g_return_val_if_fail (shrunk_y1 != NULL, FALSE);
g_return_val_if_fail (shrunk_x2 != NULL, FALSE);
g_return_val_if_fail (shrunk_y2 != NULL, FALSE);
gimp_set_busy (image->gimp);
/* You should always keep in mind that crop->tx2 and crop->ty2 are
the NOT the coordinates of the bottomright corner of the area to
be cropped. They point at the pixel located one to the right and
one to the bottom.
*/
if (active_drawable_only)
{
active_drawable = gimp_image_get_active_drawable (image);
if (! active_drawable)
goto FINISH;
pickable = GIMP_PICKABLE (active_drawable);
}
else
{
pickable = GIMP_PICKABLE (gimp_image_get_projection (image));
}
gimp_pickable_flush (pickable);
format = babl_format ("R'G'B'A u8");
switch (gimp_image_crop_guess_bgcolor (pickable, bgcolor,
x1, x2 - 1, y1, y2 - 1))
{
case AUTO_CROP_ALPHA:
colors_equal_func = (ColorsEqualFunc) gimp_image_crop_colors_alpha;
break;
case AUTO_CROP_COLOR:
colors_equal_func = (ColorsEqualFunc) gimp_image_crop_colors_equal;
break;
default:
goto FINISH;
break;
}
width = x2 - x1;
height = y2 - y1;
buffer = gimp_pickable_get_buffer (pickable);
/* The following could be optimized further by processing
* the smaller side first instead of defaulting to width --Sven
*/
buf = g_malloc (MAX (width, height) * 4);
/* Check how many of the top lines are uniform/transparent. */
rect.x = x1;
rect.y = 0;
rect.width = width;
rect.height = 1;
abort = FALSE;
for (y = y1; y < y2 && !abort; y++)
{
rect.y = y;
gegl_buffer_get (buffer, &rect, 1.0, format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (x = 0; x < width && !abort; x++)
abort = ! colors_equal_func (bgcolor, buf + x * 4);
}
if (y == y2 && !abort)
goto FINISH;
y1 = y - 1;
/* Check how many of the bottom lines are uniform/transparent. */
rect.x = x1;
rect.y = 0;
rect.width = width;
rect.height = 1;
abort = FALSE;
for (y = y2; y > y1 && !abort; y--)
{
rect.y = y - 1;
gegl_buffer_get (buffer, &rect, 1.0, format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (x = 0; x < width && !abort; x++)
abort = ! colors_equal_func (bgcolor, buf + x * 4);
}
y2 = y + 1;
/* compute a new height for the next operations */
height = y2 - y1;
/* Check how many of the left lines are uniform/transparent. */
rect.x = 0;
rect.y = y1;
rect.width = 1;
rect.height = height;
abort = FALSE;
for (x = x1; x < x2 && !abort; x++)
{
rect.x = x;
gegl_buffer_get (buffer, &rect, 1.0, format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (y = 0; y < height && !abort; y++)
abort = ! colors_equal_func (bgcolor, buf + y * 4);
}
x1 = x - 1;
/* Check how many of the right lines are uniform/transparent. */
rect.x = 0;
rect.y = y1;
rect.width = 1;
rect.height = height;
abort = FALSE;
for (x = x2; x > x1 && !abort; x--)
{
rect.x = x - 1;
gegl_buffer_get (buffer, &rect, 1.0, format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (y = 0; y < height && !abort; y++)
abort = ! colors_equal_func (bgcolor, buf + y * 4);
}
x2 = x + 1;
*shrunk_x1 = x1;
*shrunk_y1 = y1;
*shrunk_x2 = x2;
*shrunk_y2 = y2;
retval = TRUE;
FINISH:
g_free (buf);
gimp_unset_busy (image->gimp);
return retval;
}
/* private functions */
static AutoCropType
gimp_image_crop_guess_bgcolor (GimpPickable *pickable,
guchar *color,
gint x1,
gint x2,
gint y1,
gint y2)
{
const Babl *format = babl_format ("R'G'B'A u8");
guchar tl[4];
guchar tr[4];
guchar bl[4];
guchar br[4];
gint i;
for (i = 0; i < 4; i++)
color[i] = 0;
/* First check if there's transparency to crop. If not, guess the
* background-color to see if at least 2 corners are equal.
*/
if (! gimp_pickable_get_pixel_at (pickable, x1, y1, format, tl) ||
! gimp_pickable_get_pixel_at (pickable, x1, y2, format, tr) ||
! gimp_pickable_get_pixel_at (pickable, x2, y1, format, bl) ||
! gimp_pickable_get_pixel_at (pickable, x2, y2, format, br))
{
return AUTO_CROP_NOTHING;
}
if ((tl[ALPHA] == 0 && tr[ALPHA] == 0) ||
(tl[ALPHA] == 0 && bl[ALPHA] == 0) ||
(tr[ALPHA] == 0 && br[ALPHA] == 0) ||
(bl[ALPHA] == 0 && br[ALPHA] == 0))
{
return AUTO_CROP_ALPHA;
}
if (gimp_image_crop_colors_equal (tl, tr) ||
gimp_image_crop_colors_equal (tl, bl))
{
memcpy (color, tl, 4);
return AUTO_CROP_COLOR;
}
if (gimp_image_crop_colors_equal (br, bl) ||
gimp_image_crop_colors_equal (br, tr))
{
memcpy (color, br, 4);
return AUTO_CROP_COLOR;
}
return AUTO_CROP_NOTHING;
}
static int
gimp_image_crop_colors_equal (guchar *col1,
guchar *col2)
{
gint b;
for (b = 0; b < 4; b++)
{
if (col1[b] != col2[b])
return FALSE;
}
return TRUE;
}
static gboolean
gimp_image_crop_colors_alpha (guchar *dummy,
guchar *col)
{
return (col[ALPHA] == 0);
}

View File

@ -28,16 +28,5 @@ void gimp_image_crop (GimpImage *image,
gboolean active_layer_only,
gboolean crop_layers);
gboolean gimp_image_crop_auto_shrink (GimpImage *image,
gint x1,
gint y1,
gint x2,
gint y2,
gboolean active_drawable_only,
gint *shrunk_x1,
gint *shrunk_y1,
gint *shrunk_x2,
gint *shrunk_y2);
#endif /* __GIMP_IMAGE_CROP_H__ */

View File

@ -0,0 +1,300 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include "core-types.h"
#include "gimp.h"
#include "gimpimage.h"
#include "gimppickable.h"
#include "gimppickable-auto-shrink.h"
typedef enum
{
AUTO_SHRINK_NOTHING = 0,
AUTO_SHRINK_ALPHA = 1,
AUTO_SHRINK_COLOR = 2
} AutoShrinkType;
typedef gboolean (* ColorsEqualFunc) (guchar *col1,
guchar *col2);
/* local function prototypes */
static AutoShrinkType gimp_pickable_guess_bgcolor (GimpPickable *pickable,
guchar *color,
gint x1,
gint x2,
gint y1,
gint y2);
static gboolean gimp_pickable_colors_equal (guchar *col1,
guchar *col2);
static gboolean gimp_pickable_colors_alpha (guchar *col1,
guchar *col2);
/* public functions */
gboolean
gimp_pickable_auto_shrink (GimpPickable *pickable,
gint start_x1,
gint start_y1,
gint start_x2,
gint start_y2,
gint *shrunk_x1,
gint *shrunk_y1,
gint *shrunk_x2,
gint *shrunk_y2)
{
GeglBuffer *buffer;
GeglRectangle rect;
ColorsEqualFunc colors_equal_func;
guchar bgcolor[MAX_CHANNELS] = { 0, 0, 0, 0 };
guchar *buf = NULL;
gint x1, y1, x2, y2;
gint width, height;
const Babl *format;
gint x, y, abort;
gboolean retval = FALSE;
g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), FALSE);
g_return_val_if_fail (shrunk_x1 != NULL, FALSE);
g_return_val_if_fail (shrunk_y1 != NULL, FALSE);
g_return_val_if_fail (shrunk_x2 != NULL, FALSE);
g_return_val_if_fail (shrunk_y2 != NULL, FALSE);
gimp_set_busy (gimp_pickable_get_image (pickable)->gimp);
/* You should always keep in mind that x2 and y2 are the NOT the
* coordinates of the bottomright corner of the area to be
* cropped. They point at the pixel located one to the right and one
* to the bottom.
*/
gimp_pickable_flush (pickable);
buffer = gimp_pickable_get_buffer (pickable);
x1 = MAX (start_x1, 0);
y1 = MAX (start_y1, 0);
x2 = MIN (start_x2, gegl_buffer_get_width (buffer));
y2 = MIN (start_y2, gegl_buffer_get_height (buffer));
format = babl_format ("R'G'B'A u8");
switch (gimp_pickable_guess_bgcolor (pickable, bgcolor,
x1, x2 - 1, y1, y2 - 1))
{
case AUTO_SHRINK_ALPHA:
colors_equal_func = gimp_pickable_colors_alpha;
break;
case AUTO_SHRINK_COLOR:
colors_equal_func = gimp_pickable_colors_equal;
break;
default:
goto FINISH;
break;
}
width = x2 - x1;
height = y2 - y1;
/* The following could be optimized further by processing
* the smaller side first instead of defaulting to width --Sven
*/
buf = g_malloc (MAX (width, height) * 4);
/* Check how many of the top lines are uniform/transparent. */
rect.x = x1;
rect.y = 0;
rect.width = width;
rect.height = 1;
abort = FALSE;
for (y = y1; y < y2 && !abort; y++)
{
rect.y = y;
gegl_buffer_get (buffer, &rect, 1.0, format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (x = 0; x < width && !abort; x++)
abort = ! colors_equal_func (bgcolor, buf + x * 4);
}
if (y == y2 && !abort)
goto FINISH;
y1 = y - 1;
/* Check how many of the bottom lines are uniform/transparent. */
rect.x = x1;
rect.y = 0;
rect.width = width;
rect.height = 1;
abort = FALSE;
for (y = y2; y > y1 && !abort; y--)
{
rect.y = y - 1;
gegl_buffer_get (buffer, &rect, 1.0, format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (x = 0; x < width && !abort; x++)
abort = ! colors_equal_func (bgcolor, buf + x * 4);
}
y2 = y + 1;
/* compute a new height for the next operations */
height = y2 - y1;
/* Check how many of the left lines are uniform/transparent. */
rect.x = 0;
rect.y = y1;
rect.width = 1;
rect.height = height;
abort = FALSE;
for (x = x1; x < x2 && !abort; x++)
{
rect.x = x;
gegl_buffer_get (buffer, &rect, 1.0, format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (y = 0; y < height && !abort; y++)
abort = ! colors_equal_func (bgcolor, buf + y * 4);
}
x1 = x - 1;
/* Check how many of the right lines are uniform/transparent. */
rect.x = 0;
rect.y = y1;
rect.width = 1;
rect.height = height;
abort = FALSE;
for (x = x2; x > x1 && !abort; x--)
{
rect.x = x - 1;
gegl_buffer_get (buffer, &rect, 1.0, format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
for (y = 0; y < height && !abort; y++)
abort = ! colors_equal_func (bgcolor, buf + y * 4);
}
x2 = x + 1;
FINISH:
if (x1 != start_x1 || y1 != start_y1 ||
x2 != start_x2 || y2 != start_y2)
{
*shrunk_x1 = x1;
*shrunk_y1 = y1;
*shrunk_x2 = x2;
*shrunk_y2 = y2;
retval = TRUE;
}
g_free (buf);
gimp_unset_busy (gimp_pickable_get_image (pickable)->gimp);
return retval;
}
/* private functions */
static AutoShrinkType
gimp_pickable_guess_bgcolor (GimpPickable *pickable,
guchar *color,
gint x1,
gint x2,
gint y1,
gint y2)
{
const Babl *format = babl_format ("R'G'B'A u8");
guchar tl[4];
guchar tr[4];
guchar bl[4];
guchar br[4];
gint i;
for (i = 0; i < 4; i++)
color[i] = 0;
/* First check if there's transparency to crop. If not, guess the
* background-color to see if at least 2 corners are equal.
*/
if (! gimp_pickable_get_pixel_at (pickable, x1, y1, format, tl) ||
! gimp_pickable_get_pixel_at (pickable, x1, y2, format, tr) ||
! gimp_pickable_get_pixel_at (pickable, x2, y1, format, bl) ||
! gimp_pickable_get_pixel_at (pickable, x2, y2, format, br))
{
return AUTO_SHRINK_NOTHING;
}
if ((tl[ALPHA] == 0 && tr[ALPHA] == 0) ||
(tl[ALPHA] == 0 && bl[ALPHA] == 0) ||
(tr[ALPHA] == 0 && br[ALPHA] == 0) ||
(bl[ALPHA] == 0 && br[ALPHA] == 0))
{
return AUTO_SHRINK_ALPHA;
}
if (gimp_pickable_colors_equal (tl, tr) ||
gimp_pickable_colors_equal (tl, bl))
{
memcpy (color, tl, 4);
return AUTO_SHRINK_COLOR;
}
if (gimp_pickable_colors_equal (br, bl) ||
gimp_pickable_colors_equal (br, tr))
{
memcpy (color, br, 4);
return AUTO_SHRINK_COLOR;
}
return AUTO_SHRINK_NOTHING;
}
static gboolean
gimp_pickable_colors_equal (guchar *col1,
guchar *col2)
{
gint b;
for (b = 0; b < 4; b++)
{
if (col1[b] != col2[b])
return FALSE;
}
return TRUE;
}
static gboolean
gimp_pickable_colors_alpha (guchar *dummy,
guchar *col)
{
return (col[ALPHA] == 0);
}

View File

@ -0,0 +1,33 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GIMP_PICKABLE_AUTO_SHRINK_H__
#define __GIMP_PICKABLE_AUTO_SHRINK_H__
gboolean gimp_pickable_auto_shrink (GimpPickable *pickable,
gint x1,
gint y1,
gint x2,
gint y2,
gint *shrunk_x1,
gint *shrunk_y1,
gint *shrunk_x2,
gint *shrunk_y2);
#endif /* __GIMP_PICKABLE_AUTO_SHRINK_H__ */

View File

@ -34,10 +34,10 @@
#include "core/gimp.h"
#include "core/gimpchannel.h"
#include "core/gimpcontext.h"
#include "core/gimpimage-crop.h"
#include "core/gimpimage.h"
#include "core/gimpmarshal.h"
#include "core/gimppickable.h"
#include "core/gimppickable-auto-shrink.h"
#include "core/gimptoolinfo.h"
#include "widgets/gimpwidgets-utils.h"
@ -2502,6 +2502,7 @@ gimp_rectangle_tool_auto_shrink (GimpRectangleTool *rect_tool)
GimpRectangleToolPrivate *private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
GimpDisplay *display = tool->display;
GimpImage *image;
GimpPickable *pickable;
gint offset_x = 0;
gint offset_y = 0;
gint x1, y1;
@ -2523,30 +2524,30 @@ gimp_rectangle_tool_auto_shrink (GimpRectangleTool *rect_tool)
if (shrink_merged)
{
x1 = MAX (private->x1, 0);
y1 = MAX (private->y1, 0);
x2 = MIN (private->x2, gimp_image_get_width (image));
y2 = MIN (private->y2, gimp_image_get_height (image));
pickable = GIMP_PICKABLE (gimp_image_get_projection (image));
x1 = private->x1;
y1 = private->y1;
x2 = private->x2;
y2 = private->y2;
}
else
{
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
GimpItem *item = GIMP_ITEM (drawable);
pickable = GIMP_PICKABLE (gimp_image_get_active_drawable (image));
if (! drawable)
if (! pickable)
return;
gimp_item_get_offset (item, &offset_x, &offset_y);
gimp_item_get_offset (GIMP_ITEM (pickable), &offset_x, &offset_y);
x1 = MAX (private->x1 - offset_x, 0);
y1 = MAX (private->y1 - offset_y, 0);
x2 = MIN (private->x2 - offset_x, gimp_item_get_width (item));
y2 = MIN (private->y2 - offset_y, gimp_item_get_height (item));
x1 = private->x1 - offset_x;
y1 = private->y1 - offset_y;
x2 = private->x2 - offset_x;
y2 = private->y2 - offset_y;
}
if (gimp_image_crop_auto_shrink (image,
if (gimp_pickable_auto_shrink (pickable,
x1, y1, x2, y2,
! shrink_merged,
&shrunk_x1,
&shrunk_y1,
&shrunk_x2,