Fix issue #106 - Trim is not working for certain images.

Two pixels with alpha=0 should be treated as the same color (even when
they RGB values are different).
This commit is contained in:
David Capello 2012-05-19 15:52:55 -03:00
parent fddb323fdd
commit 608a139449
5 changed files with 93 additions and 64 deletions

View File

@ -91,7 +91,7 @@ protected:
AutocropSpriteCommand::AutocropSpriteCommand() AutocropSpriteCommand::AutocropSpriteCommand()
: Command("AutocropSprite", : Command("AutocropSprite",
"Autocrop Sprite", "Trim Sprite",
CmdRecordableFlag) CmdRecordableFlag)
{ {
} }
@ -109,8 +109,8 @@ void AutocropSpriteCommand::onExecute(Context* context)
{ {
int bgcolor = color_utils::color_for_image(app_get_colorbar()->getBgColor(), sprite->getPixelFormat()); int bgcolor = color_utils::color_for_image(app_get_colorbar()->getBgColor(), sprite->getPixelFormat());
UndoTransaction undoTransaction(document, "Sprite Autocrop"); UndoTransaction undoTransaction(document, "Trim Sprite");
undoTransaction.autocropSprite(bgcolor); undoTransaction.trimSprite(bgcolor);
undoTransaction.commit(); undoTransaction.commit();
} }
document->generateMaskBoundaries(); document->generateMaskBoundaries();

View File

@ -634,46 +634,85 @@ int image_count_diff(const Image* i1, const Image* i2)
return diff; return diff;
} }
bool image_shrink_rect(Image *image, int *x1, int *y1, int *x2, int *y2, int refpixel) static bool is_same_pixel(PixelFormat pixelFormat, int pixel1, int pixel2)
{ {
#define SHRINK_SIDE(u_begin, u_op, u_final, u_add, \ switch (pixelFormat) {
v_begin, v_op, v_final, v_add, U, V, var) \ case IMAGE_RGB:
do { \ if (_rgba_geta(pixel1) == 0 && _rgba_geta(pixel2) == 0)
for (u = u_begin; u u_op u_final; u u_add) { \ return true;
for (v = v_begin; v v_op v_final; v v_add) { \ break;
if (image->getpixel(U, V) != refpixel) \ case IMAGE_GRAYSCALE:
break; \ if (_graya_geta(pixel1) == 0 && _graya_geta(pixel2) == 0)
} \ return true;
if (v == v_final) \ break;
var; \ }
else \ return pixel1 == pixel2;
break; \ }
} \
} while (0)
bool image_shrink_rect(Image *image, gfx::Rect& bounds, int refpixel)
{
bool shrink;
int u, v; int u, v;
*x1 = 0; bounds = gfx::Rect(0, 0, image->w, image->h);
*y1 = 0;
*x2 = image->w-1;
*y2 = image->h-1;
SHRINK_SIDE(0, <, image->w, ++, // Shrink left side
0, <, image->h, ++, u, v, (*x1)++); for (u=bounds.x; u<bounds.x+bounds.w; ++u) {
shrink = true;
for (v=bounds.y; v<bounds.y+bounds.h; ++v) {
if (!is_same_pixel(image->getPixelFormat(), image->getpixel(u, v), refpixel)) {
shrink = false;
break;
}
}
if (!shrink)
break;
++bounds.x;
--bounds.w;
}
SHRINK_SIDE(0, <, image->h, ++, // Shrink right side
0, <, image->w, ++, v, u, (*y1)++); for (u=bounds.x+bounds.w-1; u>=bounds.x; --u) {
shrink = true;
for (v=bounds.y; v<bounds.y+bounds.h; ++v) {
if (!is_same_pixel(image->getPixelFormat(), image->getpixel(u, v), refpixel)) {
shrink = false;
break;
}
}
if (!shrink)
break;
--bounds.w;
}
SHRINK_SIDE(image->w-1, >, 0, --, // Shrink top side
0, <, image->h, ++, u, v, (*x2)--); for (v=bounds.y; v<bounds.y+bounds.h; ++v) {
shrink = true;
for (u=bounds.x; u<bounds.x+bounds.w; ++u) {
if (!is_same_pixel(image->getPixelFormat(), image->getpixel(u, v), refpixel)) {
shrink = false;
break;
}
}
if (!shrink)
break;
++bounds.y;
--bounds.h;
}
SHRINK_SIDE(image->h-1, >, 0, --, // Shrink bottom side
0, <, image->w, ++, v, u, (*y2)--); for (v=bounds.y+bounds.h-1; v>=bounds.y; --v) {
shrink = true;
for (u=bounds.x; u<bounds.x+bounds.w; ++u) {
if (!is_same_pixel(image->getPixelFormat(), image->getpixel(u, v), refpixel)) {
shrink = false;
break;
}
}
if (!shrink)
break;
--bounds.h;
}
if ((*x1 > *x2) || (*y1 > *y2)) return (!bounds.isEmpty());
return false;
else
return true;
#undef SHRINK_SIDE
} }

