diff --git a/src/commands/cmd_flip.cpp b/src/commands/cmd_flip.cpp index eb0cfabac..a7244605b 100644 --- a/src/commands/cmd_flip.cpp +++ b/src/commands/cmd_flip.cpp @@ -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 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 diff --git a/src/raster/algorithm/flip_image.cpp b/src/raster/algorithm/flip_image.cpp index f0c8ed69b..4a36c5459 100644 --- a/src/raster/algorithm/flip_image.cpp +++ b/src/raster/algorithm/flip_image.cpp @@ -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 -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 originalRow(Image::create(image->getPixelFormat(), mask->getBounds().w, 1)); + + for (int y=bounds.y; ycontainsPoint(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 originalCol(Image::create(image->getPixelFormat(), 1, mask->getBounds().h)); + + for (int x=bounds.x; xcontainsPoint(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 diff --git a/src/raster/algorithm/flip_image.h b/src/raster/algorithm/flip_image.h index 1dcc73c4f..613ce0b90 100644 --- a/src/raster/algorithm/flip_image.h +++ b/src/raster/algorithm/flip_image.h @@ -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); + } } diff --git a/src/raster/mask.cpp b/src/raster/mask.cpp index 52bc245d9..5b097794c 100644 --- a/src/raster/mask.cpp +++ b/src/raster/mask.cpp @@ -88,6 +88,26 @@ void Mask::unfreeze() shrink(); } +bool Mask::isRectangular() const +{ + if (!m_bitmap) + return false; + + for (int y=0; yh; ++y) { + uint8_t* address = ((uint8_t**)m_bitmap->line)[y]; + div_t d = div(0, 8); + + for (int x=0; xw; ++x) { + if (((*address) & (1 << d.rem)) == 0) + return false; + + _image_bitmap_next_bit(d, address); + } + } + + return true; +} + void Mask::copyFrom(const Mask* sourceMask) { clear(); diff --git a/src/raster/mask.h b/src/raster/mask.h index 878510e71..888bc3cae 100644 --- a/src/raster/mask.h +++ b/src/raster/mask.h @@ -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(); diff --git a/src/undo_transaction.cpp b/src/undo_transaction.cpp index c7d53a571..974ccbf07 100644 --- a/src/undo_transaction.cpp +++ b/src/undo_transaction.cpp @@ -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 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((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(); diff --git a/src/undo_transaction.h b/src/undo_transaction.h index 84d893531..e62a57181 100644 --- a/src/undo_transaction.h +++ b/src/undo_transaction.h @@ -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