1
0
mirror of https://github.com/aseprite/aseprite.git synced 2025-01-31 09:32:49 +00:00

Add support to flip irregular selections.

- Added raster::algorithm::flip_image_with_mask() function.
- Added UndoTransaction::flipImageWithMask() member function.
- Added Mask::isRectangular() member function.
This commit is contained in:
David Capello 2012-02-20 19:12:06 -03:00
parent cd8e92ce18
commit 3422b82bc4
7 changed files with 139 additions and 4 deletions

@ -18,6 +18,7 @@
#include "config.h"
#include "app.h"
#include "commands/command.h"
#include "commands/params.h"
#include "document_wrappers.h"
@ -25,6 +26,7 @@
#include "gui/list.h"
#include "modules/editors.h"
#include "modules/gui.h"
#include "raster/algorithm/flip_image.h"
#include "raster/cel.h"
#include "raster/image.h"
#include "raster/mask.h"
@ -100,6 +102,9 @@ void FlipCommand::onExecute(Context* context)
if (!image)
return;
Mask* mask = NULL;
bool alreadyFlipped = false;
// This variable will be the area to be flipped inside the image.
gfx::Rect bounds(gfx::Point(0, 0),
gfx::Size(image->w, image->h));
@ -108,7 +113,7 @@ void FlipCommand::onExecute(Context* context)
// selected region only. If the mask isn't visible, we flip the
// whole image.
if (document->isMaskVisible()) {
Mask* mask = document->getMask();
mask = document->getMask();
gfx::Rect maskBounds = mask->getBounds();
// Adjust the mask depending on the cel position.
@ -118,10 +123,39 @@ void FlipCommand::onExecute(Context* context)
// bounds, so we don't request to flip an area outside the
// image's bounds.
bounds = bounds.createIntersect(maskBounds);
// If the mask isn't a rectangular area, we've to flip the mask too.
if (mask->getBitmap() != NULL && !mask->isRectangular()) {
int bgcolor = app_get_color_to_clear_layer(sprite->getCurrentLayer());
// Flip the portion of image specified by the mask.
undoTransaction.flipImageWithMask(image, mask, m_flipType, bgcolor);
alreadyFlipped = true;
// Flip the mask.
Image* maskBitmap = mask->getBitmap();
if (maskBitmap != NULL) {
// Create a flipped copy of the current mask.
UniquePtr<Mask> newMask(new Mask(*mask));
newMask->freeze();
raster::algorithm::flip_image(newMask->getBitmap(),
gfx::Rect(gfx::Point(0, 0),
gfx::Size(maskBitmap->w, maskBitmap->h)),
m_flipType);
newMask->unfreeze();
// Change the current mask and generate the new boundaries.
undoTransaction.copyToCurrentMask(newMask);
document->generateMaskBoundaries();
}
}
}
// Flip the portion of image specified by "bounds" variable.
undoTransaction.flipImage(image, bounds, m_flipType);
if (!alreadyFlipped) {
undoTransaction.flipImage(image, bounds, m_flipType);
}
}
else {
// get all sprite cels

@ -20,12 +20,15 @@
#include "raster/algorithm/flip_image.h"
#include "base/unique_ptr.h"
#include "gfx/rect.h"
#include "raster/image.h"
#include "raster/mask.h"
#include <vector>
namespace raster { namespace algorithm {
namespace raster {
namespace algorithm {
void flip_image(Image* image, const gfx::Rect& bounds, FlipType flipType)
{
@ -62,4 +65,52 @@ void flip_image(Image* image, const gfx::Rect& bounds, FlipType flipType)
}
}
} }
void flip_image_with_mask(Image* image, const Mask* mask, FlipType flipType, int bgcolor)
{
gfx::Rect bounds = mask->getBounds();
switch (flipType) {
case FlipHorizontal: {
UniquePtr<Image> originalRow(Image::create(image->getPixelFormat(), mask->getBounds().w, 1));
for (int y=bounds.y; y<bounds.y+bounds.h; ++y) {
// Copy the current row.
image_copy(originalRow, image, -bounds.x, -y);
int u = bounds.x+bounds.w-1;
for (int x=bounds.x; x<bounds.x+bounds.w; ++x, --u) {
if (mask->containsPoint(x, y)) {
image_putpixel(image, u, y, image_getpixel(originalRow, x-bounds.x, 0));
if (!mask->containsPoint(u, y))
image_putpixel(image, x, y, bgcolor);
}
}
}
break;
}
case FlipVertical:{
UniquePtr<Image> originalCol(Image::create(image->getPixelFormat(), 1, mask->getBounds().h));
for (int x=bounds.x; x<bounds.x+bounds.w; ++x) {
// Copy the current column.
image_copy(originalCol, image, -x, -bounds.y);
int v = bounds.y+bounds.h-1;
for (int y=bounds.y; y<bounds.y+bounds.h; ++y, --v) {
if (mask->containsPoint(x, y)) {
image_putpixel(image, x, v, image_getpixel(originalCol, 0, y-bounds.y));
if (!mask->containsPoint(x, v))
image_putpixel(image, x, y, bgcolor);
}
}
}
break;
}
}
}
} // namespace algorithm
} // namespace raster

