TEST(LayerList, AreLayersAdjacent) { std::unique_ptr<Sprite> spr(new Sprite(IMAGE_RGB, 32, 32, 256)); LayerGroup* root = spr->root(); Layer* layer1 = new LayerImage(spr.get()); Layer* layer2 = new LayerImage(spr.get()); Layer* layer3 = new LayerImage(spr.get()); Layer* layer4 = new LayerImage(spr.get()); root->addLayer(layer1); root->addLayer(layer2); root->addLayer(layer3); root->addLayer(layer4); LayerList layers; root->allLayers(layers); EXPECT_EQ(4, layers.size()); EXPECT_TRUE(are_layers_adjacent(layers)); std::reverse(layers.begin(), layers.end()); EXPECT_EQ(4, layers.size()); EXPECT_TRUE(are_layers_adjacent(layers)); layers.erase(layers.begin()); EXPECT_EQ(3, layers.size()); EXPECT_TRUE(are_layers_adjacent(layers)); layers.erase(layers.begin()+1); EXPECT_EQ(2, layers.size()); EXPECT_FALSE(are_layers_adjacent(layers)); std::reverse(layers.begin(), layers.end()); EXPECT_EQ(2, layers.size()); EXPECT_FALSE(are_layers_adjacent(layers)); layers.erase(layers.begin()); EXPECT_EQ(1, layers.size()); EXPECT_TRUE(are_layers_adjacent(layers)); }
void Controller::DispatchUpdate(DeltaTime &delta) { if (!mScene) { return; } LayerList *layers = &mScene->GetLayers(); LayerIter liter = layers->begin(); mScene->Update(delta); while (liter != layers->end()) { (*liter)->UpdateSelfAndChildren(delta); liter++; } }
// Reports whether the passed in layers have visible damage or are otherwise // dirty because render properties changed. // Assumes that layers in the list belong to same composition. ie. damage to // one layer affects the others. A warning is logged if the assumption is wrong. bool GLXGraphicsystem::needsRedraw(LayerList layers) { // TODO: Ignore damage from completely obscured surfaces for (LayerListConstIterator layer = layers.begin(); layer != layers.end(); layer++) { if ((*layer)->getLayerType() == Hardware && layers.size() > 1) { // Damage in a hardware layer should not imply a redraw in other layers LOG_WARNING("GLXGraphicsystem", "needsRedraw() called with layers not in the same composition"); } if (needsRedraw(*layer)) { return true; } } return false; }
void GLXGraphicsystem::renderSWLayers(LayerList layers, bool clear) { // This is a stub. // // TODO: render in a more optimal way // 1. Turn off blending for first surface rendered // 2. Don't clear when it's legal to avoid it // eg. a fullscreen opaque surface exists // 3. Render multiple surfaces at time via multi-texturing // 4. Remove fully obscured layers/surfaces if (clear) { clearBackground(); } for (LayerListConstIterator layer = layers.begin(); layer != layers.end(); layer++) { renderSWLayer(*layer, false); // Don't clear } }
static DocRange drop_range_op( Doc* doc, const Op op, const DocRange& from, DocRangePlace place, const TagsHandling tagsHandling, DocRange to) { // Convert "first child" operation into a insert after last child. LayerGroup* parent = nullptr; if (to.type() == DocRange::kLayers && !to.selectedLayers().empty()) { if (place == kDocRangeFirstChild && (*to.selectedLayers().begin())->isGroup()) { place = kDocRangeAfter; parent = static_cast<LayerGroup*>((*to.selectedLayers().begin())); to.clearRange(); to.startRange(parent->lastLayer(), -1, DocRange::kLayers); to.endRange(parent->lastLayer(), -1); } else { parent = (*to.selectedLayers().begin())->parent(); } // Check that we're not moving a group inside itself for (auto moveThis : from.selectedLayers()) { if (moveThis == parent || (moveThis->isGroup() && has_child(static_cast<LayerGroup*>(moveThis), parent))) return from; } } if (place != kDocRangeBefore && place != kDocRangeAfter) { ASSERT(false); throw std::invalid_argument("Invalid 'place' argument"); } Sprite* sprite = doc->sprite(); // Check noop/trivial/do nothing cases, i.e., move a range to the same place. // Also check invalid cases, like moving a Background layer. switch (from.type()) { case DocRange::kCels: if (from == to) return from; break; case DocRange::kFrames: if (op == Move) { // Simple cases with one continuos range of frames that are a // no-op. if ((from.selectedFrames().ranges() == 1) && ((to.firstFrame() >= from.firstFrame() && to.lastFrame() <= from.lastFrame()) || (place == kDocRangeBefore && to.firstFrame() == from.lastFrame()+1) || (place == kDocRangeAfter && to.lastFrame() == from.firstFrame()-1)) && // If there are tags, this might not be a no-op (sprite->frameTags().empty() || tagsHandling == kDontAdjustTags)) { return from; } } break; case DocRange::kLayers: if (op == Move) { SelectedLayers srcSelLayers = from.selectedLayers(); SelectedLayers dstSelLayers = to.selectedLayers(); LayerList srcLayers = srcSelLayers.toLayerList(); LayerList dstLayers = dstSelLayers.toLayerList(); ASSERT(!srcLayers.empty()); if (srcLayers.empty()) return from; // dstLayers can be nullptr when we insert the first child in // a group. // Check no-ops when we move layers at the same level (all // layers with the same parent), all adjacents, and which are // moved to the same place. if (!dstSelLayers.empty() && srcSelLayers.hasSameParent() && dstSelLayers.hasSameParent() && are_layers_adjacent(srcLayers) && are_layers_adjacent(dstLayers)) { for (Layer* srcLayer : srcLayers) if (dstSelLayers.contains(srcLayer)) return from; if ((place == kDocRangeBefore && dstLayers.front() == srcLayers.back()->getNext()) || (place == kDocRangeAfter && dstLayers.back() == srcLayers.front()->getPrevious())) return from; } // We cannot move the background for (Layer* layer : srcSelLayers) if (layer->isBackground()) throw std::runtime_error("The background layer cannot be moved"); } // Before background if (place == kDocRangeBefore) { for (Layer* background : to.selectedLayers()) { if (background && background->isBackground()) throw std::runtime_error("You cannot move or copy something below the background layer"); } } break; } const char* undoLabel = NULL; switch (op) { case Move: undoLabel = "Move Range"; break; case Copy: undoLabel = "Copy Range"; break; default: ASSERT(false); throw std::invalid_argument("Invalid 'op' argument"); } DocRange resultRange; { const app::Context* context = static_cast<app::Context*>(doc->context()); const ContextReader reader(context); ContextWriter writer(reader, 500); Transaction transaction(writer.context(), undoLabel, ModifyDocument); DocApi api = doc->getApi(transaction); // TODO Try to add the range with just one call to DocApi // methods, to avoid generating a lot of cmd::SetCelFrame (see // DocApi::setCelFramePosition() function). switch (from.type()) { case DocRange::kCels: { LayerList allLayers = sprite->allBrowsableLayers(); if (allLayers.empty()) break; LayerList srcLayers = from.selectedLayers().toLayerList(); LayerList dstLayers = to.selectedLayers().toLayerList(); if (srcLayers.empty() || dstLayers.empty()) throw std::invalid_argument("You need to specify a non-empty cels range"); if (find_layer_index(allLayers, srcLayers.front()) < find_layer_index(allLayers, dstLayers.front())) { std::reverse(srcLayers.begin(), srcLayers.end()); std::reverse(dstLayers.begin(), dstLayers.end()); } if (from.firstFrame() < to.firstFrame()) { auto srcFrames = from.selectedFrames().makeReverse(); auto dstFrames = to.selectedFrames().makeReverse(); move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames); } else { const auto& srcFrames = from.selectedFrames(); const auto& dstFrames = to.selectedFrames(); move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames); } resultRange = to; break; } case DocRange::kFrames: { frame_t dstFrame; if (place == kDocRangeBefore) dstFrame = to.firstFrame(); else dstFrame = to.lastFrame(); resultRange = move_or_copy_frames(api, op, sprite, from, dstFrame, place, tagsHandling); break; } case DocRange::kLayers: { LayerList allLayers = sprite->allBrowsableLayers(); if (allLayers.empty()) break; LayerList srcLayers = from.selectedLayers().toLayerList(); LayerList dstLayers = to.selectedLayers().toLayerList(); ASSERT(!srcLayers.empty()); switch (op) { case Move: if (place == kDocRangeBefore) { Layer* beforeThis = (!dstLayers.empty() ? dstLayers.front(): nullptr); Layer* afterThis = nullptr; for (Layer* srcLayer : srcLayers) { if (afterThis) api.restackLayerAfter(srcLayer, parent, afterThis); else api.restackLayerBefore(srcLayer, parent, beforeThis); afterThis = srcLayer; } } else if (place == kDocRangeAfter) { Layer* afterThis = (!dstLayers.empty() ? dstLayers.back(): nullptr); for (Layer* srcLayer : srcLayers) { api.restackLayerAfter(srcLayer, parent, afterThis); afterThis = srcLayer; } } // Same set of layers than the "from" range resultRange = from; break; case Copy: { if (place == kDocRangeBefore) { Layer* beforeThis = (!dstLayers.empty() ? dstLayers.front(): nullptr); for (Layer* srcLayer : srcLayers) { Layer* copiedLayer = api.duplicateLayerBefore( srcLayer, parent, beforeThis); resultRange.startRange(copiedLayer, -1, DocRange::kLayers); resultRange.endRange(copiedLayer, -1); } } else if (place == kDocRangeAfter) { std::reverse(srcLayers.begin(), srcLayers.end()); Layer* afterThis = (!dstLayers.empty() ? dstLayers.back(): nullptr); for (Layer* srcLayer : srcLayers) { Layer* copiedLayer = api.duplicateLayerAfter( srcLayer, parent, afterThis); resultRange.startRange(copiedLayer, -1, DocRange::kLayers); resultRange.endRange(copiedLayer, -1); } } break; } } break; } } if (resultRange.type() != DocRange::kNone) transaction.setNewDocRange(resultRange); transaction.commit(); } return resultRange; }
void ImportSpriteSheetCommand::onExecute(Context* context) { ImportSpriteSheetWindow window(context); retry:; window.openWindowInForeground(); if (!window.ok()) return; Document* document = window.document(); DocumentPreferences* docPref = window.docPref(); gfx::Rect frameBounds = window.frameBounds(); // The user don't select a sheet yet. if (!document) { Alert::show("Import Sprite Sheet<<Select a sprite first.||&Close"); goto retry; } // The list of frames imported from the sheet std::vector<ImageRef> animation; try { Sprite* sprite = document->sprite(); frame_t currentFrame = context->activeSite().frame(); render::Render render; // As first step, we cut each tile and add them into "animation" list. for (int y=frameBounds.y; y<sprite->height(); y += frameBounds.h) { for (int x=frameBounds.x; x<sprite->width(); x += frameBounds.w) { ImageRef resultImage( Image::create(sprite->pixelFormat(), frameBounds.w, frameBounds.h)); // Render the portion of sheet. render.renderSprite(resultImage.get(), sprite, currentFrame, gfx::Clip(0, 0, x, y, frameBounds.w, frameBounds.h)); animation.push_back(resultImage); } } if (animation.size() == 0) { Alert::show("Import Sprite Sheet" "<<The specified rectangle does not create any tile." "<<Select a rectangle inside the sprite region." "||&OK"); return; } // The following steps modify the sprite, so we wrap all // operations in a undo-transaction. ContextWriter writer(context); Transaction transaction(writer.context(), "Import Sprite Sheet", ModifyDocument); DocumentApi api = document->getApi(transaction); // Add the layer in the sprite. LayerImage* resultLayer = api.newLayer(sprite); // Add all frames+cels to the new layer for (size_t i=0; i<animation.size(); ++i) { // Create the cel. base::UniquePtr<Cel> resultCel(new Cel(frame_t(i), animation[i])); // Add the cel in the layer. api.addCel(resultLayer, resultCel); resultCel.release(); } // Copy the list of layers (because we will modify it in the iteration). LayerList layers = sprite->folder()->getLayersList(); // Remove all other layers for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) { if (*it != resultLayer) api.removeLayer(*it); } // Change the number of frames api.setTotalFrames(sprite, frame_t(animation.size())); // Set the size of the sprite to the tile size. api.setSpriteSize(sprite, frameBounds.w, frameBounds.h); transaction.commit(); ASSERT(docPref); if (docPref) docPref->importSpriteSheet.bounds(frameBounds); } catch (...) { throw; } update_screen_for_document(document); }
void onImport() { // The user don't select a sheet yet. if (!m_document) { Alert::show("Import Sprite Sheet<<Select a sprite first.||&Close"); return; } // The list of frames imported from the sheet std::vector<Image*> animation; try { Sprite* sprite = m_document->getSprite(); FrameNumber currentFrame = m_context->getActiveLocation().frame(); // As first step, we cut each tile and add them into "animation" list. for (int y=m_rect.y; y<sprite->getHeight(); y += m_rect.h) { for (int x=m_rect.x; x<sprite->getWidth(); x += m_rect.w) { base::UniquePtr<Image> resultImage(Image::create(sprite->getPixelFormat(), m_rect.w, m_rect.h)); // Clear the image with mask color. image_clear(resultImage, 0); // Render the portion of sheet. sprite->render(resultImage, -x, -y, currentFrame); animation.push_back(resultImage); resultImage.release(); } } if (animation.size() == 0) { Alert::show("Import Sprite Sheet" "<<The specified rectangle does not create any tile." "<<Select a rectangle inside the sprite region." "||&OK"); return; } // The following steps modify the sprite, so we wrap all // operations in a undo-transaction. ContextWriter writer(m_context); UndoTransaction undoTransaction(writer.context(), "Import Sprite Sheet", undo::ModifyDocument); DocumentApi api = m_document->getApi(); // Add the layer in the sprite. LayerImage* resultLayer = api.newLayer(sprite); // Add all frames+cels to the new layer for (size_t i=0; i<animation.size(); ++i) { int indexInStock; // Add the image into the sprite's stock indexInStock = api.addImageInStock(sprite, animation[i]); animation[i] = NULL; // Create the cel. base::UniquePtr<Cel> resultCel(new Cel(FrameNumber(i), indexInStock)); // Add the cel in the layer. api.addCel(resultLayer, resultCel); resultCel.release(); } // Copy the list of layers (because we will modify it in the iteration). LayerList layers = sprite->getFolder()->getLayersList(); // Remove all other layers for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) { if (*it != resultLayer) api.removeLayer(*it); } // Change the number of frames api.setTotalFrames(sprite, FrameNumber(animation.size())); // Set the size of the sprite to the tile size. api.setSpriteSize(sprite, m_rect.w, m_rect.h); undoTransaction.commit(); } catch (...) { for (size_t i=0; i<animation.size(); ++i) delete animation[i]; throw; } update_screen_for_document(m_document); closeWindow(NULL); }
void UndoTransaction::flattenLayers(int bgcolor) { Image* cel_image; Cel* cel; int frame; // create a temporary image UniquePtr<Image> image_wrap(Image::create(m_sprite->getPixelFormat(), m_sprite->getWidth(), m_sprite->getHeight())); Image* image = image_wrap.get(); /* get the background layer from the sprite */ LayerImage* background = m_sprite->getBackgroundLayer(); if (!background) { /* if there aren't a background layer we must to create the background */ background = new LayerImage(m_sprite); if (isEnabled()) m_undoHistory->pushUndoer(new undoers::AddLayer(m_undoHistory->getObjects(), m_sprite->getFolder(), background)); m_sprite->getFolder()->add_layer(background); if (isEnabled()) m_undoHistory->pushUndoer(new undoers::MoveLayer(m_undoHistory->getObjects(), background)); background->configureAsBackground(); } /* copy all frames to the background */ for (frame=0; frame<m_sprite->getTotalFrames(); frame++) { /* clear the image and render this frame */ image_clear(image, bgcolor); layer_render(m_sprite->getFolder(), image, 0, 0, frame); cel = background->getCel(frame); if (cel) { cel_image = m_sprite->getStock()->getImage(cel->getImage()); ASSERT(cel_image != NULL); /* we have to save the current state of `cel_image' in the undo */ if (isEnabled()) { Dirty* dirty = new Dirty(cel_image, image); dirty->saveImagePixels(cel_image); m_undoHistory->pushUndoer(new undoers::DirtyArea( m_undoHistory->getObjects(), cel_image, dirty)); delete dirty; } } else { /* if there aren't a cel in this frame in the background, we have to create a copy of the image for the new cel */ cel_image = Image::createCopy(image); /* TODO error handling: if (!cel_image) { ... } */ /* here we create the new cel (with the new image `cel_image') */ cel = new Cel(frame, m_sprite->getStock()->addImage(cel_image)); /* TODO error handling: if (!cel) { ... } */ /* and finally we add the cel in the background */ background->addCel(cel); } image_copy(cel_image, image, 0, 0); } /* select the background */ if (m_sprite->getCurrentLayer() != background) { if (isEnabled()) m_undoHistory->pushUndoer(new undoers::SetCurrentLayer( m_undoHistory->getObjects(), m_sprite)); m_sprite->setCurrentLayer(background); } // Remove old layers. LayerList layers = m_sprite->getFolder()->get_layers_list(); LayerIterator it = layers.begin(); LayerIterator end = layers.end(); for (; it != end; ++it) { if (*it != background) { Layer* old_layer = *it; // Remove the layer if (isEnabled()) m_undoHistory->pushUndoer(new undoers::RemoveLayer(m_undoHistory->getObjects(), old_layer)); m_sprite->getFolder()->remove_layer(old_layer); // Destroy the layer delete old_layer; } } }
DocDiff compare_docs(const Doc* a, const Doc* b) { DocDiff diff; // Don't compare filenames //if (a->filename() != b->filename())... // Compare sprite specs if (a->sprite()->width() != b->sprite()->width() || a->sprite()->height() != b->sprite()->height() || a->sprite()->pixelFormat() != b->sprite()->pixelFormat()) { diff.anything = diff.canvas = true; } // Frames layers if (a->sprite()->totalFrames() != b->sprite()->totalFrames()) { diff.anything = diff.totalFrames = true; } else { for (frame_t f=0; f<a->sprite()->totalFrames(); ++f) { if (a->sprite()->frameDuration(f) != b->sprite()->frameDuration(f)) { diff.anything = diff.frameDuration = true; break; } } } // Tags if (a->sprite()->frameTags().size() != b->sprite()->frameTags().size()) { diff.anything = diff.frameTags = true; } else { auto aIt = a->sprite()->frameTags().begin(), aEnd = a->sprite()->frameTags().end(); auto bIt = b->sprite()->frameTags().begin(), bEnd = b->sprite()->frameTags().end(); for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) { const FrameTag* aTag = *aIt; const FrameTag* bTag = *bIt; if (aTag->fromFrame() != bTag->fromFrame() || aTag->toFrame() != bTag->toFrame() || aTag->name() != bTag->name() || aTag->color() != bTag->color() || aTag->aniDir() != bTag->aniDir()) { diff.anything = diff.frameTags = true; } } } // Palettes layers if (a->sprite()->getPalettes().size() != b->sprite()->getPalettes().size()) { const PalettesList& aPals = a->sprite()->getPalettes(); const PalettesList& bPals = b->sprite()->getPalettes(); auto aIt = aPals.begin(), aEnd = aPals.end(); auto bIt = bPals.begin(), bEnd = bPals.end(); for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) { const Palette* aPal = *aIt; const Palette* bPal = *bIt; if (aPal->countDiff(bPal, nullptr, nullptr)) { diff.anything = diff.palettes = true; break; } } } // Compare layers if (a->sprite()->allLayersCount() != b->sprite()->allLayersCount()) { diff.anything = diff.layers = true; } else { LayerList aLayers = a->sprite()->allLayers(); LayerList bLayers = b->sprite()->allLayers(); auto aIt = aLayers.begin(), aEnd = aLayers.end(); auto bIt = bLayers.begin(), bEnd = bLayers.end(); for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) { const Layer* aLay = *aIt; const Layer* bLay = *bIt; if (aLay->type() != bLay->type() || aLay->name() != bLay->name() || aLay->flags() != bLay->flags() || (aLay->isImage() && bLay->isImage() && (((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity()))) { diff.anything = diff.layers = true; break; } if (diff.totalFrames) { for (frame_t f=0; f<a->sprite()->totalFrames(); ++f) { const Cel* aCel = aLay->cel(f); const Cel* bCel = bLay->cel(f); if ((!aCel && bCel) || (aCel && !bCel)) { diff.anything = diff.cels = true; } else if (aCel && bCel) { if (aCel->frame() == bCel->frame() || aCel->bounds() == bCel->bounds() || aCel->opacity() == bCel->opacity()) { diff.anything = diff.cels = true; } if (aCel->image() && bCel->image()) { if (aCel->image()->bounds() != bCel->image()->bounds() || count_diff_between_images(aCel->image(), bCel->image())) diff.anything = diff.images = true; } else if (aCel->image() != bCel->image()) diff.anything = diff.images = true; } } } } } // Compare color spaces if (!a->sprite()->colorSpace()->nearlyEqual(*b->sprite()->colorSpace())) { diff.anything = diff.colorProfiles = true; } return diff; }
void ImportSpriteSheetCommand::onExecute(Context* context) { ImportSpriteSheetWindow window(context); window.openWindowInForeground(); if (!window.ok()) return; Document* document = window.document(); DocumentPreferences* docPref = window.docPref(); gfx::Rect frameBounds = window.frameBounds(); bool partialTiles = window.partialTilesValue(); auto sheetType = window.sheetTypeValue(); ASSERT(document); if (!document) return; // The list of frames imported from the sheet std::vector<ImageRef> animation; try { Sprite* sprite = document->sprite(); frame_t currentFrame = context->activeSite().frame(); render::Render render; // Each sprite in the sheet std::vector<gfx::Rect> tileRects; int widthStop = sprite->width(); int heightStop = sprite->height(); if (partialTiles) { widthStop += frameBounds.w-1; heightStop += frameBounds.h-1; } switch (sheetType) { case app::SpriteSheetType::Horizontal: for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x += frameBounds.w) { tileRects.push_back(gfx::Rect(x, frameBounds.y, frameBounds.w, frameBounds.h)); } break; case app::SpriteSheetType::Vertical: for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y += frameBounds.h) { tileRects.push_back(gfx::Rect(frameBounds.x, y, frameBounds.w, frameBounds.h)); } break; case app::SpriteSheetType::Rows: for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y += frameBounds.h) { for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x += frameBounds.w) { tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h)); } } break; case app::SpriteSheetType::Columns: for (int x=frameBounds.x; x+frameBounds.w<=sprite->width(); x += frameBounds.w) { for (int y=frameBounds.y; y+frameBounds.h<=sprite->height(); y += frameBounds.h) { tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h)); } } break; } // As first step, we cut each tile and add them into "animation" list. for (const auto& tileRect : tileRects) { ImageRef resultImage( Image::create( sprite->pixelFormat(), tileRect.w, tileRect.h)); // Render the portion of sheet. render.renderSprite( resultImage.get(), sprite, currentFrame, gfx::Clip(0, 0, tileRect)); animation.push_back(resultImage); } if (animation.size() == 0) { Alert::show("Import Sprite Sheet" "<<The specified rectangle does not create any tile." "<<Select a rectangle inside the sprite region." "||&OK"); return; } // The following steps modify the sprite, so we wrap all // operations in a undo-transaction. ContextWriter writer(context); Transaction transaction(writer.context(), "Import Sprite Sheet", ModifyDocument); DocumentApi api = document->getApi(transaction); // Add the layer in the sprite. LayerImage* resultLayer = api.newLayer(sprite, "Sprite Sheet"); // Add all frames+cels to the new layer for (size_t i=0; i<animation.size(); ++i) { // Create the cel. base::UniquePtr<Cel> resultCel(new Cel(frame_t(i), animation[i])); // Add the cel in the layer. api.addCel(resultLayer, resultCel); resultCel.release(); } // Copy the list of layers (because we will modify it in the iteration). LayerList layers = sprite->folder()->getLayersList(); // Remove all other layers for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) { if (*it != resultLayer) api.removeLayer(*it); } // Change the number of frames api.setTotalFrames(sprite, frame_t(animation.size())); // Set the size of the sprite to the tile size. api.setSpriteSize(sprite, frameBounds.w, frameBounds.h); transaction.commit(); ASSERT(docPref); if (docPref) { docPref->importSpriteSheet.type(sheetType); docPref->importSpriteSheet.bounds(frameBounds); docPref->importSpriteSheet.partialTiles(partialTiles); } } catch (...) { throw; } update_screen_for_document(document); }
void DocumentApi::flattenLayers(Sprite* sprite, int bgcolor) { Image* cel_image; Cel* cel; DocumentUndo* undo = m_document->getUndo(); // Create a temporary image. UniquePtr<Image> image_wrap(Image::create(sprite->getPixelFormat(), sprite->getWidth(), sprite->getHeight())); Image* image = image_wrap.get(); // Get the background layer from the sprite. LayerImage* background = sprite->getBackgroundLayer(); if (!background) { // If there aren't a background layer we must to create the background. background = new LayerImage(sprite); addLayer(sprite->getFolder(), background, NULL); configureLayerAsBackground(background); } // Copy all frames to the background. for (FrameNumber frame(0); frame<sprite->getTotalFrames(); ++frame) { // Clear the image and render this frame. image_clear(image, bgcolor); layer_render(sprite->getFolder(), image, 0, 0, frame); cel = background->getCel(frame); if (cel) { cel_image = sprite->getStock()->getImage(cel->getImage()); ASSERT(cel_image != NULL); // We have to save the current state of `cel_image' in the undo. if (undo->isEnabled()) { Dirty* dirty = new Dirty(cel_image, image); dirty->saveImagePixels(cel_image); m_undoers->pushUndoer(new undoers::DirtyArea( getObjects(), cel_image, dirty)); delete dirty; } } else { // If there aren't a cel in this frame in the background, we // have to create a copy of the image for the new cel. cel_image = Image::createCopy(image); // TODO error handling: if createCopy throws // Here we create the new cel (with the new image `cel_image'). cel = new Cel(frame, sprite->getStock()->addImage(cel_image)); // TODO error handling: if new Cel throws // And finally we add the cel in the background. background->addCel(cel); } image_copy(cel_image, image, 0, 0); } // Delete old layers. LayerList layers = sprite->getFolder()->getLayersList(); LayerIterator it = layers.begin(); LayerIterator end = layers.end(); for (; it != end; ++it) if (*it != background) removeLayer(*it); }