Add ExpandCelCanvas to simplify ToolLoopImpl implementation.

This commit is contained in:
David Capello 2011-10-29 20:50:18 -03:00
parent 1dcb42a10a
commit b9b93e6869
4 changed files with 316 additions and 171 deletions

View File

@ -293,6 +293,7 @@ add_library(aseprite-library
util/celmove.cpp
util/clipboard.cpp
util/col_file.cpp
util/expand_cel_canvas.cpp
util/filetoks.cpp
util/misc.cpp
util/msk_file.cpp

View File

@ -0,0 +1,221 @@
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2011 David Capello
*
* 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 "util/expand_cel_canvas.h"
#include "base/unique_ptr.h"
#include "document.h"
#include "raster/cel.h"
#include "raster/dirty.h"
#include "raster/layer.h"
#include "raster/sprite.h"
#include "raster/stock.h"
#include "undo/undo_history.h"
#include "undoers/add_cel.h"
#include "undoers/add_image.h"
#include "undoers/close_group.h"
#include "undoers/dirty_area.h"
#include "undoers/open_group.h"
#include "undoers/replace_image.h"
#include "undoers/set_cel_position.h"
ExpandCelCanvas::ExpandCelCanvas(Document* document, Sprite* sprite, Layer* layer, TiledMode tiledMode)
: m_document(document)
, m_sprite(sprite)
, m_layer(layer)
, m_cel(NULL)
, m_celImage(NULL)
, m_celCreated(false)
, m_closed(false)
, m_committed(false)
{
if (m_layer->is_image()) {
m_cel = static_cast<LayerImage*>(layer)->getCel(m_sprite->getCurrentFrame());
if (m_cel)
m_celImage = m_sprite->getStock()->getImage(m_cel->getImage());
}
// If there is no Cel
if (m_cel == NULL) {
// Create the image
m_celImage = image_new(m_sprite->getImgType(), m_sprite->getWidth(), m_sprite->getHeight());
image_clear(m_celImage,
m_celImage->mask_color);
// create the cel
m_cel = new Cel(m_sprite->getCurrentFrame(), 0);
static_cast<LayerImage*>(m_layer)->addCel(m_cel);
m_celCreated = true;
}
m_originalCelX = m_cel->getX();
m_originalCelY = m_cel->getY();
// region to draw
int x1, y1, x2, y2;
if (tiledMode == TILED_NONE) { // non-tiled
x1 = MIN(m_cel->getX(), 0);
y1 = MIN(m_cel->getY(), 0);
x2 = MAX(m_cel->getX()+m_celImage->w, m_sprite->getWidth());
y2 = MAX(m_cel->getY()+m_celImage->h, m_sprite->getHeight());
}
else { // tiled
x1 = 0;
y1 = 0;
x2 = m_sprite->getWidth();
y2 = m_sprite->getHeight();
}
// create two copies of the image region which we'll modify with the tool
m_srcImage = image_crop(m_celImage,
x1-m_cel->getX(),
y1-m_cel->getY(), x2-x1, y2-y1,
m_celImage->mask_color);
m_dstImage = image_new_copy(m_srcImage);
// We have to adjust the cel position to match the m_dstImage
// position (the new m_dstImage will be used in RenderEngine to
// draw this cel).
m_cel->setPosition(x1, y1);
}
ExpandCelCanvas::~ExpandCelCanvas()
{
try {
if (!m_committed && !m_closed)
rollback();
}
catch (...) {
// Do nothing
}
delete m_srcImage;
delete m_dstImage;
}
void ExpandCelCanvas::commit()
{
ASSERT(!m_closed);
ASSERT(!m_committed);
undo::UndoHistory* undo = m_document->getUndoHistory();
// If the size of each image is the same, we can create an undo
// with only the differences between both images.
if (m_cel->getX() == m_originalCelX &&
m_cel->getY() == m_originalCelY &&
m_celImage->w == m_dstImage->w &&
m_celImage->h == m_dstImage->h) {
// Was m_celImage created in the start of the tool-loop?.
if (m_celCreated) {
// We can keep the m_celImage
// We copy the destination image to the m_celImage
image_copy(m_celImage, m_dstImage, 0, 0);
// Add the m_celImage in the images stock of the sprite.
m_cel->setImage(m_sprite->getStock()->addImage(m_celImage));
// Is the undo enabled?.
if (undo->isEnabled()) {
// We can temporary remove the cel.
static_cast<LayerImage*>(m_sprite->getCurrentLayer())->removeCel(m_cel);
// We create the undo information (for the new m_celImage
// in the stock and the new cel in the layer)...
undo->pushUndoer(new undoers::OpenGroup());
undo->pushUndoer(new undoers::AddImage(undo->getObjects(),
m_sprite->getStock(), m_cel->getImage()));
undo->pushUndoer(new undoers::AddCel(undo->getObjects(),
m_sprite->getCurrentLayer(), m_cel));
undo->pushUndoer(new undoers::CloseGroup());
// And finally we add the cel again in the layer.
static_cast<LayerImage*>(m_sprite->getCurrentLayer())->addCel(m_cel);
}
}
// If the m_celImage was already created before the whole process...
else {
// Add to the undo history the differences between m_celImage and m_dstImage
if (undo->isEnabled()) {
UniquePtr<Dirty> dirty(new Dirty(m_celImage, m_dstImage));
dirty->saveImagePixels(m_celImage);
if (dirty != NULL)
undo->pushUndoer(new undoers::DirtyArea(undo->getObjects(), m_celImage, dirty));
}
// Copy the destination to the cel image.
image_copy(m_celImage, m_dstImage, 0, 0);
}
}
// If the size of both images are different, we have to
// replace the entire image.
else {
if (undo->isEnabled()) {
undo->pushUndoer(new undoers::OpenGroup());
if (m_cel->getX() != m_originalCelX ||
m_cel->getY() != m_originalCelY) {
int x = m_cel->getX();
int y = m_cel->getY();
m_cel->setPosition(m_originalCelX, m_originalCelY);
undo->pushUndoer(new undoers::SetCelPosition(undo->getObjects(), m_cel));
m_cel->setPosition(x, y);
}
undo->pushUndoer(new undoers::ReplaceImage(undo->getObjects(),
m_sprite->getStock(), m_cel->getImage()));
undo->pushUndoer(new undoers::CloseGroup());
}
// Replace the image in the stock.
m_sprite->getStock()->replaceImage(m_cel->getImage(), m_dstImage);
// Destroy the old cel image.
image_free(m_celImage);
// Now the m_dstImage is used, so we haven't to destroy it.
m_dstImage = NULL;
}
m_committed = true;
}
void ExpandCelCanvas::rollback()
{
ASSERT(!m_closed);
ASSERT(!m_committed);
// Here we destroy the temporary 'cel' created and restore all as it was before
m_cel->setPosition(m_originalCelX, m_originalCelY);
if (m_celCreated) {
static_cast<LayerImage*>(m_layer)->removeCel(m_cel);
delete m_cel;
delete m_celImage;
}
m_closed = true;
}

View File

@ -0,0 +1,80 @@
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2011 David Capello
*
* 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
*/
#ifndef UTIL_EXPAND_CEL_CANVAS_H_INCLUDED
#define UTIL_EXPAND_CEL_CANVAS_H_INCLUDED
#include "filters/tiled_mode.h"
class Cel;
class Document;
class Image;
class Layer;
class Sprite;
// This class can be used to expand the canvas of the current cel to
// the visible portion of sprite. If the user cancels the operation,
// you've a rollback() method to restore the cel to its original
// state. If all changes are committed, some undo information is
// stored in the document's UndoHistory to go back to the original
// state using "Undo" command.
class ExpandCelCanvas
{
public:
ExpandCelCanvas(Document* document, Sprite* sprite, Layer* layer, TiledMode tiledMode);
~ExpandCelCanvas();
// Commit changes made in getDestCanvas() in the cel's image. Adds
// information in the undo history so the user can undo the
// modifications in the canvas.
void commit();
// Restore the cel as its original state as when ExpandCelCanvas()
// was created.
void rollback();
// You can read pixels from here
Image* getSourceCanvas() { // TODO this should be "const"
return m_srcImage;
}
// You can write pixels right here
Image* getDestCanvas() {
return m_dstImage;
}
const Cel* getCel() const {
return m_cel;
}
private:
Document* m_document;
Sprite* m_sprite;
Layer* m_layer;
Cel* m_cel;
Image* m_celImage;
bool m_celCreated;
int m_originalCelX;
int m_originalCelY;
Image* m_srcImage;
Image* m_dstImage;
bool m_closed;
bool m_committed;
};
#endif

View File

@ -23,32 +23,22 @@
#include "app.h"
#include "app/color.h"
#include "app/color_utils.h"
#include "base/unique_ptr.h"
#include "context.h"
#include "gui/gui.h"
#include "modules/editors.h"
#include "raster/cel.h"
#include "raster/dirty.h"
#include "raster/image.h"
#include "raster/layer.h"
#include "raster/mask.h"
#include "raster/pen.h"
#include "raster/sprite.h"
#include "raster/stock.h"
#include "settings/settings.h"
#include "tools/ink.h"
#include "tools/tool.h"
#include "tools/tool_loop.h"
#include "undo/undo_history.h"
#include "undoers/add_cel.h"
#include "undoers/add_image.h"
#include "undoers/close_group.h"
#include "undoers/dirty_area.h"
#include "undoers/open_group.h"
#include "undoers/replace_image.h"
#include "undoers/set_cel_position.h"
#include "widgets/editor/editor.h"
#include "util/expand_cel_canvas.h"
#include "widgets/color_bar.h"
#include "widgets/editor/editor.h"
#include "widgets/statebar.h"
#include <allegro.h>
@ -63,17 +53,11 @@ class ToolLoopImpl : public tools::ToolLoop
Sprite* m_sprite;
Layer* m_layer;
Cel* m_cel;
Image* m_cel_image;
bool m_cel_created;
int m_old_cel_x;
int m_old_cel_y;
bool m_filled;
bool m_previewFilled;
int m_sprayWidth;
int m_spraySpeed;
TiledMode m_tiled_mode;
Image* m_src_image;
Image* m_dst_image;
bool m_useMask;
Mask* m_mask;
gfx::Point m_maskOrigin;
@ -85,6 +69,7 @@ class ToolLoopImpl : public tools::ToolLoop
tools::ToolLoop::Button m_button;
int m_primary_color;
int m_secondary_color;
ExpandCelCanvas m_expandCelCanvas;
public:
ToolLoopImpl(Editor* editor,
@ -102,19 +87,16 @@ public:
, m_document(document)
, m_sprite(sprite)
, m_layer(layer)
, m_cel(NULL)
, m_cel_image(NULL)
, m_cel_created(false)
, m_canceled(false)
, m_tiled_mode(m_context->getSettings()->getTiledMode())
, m_button(button)
, m_primary_color(color_utils::color_for_layer(primary_color, layer))
, m_secondary_color(color_utils::color_for_layer(secondary_color, layer))
, m_expandCelCanvas(document, sprite, layer, m_tiled_mode)
{
// Settings
ISettings* settings = m_context->getSettings();
m_tiled_mode = settings->getTiledMode();
switch (tool->getFill(m_button)) {
case tools::FillNone:
m_filled = false;
@ -139,54 +121,6 @@ public:
pen_settings->getSize(),
pen_settings->getAngle());
// Get cel and image where we can draw
if (m_layer->is_image()) {
m_cel = static_cast<LayerImage*>(sprite->getCurrentLayer())->getCel(sprite->getCurrentFrame());
if (m_cel)
m_cel_image = sprite->getStock()->getImage(m_cel->getImage());
}
if (m_cel == NULL) {
// create the image
m_cel_image = image_new(sprite->getImgType(), sprite->getWidth(), sprite->getHeight());
image_clear(m_cel_image,
m_cel_image->mask_color);
// create the cel
m_cel = new Cel(sprite->getCurrentFrame(), 0);
static_cast<LayerImage*>(sprite->getCurrentLayer())->addCel(m_cel);
m_cel_created = true;
}
m_old_cel_x = m_cel->getX();
m_old_cel_y = m_cel->getY();
// region to draw
int x1, y1, x2, y2;
// non-tiled
if (m_tiled_mode == TILED_NONE) {
x1 = MIN(m_cel->getX(), 0);
y1 = MIN(m_cel->getY(), 0);
x2 = MAX(m_cel->getX()+m_cel_image->w, m_sprite->getWidth());
y2 = MAX(m_cel->getY()+m_cel_image->h, m_sprite->getHeight());
}
else { // tiled
x1 = 0;
y1 = 0;
x2 = m_sprite->getWidth();
y2 = m_sprite->getHeight();
}
// create two copies of the image region which we'll modify with the tool
m_src_image = image_crop(m_cel_image,
x1-m_cel->getX(),
y1-m_cel->getY(), x2-x1, y2-y1,
m_cel_image->mask_color);
m_dst_image = image_new_copy(m_src_image);
m_useMask = m_document->isMaskVisible();
// Selection ink
@ -195,6 +129,9 @@ public:
m_document->setMask(&emptyMask);
}
int x1 = m_expandCelCanvas.getCel()->getX();
int y1 = m_expandCelCanvas.getCel()->getY();
m_mask = m_document->getMask();
m_maskOrigin = (!m_mask->is_empty() ? gfx::Point(m_mask->x-x1, m_mask->y-y1):
gfx::Point(0, 0));
@ -204,9 +141,6 @@ public:
m_speed.x = 0;
m_speed.y = 0;
// we have to modify the cel position because it's used in the
// `render_sprite' routine to draw the `dst_image'
m_cel->setPosition(x1, y1);
m_offset.x = -x1;
m_offset.y = -y1;
@ -227,112 +161,21 @@ public:
~ToolLoopImpl()
{
if (!m_canceled) {
undo::UndoHistory* undo = m_document->getUndoHistory();
// Paint ink
if (getInk()->isPaint()) {
// If the size of each image is the same, we can create an
// undo with only the differences between both images.
if (m_cel->getX() == m_old_cel_x &&
m_cel->getY() == m_old_cel_y &&
m_cel_image->w == m_dst_image->w &&
m_cel_image->h == m_dst_image->h) {
// Was the 'cel_image' created in the start of the tool-loop?.
if (m_cel_created) {
// Then we can keep the 'cel_image'...
// We copy the 'destination' image to the 'cel_image'.
image_copy(m_cel_image, m_dst_image, 0, 0);
// Add the 'cel_image' in the images' stock of the sprite.
m_cel->setImage(m_sprite->getStock()->addImage(m_cel_image));
// Is the undo enabled?.
if (undo->isEnabled()) {
// We can temporary remove the cel.
static_cast<LayerImage*>(m_sprite->getCurrentLayer())->removeCel(m_cel);
// We create the undo information (for the new cel_image
// in the stock and the new cel in the layer)...
undo->pushUndoer(new undoers::OpenGroup());
undo->pushUndoer(new undoers::AddImage(undo->getObjects(),
m_sprite->getStock(), m_cel->getImage()));
undo->pushUndoer(new undoers::AddCel(undo->getObjects(),
m_sprite->getCurrentLayer(), m_cel));
undo->pushUndoer(new undoers::CloseGroup());
// And finally we add the cel again in the layer.
static_cast<LayerImage*>(m_sprite->getCurrentLayer())->addCel(m_cel);
}
}
else {
// Undo the dirty region.
if (undo->isEnabled()) {
UniquePtr<Dirty> dirty(new Dirty(m_cel_image, m_dst_image));
dirty->saveImagePixels(m_cel_image);
if (dirty != NULL)
undo->pushUndoer(new undoers::DirtyArea(undo->getObjects(),
m_cel_image, dirty));
}
// Copy the 'dst_image' to the cel_image.
image_copy(m_cel_image, m_dst_image, 0, 0);
}
}
// If the size of both images are different, we have to
// replace the entire image.
else {
if (undo->isEnabled()) {
undo->pushUndoer(new undoers::OpenGroup());
if (m_cel->getX() != m_old_cel_x ||
m_cel->getY() != m_old_cel_y) {
int x = m_cel->getX();
int y = m_cel->getY();
m_cel->setPosition(m_old_cel_x, m_old_cel_y);
undo->pushUndoer(new undoers::SetCelPosition(undo->getObjects(), m_cel));
m_cel->setPosition(x, y);
}
undo->pushUndoer(new undoers::ReplaceImage(undo->getObjects(),
m_sprite->getStock(), m_cel->getImage()));
undo->pushUndoer(new undoers::CloseGroup());
}
// Replace the image in the stock.
m_sprite->getStock()->replaceImage(m_cel->getImage(), m_dst_image);
// Destroy the old cel image.
image_free(m_cel_image);
// Now the `dst_image' is used, so we haven't to destroy it.
m_dst_image = NULL;
}
m_expandCelCanvas.commit();
}
// Selection ink
if (getInk()->isSelection())
else if (getInk()->isSelection()) {
m_document->generateMaskBoundaries();
}
}
// If the trace was not canceled or it is not a 'paint' ink...
if (m_canceled || !getInk()->isPaint()) {
// Here we destroy the temporary 'cel' created and restore all as it was before
m_cel->setPosition(m_old_cel_x, m_old_cel_y);
if (m_cel_created) {
static_cast<LayerImage*>(m_layer)->removeCel(m_cel);
delete m_cel;
delete m_cel_image;
}
m_expandCelCanvas.rollback();
}
delete m_src_image;
delete m_dst_image;
delete m_pen;
}
@ -343,8 +186,8 @@ public:
Document* getDocument() { return m_document; }
Sprite* getSprite() { return m_sprite; }
Layer* getLayer() { return m_layer; }
Image* getSrcImage() { return m_src_image; }
Image* getDstImage() { return m_dst_image; }
Image* getSrcImage() { return m_expandCelCanvas.getSourceCanvas(); }
Image* getDstImage() { return m_expandCelCanvas.getDestCanvas(); }
bool useMask() { return m_useMask; }
Mask* getMask() { return m_mask; }
gfx::Point getMaskOrigin() { return m_maskOrigin; }