View File

@ -19,14 +19,16 @@
#ifndef RASTER_IMAGE_H_INCLUDED #ifndef RASTER_IMAGE_H_INCLUDED
#define RASTER_IMAGE_H_INCLUDED #define RASTER_IMAGE_H_INCLUDED
#include "gfx/rect.h"
#include "raster/blend.h" #include "raster/blend.h"
#include "raster/gfxobj.h" #include "raster/gfxobj.h"
#include "raster/pixel_format.h" #include "raster/pixel_format.h"
#include <allegro/color.h> #include <allegro/color.h>
class Palette; struct BITMAP;
class Palette;
class Pen; class Pen;
class RgbMap; class RgbMap;
@ -35,8 +37,6 @@ enum ResizeMethod {
RESIZE_METHOD_BILINEAR, RESIZE_METHOD_BILINEAR,
}; };
struct BITMAP;
class Image : public GfxObj class Image : public GfxObj
{ {
public: public:
@ -98,7 +98,7 @@ void image_to_allegro(const Image* image, BITMAP* bmp, int x, int y, const Palet
void image_fixup_transparent_colors(Image* image); void image_fixup_transparent_colors(Image* image);
void image_resize(const Image* src, Image* dst, ResizeMethod method, const Palette* palette, const RgbMap* rgbmap); void image_resize(const Image* src, Image* dst, ResizeMethod method, const Palette* palette, const RgbMap* rgbmap);
int image_count_diff(const Image* i1, const Image* i2); int image_count_diff(const Image* i1, const Image* i2);
bool image_shrink_rect(Image *image, int *x1, int *y1, int *x2, int *y2, int refpixel); bool image_shrink_rect(Image *image, gfx::Rect& bounds, int refpixel);
#include "raster/image_traits.h" #include "raster/image_traits.h"

View File

@ -202,42 +202,32 @@ void UndoTransaction::cropSprite(const gfx::Rect& bounds, int bgcolor)
m_document->getMask()->getBounds().y-bounds.y); m_document->getMask()->getBounds().y-bounds.y);
} }
void UndoTransaction::autocropSprite(int bgcolor) void UndoTransaction::trimSprite(int bgcolor)
{ {
int old_frame = m_sprite->getCurrentFrame(); int old_frame = m_sprite->getCurrentFrame();
int x1, y1, x2, y2; gfx::Rect bounds;
int u1, v1, u2, v2;
x1 = y1 = INT_MAX; UniquePtr<Image> image_wrap(Image::create(m_sprite->getPixelFormat(),
x2 = y2 = INT_MIN; m_sprite->getWidth(),
m_sprite->getHeight()));
Image* image = Image::create(m_sprite->getPixelFormat(), Image* image = image_wrap.get();
m_sprite->getWidth(),
m_sprite->getHeight());
for (int frame=0; frame<m_sprite->getTotalFrames(); ++frame) { for (int frame=0; frame<m_sprite->getTotalFrames(); ++frame) {
image->clear(0);
m_sprite->setCurrentFrame(frame); m_sprite->setCurrentFrame(frame);
m_sprite->render(image, 0, 0); m_sprite->render(image, 0, 0);
// TODO configurable (what color pixel to use as "refpixel", // TODO configurable (what color pixel to use as "refpixel",
// here we are using the top-left pixel by default) // here we are using the top-left pixel by default)
if (image_shrink_rect(image, &u1, &v1, &u2, &v2, gfx::Rect frameBounds;
image_getpixel(image, 0, 0))) { if (image_shrink_rect(image, frameBounds, image_getpixel(image, 0, 0)))
x1 = MIN(x1, u1); bounds = bounds.createUnion(frameBounds);
y1 = MIN(y1, v1);
x2 = MAX(x2, u2);
y2 = MAX(y2, v2);
}
} }
m_sprite->setCurrentFrame(old_frame); m_sprite->setCurrentFrame(old_frame);
image_free(image); if (!bounds.isEmpty())
cropSprite(bounds, bgcolor);
// do nothing
if (x1 > x2 || y1 > y2)
return;
cropSprite(gfx::Rect(x1, y1, x2-x1+1, y2-y1+1), bgcolor);
} }
void UndoTransaction::setPixelFormat(PixelFormat newFormat, DitheringMethod dithering_method) void UndoTransaction::setPixelFormat(PixelFormat newFormat, DitheringMethod dithering_method)

View File

@ -79,7 +79,7 @@ public:
void setCurrentLayer(Layer* layer); void setCurrentLayer(Layer* layer);
void setSpriteSize(int w, int h); void setSpriteSize(int w, int h);
void cropSprite(const gfx::Rect& bounds, int bgcolor); void cropSprite(const gfx::Rect& bounds, int bgcolor);
void autocropSprite(int bgcolor); void trimSprite(int bgcolor);
void setPixelFormat(PixelFormat newFormat, DitheringMethod dithering_method); void setPixelFormat(PixelFormat newFormat, DitheringMethod dithering_method);
// for images in stock // for images in stock