@ -23,12 +23,19 @@
#include "raster/algorithm/flip_type.h"
class Image;
class Mask;
namespace raster {
namespace algorithm {
// Flips the rectangular region specified in the "bounds" parameter.
void flip_image(Image* image, const gfx::Rect& bounds, FlipType flipType);
// Flips an irregular region specified by the "mask". The
// "bgcolor" is used to clear areas that aren't covered by a
// mirrored pixel.
void flip_image_with_mask(Image* image, const Mask* mask, FlipType flipType, int bgcolor);
}
}

@ -88,6 +88,26 @@ void Mask::unfreeze()
shrink();
}
bool Mask::isRectangular() const
{
if (!m_bitmap)
return false;
for (int y=0; y<m_bitmap->h; ++y) {
uint8_t* address = ((uint8_t**)m_bitmap->line)[y];
div_t d = div(0, 8);
for (int x=0; x<m_bitmap->w; ++x) {
if (((*address) & (1 << d.rem)) == 0)
return false;
_image_bitmap_next_bit(d, address);
}
}
return true;
}
void Mask::copyFrom(const Mask* sourceMask)
{
clear();

@ -74,6 +74,9 @@ public:
// Returns true if the mask is frozen (See freeze/unfreeze functions).
bool isFrozen() const { return m_freeze_count > 0; }
// Returns true if the mask is a rectangular region.
bool isRectangular() const;
// Clears the mask.
void clear();

@ -1065,6 +1065,25 @@ void UndoTransaction::flipImage(Image* image,
raster::algorithm::flip_image(image, bounds, flipType);
}
void UndoTransaction::flipImageWithMask(Image* image, const Mask* mask, raster::algorithm::FlipType flipType, int bgcolor)
{
UniquePtr<Image> flippedImage((Image::createCopy(image)));
// Flip the portion of the bitmap.
raster::algorithm::flip_image_with_mask(flippedImage, mask, flipType, bgcolor);
// Insert the undo operation.
if (isEnabled()) {
UniquePtr<Dirty> dirty((new Dirty(image, flippedImage)));
dirty->saveImagePixels(image);
m_undoHistory->pushUndoer(new undoers::DirtyArea(m_undoHistory->getObjects(), image, dirty));
}
// Copy the flipped image into the image specified as argument.
image_copy(image, flippedImage, 0, 0);
}
void UndoTransaction::pasteImage(const Image* src_image, int x, int y, int opacity)
{
const Layer* layer = m_sprite->getCurrentLayer();

@ -120,6 +120,7 @@ public:
Image* getCelImage(Cel* cel);
void clearMask(int bgcolor);
void flipImage(Image* image, const gfx::Rect& bounds, raster::algorithm::FlipType flipType);
void flipImageWithMask(Image* image, const Mask* mask, raster::algorithm::FlipType flipType, int bgcolor);
void pasteImage(const Image* src_image, int x, int y, int opacity);
// for mask