File: expand_cel_canvas.cpp

package info (click to toggle)
aseprite 1.0.5+ds-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 9,504 kB
  • ctags: 18,296
  • sloc: cpp: 84,144; ansic: 49,119; xml: 1,971; objc: 1,211; asm: 117; makefile: 45
file content (265 lines) | stat: -rw-r--r-- 7,473 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/* Aseprite
 * Copyright (C) 2001-2013  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
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "app/util/expand_cel_canvas.h"

#include "app/app.h"
#include "app/context.h"
#include "app/document.h"
#include "app/document_location.h"
#include "app/undo_transaction.h"
#include "app/undoers/add_cel.h"
#include "app/undoers/add_image.h"
#include "app/undoers/dirty_area.h"
#include "app/undoers/replace_image.h"
#include "app/undoers/set_cel_position.h"
#include "base/unique_ptr.h"
#include "raster/cel.h"
#include "raster/dirty.h"
#include "raster/layer.h"
#include "raster/primitives.h"
#include "raster/sprite.h"
#include "raster/stock.h"

namespace {

static raster::ImageBufferPtr src_buffer;
static raster::ImageBufferPtr dst_buffer;

static void destroy_buffers()
{
  src_buffer.reset(NULL);
  dst_buffer.reset(NULL);
}

static void create_buffers()
{
  if (!src_buffer) {
    app::App::instance()->Exit.connect(&destroy_buffers);

    src_buffer.reset(new raster::ImageBuffer(1));
    dst_buffer.reset(new raster::ImageBuffer(1));
  }
}

}

namespace app {

ExpandCelCanvas::ExpandCelCanvas(Context* context, TiledMode tiledMode, UndoTransaction& undo)
  : m_cel(NULL)
  , m_celImage(NULL)
  , m_celCreated(false)
  , m_closed(false)
  , m_committed(false)
  , m_undo(undo)
{
  create_buffers();

  DocumentLocation location = context->activeLocation();
  m_document = location.document();
  m_sprite = location.sprite();
  m_layer = location.layer();

  if (m_layer->isImage()) {
    m_cel = static_cast<LayerImage*>(m_layer)->getCel(location.frame());
    if (m_cel)
      m_celImage = m_cel->image();
  }

  // If there is no Cel
  if (m_cel == NULL) {
    // Create the image
    m_celImage = Image::create(m_sprite->pixelFormat(),
      m_sprite->width(),
      m_sprite->height());

    color_t bg = m_sprite->transparentColor();
    m_celImage->setMaskColor(bg);
    clear_image(m_celImage, bg);

    // Create the cel
    m_cel = new Cel(location.frame(), 0);
    static_cast<LayerImage*>(m_layer)->addCel(m_cel);

    m_celCreated = true;
  }

  m_originalCelX = m_cel->x();
  m_originalCelY = m_cel->y();

  // Region to draw
  gfx::Rect celBounds(
    m_cel->x(),
    m_cel->y(),
    m_celImage->width(),
    m_celImage->height());

  gfx::Rect spriteBounds(0, 0,
    m_sprite->width(),
    m_sprite->height());

  gfx::Rect bounds;

  if (tiledMode == TILED_NONE) { // Non-tiled
    bounds = celBounds.createUnion(spriteBounds);
  }
  else {                        // Tiled
    bounds = spriteBounds;
  }

  // create two copies of the image region which we'll modify with the tool
  m_srcImage = crop_image(m_celImage,
    bounds.x - celBounds.x,
    bounds.y - celBounds.y,
    bounds.w,
    bounds.h,
    m_sprite->transparentColor(),
    src_buffer);

  m_dstImage = Image::createCopy(m_srcImage, dst_buffer);

  // 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(bounds.x, bounds.y);
}

ExpandCelCanvas::~ExpandCelCanvas()
{
  try {
    if (!m_committed && !m_closed)
      rollback();
  }
  catch (...) {
    // Do nothing
  }
  delete m_srcImage;
  delete m_dstImage;
}

void ExpandCelCanvas::commit(const gfx::Rect& bounds)
{
  ASSERT(!m_closed);
  ASSERT(!m_committed);

  // If the size of each image is the same, we can create an undo
  // with only the differences between both images.
  if (m_cel->x() == m_originalCelX &&
      m_cel->y() == m_originalCelY &&
      m_celImage->width() == m_dstImage->width() &&
      m_celImage->height() == m_dstImage->height()) {
    // 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
      copy_image(m_celImage, m_dstImage, 0, 0);

      // Add the m_celImage in the images stock of the sprite.
      m_cel->setImage(m_sprite->stock()->addImage(m_celImage));

      // Is the undo enabled?.
      if (m_undo.isEnabled()) {
        // We can temporary remove the cel.
        static_cast<LayerImage*>(m_layer)->removeCel(m_cel);

        // We create the undo information (for the new m_celImage
        // in the stock and the new cel in the layer)...
        m_undo.pushUndoer(new undoers::AddImage(m_undo.getObjects(),
            m_sprite->stock(), m_cel->imageIndex()));
        m_undo.pushUndoer(new undoers::AddCel(m_undo.getObjects(),
            m_layer, m_cel));

        // And finally we add the cel again in the layer.
        static_cast<LayerImage*>(m_layer)->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 (m_undo.isEnabled()) {
        gfx::Rect dirtyBounds;
        if (bounds.isEmpty())
          dirtyBounds = m_celImage->bounds();
        else
          dirtyBounds = m_celImage->bounds().createIntersect(
            gfx::Rect(bounds).offset(-m_originalCelX, -m_originalCelY));

        base::UniquePtr<Dirty> dirty(new Dirty(m_celImage, m_dstImage, dirtyBounds));

        dirty->saveImagePixels(m_celImage);
        if (dirty != NULL)
          m_undo.pushUndoer(new undoers::DirtyArea(m_undo.getObjects(), m_celImage, dirty));
      }

      // Copy the destination to the cel image.
      copy_image(m_celImage, m_dstImage, 0, 0);
    }
  }
  // If the size of both images are different, we have to
  // replace the entire image.
  else {
    if (m_undo.isEnabled()) {
      if (m_cel->x() != m_originalCelX ||
          m_cel->y() != m_originalCelY) {
        int newX = m_cel->x();
        int newY = m_cel->y();
        m_cel->setPosition(m_originalCelX, m_originalCelY);
        m_undo.pushUndoer(new undoers::SetCelPosition(m_undo.getObjects(), m_cel));
        m_cel->setPosition(newX, newY);
      }

      m_undo.pushUndoer(new undoers::ReplaceImage(m_undo.getObjects(),
          m_sprite->stock(), m_cel->imageIndex()));
    }

    // Replace the image in the stock. We need to create a copy of
    // image because m_dstImage's ImageBuffer cannot be shared.
    m_sprite->stock()->replaceImage(m_cel->imageIndex(),
      Image::createCopy(m_dstImage));

    // Destroy the old cel image.
    delete m_celImage;
  }

  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;
}

} // namespace app