/* ASE - Allegro Sprite Editor * Copyright (C) 2001-2010 David Capello * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "raster/blend.h" #include "raster/cel.h" #include "raster/image.h" #include "raster/layer.h" #include "raster/sprite.h" #include "raster/stock.h" #include "raster/undo.h" static bool has_cels(const Layer* layer, int frame); ////////////////////////////////////////////////////////////////////// // Layer class Layer::Layer(GfxObjType type, Sprite* sprite) : GfxObj(type) { ASSERT(type == GFXOBJ_LAYER_IMAGE || type == GFXOBJ_LAYER_FOLDER); setName("Layer"); m_sprite = sprite; m_parent = NULL; m_flags = LAYER_IS_READABLE | LAYER_IS_WRITABLE; } Layer::Layer(const Layer* src_layer, Sprite* dst_sprite) : GfxObj(src_layer->getType()) { m_sprite = dst_sprite; m_parent = NULL; m_flags = LAYER_IS_READABLE | LAYER_IS_WRITABLE; setName(src_layer->getName()); m_flags = src_layer->m_flags; } Layer::~Layer() { } int Layer::getMemSize() const { return sizeof(Layer); } /** * Gets the previous layer of "layer" that are in the parent set. */ Layer* Layer::get_prev() const { if (m_parent != NULL) { LayerConstIterator it = std::find(m_parent->get_layer_begin(), m_parent->get_layer_end(), this); if (it != m_parent->get_layer_end() && it != m_parent->get_layer_begin()) { it--; return *it; } } return NULL; } Layer* Layer::get_next() const { if (m_parent != NULL) { LayerConstIterator it = std::find(m_parent->get_layer_begin(), m_parent->get_layer_end(), this); if (it != m_parent->get_layer_end()) { it++; if (it != m_parent->get_layer_end()) return *it; } } return NULL; } ////////////////////////////////////////////////////////////////////// // LayerImage class LayerImage::LayerImage(Sprite* sprite) : Layer(GFXOBJ_LAYER_IMAGE, sprite) { m_blend_mode = BLEND_MODE_NORMAL; } LayerImage::LayerImage(const LayerImage* src_layer, Sprite* dst_sprite) : Layer(src_layer, dst_sprite) { set_blend_mode(src_layer->get_blend_mode()); try { // copy cels CelConstIterator it = src_layer->getCelBegin(); CelConstIterator end = src_layer->getCelEnd(); for (; it != end; ++it) { const Cel* cel = *it; Cel* cel_copy = cel_new_copy(cel); ASSERT((cel->image >= 0) && (cel->image < src_layer->getSprite()->getStock()->size())); Image* image = src_layer->getSprite()->getStock()->getImage(cel->image); ASSERT(image != NULL); Image* image_copy = image_new_copy(image); cel_copy->image = dst_sprite->getStock()->addImage(image_copy); if (dst_sprite->getUndo()->isEnabled()) dst_sprite->getUndo()->undo_add_image(dst_sprite->getStock(), cel_copy->image); addCel(cel_copy); } } catch (...) { destroy_all_cels(); throw; } } LayerImage::~LayerImage() { destroy_all_cels(); } int LayerImage::getMemSize() const { int size = sizeof(LayerImage); CelConstIterator it = getCelBegin(); CelConstIterator end = getCelEnd(); for (; it != end; ++it) { const Cel* cel = *it; size += cel->getMemSize(); const Image* image = getSprite()->getStock()->getImage(cel->image); size += image->getMemSize(); } return size; } void LayerImage::destroy_all_cels() { CelIterator it = getCelBegin(); CelIterator end = getCelEnd(); for (; it != end; ++it) { Cel* cel = *it; Image* image = getSprite()->getStock()->getImage(cel->image); ASSERT(image != NULL); getSprite()->getStock()->removeImage(image); image_free(image); cel_free(cel); } m_cels.clear(); } void LayerImage::set_blend_mode(int blend_mode) { m_blend_mode = blend_mode; } void LayerImage::getCels(CelList& cels) { CelIterator it = getCelBegin(); CelIterator end = getCelEnd(); for (; it != end; ++it) cels.push_back(*it); } void LayerImage::addCel(Cel *cel) { CelIterator it = getCelBegin(); CelIterator end = getCelEnd(); for (; it != end; ++it) { if ((*it)->frame > cel->frame) break; } m_cels.insert(it, cel); } /** * Removes the cel from the layer. * * It doesn't destroy the cel, you have to delete it after calling * this routine. */ void LayerImage::removeCel(Cel *cel) { CelIterator it = std::find(m_cels.begin(), m_cels.end(), cel); ASSERT(it != m_cels.end()); m_cels.erase(it); } const Cel* LayerImage::getCel(int frame) const { CelConstIterator it = getCelBegin(); CelConstIterator end = getCelEnd(); for (; it != end; ++it) { const Cel* cel = *it; if (cel->frame == frame) return cel; } return NULL; } Cel* LayerImage::getCel(int frame) { CelIterator it = getCelBegin(); CelIterator end = getCelEnd(); for (; it != end; ++it) { Cel* cel = *it; if (cel->frame == frame) return cel; } return NULL; } /** * Configures some properties of the specified layer to make it as the * "Background" of the sprite. * * You can't use this routine if the sprite already has a background * layer. */ void LayerImage::configureAsBackground() { ASSERT(getSprite() != NULL); ASSERT(getSprite()->getBackgroundLayer() == NULL); *flags_addr() |= LAYER_IS_LOCKMOVE | LAYER_IS_BACKGROUND; setName("Background"); getSprite()->getFolder()->move_layer(this, NULL); } ////////////////////////////////////////////////////////////////////// // LayerFolder class LayerFolder::LayerFolder(Sprite* sprite) : Layer(GFXOBJ_LAYER_FOLDER, sprite) { setName("Layer Set"); } LayerFolder::LayerFolder(const LayerFolder* src_layer, Sprite* dst_sprite) : Layer(src_layer, dst_sprite) { try { LayerConstIterator it = src_layer->get_layer_begin(); LayerConstIterator end = src_layer->get_layer_end(); for (; it != end; ++it) { // duplicate the child Layer* child_copy = (*it)->duplicate_for(dst_sprite); // add the new child in the layer copy add_layer(child_copy); } } catch (...) { destroyAllLayers(); throw; } } LayerFolder::~LayerFolder() { destroyAllLayers(); } void LayerFolder::destroyAllLayers() { LayerIterator it = get_layer_begin(); LayerIterator end = get_layer_end(); for (; it != end; ++it) { Layer* layer = *it; delete layer; } m_layers.clear(); } int LayerFolder::getMemSize() const { int size = sizeof(LayerFolder); LayerConstIterator it = get_layer_begin(); LayerConstIterator end = get_layer_end(); for (; it != end; ++it) { const Layer* layer = *it; size += layer->getMemSize(); } return size; } void LayerFolder::getCels(CelList& cels) { LayerIterator it = get_layer_begin(); LayerIterator end = get_layer_end(); for (; it != end; ++it) (*it)->getCels(cels); } void LayerFolder::add_layer(Layer* layer) { m_layers.push_back(layer); layer->set_parent(this); } void LayerFolder::remove_layer(Layer* layer) { LayerIterator it = std::find(m_layers.begin(), m_layers.end(), layer); ASSERT(it != m_layers.end()); m_layers.erase(it); layer->set_parent(NULL); } void LayerFolder::move_layer(Layer* layer, Layer* after) { LayerIterator it = std::find(m_layers.begin(), m_layers.end(), layer); ASSERT(it != m_layers.end()); m_layers.erase(it); if (after) { LayerIterator after_it = std::find(m_layers.begin(), m_layers.end(), after); ASSERT(after_it != m_layers.end()); after_it++; m_layers.insert(after_it, layer); } else m_layers.push_front(layer); // TODO // if (after) { // JLink before = jlist_find(m_layers, after)->next; // jlist_insert_before(m_layers, before, layer); // } // else // jlist_prepend(m_layers, layer); } ////////////////////////////////////////////////////////////////////// /** * Returns a new layer (flat_layer) with all "layer" rendered frame by * frame from "frmin" to "frmax" (inclusive). "layer" can be a set of * layers, so the routine flattens all children to an unique output * layer. * * @param dst_sprite The sprite where to put the new flattened layer. * @param src_layer Generally a set of layers to be flattened. */ Layer* layer_new_flatten_copy(Sprite* dst_sprite, const Layer* src_layer, int x, int y, int w, int h, int frmin, int frmax) { LayerImage* flat_layer = new LayerImage(dst_sprite); try { for (int frame=frmin; frame<=frmax; frame++) { /* does this frame have cels to render? */ if (has_cels(src_layer, frame)) { /* create a new image */ Image* image = image_new(flat_layer->getSprite()->getImgType(), w, h); try { /* create the new cel for the output layer (add the image to stock too) */ Cel* cel = cel_new(frame, flat_layer->getSprite()->getStock()->addImage(image)); cel_set_position(cel, x, y); /* clear the image and render this frame */ image_clear(image, 0); layer_render(src_layer, image, -x, -y, frame); flat_layer->addCel(cel); } catch (...) { delete image; throw; } } } } catch (...) { delete flat_layer; throw; } return flat_layer; } void layer_render(const Layer* layer, Image* image, int x, int y, int frame) { if (!layer->is_readable()) return; switch (layer->getType()) { case GFXOBJ_LAYER_IMAGE: { const Cel* cel = static_cast(layer)->getCel(frame); Image* src_image; if (cel) { ASSERT((cel->image >= 0) && (cel->image < layer->getSprite()->getStock()->size())); src_image = layer->getSprite()->getStock()->getImage(cel->image); ASSERT(src_image != NULL); image_merge(image, src_image, cel->x + x, cel->y + y, MID (0, cel->opacity, 255), static_cast(layer)->get_blend_mode()); } break; } case GFXOBJ_LAYER_FOLDER: { LayerConstIterator it = static_cast(layer)->get_layer_begin(); LayerConstIterator end = static_cast(layer)->get_layer_end(); for (; it != end; ++it) layer_render(*it, image, x, y, frame); break; } } } /** * Returns true if the "layer" (or him childs) has cels to render in * frame. */ static bool has_cels(const Layer* layer, int frame) { if (!layer->is_readable()) return false; switch (layer->getType()) { case GFXOBJ_LAYER_IMAGE: return static_cast(layer)->getCel(frame) ? true: false; case GFXOBJ_LAYER_FOLDER: { LayerConstIterator it = static_cast(layer)->get_layer_begin(); LayerConstIterator end = static_cast(layer)->get_layer_end(); for (; it != end; ++it) { if (has_cels(*it, frame)) return true; } break; } } return false; }