Merge branch 'feature/continuous-cels'

This commit is contained in:
David Capello 2015-02-09 11:42:01 -03:00
commit 52003da721
71 changed files with 1771 additions and 626 deletions

View File

@ -43,6 +43,7 @@
# Refactoring
* Replace get_unique_cels() with CelsRange
* Make one level of layers (folders should modify only timeline/UI)
* Convert doc::PixelFormat to a enum class
* Add doc::Spec with width/height/channels/ColorMode/ncolors

View File

@ -648,6 +648,7 @@
<item command="CelProperties" text="&amp;Properties..." />
<separator />
<item command="ClearCel" text="&amp;Clear" />
<item command="UnlinkCel" text="&amp;Unlink" />
</menu>
<menu id="cel_movement_popup">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -296,6 +296,10 @@
<part id="timeline_open_padlock_active" x="252" y="36" w="12" h="12" />
<part id="timeline_closed_padlock_normal" x="240" y="48" w="12" h="12" />
<part id="timeline_closed_padlock_active" x="252" y="48" w="12" h="12" />
<part id="timeline_continuous_normal" x="276" y="36" w="12" h="12" />
<part id="timeline_continuous_active" x="288" y="36" w="12" h="12" />
<part id="timeline_discontinuous_normal" x="276" y="48" w="12" h="12" />
<part id="timeline_discontinuous_active" x="288" y="48" w="12" h="12" />
<part id="timeline_empty_frame_normal" x="240" y="60" w="12" h="12" />
<part id="timeline_empty_frame_active" x="252" y="60" w="12" h="12" />
<part id="timeline_keyframe_normal" x="240" y="72" w="12" h="12" />
@ -306,6 +310,9 @@
<part id="timeline_fromright_active" x="252" y="96" w="12" h="12" />
<part id="timeline_fromboth_normal" x="240" y="108" w="12" h="12" />
<part id="timeline_fromboth_active" x="252" y="108" w="12" h="12" />
<part id="timeline_leftlink_active" x="264" y="84" w="12" h="12" />
<part id="timeline_bothlinks_active" x="264" y="96" w="12" h="12" />
<part id="timeline_rightlink_active" x="264" y="108" w="12" h="12" />
<part id="timeline_gear" x="264" y="12" w="12" h="12" />
<part id="timeline_gear_active" x="264" y="24" w="12" h="12" />
<part id="timeline_onionskin" x="264" y="36" w="12" h="12" />
@ -434,6 +441,20 @@
<icon part="timeline_closed_padlock_active" />
</style>
<!-- timeline_continuous -->
<style id="timeline_continuous" base="timeline_box">
<icon part="timeline_continuous_normal" />
</style>
<style id="timeline_continuous:active">
<icon part="timeline_continuous_active" />
</style>
<style id="timeline_discontinuous" base="timeline_box">
<icon part="timeline_discontinuous_normal" />
</style>
<style id="timeline_discontinuous:active">
<icon part="timeline_discontinuous_active" />
</style>
<!-- timeline_layer -->
<style id="timeline_layer" base="timeline_box">
<text align="left" valign="middle" padding-left="4" />
@ -482,6 +503,16 @@
<icon part="timeline_fromboth_active" />
</style>
<style id="timeline_leftlink">
<icon part="timeline_leftlink_active" />
</style>
<style id="timeline_rightlink">
<icon part="timeline_rightlink_active" />
</style>
<style id="timeline_bothlinks">
<icon part="timeline_bothlinks_active" />
</style>
<!-- timeline_gear -->
<style id="timeline_gear" base="timeline_box">
<icon part="timeline_gear" />

View File

@ -119,6 +119,7 @@ Layer Chunk (0x2004)
2 = Editable
4 = Lock movement
8 = Background
16 = Prefer linked cels
WORD Layer type (0=normal (image) layer, 1=layer set)
WORD Layer child level (see NOTE.1)
WORD Default layer width in pixels (ignored)

View File

@ -75,13 +75,13 @@ add_library(app-lib
cmd/layer_from_background.cpp
cmd/move_cel.cpp
cmd/move_layer.cpp
cmd/object_io.cpp
cmd/remove_cel.cpp
cmd/remove_frame.cpp
cmd/remove_layer.cpp
cmd/remove_palette.cpp
cmd/replace_image.cpp
cmd/reselect_mask.cpp
cmd/set_cel_data.cpp
cmd/set_cel_frame.cpp
cmd/set_cel_opacity.cpp
cmd/set_cel_position.cpp
@ -95,6 +95,7 @@ add_library(app-lib
cmd/set_sprite_size.cpp
cmd/set_total_frames.cpp
cmd/set_transparent_color.cpp
cmd/unlink_cel.cpp
cmd/with_cel.cpp
cmd/with_document.cpp
cmd/with_image.cpp
@ -185,6 +186,7 @@ add_library(app-lib
commands/cmd_switch_colors.cpp
commands/cmd_timeline.cpp
commands/cmd_undo.cpp
commands/cmd_unlink_cel.cpp
commands/cmd_zoom.cpp
commands/command.cpp
commands/commands.cpp

View File

@ -22,15 +22,21 @@
#include "app/cmd/add_cel.h"
#include "app/cmd/object_io.h"
#include "base/serialization.h"
#include "doc/cel.h"
#include "doc/cel_io.h"
#include "doc/cel_data_io.h"
#include "doc/document.h"
#include "doc/document_event.h"
#include "doc/image_io.h"
#include "doc/layer.h"
#include "doc/subobjects_io.h"
namespace app {
namespace cmd {
using namespace base::serialization;
using namespace base::serialization::little_endian;
using namespace doc;
AddCel::AddCel(Layer* layer, Cel* cel)
@ -52,7 +58,14 @@ void AddCel::onUndo()
Layer* layer = this->layer();
Cel* cel = this->cel();
ObjectIO(layer->sprite()).write_cel(m_stream, cel);
// Save the CelData only if the cel isn't linked
bool has_data = (cel->links() == 0);
write8(m_stream, has_data ? 1: 0);
if (has_data) {
write_image(m_stream, cel->image());
write_celdata(m_stream, cel->data());
}
write_cel(m_stream, cel);
removeCel(layer, cel);
}
@ -60,7 +73,17 @@ void AddCel::onUndo()
void AddCel::onRedo()
{
Layer* layer = this->layer();
Cel* cel = ObjectIO(layer->sprite()).read_cel(m_stream);
SubObjectsIO io(layer->sprite());
bool has_data = (read8(m_stream) != 0);
if (has_data) {
ImageRef image(read_image(m_stream));
io.addImageRef(image);
CelDataRef celdata(read_celdata(m_stream, &io));
io.addCelDataRef(celdata);
}
Cel* cel = read_cel(m_stream, &io);
addCel(layer, cel);

View File

@ -22,11 +22,11 @@
#include "app/cmd/add_layer.h"
#include "app/cmd/object_io.h"
#include "doc/layer.h"
#include "doc/document.h"
#include "doc/document_event.h"
#include "doc/layer.h"
#include "doc/layer_io.h"
#include "doc/subobjects_io.h"
namespace app {
namespace cmd {
@ -54,7 +54,7 @@ void AddLayer::onUndo()
Layer* folder = m_folder.layer();
Layer* layer = m_newLayer.layer();
ObjectIO(folder->sprite()).write_layer(m_stream, layer);
write_layer(m_stream, layer);
removeLayer(folder, layer);
}
@ -62,7 +62,8 @@ void AddLayer::onUndo()
void AddLayer::onRedo()
{
Layer* folder = m_folder.layer();
Layer* newLayer = ObjectIO(folder->sprite()).read_layer(m_stream);
SubObjectsIO io(folder->sprite());
Layer* newLayer = read_layer(m_stream, &io);
Layer* afterThis = m_afterThis.layer();
addLayer(folder, newLayer, afterThis);

View File

@ -27,6 +27,8 @@
#include "app/cmd/clear_cel.h"
#include "app/cmd/copy_rect.h"
#include "app/cmd/remove_cel.h"
#include "app/cmd/set_cel_data.h"
#include "app/cmd/unlink_cel.h"
#include "app/document.h"
#include "doc/cel.h"
#include "doc/layer.h"
@ -70,8 +72,11 @@ void CopyCel::onExecute()
// Clear destination cel if it does exist. It'll be overriden by the
// copy of srcCel.
if (dstCel)
if (dstCel) {
if (dstCel->links())
executeAndAdd(new cmd::UnlinkCel(dstCel));
executeAndAdd(new cmd::ClearCel(dstCel));
}
// Add empty frames until newFrame
while (dstSprite->totalFrames() <= m_dstFrame)
@ -83,35 +88,41 @@ void CopyCel::onExecute()
if (dstCel)
dstImage = dstCel->imageRef();
if (dstLayer->isBackground()) {
if (srcCel) {
ASSERT(dstImage);
if (dstImage) {
int blend = (srcLayer->isBackground() ?
BLEND_MODE_COPY: BLEND_MODE_NORMAL);
bool createLink =
(srcLayer == dstLayer && dstLayer->isContinuous());
ImageRef tmp(Image::createCopy(dstImage));
render::composite_image(tmp, srcImage,
srcCel->x(), srcCel->y(), 255, blend);
executeAndAdd(new cmd::CopyRect(dstImage, tmp, gfx::Clip(tmp->bounds())));
}
// For background layer
if (dstLayer->isBackground()) {
ASSERT(dstCel);
ASSERT(dstImage);
if (!dstCel || !dstImage ||
!srcCel || !srcImage)
return;
if (createLink) {
executeAndAdd(new cmd::SetCelData(dstCel, srcCel->dataRef()));
}
else {
ASSERT(dstCel);
if (dstCel)
executeAndAdd(new cmd::ClearCel(dstCel));
int blend = (srcLayer->isBackground() ?
BLEND_MODE_COPY: BLEND_MODE_NORMAL);
ImageRef tmp(Image::createCopy(dstImage));
render::composite_image(tmp, srcImage,
srcCel->x(), srcCel->y(), 255, blend);
executeAndAdd(new cmd::CopyRect(dstImage, tmp, gfx::Clip(tmp->bounds())));
}
}
// For transparent layers
else {
if (dstCel)
executeAndAdd(new cmd::RemoveCel(dstCel));
if (srcCel) {
dstImage.reset(Image::createCopy(srcImage));
dstCel = new Cel(*srcCel);
if (createLink)
dstCel = Cel::createLink(srcCel);
else
dstCel = Cel::createCopy(srcCel);
dstCel->setFrame(m_dstFrame);
dstCel->setImage(dstImage);
executeAndAdd(new cmd::AddCel(dstLayer, dstCel));
}

View File

@ -70,6 +70,8 @@ void FlattenLayers::onExecute()
clear_image(image, bgcolor);
render.renderSprite(image, sprite, frame);
// TODO Keep cel links when possible
ImageRef cel_image;
Cel* cel = background->cel(frame);
if (cel) {

View File

@ -28,7 +28,9 @@
#include "app/cmd/clear_image.h"
#include "app/cmd/copy_rect.h"
#include "app/cmd/remove_cel.h"
#include "app/cmd/set_cel_data.h"
#include "app/cmd/set_cel_frame.h"
#include "app/cmd/unlink_cel.h"
#include "app/document.h"
#include "doc/cel.h"
#include "doc/layer.h"
@ -72,8 +74,11 @@ void MoveCel::onExecute()
// Clear destination cel if it does exist. It'll be overriden by the
// copy of srcCel.
if (dstCel)
if (dstCel) {
if (dstCel->links())
executeAndAdd(new cmd::UnlinkCel(dstCel));
executeAndAdd(new cmd::ClearCel(dstCel));
}
// Add empty frames until newFrame
while (dstSprite->totalFrames() <= m_dstFrame)
@ -85,49 +90,47 @@ void MoveCel::onExecute()
if (dstCel)
dstImage = dstCel->imageRef();
if (srcCel) {
if (srcLayer == dstLayer) {
if (dstLayer->isBackground()) {
ASSERT(dstImage);
if (dstImage) {
int blend = (srcLayer->isBackground() ?
BLEND_MODE_COPY: BLEND_MODE_NORMAL);
bool createLink =
(srcLayer == dstLayer && dstLayer->isContinuous());
ImageRef tmp(Image::createCopy(dstImage));
render::composite_image(tmp, srcImage,
srcCel->x(), srcCel->y(), 255, blend);
executeAndAdd(new cmd::CopyRect(dstImage, tmp, gfx::Clip(tmp->bounds())));
}
// For background layer
if (dstLayer->isBackground()) {
ASSERT(dstCel);
ASSERT(dstImage);
if (!dstCel || !dstImage ||
!srcCel || !srcImage)
return;
executeAndAdd(new cmd::ClearImage(srcImage,
static_cast<app::Document*>(srcSprite->document())
->bgColor(srcLayer)));
}
// Move the cel in the same layer.
else {
executeAndAdd(new cmd::SetCelFrame(srcCel, m_dstFrame));
}
if (createLink) {
executeAndAdd(new cmd::SetCelData(dstCel, srcCel->dataRef()));
executeAndAdd(new cmd::UnlinkCel(srcCel));
}
// Move the cel between different layers.
else {
if (!dstCel) {
dstImage.reset(Image::createCopy(srcImage));
int blend = (srcLayer->isBackground() ?
BLEND_MODE_COPY: BLEND_MODE_NORMAL);
dstCel = new Cel(*srcCel);
dstCel->setFrame(m_dstFrame);
dstCel->setImage(dstImage);
}
ImageRef tmp(Image::createCopy(dstImage));
render::composite_image(tmp, srcImage,
srcCel->x(), srcCel->y(), 255, blend);
executeAndAdd(new cmd::CopyRect(dstImage, tmp, gfx::Clip(tmp->bounds())));
}
executeAndAdd(new cmd::ClearCel(srcCel));
}
// For transparent layers
else if (srcCel) {
ASSERT(!dstCel);
if (dstCel)
return;
if (dstLayer->isBackground()) {
ImageRef tmp(Image::createCopy(dstImage));
render::composite_image(tmp, srcImage,
srcCel->x(), srcCel->y(), 255, BLEND_MODE_NORMAL);
executeAndAdd(new cmd::CopyRect(dstImage, tmp, gfx::Clip(tmp->bounds())));
}
else {
executeAndAdd(new cmd::AddCel(dstLayer, dstCel));
}
// Move the cel in the same layer.
if (srcLayer == dstLayer) {
executeAndAdd(new cmd::SetCelFrame(srcCel, m_dstFrame));
}
else {
dstCel = Cel::createCopy(srcCel);
dstCel->setFrame(m_dstFrame);
executeAndAdd(new cmd::AddCel(dstLayer, dstCel));
executeAndAdd(new cmd::ClearCel(srcCel));
}
}

View File

@ -1,85 +0,0 @@
/* Aseprite
* Copyright (C) 2001-2015 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/cmd/object_io.h"
#include "doc/cel.h"
#include "doc/cel_io.h"
#include "doc/image.h"
#include "doc/image_io.h"
#include "doc/layer.h"
#include "doc/layer_io.h"
namespace app {
namespace cmd {
using namespace doc;
ObjectIO::ObjectIO(Sprite* sprite)
: m_sprite(sprite)
{
}
ObjectIO::~ObjectIO()
{
}
void ObjectIO::write_cel(std::ostream& os, Cel* cel)
{
write_object(os, cel, [this](std::ostream& os, Cel* cel) {
doc::write_cel(os, this, cel);
});
}
void ObjectIO::write_image(std::ostream& os, Image* image)
{
write_object(os, image, doc::write_image);
}
void ObjectIO::write_layer(std::ostream& os, Layer* layer)
{
write_object(os, layer, [this](std::ostream& os, Layer* layer) {
doc::write_layer(os, this, layer);
});
}
Cel* ObjectIO::read_cel(std::istream& is)
{
return read_object<Cel>(is, [this](std::istream& is) {
return doc::read_cel(is, this, m_sprite);
});
}
Image* ObjectIO::read_image(std::istream& is)
{
return read_object<Image>(is, doc::read_image);
}
Layer* ObjectIO::read_layer(std::istream& is)
{
return read_object<Layer>(is, [this](std::istream& is) {
return doc::read_layer(is, this, m_sprite);
});
}
} // namespace cmd
} // namespace app

View File

@ -1,92 +0,0 @@
/* Aseprite
* Copyright (C) 2001-2015 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 APP_CMD_OBJECT_IO_H_INCLUDED
#define APP_CMD_OBJECT_IO_H_INCLUDED
#pragma once
#include "base/serialization.h"
#include "base/unique_ptr.h"
#include "doc/object_id.h"
#include "doc/subobjects_io.h"
namespace doc {
class Cel;
class Image;
class Layer;
class Sprite;
}
namespace app {
namespace cmd {
using namespace doc;
class ObjectIO : public SubObjectsIO {
public:
ObjectIO(Sprite* sprite);
virtual ~ObjectIO();
// How to write cels, images, and sub-layers
void write_cel(std::ostream& os, Cel* cel) override;
void write_image(std::ostream& os, Image* image) override;
void write_layer(std::ostream& os, Layer* layer) override;
// How to read cels, images, and sub-layers
Cel* read_cel(std::istream& is) override;
Image* read_image(std::istream& is) override;
Layer* read_layer(std::istream& is) override;
private:
// read_object and write_object functions can be used to serialize an
// object into a stream, and restore it back into the memory with the
// same ID which were assigned in the ObjectsContainer previously.
// Serializes the given object into the stream identifying it with an
// ID from the ObjectsContainer. When the object is deserialized with
// read_object, the object is added to the container with the same ID.
template<class T, class Writer>
void write_object(std::ostream& os, T* object, Writer writer)
{
using base::serialization::little_endian::write32;
write32(os, object->id()); // Write the ID
writer(os, object); // Write the object
}
// Deserializes the given object from the stream, adding the object
// into the ObjectsContainer with the same ID saved with write_object().
template<class T, class Reader>
T* read_object(std::istream& is, Reader reader)
{
using base::serialization::little_endian::read32;
doc::ObjectId objectId = read32(is); // Read the ID
base::UniquePtr<T> object(reader(is)); // Read the object
object->setId(objectId);
return object.release();
}
Sprite* m_sprite;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -23,6 +23,7 @@
#include "app/cmd/remove_frame.h"
#include "app/cmd/remove_cel.h"
#include "doc/cels_range.h"
#include "doc/document.h"
#include "doc/document_event.h"
#include "doc/sprite.h"

View File

@ -22,11 +22,11 @@
#include "app/cmd/replace_image.h"
#include "app/cmd/object_io.h"
#include "doc/image.h"
#include "doc/image_io.h"
#include "doc/image_ref.h"
#include "doc/sprite.h"
#include "doc/subobjects_io.h"
namespace app {
namespace cmd {
@ -46,7 +46,7 @@ void ReplaceImage::onExecute()
// Save old image in m_copy. We cannot keep an ImageRef to this
// image, because there are other undo branches that could try to
// modify/re-add this same image ID
ImageRef oldImage = sprite()->getImage(m_oldImageId);
ImageRef oldImage = sprite()->getImageRef(m_oldImageId);
ASSERT(oldImage);
m_copy.reset(Image::createCopy(oldImage));
@ -56,9 +56,9 @@ void ReplaceImage::onExecute()
void ReplaceImage::onUndo()
{
ImageRef newImage = sprite()->getImage(m_newImageId);
ImageRef newImage = sprite()->getImageRef(m_newImageId);
ASSERT(newImage);
ASSERT(!sprite()->getImage(m_oldImageId));
ASSERT(!sprite()->getImageRef(m_oldImageId));
m_copy->setId(m_oldImageId);
sprite()->replaceImage(m_newImageId, m_copy);
@ -67,9 +67,9 @@ void ReplaceImage::onUndo()
void ReplaceImage::onRedo()
{
ImageRef oldImage = sprite()->getImage(m_oldImageId);
ImageRef oldImage = sprite()->getImageRef(m_oldImageId);
ASSERT(oldImage);
ASSERT(!sprite()->getImage(m_newImageId));
ASSERT(!sprite()->getImageRef(m_newImageId));
m_copy->setId(m_newImageId);
sprite()->replaceImage(m_oldImageId, m_copy);

View File

@ -22,7 +22,6 @@
#include "app/cmd.h"
#include "app/cmd/with_sprite.h"
// #include "app/cmd/with_image.h"
#include "doc/image_ref.h"
#include <sstream>

View File

@ -0,0 +1,96 @@
/* Aseprite
* Copyright (C) 2001-2015 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/cmd/set_cel_data.h"
#include "doc/cel.h"
#include "doc/image.h"
#include "doc/image_io.h"
#include "doc/image_ref.h"
#include "doc/sprite.h"
#include "doc/subobjects_io.h"
namespace app {
namespace cmd {
using namespace doc;
SetCelData::SetCelData(Cel* cel, const CelDataRef& newData)
: WithCel(cel)
, m_oldDataId(cel->data()->id())
, m_oldImageId(cel->image()->id())
, m_newDataId(newData->id())
, m_newData(newData)
{
}
void SetCelData::onExecute()
{
Cel* cel = this->cel();
if (!cel->links())
createCopy();
cel->setDataRef(m_newData);
m_newData.reset(nullptr);
}
void SetCelData::onUndo()
{
Cel* cel = this->cel();
if (m_dataCopy) {
ASSERT(!cel->sprite()->getCelDataRef(m_oldDataId));
m_dataCopy->setId(m_oldDataId);
m_dataCopy->image()->setId(m_oldImageId);
cel->setDataRef(m_dataCopy);
m_dataCopy.reset(nullptr);
}
else {
CelDataRef oldData = cel->sprite()->getCelDataRef(m_oldDataId);
ASSERT(oldData);
cel->setDataRef(oldData);
}
}
void SetCelData::onRedo()
{
Cel* cel = this->cel();
if (!cel->links())
createCopy();
CelDataRef newData = cel->sprite()->getCelDataRef(m_newDataId);
ASSERT(newData);
cel->setDataRef(newData);
}
void SetCelData::createCopy()
{
Cel* cel = this->cel();
ASSERT(!m_dataCopy);
m_dataCopy.reset(new CelData(*cel->data()));
m_dataCopy->setImage(ImageRef(Image::createCopy(cel->image())));
}
} // namespace cmd
} // namespace app

View File

@ -0,0 +1,64 @@
/* Aseprite
* Copyright (C) 2001-2015 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 3 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 APP_CMD_SET_CEL_DATA_H_INCLUDED
#define APP_CMD_SET_CEL_DATA_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_cel.h"
#include "doc/cel_data.h"
#include <sstream>
namespace app {
namespace cmd {
using namespace doc;
class SetCelData : public Cmd
, public WithCel {
public:
SetCelData(Cel* cel, const CelDataRef& newData);
protected:
void onExecute() override;
void onUndo() override;
void onRedo() override;
size_t onMemSize() const override {
return sizeof(*this) +
(m_dataCopy ? m_dataCopy->getMemSize(): 0);
}
private:
void createCopy();
ObjectId m_oldDataId;
ObjectId m_oldImageId;
ObjectId m_newDataId;
CelDataRef m_dataCopy;
// Reference used only to keep the copy of the new CelData from
// the SetCelData() ctor until the SetCelData::onExecute() call.
// Then the reference is not used anymore.
CelDataRef m_newData;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -29,6 +29,7 @@
#include "app/document.h"
#include "base/unique_ptr.h"
#include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/document.h"
#include "doc/layer.h"
#include "doc/palette.h"
@ -80,15 +81,12 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
is_image_from_background));
m_seq.add(new cmd::ReplaceImage(sprite,
sprite->getImage(old_image->id()), new_image));
sprite->getImageRef(old_image->id()), new_image));
}
// Set all cels opacity to 100% if we are converting to indexed.
if (newFormat == IMAGE_INDEXED) {
CelList cels;
sprite->getCels(cels);
for (auto it = cels.begin(), end = cels.end(); it != end; ++it) {
Cel* cel = *it;
for (Cel* cel : sprite->uniqueCels()) {
if (cel->opacity() < 255)
m_seq.add(new cmd::SetCelOpacity(cel, 255));
}

View File

@ -0,0 +1,75 @@
/* Aseprite
* Copyright (C) 2001-2015 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/cmd/unlink_cel.h"
#include "doc/cel.h"
#include "doc/image.h"
#include "doc/sprite.h"
namespace app {
namespace cmd {
using namespace doc;
UnlinkCel::UnlinkCel(Cel* cel)
: WithCel(cel)
, m_newImageId(0)
, m_oldCelDataId(cel->dataRef()->id())
, m_newCelDataId(0)
{
ASSERT(cel->links());
}
void UnlinkCel::onExecute()
{
Cel* cel = this->cel();
CelDataRef oldCelData = cel->sprite()->getCelDataRef(m_oldCelDataId);
ASSERT(oldCelData);
ImageRef imgCopy(Image::createCopy(oldCelData->image()));
CelDataRef celDataCopy(new CelData(*oldCelData));
celDataCopy->setImage(imgCopy);
if (m_newImageId) {
imgCopy->setId(m_newImageId);
celDataCopy->setId(m_newCelDataId);
}
else {
m_newImageId = imgCopy->id();
m_newCelDataId = celDataCopy->id();
}
cel->setDataRef(celDataCopy);
}
void UnlinkCel::onUndo()
{
Cel* cel = this->cel();
CelDataRef oldCelData = cel->sprite()->getCelDataRef(m_oldCelDataId);
ASSERT(oldCelData);
cel->setDataRef(oldCelData);
}
} // namespace cmd
} // namespace app

48
src/app/cmd/unlink_cel.h Normal file
View File

@ -0,0 +1,48 @@
/* Aseprite
* Copyright (C) 2001-2015 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 APP_CMD_UNLINK_CEL_H_INCLUDED
#define APP_CMD_UNLINK_CEL_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_cel.h"
namespace app {
namespace cmd {
using namespace doc;
class UnlinkCel : public Cmd
, public WithCel {
public:
UnlinkCel(Cel* cel);
protected:
void onExecute() override;
void onUndo() override;
private:
ObjectId m_newImageId;
ObjectId m_oldCelDataId;
ObjectId m_newCelDataId;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -25,9 +25,10 @@
#include "app/context_access.h"
#include "app/document_api.h"
#include "app/modules/gui.h"
#include "app/ui/main_window.h"
#include "app/ui/timeline.h"
#include "app/transaction.h"
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
@ -53,17 +54,14 @@ ClearCelCommand::ClearCelCommand()
bool ClearCelCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::ActiveLayerIsVisible |
ContextFlags::ActiveLayerIsEditable |
ContextFlags::ActiveLayerIsImage |
ContextFlags::HasActiveCel);
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
void ClearCelCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Document* document(writer.document());
bool nonEditableLayers = false;
{
Transaction transaction(writer.context(), "Clear Cel");
@ -83,16 +81,29 @@ void ClearCelCommand::onExecute(Context* context)
begin = range.frameBegin()-1;
frame != begin;
--frame) {
document->getApi(transaction).clearCel(layerImage, frame);
if (layerImage->cel(frame)) {
if (layerImage->isEditable())
document->getApi(transaction).clearCel(layerImage, frame);
else
nonEditableLayers = true;
}
}
}
}
else {
document->getApi(transaction).clearCel(writer.cel());
else if (writer.cel()) {
if (writer.layer()->isEditable())
document->getApi(transaction).clearCel(writer.cel());
else
nonEditableLayers = true;
}
transaction.commit();
}
if (nonEditableLayers)
StatusBar::instance()->showTip(1000,
"There are locked layers");
update_screen_for_document(document);
}

View File

@ -36,6 +36,7 @@
#include "app/util/range_utils.h"
#include "doc/algorithm/flip_image.h"
#include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/mask.h"
@ -92,7 +93,7 @@ void FlipCommand::onExecute(Context* context)
DocumentLocation loc = *writer.location();
DocumentRange range = App::instance()->getMainWindow()->getTimeline()->range();
if (range.enabled())
cels = get_cels_in_range(sprite, range);
cels = get_unique_cels(sprite, range);
else if (writer.cel())
cels.push_back(writer.cel());
@ -152,23 +153,17 @@ void FlipCommand::onExecute(Context* context)
}
}
else {
// get all sprite cels
CelList cels;
sprite->getCels(cels);
// for each cel...
for (CelIterator it = cels.begin(); it != cels.end(); ++it) {
Cel* cel = *it;
for (Cel* cel : sprite->uniqueCels()) {
Image* image = cel->image();
api.setCelPosition
(sprite, cel,
(m_flipType == doc::algorithm::FlipHorizontal ?
sprite->width() - image->width() - cel->x():
cel->x()),
(m_flipType == doc::algorithm::FlipVertical ?
sprite->height() - image->height() - cel->y():
cel->y()));
(m_flipType == doc::algorithm::FlipHorizontal ?
sprite->width() - image->width() - cel->x():
cel->x()),
(m_flipType == doc::algorithm::FlipVertical ?
sprite->height() - image->height() - cel->y():
cel->y()));
api.flipImage(image, image->bounds(), m_flipType);
}

View File

@ -24,6 +24,7 @@
#include "app/cmd/add_cel.h"
#include "app/cmd/replace_image.h"
#include "app/cmd/set_cel_position.h"
#include "app/cmd/unlink_cel.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/document.h"
@ -147,6 +148,9 @@ void MergeDownLayerCommand::onExecute(Context* context)
transaction.execute(new cmd::SetCelPosition(dst_cel,
bounds.x, bounds.y));
if (dst_cel->links())
transaction.execute(new cmd::UnlinkCel(dst_cel));
transaction.execute(new cmd::ReplaceImage(sprite,
dst_cel->imageRef(), new_image));
}

View File

@ -28,13 +28,14 @@
#include "app/document_range.h"
#include "app/job.h"
#include "app/modules/gui.h"
#include "app/transaction.h"
#include "app/ui/color_bar.h"
#include "app/ui/main_window.h"
#include "app/ui/timeline.h"
#include "app/transaction.h"
#include "app/util/range_utils.h"
#include "base/convert_to.h"
#include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"
@ -82,39 +83,42 @@ public:
protected:
/**
* [working thread]
*/
// [working thread]
virtual void onJob()
{
Transaction transaction(m_writer.context(), "Rotate Canvas");
DocumentApi api = m_document->getApi(transaction);
// for each cel...
// 1) Rotate cel positions
for (Cel* cel : m_cels) {
Image* image = cel->image();
if (!image)
continue;
switch (m_angle) {
case 180:
api.setCelPosition(m_sprite, cel,
m_sprite->width() - cel->x() - image->width(),
m_sprite->height() - cel->y() - image->height());
break;
case 90:
api.setCelPosition(m_sprite, cel,
m_sprite->height() - cel->y() - image->height(),
cel->x());
break;
case -90:
api.setCelPosition(m_sprite, cel,
cel->y(),
m_sprite->width() - cel->x() - image->width());
break;
}
}
// 2) Rotate images
int i = 0;
for (Cel* cel : m_cels) {
Image* image = cel->image();
if (image) {
// change it location
switch (m_angle) {
case 180:
api.setCelPosition(m_sprite, cel,
m_sprite->width() - cel->x() - image->width(),
m_sprite->height() - cel->y() - image->height());
break;
case 90:
api.setCelPosition(m_sprite, cel,
m_sprite->height() - cel->y() - image->height(),
cel->x());
break;
case -90:
api.setCelPosition(m_sprite, cel,
cel->y(),
m_sprite->width() - cel->x() - image->width());
break;
}
// rotate the image
ImageRef new_image(Image::create(image->pixelFormat(),
m_angle == 180 ? image->width(): image->height(),
m_angle == 180 ? image->height(): image->width()));
@ -214,13 +218,15 @@ void RotateCommand::onExecute(Context* context)
if (m_flipMask) {
DocumentRange range = App::instance()->getMainWindow()->getTimeline()->range();
if (range.enabled())
cels = get_cels_in_range(reader.sprite(), range);
cels = get_unique_cels(reader.sprite(), range);
else if (reader.cel())
cels.push_back(reader.cel());
}
// Flip the whole sprite
else if (reader.sprite()) {
reader.sprite()->getCels(cels);
for (Cel* cel : reader.sprite()->uniqueCels())
cels.push_back(cel);
rotateSprite = true;
}

View File

@ -31,12 +31,13 @@
#include "app/load_widget.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/ui_context.h"
#include "app/transaction.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/unique_ptr.h"
#include "doc/algorithm/resize_image.h"
#include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/primitives.h"
@ -84,37 +85,35 @@ protected:
Transaction transaction(m_writer.context(), "Sprite Size");
DocumentApi api = m_writer.document()->getApi(transaction);
// Get all sprite cels
CelList cels;
m_sprite->getCels(cels);
int cels_count = 0;
for (Cel* cel : m_sprite->uniqueCels())
++cels_count;
// For each cel...
int progress = 0;
for (CelIterator it = cels.begin(); it != cels.end(); ++it, ++progress) {
Cel* cel = *it;
for (Cel* cel : m_sprite->uniqueCels()) {
// Change its location
api.setCelPosition(m_sprite, cel, scale_x(cel->x()), scale_y(cel->y()));
// Get cel's image
Image* image = cel->image();
if (!image)
continue;
if (image && !cel->link()) {
// Resize the image
int w = scale_x(image->width());
int h = scale_y(image->height());
ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)));
// Resize the image
int w = scale_x(image->width());
int h = scale_y(image->height());
ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)));
doc::algorithm::fixup_image_transparent_colors(image);
doc::algorithm::resize_image(image, new_image,
doc::algorithm::fixup_image_transparent_colors(image);
doc::algorithm::resize_image(image, new_image,
m_resize_method,
m_sprite->palette(cel->frame()),
m_sprite->rgbMap(cel->frame()));
api.replaceImage(m_sprite, cel->imageRef(), new_image);
api.replaceImage(m_sprite, cel->imageRef(), new_image);
}
jobProgress((float)progress / cels.size());
jobProgress((float)progress / cels_count);
++progress;
// cancel all the operation?
if (isCanceled())

View File

@ -0,0 +1,119 @@
/* Aseprite
* Copyright (C) 2001-2015 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/app.h"
#include "app/cmd/unlink_cel.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/modules/gui.h"
#include "app/transaction.h"
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace app {
class UnlinkCelCommand : public Command {
public:
UnlinkCelCommand();
Command* clone() const override { return new UnlinkCelCommand(*this); }
protected:
bool onEnabled(Context* context);
void onExecute(Context* context);
};
UnlinkCelCommand::UnlinkCelCommand()
: Command("UnlinkCel",
"Unlink Cel",
CmdRecordableFlag)
{
}
bool UnlinkCelCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
void UnlinkCelCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Document* document(writer.document());
bool nonEditableLayers = false;
{
Transaction transaction(writer.context(), "Unlink Cel");
// TODO the range of selected frames should be in the DocumentLocation.
Timeline::Range range = App::instance()->getMainWindow()->getTimeline()->range();
if (range.enabled()) {
Sprite* sprite = writer.sprite();
for (LayerIndex layerIdx = range.layerBegin(); layerIdx <= range.layerEnd(); ++layerIdx) {
Layer* layer = sprite->indexToLayer(layerIdx);
if (!layer->isImage())
continue;
LayerImage* layerImage = static_cast<LayerImage*>(layer);
for (frame_t frame = range.frameEnd(),
begin = range.frameBegin()-1;
frame != begin;
--frame) {
Cel* cel = layerImage->cel(frame);
if (cel && cel->links()) {
if (layerImage->isEditable())
transaction.execute(new cmd::UnlinkCel(cel));
else
nonEditableLayers = true;
}
}
}
}
else {
Cel* cel = writer.cel();
if (cel && cel->links()) {
if (cel->layer()->isEditable())
transaction.execute(new cmd::UnlinkCel(writer.cel()));
else
nonEditableLayers = true;
}
}
transaction.commit();
}
if (nonEditableLayers)
StatusBar::instance()->showTip(1000,
"There are locked layers");
update_screen_for_document(document);
}
Command* CommandFactory::createUnlinkCelCommand()
{
return new UnlinkCelCommand;
}
} // namespace app

View File

@ -115,4 +115,5 @@ FOR_EACH_COMMAND(SpriteSize)
FOR_EACH_COMMAND(SwitchColors)
FOR_EACH_COMMAND(Timeline)
FOR_EACH_COMMAND(Undo)
FOR_EACH_COMMAND(UnlinkCel)
FOR_EACH_COMMAND(Zoom)

View File

@ -23,24 +23,26 @@
#include "app/commands/filters/filter_manager_impl.h"
#include "app/cmd/copy_rect.h"
#include "app/cmd/unlink_cel.h"
#include "app/context_access.h"
#include "app/ini_file.h"
#include "app/modules/editors.h"
#include "app/ui/editor/editor.h"
#include "app/transaction.h"
#include "filters/filter.h"
#include "app/ui/editor/editor.h"
#include "doc/cel.h"
#include "doc/image.h"
#include "doc/images_collector.h"
#include "doc/layer.h"
#include "doc/mask.h"
#include "doc/sprite.h"
#include "filters/filter.h"
#include "ui/manager.h"
#include "ui/view.h"
#include "ui/widget.h"
#include <cstdlib>
#include <cstring>
#include <set>
namespace app {
@ -224,11 +226,20 @@ void FilterManagerImpl::applyToTarget()
m_progressBase = 0.0f;
m_progressWidth = 1.0f / images.size();
std::set<ObjectId> visited;
// For each target image
for (ImagesCollector::ItemsIterator it = images.begin();
for (auto it = images.begin();
it != images.end() && !cancelled;
++it) {
applyToImage(transaction, it->layer(), it->image(), it->cel()->x(), it->cel()->y());
Image* image = it->image();
// Avoid applying the filter two times to the same image
if (visited.find(image->id()) == visited.end()) {
visited.insert(image->id());
applyToImage(transaction, it->layer(),
image, it->cel()->x(), it->cel()->y());
}
// Is there a delegate to know if the process was cancelled by the user?
if (m_progressDelegate)

View File

@ -44,6 +44,8 @@
#include "doc/palette.h"
#include "doc/sprite.h"
#include <map>
namespace app {
using namespace base;
@ -355,6 +357,7 @@ void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, La
{
// Copy the layer name
destLayer0->setName(sourceLayer0->name());
destLayer0->setFlags(sourceLayer0->flags());
if (sourceLayer0->isImage() && destLayer0->isImage()) {
const LayerImage* sourceLayer = static_cast<const LayerImage*>(sourceLayer0);
@ -364,18 +367,24 @@ void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, La
CelConstIterator it = sourceLayer->getCelBegin();
CelConstIterator end = sourceLayer->getCelEnd();
std::map<ObjectId, Cel*> linked;
for (; it != end; ++it) {
const Cel* sourceCel = *it;
if (sourceCel->frame() > destLayer->sprite()->lastFrame())
break;
base::UniquePtr<Cel> newCel(new Cel(*sourceCel));
base::UniquePtr<Cel> newCel;
const Image* sourceImage = sourceCel->image();
ASSERT(sourceImage != NULL);
ImageRef newImage(Image::createCopy(sourceImage));
newCel->setImage(newImage);
auto it = linked.find(sourceCel->data()->id());
if (it != linked.end()) {
newCel.reset(Cel::createLink(it->second));
newCel->setFrame(sourceCel->frame());
}
else {
newCel.reset(Cel::createCopy(sourceCel));
linked.insert(std::make_pair(sourceCel->data()->id(), newCel.get()));
}
destLayer->addCel(newCel);
newCel.release();

View File

@ -65,6 +65,8 @@
#include "render/quantization.h"
#include "render/render.h"
#include <set>
namespace app {
DocumentApi::DocumentApi(Document* document, Transaction& transaction)
@ -86,11 +88,47 @@ void DocumentApi::setSpriteTransparentColor(Sprite* sprite, color_t maskColor)
void DocumentApi::cropSprite(Sprite* sprite, const gfx::Rect& bounds)
{
setSpriteSize(sprite, bounds.w, bounds.h);
displaceLayers(sprite->folder(), -bounds.x, -bounds.y);
Layer *background_layer = sprite->backgroundLayer();
if (background_layer)
cropLayer(background_layer, 0, 0, sprite->width(), sprite->height());
app::Document* doc = static_cast<app::Document*>(sprite->document());
std::vector<Layer*> layers;
sprite->getLayersList(layers);
for (Layer* layer : layers) {
if (!layer->isImage())
continue;
std::set<ObjectId> visited;
CelIterator it = ((LayerImage*)layer)->getCelBegin();
CelIterator end = ((LayerImage*)layer)->getCelEnd();
for (; it != end; ++it) {
Cel* cel = *it;
if (visited.find(cel->data()->id()) != visited.end())
continue;
visited.insert(cel->data()->id());
if (layer->isBackground()) {
Image* image = cel->image();
if (image && !cel->link()) {
ASSERT(cel->x() == 0);
ASSERT(cel->y() == 0);
// Create the new image through a crop
ImageRef new_image(
crop_image(image,
bounds.x, bounds.y,
bounds.w, bounds.h,
doc->bgColor(layer)));
// Replace the image in the stock that is pointed by the cel
replaceImage(sprite, cel->imageRef(), new_image);
}
}
else {
// Update the cel's position
setCelPosition(sprite, cel,
cel->x()-bounds.x, cel->y()-bounds.y);
}
}
}
if (!m_document->mask()->isEmpty())
setMaskPosition(m_document->mask()->bounds().x-bounds.x,
@ -228,7 +266,7 @@ void DocumentApi::moveFrameLayer(Layer* layer, frame_t frame, frame_t beforeFram
frame_t celFrame = cel->frame();
frame_t newFrame = celFrame;
// fthe frame to the future
// moving the frame to the future
if (frame < beforeFrame) {
if (celFrame == frame) {
newFrame = beforeFrame-1;
@ -298,23 +336,6 @@ void DocumentApi::setCelOpacity(Sprite* sprite, Cel* cel, int newOpacity)
m_transaction.execute(new cmd::SetCelOpacity(cel, newOpacity));
}
void DocumentApi::cropCel(Sprite* sprite, Cel* cel, int x, int y, int w, int h)
{
Image* cel_image = cel->image();
ASSERT(cel_image);
// create the new image through a crop
ImageRef new_image(crop_image(cel_image,
x-cel->x(), y-cel->y(), w, h,
static_cast<app::Document*>(sprite->document())->bgColor(cel->layer())));
// replace the image in the stock that is pointed by the cel
replaceImage(sprite, cel->imageRef(), new_image);
// update the cel's position
setCelPosition(sprite, cel, x, y);
}
void DocumentApi::clearCel(LayerImage* layer, frame_t frame)
{
if (Cel* cel = layer->cel(frame))
@ -406,44 +427,6 @@ void DocumentApi::restackLayerBefore(Layer* layer, Layer* beforeThis)
restackLayerAfter(layer, layer->sprite()->indexToLayer(afterThisIdx));
}
void DocumentApi::cropLayer(Layer* layer, int x, int y, int w, int h)
{
if (!layer->isImage())
return;
Sprite* sprite = layer->sprite();
CelIterator it = ((LayerImage*)layer)->getCelBegin();
CelIterator end = ((LayerImage*)layer)->getCelEnd();
for (; it != end; ++it)
cropCel(sprite, *it, x, y, w, h);
}
// Moves every frame in @a layer with the offset (@a dx, @a dy).
void DocumentApi::displaceLayers(Layer* layer, int dx, int dy)
{
switch (layer->type()) {
case ObjectType::LayerImage: {
CelIterator it = ((LayerImage*)layer)->getCelBegin();
CelIterator end = ((LayerImage*)layer)->getCelEnd();
for (; it != end; ++it) {
Cel* cel = *it;
setCelPosition(layer->sprite(), cel, cel->x()+dx, cel->y()+dy);
}
break;
}
case ObjectType::LayerFolder: {
LayerIterator it = ((LayerFolder*)layer)->getLayerBegin();
LayerIterator end = ((LayerFolder*)layer)->getLayerEnd();
for (; it != end; ++it)
displaceLayers(*it, dx, dy);
break;
}
}
}
void DocumentApi::backgroundFromLayer(Layer* layer)
{
m_transaction.execute(new cmd::BackgroundFromLayer(layer));
@ -499,11 +482,6 @@ void DocumentApi::replaceImage(Sprite* sprite, const ImageRef& oldImage, const I
sprite, oldImage, newImage));
}
void DocumentApi::clearImage(Image* image, color_t bgcolor)
{
m_transaction.execute(new cmd::ClearImage(image, bgcolor));
}
void DocumentApi::flipImage(Image* image, const gfx::Rect& bounds,
doc::algorithm::FlipType flipType)
{

View File

@ -78,7 +78,6 @@ namespace app {
void clearCel(Cel* cel);
void setCelPosition(Sprite* sprite, Cel* cel, int x, int y);
void setCelOpacity(Sprite* sprite, Cel* cel, int newOpacity);
void cropCel(Sprite* sprite, Cel* cel, int x, int y, int w, int h);
void moveCel(
LayerImage* srcLayer, frame_t srcFrame,
LayerImage* dstLayer, frame_t dstFrame);
@ -95,8 +94,6 @@ namespace app {
void removeLayer(Layer* layer);
void restackLayerAfter(Layer* layer, Layer* afterThis);
void restackLayerBefore(Layer* layer, Layer* beforeThis);
void cropLayer(Layer* layer, int x, int y, int w, int h);
void displaceLayers(Layer* layer, int dx, int dy);
void backgroundFromLayer(Layer* layer);
void layerFromBackground(Layer* layer);
void flattenLayers(Sprite* sprite);
@ -107,7 +104,6 @@ namespace app {
void replaceImage(Sprite* sprite, const ImageRef& oldImage, const ImageRef& newImage);
// Image API
void clearImage(Image* image, color_t bgcolor);
void flipImage(Image* image, const gfx::Rect& bounds, doc::algorithm::FlipType flipType);
void flipImageWithMask(Layer* layer, Image* image, doc::algorithm::FlipType flipType);

View File

@ -22,10 +22,33 @@
#include "app/document_range.h"
#include "doc/cel.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace app {
using namespace doc;
DocumentRange::DocumentRange()
: m_type(kNone)
, m_layerBegin(0)
, m_layerEnd(-1)
, m_frameBegin(0)
, m_frameEnd(-1)
{
}
DocumentRange::DocumentRange(Cel* cel)
: m_type(kCels)
, m_layerBegin(cel->sprite()->layerToIndex(cel->layer()))
, m_layerEnd(m_layerBegin)
, m_frameBegin(cel->frame())
, m_frameEnd(m_frameBegin)
{
}
void DocumentRange::startRange(LayerIndex layer, frame_t frame, Type type)
{
m_type = type;
@ -88,4 +111,25 @@ void DocumentRange::displace(int layerDelta, int frameDelta)
m_frameEnd += frame_t(frameDelta);
}
bool DocumentRange::convertToCels(Sprite* sprite)
{
switch (m_type) {
case DocumentRange::kNone:
return false;
case DocumentRange::kCels:
break;
case DocumentRange::kFrames:
m_layerBegin = sprite->firstLayer();
m_layerEnd = sprite->lastLayer();
m_type = DocumentRange::kCels;
break;
case DocumentRange::kLayers:
m_frameBegin = frame_t(0);
m_frameEnd = sprite->lastFrame();
m_type = DocumentRange::kCels;
break;
}
return true;
}
} // namespace app

View File

@ -25,6 +25,11 @@
#include <algorithm>
namespace doc {
class Cel;
class Sprite;
}
namespace app {
using namespace doc;
@ -32,7 +37,8 @@ namespace app {
public:
enum Type { kNone, kCels, kFrames, kLayers };
DocumentRange() : m_type(kNone) { }
DocumentRange();
DocumentRange(Cel* cel);
Type type() const { return m_type; }
bool enabled() const { return m_type != kNone; }
@ -61,6 +67,8 @@ namespace app {
frameBegin() == o.frameBegin() && frameEnd() == o.frameEnd();
}
bool convertToCels(Sprite* sprite);
private:
Type m_type;
LayerIndex m_layerBegin;

View File

@ -172,9 +172,12 @@ protected:
bool expect_cel(int expected_layer, int expected_frame, int layer, frame_t frame) {
color_t expected_color = white;
Cel* cel = sprite->indexToLayer(LayerIndex(layer))->cel(frame);
if (!cel)
return false;
color_t color = get_pixel(
sprite->indexToLayer(LayerIndex(layer))
->cel(frame)->image(),
cel->image(),
expected_layer, expected_frame);
EXPECT_EQ(expected_color, color);

View File

@ -1020,9 +1020,7 @@ static Cel* ase_file_read_cel_chunk(FILE* f, Sprite* sprite, frame_t frame,
}
// Create the new frame.
base::UniquePtr<Cel> cel(new Cel(frame, ImageRef(NULL)));
cel->setPosition(x, y);
cel->setOpacity(opacity);
base::UniquePtr<Cel> cel;
switch (cel_type) {
@ -1050,7 +1048,9 @@ static Cel* ase_file_read_cel_chunk(FILE* f, Sprite* sprite, frame_t frame,
break;
}
cel->setImage(image);
cel.reset(new Cel(frame, image));
cel->setPosition(x, y);
cel->setOpacity(opacity);
}
break;
}
@ -1061,12 +1061,19 @@ static Cel* ase_file_read_cel_chunk(FILE* f, Sprite* sprite, frame_t frame,
Cel* link = layer->cel(link_frame);
if (link) {
#if 1 // Create a copy of the linked cel (avoid using links cel)
ImageRef image(Image::createCopy(link->image()));
cel->setImage(image);
#else
cel->setImage(link->imageRef());
#endif
// There were a beta version that allow to the user specify
// different X, Y, or opacity per link, in that case we must
// create a copy.
if (link->x() == x && link->y() == y && link->opacity() == opacity) {
cel.reset(Cel::createLink(link));
cel->setFrame(frame);
}
else {
cel.reset(Cel::createCopy(link));
cel->setFrame(frame);
cel->setPosition(x, y);
cel->setOpacity(opacity);
}
}
else {
// Linked cel doesn't found
@ -1106,16 +1113,20 @@ static Cel* ase_file_read_cel_chunk(FILE* f, Sprite* sprite, frame_t frame,
fop_error(fop, e.what());
}
cel->setImage(image);
cel.reset(new Cel(frame, image));
cel->setPosition(x, y);
cel->setOpacity(opacity);
}
break;
}
}
Cel* newCel = cel.release();
static_cast<LayerImage*>(layer)->addCel(newCel);
return newCel;
if (!cel)
return nullptr;
static_cast<LayerImage*>(layer)->addCel(cel);
return cel.release();
}
static void ase_file_write_cel_chunk(FILE* f, ASE_FrameHeader* frame_header, Cel* cel, LayerImage* layer, Sprite* sprite)

View File

@ -461,7 +461,7 @@ void fop_operate(FileOp *fop, IFileOpProgress* progress)
// TODO set_palette for each frame???
#define SEQUENCE_IMAGE() \
do { \
fop->seq.last_cel->setImage(fop->seq.image); \
fop->seq.last_cel->data()->setImage(fop->seq.image); \
fop->seq.layer->addCel(fop->seq.last_cel); \
\
if (fop->document->sprite()->palette(frame) \

View File

@ -80,7 +80,7 @@ bool FliFormat::onLoad(FileOp* fop)
int c, w, h;
frame_t frpos_in;
frame_t frpos_out;
int index = 0;
int index = 0; // TODO this is used to create linked cels
// Open the file to read in binary mode
FileHandle f(open_file_with_exception(fop->filename, "rb"));

View File

@ -462,7 +462,7 @@ bool GifFormat::onPostLoad(FileOp* fop)
try {
// Add the image in the sprite's stock and update the cel's
// reference to the new stock's image.
cel->setImage(cel_image);
cel->data()->setImage(cel_image);
}
catch (...) {
throw;

View File

@ -95,17 +95,16 @@ bool MovingCelState::onMouseUp(Editor* editor, MouseMessage* msg)
gfx::Point delta = m_celNew - m_celStart;
DocumentRange range = App::instance()->getMainWindow()->getTimeline()->range();
if (range.enabled()) {
for (Cel* cel : get_cels_in_range(writer.sprite(), range)) {
Layer* layer = cel->layer();
ASSERT(layer);
if (layer && layer->isMovable() && !layer->isBackground())
api.setCelPosition(writer.sprite(), cel, cel->x()+delta.x, cel->y()+delta.y);
if (!range.enabled())
range = DocumentRange(m_cel);
for (Cel* cel : get_unique_cels(writer.sprite(), range)) {
Layer* layer = cel->layer();
ASSERT(layer);
if (layer && layer->isMovable() && !layer->isBackground()) {
api.setCelPosition(writer.sprite(), cel, cel->x()+delta.x, cel->y()+delta.y);
}
}
else if (m_cel) {
api.setCelPosition(writer.sprite(), m_cel, m_celNew.x, m_celNew.y);
}
// Move selection if it was visible
if (m_maskVisible)

View File

@ -580,7 +580,7 @@ static void slider_change_hook(Slider* slider)
DocumentRange range = App::instance()->getMainWindow()->getTimeline()->range();
if (range.enabled()) {
for (Cel* cel : get_cels_in_range(writer.sprite(), range))
for (Cel* cel : get_unique_cels(writer.sprite(), range))
cel->setOpacity(slider->getValue());
}
else {

View File

@ -91,12 +91,17 @@ static const char* kTimelineOpenEye = "timeline_open_eye";
static const char* kTimelineClosedEye = "timeline_closed_eye";
static const char* kTimelineOpenPadlock = "timeline_open_padlock";
static const char* kTimelineClosedPadlock = "timeline_closed_padlock";
static const char* kTimelineContinuous = "timeline_continuous";
static const char* kTimelineDiscontinuous = "timeline_discontinuous";
static const char* kTimelineLayer = "timeline_layer";
static const char* kTimelineEmptyFrame = "timeline_empty_frame";
static const char* kTimelineKeyframe = "timeline_keyframe";
static const char* kTimelineFromLeft = "timeline_fromleft";
static const char* kTimelineFromRight = "timeline_fromright";
static const char* kTimelineFromBoth = "timeline_fromboth";
static const char* kTimelineLeftLink = "timeline_leftlink";
static const char* kTimelineRightLink = "timeline_rightlink";
static const char* kTimelineBothLinks = "timeline_bothlinks";
static const char* kTimelineGear = "timeline_gear";
static const char* kTimelineOnionskin = "timeline_onionskin";
static const char* kTimelineOnionskinRange = "timeline_onionskin_range";
@ -117,6 +122,7 @@ enum {
A_PART_SEPARATOR,
A_PART_HEADER_EYE,
A_PART_HEADER_PADLOCK,
A_PART_HEADER_CONTINUOUS,
A_PART_HEADER_GEAR,
A_PART_HEADER_ONIONSKIN,
A_PART_HEADER_ONIONSKIN_RANGE_LEFT,
@ -126,6 +132,7 @@ enum {
A_PART_LAYER,
A_PART_LAYER_EYE_ICON,
A_PART_LAYER_PADLOCK_ICON,
A_PART_LAYER_CONTINUOUS_ICON,
A_PART_LAYER_TEXT,
A_PART_CEL,
A_PART_RANGE_OUTLINE
@ -139,12 +146,17 @@ Timeline::Timeline()
, m_timelineClosedEyeStyle(get_style(kTimelineClosedEye))
, m_timelineOpenPadlockStyle(get_style(kTimelineOpenPadlock))
, m_timelineClosedPadlockStyle(get_style(kTimelineClosedPadlock))
, m_timelineContinuousStyle(get_style(kTimelineContinuous))
, m_timelineDiscontinuousStyle(get_style(kTimelineDiscontinuous))
, m_timelineLayerStyle(get_style(kTimelineLayer))
, m_timelineEmptyFrameStyle(get_style(kTimelineEmptyFrame))
, m_timelineKeyframeStyle(get_style(kTimelineKeyframe))
, m_timelineFromLeftStyle(get_style(kTimelineFromLeft))
, m_timelineFromRightStyle(get_style(kTimelineFromRight))
, m_timelineFromBothStyle(get_style(kTimelineFromBoth))
, m_timelineLeftLinkStyle(get_style(kTimelineLeftLink))
, m_timelineRightLinkStyle(get_style(kTimelineRightLink))
, m_timelineBothLinksStyle(get_style(kTimelineBothLinks))
, m_timelineGearStyle(get_style(kTimelineGear))
, m_timelineOnionskinStyle(get_style(kTimelineOnionskin))
, m_timelineOnionskinRangeStyle(get_style(kTimelineOnionskinRange))
@ -387,6 +399,8 @@ bool Timeline::onProcessMessage(Message* msg)
break;
case A_PART_LAYER_PADLOCK_ICON:
break;
case A_PART_LAYER_CONTINUOUS_ICON:
break;
case A_PART_CEL: {
LayerIndex old_layer = getLayerIndex(m_layer);
bool selectCel = (mouseMsg->left()
@ -547,6 +561,13 @@ bool Timeline::onProcessMessage(Message* msg)
break;
}
case A_PART_HEADER_CONTINUOUS: {
bool newContinuousState = !allLayersContinuous();
for (size_t i=0; i<m_layers.size(); i++)
m_layers[i]->setContinuous(newContinuousState);
break;
}
case A_PART_HEADER_GEAR: {
gfx::Rect gearBounds =
getPartBounds(A_PART_HEADER_GEAR).offset(getBounds().getOrigin());
@ -623,6 +644,14 @@ bool Timeline::onProcessMessage(Message* msg)
}
break;
case A_PART_LAYER_CONTINUOUS_ICON:
if (m_hot_layer == m_clk_layer && validLayer(m_hot_layer)) {
Layer* layer = m_layers[m_clk_layer];
ASSERT(layer != NULL);
layer->setContinuous(!layer->isContinuous());
}
break;
case A_PART_CEL: {
// Show the cel pop-up menu.
if (mouseMsg->right()) {
@ -1095,6 +1124,7 @@ void Timeline::drawHeader(ui::Graphics* g)
IDocumentSettings* docSettings = settings->getDocumentSettings(m_document);
bool allInvisible = allLayersInvisible();
bool allLocked = allLayersLocked();
bool allContinuous = allLayersContinuous();
drawPart(g, getPartBounds(A_PART_HEADER_EYE),
NULL,
@ -1110,6 +1140,13 @@ void Timeline::drawHeader(ui::Graphics* g)
m_hot_part == A_PART_HEADER_PADLOCK,
m_clk_part == A_PART_HEADER_PADLOCK);
drawPart(g, getPartBounds(A_PART_HEADER_CONTINUOUS),
NULL,
allContinuous ? m_timelineContinuousStyle: m_timelineDiscontinuousStyle,
m_clk_part == A_PART_HEADER_CONTINUOUS,
m_hot_part == A_PART_HEADER_CONTINUOUS,
m_clk_part == A_PART_HEADER_CONTINUOUS);
drawPart(g, getPartBounds(A_PART_HEADER_GEAR),
NULL, m_timelineGearStyle,
false,
@ -1174,6 +1211,14 @@ void Timeline::drawLayer(ui::Graphics* g, LayerIndex layerIdx)
(hotlayer && m_hot_part == A_PART_LAYER_PADLOCK_ICON),
(clklayer && m_clk_part == A_PART_LAYER_PADLOCK_ICON));
// Draw the continuous flag.
bounds = getPartBounds(A_PART_LAYER_CONTINUOUS_ICON, layerIdx);
drawPart(g, bounds, NULL,
layer->isContinuous() ? m_timelineContinuousStyle: m_timelineDiscontinuousStyle,
is_active,
(hotlayer && m_hot_part == A_PART_LAYER_CONTINUOUS_ICON),
(clklayer && m_clk_part == A_PART_LAYER_CONTINUOUS_ICON));
// Draw the layer's name.
bounds = getPartBounds(A_PART_LAYER_TEXT, layerIdx);
drawPart(g, bounds, layer->name().c_str(), m_timelineLayerStyle,
@ -1193,6 +1238,7 @@ void Timeline::drawLayer(ui::Graphics* g, LayerIndex layerIdx)
void Timeline::drawCel(ui::Graphics* g, LayerIndex layerIndex, frame_t frame, Cel* cel)
{
Layer* layer = m_layers[layerIndex];
Image* image = (cel ? cel->image(): NULL);
bool is_hover = (m_hot_part == A_PART_CEL &&
m_hot_layer == layerIndex &&
@ -1204,23 +1250,24 @@ void Timeline::drawCel(ui::Graphics* g, LayerIndex layerIndex, frame_t frame, Ce
if (!clip)
return;
if (layerIndex == getLayerIndex(m_layer) && frame == m_frame)
if (layer == m_layer && frame == m_frame)
drawPart(g, bounds, NULL, m_timelineSelectedCelStyle, false, false, true);
else
drawPart(g, bounds, NULL, m_timelineBoxStyle, is_active, is_hover);
skin::Style* style;
bool fromLeft = false;
bool fromRight = false;
if (is_empty) {
style = m_timelineEmptyFrameStyle;
}
else {
Layer* layer = m_layers[layerIndex];
Cel* left = (layer->isImage() ? layer->cel(frame-1): NULL);
Cel* right = (layer->isImage() ? layer->cel(frame+1): NULL);
ObjectId leftImg = (left ? left->image()->id(): 0);
ObjectId rightImg = (right ? right->image()->id(): 0);
bool fromLeft = (leftImg == cel->image()->id());
bool fromRight = (rightImg == cel->image()->id());
fromLeft = (leftImg == cel->image()->id());
fromRight = (rightImg == cel->image()->id());
if (fromLeft && fromRight)
style = m_timelineFromBothStyle;
@ -1232,6 +1279,56 @@ void Timeline::drawCel(ui::Graphics* g, LayerIndex layerIndex, frame_t frame, Ce
style = m_timelineKeyframeStyle;
}
drawPart(g, bounds, NULL, style, is_active, is_hover);
// Draw decorators to link the activeCel with its links.
if (layer == m_layer) {
Cel* activeCel = m_layer->cel(m_frame);
if (activeCel)
drawCelLinkDecorators(g, bounds, cel, activeCel, frame, is_active, is_hover);
}
}
void Timeline::drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
Cel* cel, Cel* activeCel, frame_t frame, bool is_active, bool is_hover)
{
ObjectId imageId = activeCel->image()->id();
// Link in some cel at the left side
bool left = false;
for (frame_t fr=frame-1; fr>=0; --fr) {
Cel* c = m_layer->cel(fr);
if (c && c->image()->id() == imageId) {
left = true;
break;
}
}
// Link in some cel at the right side
bool right = false;
for (frame_t fr=frame+1; fr<m_sprite->totalFrames(); ++fr) {
Cel* c = m_layer->cel(fr);
if (c && c->image()->id() == imageId) {
right = true;
break;
}
}
if (!cel || cel->image()->id() != imageId) {
if (left && right)
drawPart(g, bounds, NULL, m_timelineBothLinksStyle, is_active, is_hover);
}
else {
if (left) {
Cel* prevCel = m_layer->cel(cel->frame()-1);
if (!prevCel || prevCel->image()->id() != imageId)
drawPart(g, bounds, NULL, m_timelineLeftLinkStyle, is_active, is_hover);
}
if (right) {
Cel* nextCel = m_layer->cel(cel->frame()+1);
if (!nextCel || nextCel->image()->id() != imageId)
drawPart(g, bounds, NULL, m_timelineRightLinkStyle, is_active, is_hover);
}
}
}
void Timeline::drawLoopRange(ui::Graphics* g)
@ -1421,15 +1518,18 @@ gfx::Rect Timeline::getPartBounds(int part, LayerIndex layer, frame_t frame) con
case A_PART_HEADER_PADLOCK:
return gfx::Rect(FRMSIZE*1, 0, FRMSIZE, HDRSIZE);
case A_PART_HEADER_GEAR:
case A_PART_HEADER_CONTINUOUS:
return gfx::Rect(FRMSIZE*2, 0, FRMSIZE, HDRSIZE);
case A_PART_HEADER_ONIONSKIN:
case A_PART_HEADER_GEAR:
return gfx::Rect(FRMSIZE*3, 0, FRMSIZE, HDRSIZE);
case A_PART_HEADER_ONIONSKIN:
return gfx::Rect(FRMSIZE*4, 0, FRMSIZE, HDRSIZE);
case A_PART_HEADER_LAYER:
return gfx::Rect(FRMSIZE*4, 0,
m_separator_x - FRMSIZE*4, HDRSIZE);
return gfx::Rect(FRMSIZE*5, 0,
m_separator_x - FRMSIZE*5, HDRSIZE);
case A_PART_HEADER_FRAME:
if (validFrame(frame)) {
@ -1462,9 +1562,17 @@ gfx::Rect Timeline::getPartBounds(int part, LayerIndex layer, frame_t frame) con
}
break;
case A_PART_LAYER_CONTINUOUS_ICON:
if (validLayer(layer)) {
return gfx::Rect(2*FRMSIZE,
HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
FRMSIZE, LAYSIZE);
}
break;
case A_PART_LAYER_TEXT:
if (validLayer(layer)) {
int x = FRMSIZE*2;
int x = FRMSIZE*3;
return gfx::Rect(x,
HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
m_separator_x - x, LAYSIZE);
@ -1605,6 +1713,8 @@ void Timeline::updateHot(ui::Message* msg, const gfx::Point& mousePos, int& hot_
hot_part = A_PART_HEADER_EYE;
else if (getPartBounds(A_PART_HEADER_PADLOCK).contains(mousePos))
hot_part = A_PART_HEADER_PADLOCK;
else if (getPartBounds(A_PART_HEADER_CONTINUOUS).contains(mousePos))
hot_part = A_PART_HEADER_CONTINUOUS;
else if (getPartBounds(A_PART_HEADER_GEAR).contains(mousePos))
hot_part = A_PART_HEADER_GEAR;
else if (getPartBounds(A_PART_HEADER_ONIONSKIN).contains(mousePos))
@ -1623,6 +1733,8 @@ void Timeline::updateHot(ui::Message* msg, const gfx::Point& mousePos, int& hot_
hot_part = A_PART_LAYER_EYE_ICON;
else if (getPartBounds(A_PART_LAYER_PADLOCK_ICON, hot_layer).contains(mousePos))
hot_part = A_PART_LAYER_PADLOCK_ICON;
else if (getPartBounds(A_PART_LAYER_CONTINUOUS_ICON, hot_layer).contains(mousePos))
hot_part = A_PART_LAYER_CONTINUOUS_ICON;
else if (getPartBounds(A_PART_LAYER_TEXT, hot_layer).contains(mousePos))
hot_part = A_PART_LAYER_TEXT;
else
@ -1768,6 +1880,16 @@ void Timeline::updateStatusBar(ui::Message* msg)
}
break;
case A_PART_LAYER_CONTINUOUS_ICON:
if (layer != NULL) {
sb->setStatusText(0, "Layer '%s' is %s (%s)",
layer->name().c_str(),
layer->isContinuous() ? "continuous": "discontinuous",
layer->isContinuous() ? "prefer linked cels/frames": "prefer individual cels/frames");
return;
}
break;
case A_PART_HEADER_FRAME:
if (validFrame(m_hot_frame)) {
sb->setStatusText(0,
@ -1782,9 +1904,16 @@ void Timeline::updateStatusBar(ui::Message* msg)
if (layer) {
Cel* cel = (layer->isImage() ? layer->cel(m_hot_frame): NULL);
StatusBar::instance()->setStatusText(0,
"%s at frame %d",
cel ? "Cel": "Empty cel",
(int)m_hot_frame+1);
"%s at frame %d"
#ifdef _DEBUG
" (Image %d)"
#endif
, cel ? "Cel": "Empty cel"
, (int)m_hot_frame+1
#ifdef _DEBUG
, (cel ? cel->image()->id(): 0)
#endif
);
return;
}
break;
@ -1900,6 +2029,24 @@ bool Timeline::allLayersUnlocked()
return true;
}
bool Timeline::allLayersContinuous()
{
for (size_t i=0; i<m_layers.size(); i++)
if (!m_layers[i]->isContinuous())
return false;
return true;
}
bool Timeline::allLayersDiscontinuous()
{
for (size_t i=0; i<m_layers.size(); i++)
if (m_layers[i]->isContinuous())
return false;
return true;
}
LayerIndex Timeline::getLayerIndex(const Layer* layer) const
{
for (int i=0; i<(int)m_layers.size(); i++)

View File

@ -142,6 +142,8 @@ namespace app {
bool allLayersInvisible();
bool allLayersLocked();
bool allLayersUnlocked();
bool allLayersContinuous();
bool allLayersDiscontinuous();
void detachDocument();
void setCursor(ui::Message* msg, const gfx::Point& mousePos);
void getDrawableLayers(ui::Graphics* g, LayerIndex* first_layer, LayerIndex* last_layer);
@ -153,6 +155,8 @@ namespace app {
void drawHeaderFrame(ui::Graphics* g, frame_t frame);
void drawLayer(ui::Graphics* g, LayerIndex layerIdx);
void drawCel(ui::Graphics* g, LayerIndex layerIdx, frame_t frame, Cel* cel);
void drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
Cel* cel, Cel* activeCel, frame_t frame, bool is_active, bool is_hover);
void drawLoopRange(ui::Graphics* g);
void drawRangeOutline(ui::Graphics* g);
void drawPaddings(ui::Graphics* g);
@ -201,12 +205,17 @@ namespace app {
skin::Style* m_timelineClosedEyeStyle;
skin::Style* m_timelineOpenPadlockStyle;
skin::Style* m_timelineClosedPadlockStyle;
skin::Style* m_timelineContinuousStyle;
skin::Style* m_timelineDiscontinuousStyle;
skin::Style* m_timelineLayerStyle;
skin::Style* m_timelineEmptyFrameStyle;
skin::Style* m_timelineKeyframeStyle;
skin::Style* m_timelineFromLeftStyle;
skin::Style* m_timelineFromRightStyle;
skin::Style* m_timelineFromBothStyle;
skin::Style* m_timelineLeftLinkStyle;
skin::Style* m_timelineRightLinkStyle;
skin::Style* m_timelineBothLinksStyle;
skin::Style* m_timelineGearStyle;
skin::Style* m_timelineOnionskinStyle;
skin::Style* m_timelineOnionskinRangeStyle;

View File

@ -24,13 +24,14 @@
#include "app/app.h"
#include "app/cmd/add_cel.h"
#include "app/cmd/copy_region.h"
#include "app/cmd/replace_image.h"
#include "app/cmd/set_cel_position.h"
#include "app/cmd/copy_region.h"
#include "app/context.h"
#include "app/document.h"
#include "app/document_location.h"
#include "app/transaction.h"
#include "app/util/range_utils.h"
#include "base/unique_ptr.h"
#include "doc/cel.h"
#include "doc/image.h"
@ -153,7 +154,7 @@ void ExpandCelCanvas::commit()
// Add a copy of m_dstImage in the sprite's image stock
ImageRef newImage(Image::createCopy(m_dstImage));
m_cel->setImage(newImage);
m_cel->data()->setImage(newImage);
// And finally we add the cel again in the layer.
m_transaction.execute(new cmd::AddCel(m_layer, m_cel));
@ -183,8 +184,7 @@ void ExpandCelCanvas::commit()
if (m_cel->position() != m_origCelPos) {
gfx::Point newPos = m_cel->position();
m_cel->setPosition(m_origCelPos);
m_transaction.execute(new cmd::SetCelPosition(m_cel,
newPos.x, newPos.y));
m_transaction.execute(new cmd::SetCelPosition(m_cel, newPos.x, newPos.y));
}
// Validate the whole m_dstImage copying invalid areas from m_celImage

View File

@ -20,36 +20,30 @@
#include "config.h"
#endif
#include "app/util/range_utils.h"
#include "app/context_access.h"
#include "app/document.h"
#include "app/document_range.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
#include <set>
namespace app {
using namespace doc;
// TODO the DocumentRange should be "iteratable" to replace this function
CelList get_cels_in_range(Sprite* sprite, const DocumentRange& inrange)
CelList get_unique_cels(Sprite* sprite, const DocumentRange& inrange)
{
DocumentRange range = inrange;
CelList cels;
if (!range.convertToCels(sprite))
return cels;
switch (range.type()) {
case DocumentRange::kNone:
return cels;
case DocumentRange::kCels:
break;
case DocumentRange::kFrames:
range.startRange(LayerIndex(0), inrange.frameBegin(), DocumentRange::kCels);
range.endRange(LayerIndex(sprite->countLayers()-1), inrange.frameEnd());
break;
case DocumentRange::kLayers:
range.startRange(inrange.layerBegin(), frame_t(0), DocumentRange::kCels);
range.endRange(inrange.layerEnd(), sprite->lastFrame());
break;
}
std::set<ObjectId> visited;
for (LayerIndex layerIdx = range.layerBegin(); layerIdx <= range.layerEnd(); ++layerIdx) {
Layer* layer = sprite->indexToLayer(layerIdx);
@ -62,8 +56,13 @@ CelList get_cels_in_range(Sprite* sprite, const DocumentRange& inrange)
frame != begin;
--frame) {
Cel* cel = layerImage->cel(frame);
if (cel)
if (!cel)
continue;
if (visited.find(cel->data()->id()) == visited.end()) {
visited.insert(cel->data()->id());
cels.push_back(cel);
}
}
}
return cels;

View File

@ -20,14 +20,20 @@
#define APP_UTIL_RANGE_UTILS_H_INCLUDED
#pragma once
#include "doc/object.h"
#include "doc/cel_list.h"
#include <vector>
namespace doc {
class Sprite;
}
namespace app {
using namespace doc;
class DocumentRange;
CelList get_cels_in_range(doc::Sprite* sprite, const DocumentRange& range);
doc::CelList get_unique_cels(doc::Sprite* sprite, const DocumentRange& range);
} // namespace app

View File

@ -13,7 +13,10 @@ add_library(doc-lib
blend.cpp
brush.cpp
cel.cpp
cel_data.cpp
cel_data_io.cpp
cel_io.cpp
cels_range.cpp
color_scales.cpp
context.cpp
conversion_she.cpp
@ -36,4 +39,5 @@ add_library(doc-lib
primitives.cpp
rgbmap.cpp
sprite.cpp
sprites.cpp)
sprites.cpp
subobjects_io.cpp)

View File

@ -1,4 +1,4 @@
Copyright (c) 2001-2014 David Capello
Copyright (c) 2001-2015 David Capello
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -1,4 +1,4 @@
# Aseprite Document Library
*Copyright (C) 2001-2014 David Capello*
*Copyright (C) 2001-2015 David Capello*
> Distributed under [MIT license](LICENSE.txt)

View File

@ -21,24 +21,33 @@ Cel::Cel(frame_t frame, const ImageRef& image)
: Object(ObjectType::Cel)
, m_layer(NULL)
, m_frame(frame)
, m_image(image)
, m_position(0, 0)
, m_opacity(255)
, m_data(new CelData(image))
{
}
Cel::Cel(const Cel& cel)
Cel::Cel(frame_t frame, const CelDataRef& celData)
: Object(ObjectType::Cel)
, m_layer(NULL)
, m_frame(cel.m_frame)
, m_image(cel.m_image)
, m_position(cel.m_position)
, m_opacity(cel.m_opacity)
, m_frame(frame)
, m_data(celData)
{
}
Cel::~Cel()
// static
Cel* Cel::createCopy(const Cel* other)
{
Cel* cel = new Cel(other->frame(),
ImageRef(Image::createCopy(other->image())));
cel->setPosition(other->position());
cel->setOpacity(other->opacity());
return cel;
}
// static
Cel* Cel::createLink(const Cel* other)
{
return new Cel(other->frame(), other->dataRef());
}
void Cel::setFrame(frame_t frame)
@ -47,10 +56,25 @@ void Cel::setFrame(frame_t frame)
m_frame = frame;
}
void Cel::setImage(const ImageRef& image)
void Cel::setDataRef(const CelDataRef& celData)
{
m_image = image;
fixupImage();
ASSERT(celData);
m_data = celData;
}
void Cel::setPosition(int x, int y)
{
setPosition(gfx::Point(x, y));
}
void Cel::setPosition(const gfx::Point& pos)
{
m_data->setPosition(pos);
}
void Cel::setOpacity(int opacity)
{
m_data->setOpacity(opacity);
}
Document* Cel::document() const
@ -73,13 +97,14 @@ Sprite* Cel::sprite() const
Cel* Cel::link() const
{
if (m_image.get() == NULL)
ASSERT(m_data);
if (m_data.get() == NULL)
return NULL;
if (!m_image.unique()) {
if (!m_data.unique()) {
for (frame_t fr=0; fr<m_frame; ++fr) {
Cel* possible = m_layer->cel(fr);
if (possible && possible->imageRef().get() == m_image.get())
if (possible && possible->dataRef().get() == m_data.get())
return possible;
}
}
@ -87,13 +112,27 @@ Cel* Cel::link() const
return NULL;
}
size_t Cel::links() const
{
size_t links = 0;
Sprite* sprite = this->sprite();
for (frame_t fr=0; fr<sprite->totalFrames(); ++fr) {
Cel* cel = m_layer->cel(fr);
if (cel && cel != this && cel->dataRef().get() == m_data.get())
++links;
}
return links;
}
gfx::Rect Cel::bounds() const
{
Image* image = this->image();
ASSERT(image);
if (image)
return gfx::Rect(
m_position.x, m_position.y,
position().x, position().y,
image->width(), image->height());
else
return gfx::Rect();
@ -108,8 +147,8 @@ void Cel::setParentLayer(LayerImage* layer)
void Cel::fixupImage()
{
// Change the mask color to the sprite mask color
if (m_layer && m_image.get())
m_image->setMaskColor(m_layer->sprite()->transparentColor());
if (m_layer && image())
image()->setMaskColor(m_layer->sprite()->transparentColor());
}
} // namespace doc

View File

@ -8,6 +8,8 @@
#define DOC_CEL_H_INCLUDED
#pragma once
#include "base/disable_copying.h"
#include "doc/cel_data.h"
#include "doc/frame.h"
#include "doc/image_ref.h"
#include "doc/object.h"
@ -23,37 +25,39 @@ namespace doc {
class Cel : public Object {
public:
Cel(frame_t frame, const ImageRef& image);
Cel(const Cel& cel);
virtual ~Cel();
Cel(frame_t frame, const CelDataRef& celData);
static Cel* createCopy(const Cel* other);
static Cel* createLink(const Cel* other);
frame_t frame() const { return m_frame; }
int x() const { return m_position.x; }
int y() const { return m_position.y; }
gfx::Point position() const { return m_position; }
int opacity() const { return m_opacity; }
int x() const { return m_data->position().x; }
int y() const { return m_data->position().y; }
gfx::Point position() const { return m_data->position(); }
int opacity() const { return m_data->opacity(); }
LayerImage* layer() const { return m_layer; }
Image* image() const { return const_cast<Image*>(m_image.get()); };
ImageRef imageRef() const { return m_image; }
Image* image() const { return m_data->image(); }
ImageRef imageRef() const { return m_data->imageRef(); }
CelData* data() const { return const_cast<CelData*>(m_data.get()); }
CelDataRef dataRef() const { return m_data; }
Document* document() const;
Sprite* sprite() const;
Cel* link() const;
size_t links() const;
gfx::Rect bounds() const;
// You should change the frame only if the cel isn't member of a
// layer. If the cel is already in a layer, you should use
// layer. If the cel is already in a layer, you should use
// LayerImage::moveCel() member function.
void setFrame(frame_t frame);
void setImage(const ImageRef& image);
void setPosition(int x, int y) {
m_position.x = x;
m_position.y = y;
}
void setPosition(const gfx::Point& pos) { m_position = pos; }
void setOpacity(int opacity) { m_opacity = opacity; }
void setDataRef(const CelDataRef& celData);
void setPosition(int x, int y);
void setPosition(const gfx::Point& pos);
void setOpacity(int opacity);
virtual int getMemSize() const override {
return sizeof(Cel);
return sizeof(Cel) + m_data->getMemSize();
}
void setParentLayer(LayerImage* layer);
@ -63,9 +67,10 @@ namespace doc {
LayerImage* m_layer;
frame_t m_frame; // Frame position
ImageRef m_image;
gfx::Point m_position; // X/Y screen position
int m_opacity; // Opacity level
CelDataRef m_data;
Cel();
DISABLE_COPYING(Cel);
};
} // namespace doc

43
src/doc/cel_data.cpp Normal file
View File

@ -0,0 +1,43 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "doc/cel_data.h"
#include "gfx/rect.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace doc {
CelData::CelData(const ImageRef& image)
: Object(ObjectType::CelData)
, m_image(image)
, m_position(0, 0)
, m_opacity(255)
{
}
CelData::CelData(const CelData& celData)
: Object(ObjectType::CelData)
, m_image(celData.m_image)
, m_position(celData.m_position)
, m_opacity(celData.m_opacity)
{
}
void CelData::setImage(const ImageRef& image)
{
ASSERT(image.get());
m_image = image;
}
} // namespace doc

50
src/doc/cel_data.h Normal file
View File

@ -0,0 +1,50 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_CEL_DATA_H_INCLUDED
#define DOC_CEL_DATA_H_INCLUDED
#pragma once
#include "base/shared_ptr.h"
#include "doc/image_ref.h"
#include "doc/object.h"
namespace doc {
class CelData : public Object {
public:
CelData(const ImageRef& image);
CelData(const CelData& celData);
const gfx::Point& position() const { return m_position; }
int opacity() const { return m_opacity; }
Image* image() const { return const_cast<Image*>(m_image.get()); };
ImageRef imageRef() const { return m_image; }
void setImage(const ImageRef& image);
void setPosition(int x, int y) {
m_position.x = x;
m_position.y = y;
}
void setPosition(const gfx::Point& pos) { m_position = pos; }
void setOpacity(int opacity) { m_opacity = opacity; }
virtual int getMemSize() const override {
ASSERT(m_image);
return sizeof(CelData) + m_image->getMemSize();
}
private:
ImageRef m_image;
gfx::Point m_position; // X/Y screen position
int m_opacity; // Opacity level
};
typedef SharedPtr<CelData> CelDataRef;
} // namespace doc
#endif

51
src/doc/cel_data_io.cpp Normal file
View File

@ -0,0 +1,51 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "doc/cel_data_io.h"
#include "base/serialization.h"
#include "base/unique_ptr.h"
#include "doc/cel_data.h"
#include "doc/subobjects_io.h"
#include <iostream>
namespace doc {
using namespace base::serialization;
using namespace base::serialization::little_endian;
void write_celdata(std::ostream& os, CelData* celdata)
{
write32(os, celdata->id());
write16(os, (int16_t)celdata->position().x);
write16(os, (int16_t)celdata->position().y);
write8(os, celdata->opacity());
write32(os, celdata->image()->id());
}
CelData* read_celdata(std::istream& is, SubObjectsIO* subObjects)
{
ObjectId id = read32(is);
int x = (int16_t)read16(is);
int y = (int16_t)read16(is);
int opacity = read8(is);
ObjectId imageId = read32(is);
ImageRef image(subObjects->getImageRef(imageId));
base::UniquePtr<CelData> celdata(new CelData(image));
celdata->setPosition(x, y);
celdata->setOpacity(opacity);
celdata->setId(id);
return celdata.release();
}
}

23
src/doc/cel_data_io.h Normal file
View File

@ -0,0 +1,23 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_CEL_DATA_IO_H_INCLUDED
#define DOC_CEL_DATA_IO_H_INCLUDED
#pragma once
#include <iosfwd>
namespace doc {
class CelData;
class SubObjectsIO;
void write_celdata(std::ostream& os, CelData* cel);
CelData* read_celdata(std::istream& is, SubObjectsIO* subObjects);
} // namespace doc
#endif

View File

@ -13,7 +13,6 @@
#include "base/serialization.h"
#include "base/unique_ptr.h"
#include "doc/cel.h"
#include "doc/sprite.h"
#include "doc/subobjects_io.h"
#include <iostream>
@ -23,41 +22,23 @@ namespace doc {
using namespace base::serialization;
using namespace base::serialization::little_endian;
void write_cel(std::ostream& os, SubObjectsIO* subObjects, Cel* cel)
void write_cel(std::ostream& os, Cel* cel)
{
Cel* link = cel->link();
write32(os, cel->id());
write16(os, cel->frame());
write16(os, (int16_t)cel->x());
write16(os, (int16_t)cel->y());
write16(os, cel->opacity());
write16(os, link ? 1: 0);
if (link)
write32(os, link->id());
else
subObjects->write_image(os, cel->image());
write32(os, cel->dataRef()->id());
}
Cel* read_cel(std::istream& is, SubObjectsIO* subObjects, Sprite* sprite)
Cel* read_cel(std::istream& is, SubObjectsIO* subObjects)
{
ObjectId id = read32(is);
frame_t frame(read16(is));
int x = (int16_t)read16(is);
int y = (int16_t)read16(is);
int opacity = read16(is);
bool is_link = (read16(is) == 1);
base::UniquePtr<Cel> cel(new Cel(frame, ImageRef(NULL)));
cel->setPosition(x, y);
cel->setOpacity(opacity);
if (is_link) {
ObjectId imageId = read32(is);
cel->setImage(sprite->getImage(imageId));
}
else
cel->setImage(ImageRef(subObjects->read_image(is)));
ObjectId celDataId = read32(is);
CelDataRef celData(subObjects->getCelDataRef(celDataId));
ASSERT(celData);
base::UniquePtr<Cel> cel(new Cel(frame, celData));
cel->setId(id);
return cel.release();
}

View File

@ -13,11 +13,10 @@
namespace doc {
class Cel;
class Sprite;
class SubObjectsIO;
void write_cel(std::ostream& os, SubObjectsIO* subObjects, Cel* cel);
Cel* read_cel(std::istream& is, SubObjectsIO* subObjects, Sprite* sprite);
void write_cel(std::ostream& os, Cel* cel);
Cel* read_cel(std::istream& is, SubObjectsIO* subObjects);
} // namespace doc

84
src/doc/cels_range.cpp Normal file
View File

@ -0,0 +1,84 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "doc/cels_range.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace doc {
CelsRange::CelsRange(const Sprite* sprite,
frame_t first, frame_t last, Flags flags)
: m_begin(sprite, first, last, flags)
, m_end()
{
}
CelsRange::iterator::iterator()
: m_cel(nullptr)
{
}
CelsRange::iterator::iterator(const Sprite* sprite, frame_t first, frame_t last, CelsRange::Flags flags)
: m_cel(nullptr)
, m_first(first)
, m_last(last)
, m_flags(flags)
{
// Get first cel
Layer* layer = sprite->layer(sprite->firstLayer());
while (layer && !m_cel) {
for (frame_t f=first; f<=last; ++f) {
m_cel = layer->cel(f);
if (m_cel)
break;
m_cel = nullptr;
}
layer = layer->getNext();
}
if (m_cel && flags == CelsRange::UNIQUE)
m_visited.insert(m_cel->data()->id());
}
CelsRange::iterator& CelsRange::iterator::operator++()
{
if (!m_cel)
return *this;
// Get next cel
Layer* layer = m_cel->layer();
frame_t first = m_cel->frame()+1;
m_cel = nullptr;
while (layer && !m_cel) {
for (frame_t f=first; f<=m_last; ++f) {
m_cel = layer->cel(f);
if (m_cel) {
if (m_flags == CelsRange::UNIQUE) {
if (m_visited.find(m_cel->data()->id()) == m_visited.end()) {
m_visited.insert(m_cel->data()->id());
break;
}
else
m_cel = nullptr;
}
else
break;
}
}
layer = layer->getNext();
first = m_first;
}
return *this;
}
} // namespace doc

66
src/doc/cels_range.h Normal file
View File

@ -0,0 +1,66 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_CELS_RANGE_H_INCLUDED
#define DOC_CELS_RANGE_H_INCLUDED
#pragma once
#include "doc/frame.h"
#include "doc/object_id.h"
#include <set>
namespace doc {
class Cel;
class Sprite;
class CelsRange {
public:
enum Flags {
ALL,
UNIQUE,
};
CelsRange(const Sprite* sprite,
frame_t first, frame_t last, Flags flags = ALL);
class iterator {
public:
iterator();
iterator(const Sprite* sprite, frame_t first, frame_t last, Flags flags);
bool operator==(const iterator& other) const {
return m_cel == other.m_cel;
}
bool operator!=(const iterator& other) const {
return !operator==(other);
}
Cel* operator*() const {
return m_cel;
}
iterator& operator++();
private:
Cel* m_cel;
frame_t m_first, m_last;
Flags m_flags;
std::set<ObjectId> m_visited;
};
iterator begin() { return m_begin; }
iterator end() { return m_end; }
private:
iterator m_begin, m_end;
Flags m_flags;
};
} // namespace doc
#endif

View File

@ -35,6 +35,7 @@ using namespace base::serialization::little_endian;
void write_image(std::ostream& os, Image* image)
{
write32(os, image->id());
write8(os, image->pixelFormat()); // Pixel format
write16(os, image->width()); // Width
write16(os, image->height()); // Height
@ -47,6 +48,7 @@ void write_image(std::ostream& os, Image* image)
Image* read_image(std::istream& is)
{
ObjectId id = read32(is);
int pixelFormat = read8(is); // Pixel format
int width = read16(is); // Width
int height = read16(is); // Height
@ -59,6 +61,7 @@ Image* read_image(std::istream& is)
is.read((char*)image->getPixelAddress(0, c), size);
image->setMaskColor(maskColor);
image->setId(id);
return image.release();
}

View File

@ -153,8 +153,10 @@ Cel* LayerImage::getLastCel() const
return NULL;
}
void LayerImage::addCel(Cel *cel)
void LayerImage::addCel(Cel* cel)
{
ASSERT(cel->data() && "The cel doesn't contain CelData");
CelIterator it = getCelBegin();
CelIterator end = getCelEnd();

View File

@ -33,6 +33,7 @@ namespace doc {
Editable = 2, // Can be written
LockMove = 4, // Cannot be moved
Background = 8, // Stack order cannot be changed
Continuous = 16, // Prefer to link cels when the user copy them
BackgroundLayerFlags = LockMove | Background,
};
@ -64,11 +65,13 @@ namespace doc {
bool isVisible() const { return hasFlags(LayerFlags::Visible); }
bool isEditable() const { return hasFlags(LayerFlags::Editable); }
bool isMovable() const { return !hasFlags(LayerFlags::LockMove); }
bool isContinuous() const { return hasFlags(LayerFlags::Continuous); }
void setBackground(bool state) { switchFlags(LayerFlags::Background, state); }
void setVisible (bool state) { switchFlags(LayerFlags::Visible, state); }
void setEditable (bool state) { switchFlags(LayerFlags::Editable, state); }
void setMovable (bool state) { switchFlags(LayerFlags::LockMove, !state); }
void setContinuous(bool state) { switchFlags(LayerFlags::Continuous, state); }
LayerFlags flags() const {
return m_flags;

View File

@ -13,7 +13,12 @@
#include "base/serialization.h"
#include "base/unique_ptr.h"
#include "doc/cel.h"
#include "doc/cel_data.h"
#include "doc/cel_data_io.h"
#include "doc/cel_io.h"
#include "doc/image_io.h"
#include "doc/layer.h"
#include "doc/layer_io.h"
#include "doc/sprite.h"
#include "doc/subobjects_io.h"
@ -27,10 +32,12 @@ using namespace base::serialization::little_endian;
// Serialized Layer data:
void write_layer(std::ostream& os, SubObjectsIO* subObjects, Layer* layer)
void write_layer(std::ostream& os, Layer* layer)
{
std::string name = layer->name();
write32(os, layer->id());
write16(os, name.size()); // Name length
if (!name.empty())
os.write(name.c_str(), name.size()); // Name
@ -41,15 +48,39 @@ void write_layer(std::ostream& os, SubObjectsIO* subObjects, Layer* layer)
switch (layer->type()) {
case ObjectType::LayerImage: {
// Number of cels
write16(os, static_cast<LayerImage*>(layer)->getCelsCount());
CelIterator it = static_cast<LayerImage*>(layer)->getCelBegin();
CelIterator it, begin = static_cast<LayerImage*>(layer)->getCelBegin();
CelIterator end = static_cast<LayerImage*>(layer)->getCelEnd();
for (; it != end; ++it) {
// Images
int images = 0;
int celdatas = 0;
for (it=begin; it != end; ++it) {
Cel* cel = *it;
subObjects->write_cel(os, cel);
if (!cel->link()) {
++images;
++celdatas;
}
}
write16(os, images);
for (it=begin; it != end; ++it) {
Cel* cel = *it;
if (!cel->link())
write_image(os, cel->image());
}
write16(os, celdatas);
for (it=begin; it != end; ++it) {
Cel* cel = *it;
if (!cel->link())
write_celdata(os, cel->dataRef());
}
// Cels
write16(os, static_cast<LayerImage*>(layer)->getCelsCount());
for (it=begin; it != end; ++it) {
Cel* cel = *it;
write_cel(os, cel);
}
break;
}
@ -62,15 +93,16 @@ void write_layer(std::ostream& os, SubObjectsIO* subObjects, Layer* layer)
write16(os, static_cast<LayerFolder*>(layer)->getLayersCount());
for (; it != end; ++it)
subObjects->write_layer(os, *it);
write_layer(os, *it);
break;
}
}
}
Layer* read_layer(std::istream& is, SubObjectsIO* subObjects, Sprite* sprite)
Layer* read_layer(std::istream& is, SubObjectsIO* subObjects)
{
ObjectId id = read32(is);
uint16_t name_length = read16(is); // Name length
std::vector<char> name(name_length+1);
if (name_length > 0) {
@ -89,13 +121,27 @@ Layer* read_layer(std::istream& is, SubObjectsIO* subObjects, Sprite* sprite)
case ObjectType::LayerImage: {
// Create layer
layer.reset(new LayerImage(sprite));
layer.reset(new LayerImage(subObjects->sprite()));
// Read images
int images = read16(is); // Number of images
for (int c=0; c<images; ++c) {
ImageRef image(read_image(is));
subObjects->addImageRef(image);
}
// Read celdatas
int celdatas = read16(is);
for (int c=0; c<celdatas; ++c) {
CelDataRef celdata(read_celdata(is, subObjects));
subObjects->addCelDataRef(celdata);
}
// Read cels
int cels = read16(is); // Number of cels
for (int c=0; c<cels; ++c) {
// Read the cel
Cel* cel = subObjects->read_cel(is);
Cel* cel = read_cel(is, subObjects);
// Add the cel in the layer
static_cast<LayerImage*>(layer.get())->addCel(cel);
@ -105,12 +151,12 @@ Layer* read_layer(std::istream& is, SubObjectsIO* subObjects, Sprite* sprite)
case ObjectType::LayerFolder: {
// Create the layer set
layer.reset(new LayerFolder(sprite));
layer.reset(new LayerFolder(subObjects->sprite()));
// Number of sub-layers
int layers = read16(is);
for (int c=0; c<layers; c++) {
Layer* child = subObjects->read_layer(is);
Layer* child = read_layer(is, subObjects);
if (child)
static_cast<LayerFolder*>(layer.get())->addLayer(child);
else
@ -124,9 +170,10 @@ Layer* read_layer(std::istream& is, SubObjectsIO* subObjects, Sprite* sprite)
}
if (layer != NULL) {
if (layer) {
layer->setName(&name[0]);
layer->setFlags(static_cast<LayerFlags>(flags));
layer->setId(id);
}
return layer.release();

View File

@ -13,11 +13,7 @@
#include <iosfwd>
namespace doc {
class Cel;
class Image;
class Layer;
class Sprite;
class SubObjectsIO;
// Thrown when a invalid layer type is read from the istream.
@ -26,8 +22,8 @@ namespace doc {
InvalidLayerType(const char* msg) throw() : base::Exception(msg) { }
};
void write_layer(std::ostream& os, SubObjectsIO* subObjects, Layer* layer);
Layer* read_layer(std::istream& is, SubObjectsIO* subObjects, Sprite* sprite);
void write_layer(std::ostream& os, Layer* layer);
Layer* read_layer(std::istream& is, SubObjectsIO* subObjects);
} // namespace doc

View File

@ -18,6 +18,7 @@ namespace doc {
Path,
Mask,
Cel,
CelData,
LayerImage,
LayerFolder,
Sprite,

View File

@ -13,6 +13,7 @@
#include "base/memory.h"
#include "base/remove_from_container.h"
#include "base/unique_ptr.h"
#include "doc/cels_range.h"
#include "doc/doc.h"
#include "doc/image_bits.h"
#include "doc/primitives.h"
@ -208,6 +209,16 @@ LayerIndex Sprite::countLayers() const
return LayerIndex(folder()->getLayersCount());
}
LayerIndex Sprite::firstLayer() const
{
return LayerIndex(0);
}
LayerIndex Sprite::lastLayer() const
{
return LayerIndex(folder()->getLayersCount()-1);
}
Layer* Sprite::layer(int layerIndex) const
{
return indexToLayer(LayerIndex(layerIndex));
@ -397,55 +408,42 @@ void Sprite::setDurationForAllFrames(int msecs)
}
//////////////////////////////////////////////////////////////////////
// Images
// Shared Images and CelData (for linked Cels)
ImageRef Sprite::getImage(ObjectId imageId)
ImageRef Sprite::getImageRef(ObjectId imageId)
{
CelList cels;
getCels(cels);
for (auto& cel : cels) {
for (Cel* cel : cels()) {
if (cel->image()->id() == imageId)
return cel->imageRef();
}
return ImageRef(NULL);
return ImageRef(nullptr);
}
CelDataRef Sprite::getCelDataRef(ObjectId celDataId)
{
for (Cel* cel : cels()) {
if (cel->dataRef()->id() == celDataId)
return cel->dataRef();
}
return CelDataRef(nullptr);
}
//////////////////////////////////////////////////////////////////////
// Images
void Sprite::replaceImage(ObjectId curImageId, const ImageRef& newImage)
{
CelList cels;
getCels(cels);
for (auto& cel : cels) {
for (Cel* cel : cels()) {
if (cel->image()->id() == curImageId)
cel->setImage(newImage);
cel->data()->setImage(newImage);
}
}
void Sprite::getCels(CelList& cels) const
{
folder()->getCels(cels);
}
CelList Sprite::cels(frame_t frame) const
{
// TODO create a proper CelsIterator
CelList cels, final;
folder()->getCels(cels);
for (Cel* cel : cels) {
if (cel->frame() == frame)
final.push_back(cel);
}
return final;
}
// TODO replace it with a images iterator
void Sprite::getImages(std::vector<Image*>& images) const
{
CelList cels;
getCels(cels); // TODO create a cel iterator
for (const auto& cel : cels)
if (!cel->link())
images.push_back(cel->image());
for (const auto& cel : uniqueCels())
images.push_back(cel->image());
}
void Sprite::remapImages(frame_t frameFrom, frame_t frameTo, const std::vector<uint8_t>& mapping)
@ -453,12 +451,7 @@ void Sprite::remapImages(frame_t frameFrom, frame_t frameTo, const std::vector<u
ASSERT(m_format == IMAGE_INDEXED);
ASSERT(mapping.size() == 256);
CelList cels;
getCels(cels);
for (CelIterator it = cels.begin(); it != cels.end(); ++it) {
Cel* cel = *it;
for (Cel* cel : cels()) {
// Remap this Cel because is inside the specified range
if (cel->frame() >= frameFrom &&
cel->frame() <= frameTo) {
@ -524,6 +517,24 @@ void Sprite::pickCels(int x, int y, frame_t frame, int opacityThreshold, CelList
fflush(stdout);
}
//////////////////////////////////////////////////////////////////////
// CelsRange
CelsRange Sprite::cels() const
{
return CelsRange(this, frame_t(0), lastFrame());
}
CelsRange Sprite::cels(frame_t frame) const
{
return CelsRange(this, frame, frame);
}
CelsRange Sprite::uniqueCels() const
{
return CelsRange(this, frame_t(0), lastFrame(), CelsRange::UNIQUE);
}
//////////////////////////////////////////////////////////////////////
static Layer* index2layer(const Layer* layer, const LayerIndex& index, int* index_count)

View File

@ -9,6 +9,7 @@
#pragma once
#include "base/disable_copying.h"
#include "doc/cel_data.h"
#include "doc/cel_list.h"
#include "doc/color.h"
#include "doc/frame.h"
@ -23,6 +24,7 @@
namespace doc {
class CelsRange;
class Document;
class Image;
class Layer;
@ -78,6 +80,8 @@ namespace doc {
LayerImage* backgroundLayer() const;
LayerIndex countLayers() const;
LayerIndex firstLayer() const;
LayerIndex lastLayer() const;
Layer* layer(int layerIndex) const;
Layer* indexToLayer(LayerIndex index) const;
@ -115,18 +119,23 @@ namespace doc {
void setFrameRangeDuration(frame_t from, frame_t to, int msecs);
void setDurationForAllFrames(int msecs);
////////////////////////////////////////
// Shared Images and CelData (for linked Cels)
ImageRef getImageRef(ObjectId imageId);
CelDataRef getCelDataRef(ObjectId celDataId);
////////////////////////////////////////
// Images
ImageRef getImage(ObjectId imageId);
void replaceImage(ObjectId curImageId, const ImageRef& newImage);
void getCels(CelList& cels) const;
void getImages(std::vector<Image*>& images) const;
void remapImages(frame_t frameFrom, frame_t frameTo, const std::vector<uint8_t>& mapping);
void pickCels(int x, int y, frame_t frame, int opacityThreshold, CelList& cels) const;
// Returns the list of cels in the given frame
CelList cels(frame_t frame) const;
CelsRange cels() const;
CelsRange cels(frame_t frame) const;
CelsRange uniqueCels() const;
private:
Document* m_document;

108
src/doc/sprite_tests.cpp Normal file
View File

@ -0,0 +1,108 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gtest/gtest.h>
#include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/layer.h"
#include "doc/pixel_format.h"
#include "doc/sprite.h"
using namespace doc;
// lay1 = A _ B
// lay2 = C D E
TEST(Sprite, CelsRange)
{
Sprite* spr = new Sprite(IMAGE_RGB, 32, 32, 256);
spr->setTotalFrames(3);
LayerImage* lay1 = new LayerImage(spr);
LayerImage* lay2 = new LayerImage(spr);
spr->folder()->addLayer(lay1);
spr->folder()->addLayer(lay2);
ImageRef imgA(Image::create(IMAGE_RGB, 32, 32));
Cel* celA = new Cel(frame_t(0), imgA);
Cel* celB = Cel::createLink(celA);
celB->setFrame(frame_t(2));
lay1->addCel(celA);
lay1->addCel(celB);
ImageRef imgC(Image::create(IMAGE_RGB, 32, 32));
Cel* celC = new Cel(frame_t(0), imgC);
Cel* celD = Cel::createCopy(celC);
Cel* celE = Cel::createLink(celD);
celD->setFrame(frame_t(1));
celE->setFrame(frame_t(2));
lay2->addCel(celC);
lay2->addCel(celD);
lay2->addCel(celE);
int i = 0;
for (Cel* cel : spr->cels()) {
switch (i) {
case 0: EXPECT_EQ(cel, celA); break;
case 1: EXPECT_EQ(cel, celB); break;
case 2: EXPECT_EQ(cel, celC); break;
case 3: EXPECT_EQ(cel, celD); break;
case 4: EXPECT_EQ(cel, celE); break;
}
++i;
}
EXPECT_EQ(5, i);
i = 0;
for (Cel* cel : spr->uniqueCels()) {
switch (i) {
case 0: EXPECT_EQ(cel, celA); break;
case 1: EXPECT_EQ(cel, celC); break;
case 2: EXPECT_EQ(cel, celD); break;
}
++i;
}
EXPECT_EQ(3, i);
i = 0;
for (Cel* cel : spr->cels(frame_t(0))) {
switch (i) {
case 0: EXPECT_EQ(cel, celA); break;
case 1: EXPECT_EQ(cel, celC); break;
}
++i;
}
EXPECT_EQ(2, i);
i = 0;
for (Cel* cel : spr->cels(frame_t(1))) {
switch (i) {
case 0: EXPECT_EQ(cel, celD); break;
}
++i;
}
EXPECT_EQ(1, i);
i = 0;
for (Cel* cel : spr->cels(frame_t(2))) {
switch (i) {
case 0: EXPECT_EQ(cel, celB); break;
case 1: EXPECT_EQ(cel, celE); break;
}
++i;
}
EXPECT_EQ(2, i);
}
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

70
src/doc/subobjects_io.cpp Normal file
View File

@ -0,0 +1,70 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "doc/subobjects_io.h"
#include "doc/cel.h"
#include "doc/cel_io.h"
#include "doc/image.h"
#include "doc/image_io.h"
#include "doc/layer.h"
#include "doc/layer_io.h"
#include "doc/sprite.h"
namespace doc {
using namespace doc;
SubObjectsIO::SubObjectsIO(Sprite* sprite)
: m_sprite(sprite)
{
}
void SubObjectsIO::addImageRef(const ImageRef& image)
{
ASSERT(image);
ASSERT(!getImageRef(image->id()));
m_images.insert(std::make_pair(image->id(), image));
}
ImageRef SubObjectsIO::getImageRef(ObjectId imageId)
{
auto it = m_images.find(imageId);
if (it != m_images.end()) {
ImageRef image = it->second;
ASSERT(image->id() == imageId);
ASSERT(!m_sprite->getImageRef(imageId));
return image;
}
else
return m_sprite->getImageRef(imageId);
}
void SubObjectsIO::addCelDataRef(const CelDataRef& celdata)
{
ASSERT(celdata);
ASSERT(!getCelDataRef(celdata->id()));
m_celdatas.insert(std::make_pair(celdata->id(), celdata));
}
CelDataRef SubObjectsIO::getCelDataRef(ObjectId celdataId)
{
auto it = m_celdatas.find(celdataId);
if (it != m_celdatas.end()) {
CelDataRef celdata = it->second;
ASSERT(celdata->id() == celdataId);
ASSERT(!m_sprite->getCelDataRef(celdataId));
return celdata;
}
else
return m_sprite->getCelDataRef(celdataId);
}
} // namespace doc

View File

@ -8,28 +8,38 @@
#define DOC_SUBOBJECTS_IO_H_INCLUDED
#pragma once
#include "doc/cel_data.h"
#include "doc/image_ref.h"
#include <iosfwd>
#include <map>
namespace doc {
class Sprite;
class Cel;
class Image;
class Layer;
// Interface used to read sub-objects of a layer or cel.
// Helper class used to read children-objects by layers and cels.
class SubObjectsIO {
public:
virtual ~SubObjectsIO() { }
SubObjectsIO(Sprite* sprite);
// How to write cels, images, and sub-layers
virtual void write_cel(std::ostream& os, Cel* cel) = 0;
virtual void write_image(std::ostream& os, Image* image) = 0;
virtual void write_layer(std::ostream& os, Layer* layer) = 0;
Sprite* sprite() const { return m_sprite; }
// How to read cels, images, and sub-layers
virtual Cel* read_cel(std::istream& is) = 0;
virtual Image* read_image(std::istream& is) = 0;
virtual Layer* read_layer(std::istream& is) = 0;
void addImageRef(const ImageRef& image);
void addCelDataRef(const CelDataRef& celdata);
ImageRef getImageRef(ObjectId imageId);
CelDataRef getCelDataRef(ObjectId celdataId);
private:
Sprite* m_sprite;
// Images list that can be queried from doc::read_celdata() using
// getImageRef().
std::map<ObjectId, ImageRef> m_images;
// CelData list that can be queried from doc::read_cel() using
// getCelDataRef().
std::map<ObjectId, CelDataRef> m_celdatas;
};
} // namespace doc

View File

@ -26,6 +26,7 @@
#include <algorithm>
#include <limits>
#include <vector>
#include <map>
namespace render {
@ -49,7 +50,6 @@ Palette* create_palette_from_rgb(
palette = new Palette(frame_t(0), 256);
bool has_background_layer = (sprite->backgroundLayer() != NULL);
Image* flat_image;
ImagesCollector images(
sprite->folder(), // All layers
@ -58,24 +58,29 @@ Palette* create_palette_from_rgb(
false); // forWrite=false, read only
// Add a flat image with the current sprite's frame rendered
flat_image = Image::create(sprite->pixelFormat(),
sprite->width(), sprite->height());
ImageRef flat_image(Image::create(sprite->pixelFormat(),
sprite->width(), sprite->height()));
render::Render().renderSprite(flat_image, sprite, frameNumber);
// Create an array of images
size_t nimage = images.size() + 1; // +1 for flat_image
std::vector<Image*> image_array(nimage);
std::vector<Image*> image_array;
std::map<ObjectId, Image*> used_images;
size_t c = 0;
for (ImagesCollector::ItemsIterator it=images.begin(); it!=images.end(); ++it)
image_array[c++] = it->image();
image_array[c++] = flat_image; // The 'flat_image'
for (auto it=images.begin(); it!=images.end(); ++it) {
Image* image = it->image();
ObjectId imageId = image->id();
if (used_images.find(imageId) == used_images.end()) {
used_images.insert(std::make_pair(imageId, image));
image_array.push_back(image);
}
}
image_array.push_back(flat_image);
// Generate an optimized palette for all images
create_palette_from_images(image_array, palette, has_background_layer);
delete flat_image;
return palette;
}