void DuplicateLayerCommand::onExecute(Context* context) { ContextWriter writer(context); Document* document = writer.document(); Sprite* sprite = writer.sprite(); UndoTransaction undo(writer.context(), "Layer Duplication"); LayerImage* sourceLayer = static_cast<LayerImage*>(writer.layer()); // Create a new layer UniquePtr<LayerImage> newLayerPtr(new LayerImage(sprite)); // Disable undo because the layer content is added as a whole with // AddLayer() undoer. document->getUndo()->setEnabled(false); // Copy the layer content (cels + images) document->copyLayerContent(sourceLayer, document, newLayerPtr); // Restore enabled status. document->getUndo()->setEnabled(undo.isEnabled()); // Copy the layer name newLayerPtr->setName(newLayerPtr->getName() + " Copy"); // Add the new layer in the sprite. document->getApi().addLayer(sourceLayer->getParent(), newLayerPtr, sourceLayer); // Release the pointer as it is owned by the sprite now Layer* newLayer = newLayerPtr.release(); undo.commit(); update_screen_for_document(document); }
void SetCelFrame::revert(ObjectsContainer* objects, UndoersCollector* redoers) { LayerImage* layer = objects->getObjectT<LayerImage>(m_layerId); Cel* cel = objects->getObjectT<Cel>(m_celId); // Push another SetCelFrame as redoer redoers->pushUndoer(new SetCelFrame(objects, layer, cel)); layer->moveCel(cel, m_frame); }
TEST(File, SeveralSizes) { she::ScopedHandle<she::System> system(she::CreateSystem()); // Register all possible image formats. FileFormatsManager::instance().registerAllFormats(); std::vector<char> fn(256); for (int w=10; w<=10+503*2; w+=503) { for (int h=10; h<=10+503*2; h+=503) { //std::sprintf(&fn[0], "test_%dx%d.ase", w, h); std::sprintf(&fn[0], "test.ase"); { UniquePtr<Document> doc(Document::createBasicDocument(IMAGE_INDEXED, w, h, 256)); doc->setFilename(&fn[0]); // Random pixels LayerImage* layer = dynamic_cast<LayerImage*>(doc->getSprite()->getFolder()->getFirstLayer()); ASSERT_TRUE(layer != NULL); Image* image = doc->getSprite()->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage()); std::srand(w*h); int c = std::rand()%256; for (int y=0; y<h; y++) { for (int x=0; x<w; x++) { image_putpixel_fast<IndexedTraits>(image, x, y, c); if ((std::rand()&4) == 0) c = std::rand()%256; } } save_document(doc); } { UniquePtr<Document> doc(load_document(&fn[0])); ASSERT_EQ(w, doc->getSprite()->getWidth()); ASSERT_EQ(h, doc->getSprite()->getHeight()); // Same random pixels (see the seed) LayerImage* layer = dynamic_cast<LayerImage*>(doc->getSprite()->getFolder()->getFirstLayer()); ASSERT_TRUE(layer != NULL); Image* image = doc->getSprite()->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage()); std::srand(w*h); int c = std::rand()%256; for (int y=0; y<h; y++) { for (int x=0; x<w; x++) { ASSERT_EQ(c, image_getpixel_fast<IndexedTraits>(image, x, y)); if ((std::rand()&4) == 0) c = std::rand()%256; } } } } } }
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 doc::Site. auto range = App::instance()->timeline()->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); }
void LinkCelsCommand::onExecute(Context* context) { ContextWriter writer(context); Document* document(writer.document()); bool nonEditableLayers = false; { // TODO the range of selected frames should be in doc::Site. Timeline::Range range = App::instance()->getMainWindow()->getTimeline()->range(); if (!range.enabled()) return; Transaction transaction(writer.context(), friendlyName()); Sprite* sprite = writer.sprite(); frame_t begin = range.frameBegin(); frame_t end = range.frameEnd(); for (LayerIndex layerIdx = range.layerBegin(); layerIdx <= range.layerEnd(); ++layerIdx) { Layer* layer = sprite->indexToLayer(layerIdx); if (!layer->isImage()) continue; if (!layer->isEditable()) { nonEditableLayers = true; continue; } LayerImage* layerImage = static_cast<LayerImage*>(layer); for (frame_t frame=begin; frame < end+1; ++frame) { Cel* cel = layerImage->cel(frame); if (cel) { for (frame = cel->frame()+1; frame < end+1; ++frame) { transaction.execute( new cmd::CopyCel( layerImage, cel->frame(), layerImage, frame, true)); // true = force links } break; } } } transaction.commit(); } if (nonEditableLayers) StatusBar::instance()->showTip(1000, "There are locked layers"); update_screen_for_document(document); }
void LinkCelsCommand::onExecute(Context* context) { ContextWriter writer(context); Doc* document(writer.document()); bool nonEditableLayers = false; { auto site = context->activeSite(); if (!site.inTimeline()) return; Tx tx(writer.context(), friendlyName()); for (Layer* layer : site.selectedLayers()) { if (!layer->isImage()) continue; if (!layer->isEditableHierarchy()) { nonEditableLayers = true; continue; } LayerImage* layerImage = static_cast<LayerImage*>(layer); for (auto it=site.selectedFrames().begin(), end=site.selectedFrames().end(); it != end; ++it) { frame_t frame = *it; Cel* cel = layerImage->cel(frame); if (cel) { for (++it; it != end; ++it) { tx( new cmd::CopyCel( layerImage, cel->frame(), layerImage, *it, true)); // true = force links } break; } } } tx.commit(); } if (nonEditableLayers) StatusBar::instance()->showTip(1000, "There are locked layers"); update_screen_for_document(document); }
void UnlinkCelCommand::onExecute(Context* context) { ContextWriter writer(context); Document* document(writer.document()); bool nonEditableLayers = false; { Transaction transaction(writer.context(), "Unlink Cel"); const Site* site = writer.site(); if (site->inTimeline() && !site->selectedLayers().empty()) { for (Layer* layer : site->selectedLayers()) { if (!layer->isImage()) continue; if (!layer->isEditableHierarchy()) { nonEditableLayers = true; continue; } LayerImage* layerImage = static_cast<LayerImage*>(layer); for (frame_t frame : site->selectedFrames().reversed()) { Cel* cel = layerImage->cel(frame); if (cel && cel->links()) transaction.execute(new cmd::UnlinkCel(cel)); } } } else { Cel* cel = writer.cel(); if (cel && cel->links()) { if (cel->layer()->isEditableHierarchy()) 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); }
DocRangeOps() { black = rgba(0, 0, 0, 0); white = rgba(255, 255, 255, 255); doc.reset(static_cast<app::Document*>(ctx.documents().add(4, 4))); sprite = doc->sprite(); layer1 = dynamic_cast<LayerImage*>(sprite->folder()->getFirstLayer()); layer2 = new LayerImage(sprite); layer3 = new LayerImage(sprite); layer4 = new LayerImage(sprite); sprite->folder()->addLayer(layer2); sprite->folder()->addLayer(layer3); sprite->folder()->addLayer(layer4); EXPECT_LAYER_ORDER(layer1, layer2, layer3, layer4); layer1->setName("layer1"); layer2->setName("layer2"); layer3->setName("layer3"); layer4->setName("layer4"); sprite->setTotalFrames(frame_t(4)); sprite->setFrameDuration(frame_t(0), 1); // These durations can be used to identify sprite->setFrameDuration(frame_t(1), 2); // frames after a move operation sprite->setFrameDuration(frame_t(2), 3); sprite->setFrameDuration(frame_t(3), 4); for (int i=0; i<4; i++) { LayerImage* layer = static_cast<LayerImage*>(sprite->indexToLayer(LayerIndex(i))); for (int j=0; j<4; j++) { Cel* cel = layer->cel(frame_t(j)); ImageRef image; if (cel) image = cel->imageRef(); else { image.reset(Image::create(IMAGE_RGB, 4, 4)); cel = new Cel(frame_t(j), image); layer->addCel(cel); } clear_image(image.get(), black); put_pixel(image.get(), i, j, white); } } }
static void move_or_copy_cels( DocApi& api, Op op, const LayerList& srcLayers, const LayerList& dstLayers, const SelectedFrames& srcFrames, const SelectedFrames& dstFrames) { ASSERT(srcLayers.size() == dstLayers.size()); for (layer_t i=0; i<srcLayers.size(); ++i) { auto srcFrame = srcFrames.begin(); auto dstFrame = dstFrames.begin(); auto srcFrameEnd = srcFrames.end(); auto dstFrameEnd = dstFrames.end(); for (; srcFrame != srcFrameEnd && dstFrame != dstFrameEnd; ++srcFrame, ++dstFrame) { if (i >= 0 && i < srcLayers.size() && srcLayers[i]->isImage()) { LayerImage* srcLayer = static_cast<LayerImage*>(srcLayers[i]); if (i < dstLayers.size() && dstLayers[i]->isImage()) { LayerImage* srcLayer = static_cast<LayerImage*>(srcLayers[i]); LayerImage* dstLayer = static_cast<LayerImage*>(dstLayers[i]); #ifdef TRACE_RANGE_OPS std::clog << (op == Move ? "Moving": "Copying") << " cel " << srcLayer->name() << "[" << *srcFrame << "]" << " into " << dstLayer->name() << "[" << *dstFrame << "]\n"; #endif switch (op) { case Move: api.moveCel(srcLayer, *srcFrame, dstLayer, *dstFrame); break; case Copy: api.copyCel(srcLayer, *srcFrame, dstLayer, *dstFrame); break; } } else if (op == Move) { api.clearCel(srcLayer, *srcFrame); } } } } }
bool save_palette(const char *filename, Palette* pal) { std::string ext = base::string_to_lower(base::get_file_extension(filename)); bool success = false; if (ext == "col") { success = doc::file::save_col_file(pal, filename); } else if (ext == "gpl") { success = doc::file::save_gpl_file(pal, filename); } else { FileFormat* ff = FileFormatsManager::instance()->getFileFormatByExtension(ext.c_str()); if (ff->support(FILE_SUPPORT_SAVE)) { app::Context tmpContext; doc::Document* doc = tmpContext.documents().add( 16, 16, doc::ColorMode::INDEXED, Palette::MaxColors); Sprite* sprite = doc->sprite(); doc->sprite()->setPalette(pal, false); LayerImage* layer = dynamic_cast<LayerImage*>(sprite->folder()->getFirstLayer()); Image* image = layer->getCel(FrameNumber(0))->image(); int x, y, c; for (y=c=0; y<16; y++) for (x=0; x<16; x++) image->putPixel(x, y, c++); doc->setFilename(filename); success = (save_document(&tmpContext, doc) == 0); doc->close(); delete doc; } } return success; }
// TODO the DocumentRange should be "iteratable" to replace this function CelList get_cels_in_range(Sprite* sprite, const DocumentRange& range) { CelList cels; 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 (FrameNumber frame = range.frameEnd(), begin = range.frameBegin().previous(); frame != begin; frame = frame.previous()) { Cel* cel = layerImage->getCel(frame); if (cel) cels.push_back(cel); } } return cels; }
MovingCelState::MovingCelState(Editor* editor, MouseMessage* msg) : m_canceled(false) { ContextWriter writer(UIContext::instance(), 500); Document* document = editor->document(); auto range = App::instance()->timeline()->range(); LayerImage* layer = static_cast<LayerImage*>(editor->layer()); ASSERT(layer->isImage()); Cel* currentCel = layer->cel(editor->frame()); ASSERT(currentCel); // The cel cannot be null if (!range.enabled()) range = DocumentRange(currentCel); // Record start positions of all cels in selected range for (Cel* cel : get_unique_cels(writer.sprite(), range)) { Layer* layer = cel->layer(); ASSERT(layer); if (layer && layer->isMovable() && !layer->isBackground()) { m_celList.push_back(cel); m_celStarts.push_back(cel->position()); } } m_cursorStart = editor->screenToEditor(msg->position()); m_celOffset = gfx::Point(0, 0); editor->captureMouse(); // Hide the mask (temporarily, until mouse-up event) m_maskVisible = document->isMaskVisible(); if (m_maskVisible) { document->setMaskVisible(false); document->generateMaskBoundaries(); } }
void CopyCel::onExecute() { LayerImage* srcLayer = static_cast<LayerImage*>(m_srcLayer.layer()); LayerImage* dstLayer = static_cast<LayerImage*>(m_dstLayer.layer()); ASSERT(srcLayer); ASSERT(dstLayer); Sprite* srcSprite = srcLayer->sprite(); Sprite* dstSprite = dstLayer->sprite(); ASSERT(srcSprite); ASSERT(dstSprite); ASSERT(m_srcFrame >= 0 && m_srcFrame < srcSprite->totalFrames()); ASSERT(m_dstFrame >= 0); (void)srcSprite; // To avoid unused variable warning on Release mode Cel* srcCel = srcLayer->cel(m_srcFrame); Cel* dstCel = dstLayer->cel(m_dstFrame); // Clear destination cel if it does exist. It'll be overriden by the // copy of srcCel. 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) executeAndAdd(new cmd::AddFrame(dstSprite, dstSprite->totalFrames())); Image* srcImage = (srcCel ? srcCel->image(): NULL); ImageRef dstImage; dstCel = dstLayer->cel(m_dstFrame); if (dstCel) dstImage = dstCel->imageRef(); bool createLink = (srcLayer == dstLayer && dstLayer->isContinuous()); // 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 { BlendMode blend = (srcLayer->isBackground() ? BlendMode::SRC: BlendMode::NORMAL); ImageRef tmp(Image::createCopy(dstImage.get())); render::composite_image(tmp.get(), srcImage, srcCel->x(), srcCel->y(), 255, blend); executeAndAdd(new cmd::CopyRect(dstImage.get(), tmp.get(), gfx::Clip(tmp->bounds()))); } } // For transparent layers else { if (dstCel) executeAndAdd(new cmd::RemoveCel(dstCel)); if (srcCel) { if (createLink) dstCel = Cel::createLink(srcCel); else dstCel = Cel::createCopy(srcCel); dstCel->setFrame(m_dstFrame); executeAndAdd(new cmd::AddCel(dstLayer, dstCel)); } } }
bool FliFormat::onLoad(FileOp* fop) { #define SETPAL() \ do { \ for (c=0; c<256; c++) { \ pal->setEntry(c, _rgba(cmap[c*3], \ cmap[c*3+1], \ cmap[c*3+2], 255)); \ } \ pal->setFrame(frpos_out); \ sprite->setPalette(pal, true); \ } while (0) unsigned char cmap[768]; unsigned char omap[768]; s_fli_header fli_header; Image *bmp, *old, *image; Sprite *sprite; LayerImage *layer; Palette *pal; int c, w, h; FrameNumber frpos_in; FrameNumber frpos_out; int index = 0; Cel *cel; /* open the file to read in binary mode */ FileHandle f(fop->filename.c_str(), "rb"); fli_read_header(f, &fli_header); fseek(f, 128, SEEK_SET); if (fli_header.magic == NO_HEADER) { fop_error(fop, "The file doesn't have a FLIC header\n"); return false; } /* size by frame */ w = fli_header.width; h = fli_header.height; /* create the bitmaps */ bmp = Image::create(IMAGE_INDEXED, w, h); old = Image::create(IMAGE_INDEXED, w, h); pal = new Palette(FrameNumber(0), 256); if (!bmp || !old || !pal) { fop_error(fop, "Not enough memory.\n"); if (bmp) image_free(bmp); if (old) image_free(old); if (pal) delete pal; return false; } // Create the image sprite = new Sprite(IMAGE_INDEXED, w, h, 256); layer = new LayerImage(sprite); sprite->getFolder()->addLayer(layer); layer->configureAsBackground(); // Set frames and speed sprite->setTotalFrames(FrameNumber(fli_header.frames)); sprite->setDurationForAllFrames(fli_header.speed); /* write frame by frame */ for (frpos_in = frpos_out = FrameNumber(0); frpos_in < sprite->getTotalFrames(); ++frpos_in) { /* read the frame */ fli_read_frame(f, &fli_header, (unsigned char *)old->dat, omap, (unsigned char *)bmp->dat, cmap); /* first frame, or the frames changes, or the palette changes */ if ((frpos_in == 0) || (image_count_diff(old, bmp)) #ifndef USE_LINK /* TODO this should be configurable through a check-box */ || (memcmp(omap, cmap, 768) != 0) #endif ) { /* the image changes? */ if (frpos_in != 0) ++frpos_out; /* add the new frame */ image = Image::createCopy(bmp); if (!image) { fop_error(fop, "Not enough memory\n"); break; } index = sprite->getStock()->addImage(image); if (index < 0) { image_free(image); fop_error(fop, "Not enough memory\n"); break; } cel = new Cel(frpos_out, index); layer->addCel(cel); /* first frame or the palette changes */ if ((frpos_in == 0) || (memcmp(omap, cmap, 768) != 0)) SETPAL(); } #ifdef USE_LINK /* the palette changes */ else if (memcmp(omap, cmap, 768) != 0) { ++frpos_out; SETPAL(); // Add link cel = new Cel(frpos_out, index); layer_add_cel(layer, cel); } #endif // The palette and the image don't change: add duration to the last added frame else { sprite->setFrameDuration(frpos_out, sprite->getFrameDuration(frpos_out)+fli_header.speed); } /* update the old image and color-map to the new ones to compare later */ image_copy(old, bmp, 0, 0); memcpy(omap, cmap, 768); /* update progress */ fop_progress(fop, (float)(frpos_in+1) / (float)(sprite->getTotalFrames())); if (fop_is_stop(fop)) break; /* just one frame? */ if (fop->oneframe) break; } // Update number of frames sprite->setTotalFrames(frpos_out.next()); // Destroy the bitmaps image_free(bmp); image_free(old); delete pal; fop->document = new Document(sprite); return true; }
/** * Shows the "New Sprite" dialog. */ void NewFileCommand::onExecute(Context* context) { PixelFormat format; int w, h, bg, ncolors = get_default_palette()->size(); char buf[1024]; app::Color bg_table[] = { app::Color::fromMask(), app::Color::fromRgb(0, 0, 0), app::Color::fromRgb(255, 255, 255), app::Color::fromRgb(255, 0, 255) }; // Load the window widget app::gen::NewSprite window; // Default values: Indexed, 320x240, Background color format = static_cast<PixelFormat>(get_config_int("NewSprite", "Type", IMAGE_INDEXED)); // Invalid format in config file. if (format != IMAGE_RGB && format != IMAGE_INDEXED && format != IMAGE_GRAYSCALE) { format = IMAGE_INDEXED; } w = get_config_int("NewSprite", "Width", 320); h = get_config_int("NewSprite", "Height", 240); bg = get_config_int("NewSprite", "Background", 1); // Default = Black if (bg == 4) // Convert old default (Background color) to new default (Black) bg = 1; bg = MID(0, bg, 3); // If the clipboard contains an image, we can show the size of the // clipboard as default image size. gfx::Size clipboardSize; if (clipboard::get_image_size(clipboardSize)) { w = clipboardSize.w; h = clipboardSize.h; } window.width()->setTextf("%d", MAX(1, w)); window.height()->setTextf("%d", MAX(1, h)); // Select image-type switch (format) { case IMAGE_RGB: window.rgbMode()->setSelected(true); break; case IMAGE_GRAYSCALE: window.grayscaleMode()->setSelected(true); break; case IMAGE_INDEXED: window.indexedMode()->setSelected(true); break; } // Select background color window.bgBox()->selectIndex(bg); // Open the window window.openWindowInForeground(); if (window.getKiller() == window.okButton()) { bool ok = false; // Get the options if (window.rgbMode()->isSelected()) format = IMAGE_RGB; else if (window.grayscaleMode()->isSelected()) format = IMAGE_GRAYSCALE; else if (window.indexedMode()->isSelected()) format = IMAGE_INDEXED; w = window.width()->getTextInt(); h = window.height()->getTextInt(); bg = window.bgBox()->getSelectedIndex(); w = MID(1, w, 65535); h = MID(1, h, 65535); // Select the color app::Color color = app::Color::fromMask(); if (bg >= 0 && bg <= 3) { color = bg_table[bg]; ok = true; } if (ok) { // Save the configuration set_config_int("NewSprite", "Type", format); set_config_int("NewSprite", "Width", w); set_config_int("NewSprite", "Height", h); set_config_int("NewSprite", "Background", bg); // Create the new sprite ASSERT(format == IMAGE_RGB || format == IMAGE_GRAYSCALE || format == IMAGE_INDEXED); ASSERT(w > 0 && h > 0); base::UniquePtr<Sprite> sprite(Sprite::createBasicSprite(format, w, h, ncolors)); if (sprite->pixelFormat() != IMAGE_GRAYSCALE) get_default_palette()->copyColorsTo(sprite->palette(frame_t(0))); // If the background color isn't transparent, we have to // convert the `Layer 1' in a `Background' if (color.getType() != app::Color::MaskType) { Layer* layer = sprite->folder()->getFirstLayer(); if (layer && layer->isImage()) { LayerImage* layerImage = static_cast<LayerImage*>(layer); layerImage->configureAsBackground(); Image* image = layerImage->cel(frame_t(0))->image(); doc::clear_image(image, color_utils::color_for_target(color, ColorTarget( ColorTarget::BackgroundLayer, sprite->pixelFormat(), sprite->transparentColor()))); } } // Show the sprite to the user base::UniquePtr<Document> doc(new Document(sprite)); sprite.release(); sprintf(buf, "Sprite-%04d", ++_sprite_counter); doc->setFilename(buf); doc->setContext(context); doc.release(); } } }
Document* Document::duplicate(DuplicateType type) const { const Sprite* sourceSprite = sprite(); base::UniquePtr<Sprite> spriteCopyPtr(new Sprite( sourceSprite->pixelFormat(), sourceSprite->width(), sourceSprite->height(), sourceSprite->getPalette(FrameNumber(0))->size())); base::UniquePtr<Document> documentCopy(new Document(spriteCopyPtr)); Sprite* spriteCopy = spriteCopyPtr.release(); spriteCopy->setTotalFrames(sourceSprite->totalFrames()); // Copy frames duration for (FrameNumber i(0); i < sourceSprite->totalFrames(); ++i) spriteCopy->setFrameDuration(i, sourceSprite->getFrameDuration(i)); // Copy color palettes { PalettesList::const_iterator it = sourceSprite->getPalettes().begin(); PalettesList::const_iterator end = sourceSprite->getPalettes().end(); for (; it != end; ++it) { const Palette* pal = *it; spriteCopy->setPalette(pal, true); } } switch (type) { case DuplicateExactCopy: // Copy the layer folder copyLayerContent(sourceSprite->folder(), documentCopy, spriteCopy->folder()); if (sourceSprite->backgroundLayer() != NULL) { ASSERT(spriteCopy->folder()->getFirstLayer()); static_cast<LayerImage*>(spriteCopy->folder()->getFirstLayer())->configureAsBackground(); } break; case DuplicateWithFlattenLayers: { // Flatten layers ASSERT(sourceSprite->folder() != NULL); LayerImage* flatLayer = create_flatten_layer_copy (spriteCopy, sourceSprite->folder(), gfx::Rect(0, 0, sourceSprite->width(), sourceSprite->height()), FrameNumber(0), sourceSprite->lastFrame()); // Add and select the new flat layer spriteCopy->folder()->addLayer(flatLayer); // Configure the layer as background only if the original // sprite has a background layer. if (sourceSprite->backgroundLayer() != NULL) flatLayer->configureAsBackground(); } break; } documentCopy->setMask(mask()); documentCopy->m_maskVisible = m_maskVisible; documentCopy->generateMaskBoundaries(); return documentCopy.release(); }
void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, Layer* destLayer0) const { // Copy the layer name destLayer0->setName(sourceLayer0->name()); if (sourceLayer0->isImage() && destLayer0->isImage()) { const LayerImage* sourceLayer = static_cast<const LayerImage*>(sourceLayer0); LayerImage* destLayer = static_cast<LayerImage*>(destLayer0); // copy cels CelConstIterator it = sourceLayer->getCelBegin(); CelConstIterator end = sourceLayer->getCelEnd(); for (; it != end; ++it) { const Cel* sourceCel = *it; if (sourceCel->frame() > destLayer->sprite()->lastFrame()) break; base::UniquePtr<Cel> newCel(new Cel(*sourceCel)); const Image* sourceImage = sourceCel->image(); ASSERT(sourceImage != NULL); Image* newImage = Image::createCopy(sourceImage); newCel->setImage(destLayer->sprite()->stock()->addImage(newImage)); destLayer->addCel(newCel); newCel.release(); } } else if (sourceLayer0->isFolder() && destLayer0->isFolder()) { const LayerFolder* sourceLayer = static_cast<const LayerFolder*>(sourceLayer0); LayerFolder* destLayer = static_cast<LayerFolder*>(destLayer0); LayerConstIterator it = sourceLayer->getLayerBegin(); LayerConstIterator end = sourceLayer->getLayerEnd(); for (; it != end; ++it) { Layer* sourceChild = *it; base::UniquePtr<Layer> destChild(NULL); if (sourceChild->isImage()) { destChild.reset(new LayerImage(destLayer->sprite())); copyLayerContent(sourceChild, destDoc, destChild); } else if (sourceChild->isFolder()) { destChild.reset(new LayerFolder(destLayer->sprite())); copyLayerContent(sourceChild, destDoc, destChild); } else { ASSERT(false); } ASSERT(destChild != NULL); // Add the new layer in the sprite. Layer* newLayer = destChild.release(); Layer* afterThis = destLayer->getLastLayer(); destLayer->addLayer(newLayer); destChild.release(); destLayer->stackLayer(newLayer, afterThis); } } else { ASSERT(false && "Trying to copy two incompatible layers"); } }
void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, Layer* destLayer0) const { LayerFlags dstFlags = sourceLayer0->flags(); // Remove the "background" flag if the destDoc already has a background layer. if (((int)dstFlags & (int)LayerFlags::Background) == (int)LayerFlags::Background && (destDoc->sprite()->backgroundLayer())) { dstFlags = (LayerFlags)((int)dstFlags & ~(int)(LayerFlags::BackgroundLayerFlags)); } // Copy the layer name destLayer0->setName(sourceLayer0->name()); destLayer0->setFlags(dstFlags); if (sourceLayer0->isImage() && destLayer0->isImage()) { const LayerImage* sourceLayer = static_cast<const LayerImage*>(sourceLayer0); LayerImage* destLayer = static_cast<LayerImage*>(destLayer0); // copy cels 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; 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(); } } else if (sourceLayer0->isFolder() && destLayer0->isFolder()) { const LayerFolder* sourceLayer = static_cast<const LayerFolder*>(sourceLayer0); LayerFolder* destLayer = static_cast<LayerFolder*>(destLayer0); LayerConstIterator it = sourceLayer->getLayerBegin(); LayerConstIterator end = sourceLayer->getLayerEnd(); for (; it != end; ++it) { Layer* sourceChild = *it; base::UniquePtr<Layer> destChild(NULL); if (sourceChild->isImage()) { destChild.reset(new LayerImage(destLayer->sprite())); copyLayerContent(sourceChild, destDoc, destChild); } else if (sourceChild->isFolder()) { destChild.reset(new LayerFolder(destLayer->sprite())); copyLayerContent(sourceChild, destDoc, destChild); } else { ASSERT(false); } ASSERT(destChild != NULL); // Add the new layer in the sprite. Layer* newLayer = destChild.release(); Layer* afterThis = destLayer->getLastLayer(); destLayer->addLayer(newLayer); destChild.release(); destLayer->stackLayer(newLayer, afterThis); } } else { ASSERT(false && "Trying to copy two incompatible layers"); } }
// Gives to the user the possibility to move the sprite's layer in the // current editor, returns true if the position was changed. int interactive_move_layer(int mode, bool use_undo, int (*callback)()) { Editor* editor = current_editor; Document* document = editor->getDocument(); undo::UndoHistory* undo = document->getUndoHistory(); Sprite* sprite = document->getSprite(); ASSERT(sprite->getCurrentLayer()->is_image()); LayerImage* layer = static_cast<LayerImage*>(sprite->getCurrentLayer()); Cel *cel = layer->getCel(sprite->getCurrentFrame()); int start_x, new_x; int start_y, new_y; int start_b; int ret; int update = false; int quiet_clock = -1; int first_time = true; int begin_x; int begin_y; if (!cel) return false; begin_x = cel->getX(); begin_y = cel->getY(); editor->hideDrawingCursor(); jmouse_set_cursor(JI_CURSOR_MOVE); editor->editor_click_start(mode, &start_x, &start_y, &start_b); do { if (update) { cel->setPosition(begin_x - start_x + new_x, begin_y - start_y + new_y); // Update layer-bounds. editor->invalidate(); // Update status bar. app_get_statusbar()->setStatusText (0, "Pos %3d %3d Offset %3d %3d", (int)cel->getX(), (int)cel->getY(), (int)(cel->getX() - begin_x), (int)(cel->getY() - begin_y)); /* update clock */ quiet_clock = ji_clock; first_time = false; } /* call the user's routine */ if (callback) (*callback)(); /* redraw dirty widgets */ jwidget_flush_redraw(ji_get_default_manager()); jmanager_dispatch_messages(ji_get_default_manager()); gui_feedback(); } while (editor->editor_click(&new_x, &new_y, &update, NULL)); new_x = cel->getX(); new_y = cel->getY(); cel->setPosition(begin_x, begin_y); /* the position was changed */ if (!editor->editor_click_cancel()) { if (use_undo && undo->isEnabled()) { undo->setLabel("Cel Movement"); undo->setModification(undo::ModifyDocument); undo->pushUndoer(new undoers::SetCelPosition(undo->getObjects(), cel)); } cel->setPosition(new_x, new_y); ret = true; } /* the position wasn't changed */ else { ret = false; } /* redraw the sprite in all editors */ update_screen_for_document(document); /* restore the cursor */ editor->showDrawingCursor(); editor->editor_click_done(); return ret; }
/** * Shows the "New Sprite" dialog. */ void NewFileCommand::onExecute(Context* context) { Preferences& pref = Preferences::instance(); int ncolors = get_default_palette()->size(); char buf[1024]; app::Color bg_table[] = { app::Color::fromMask(), app::Color::fromRgb(255, 255, 255), app::Color::fromRgb(0, 0, 0), }; // Load the window widget app::gen::NewSprite window; // Default values: Indexed, 320x240, Background color PixelFormat format = pref.newFile.colorMode(); // Invalid format in config file. if (format != IMAGE_RGB && format != IMAGE_INDEXED && format != IMAGE_GRAYSCALE) { format = IMAGE_INDEXED; } int w = pref.newFile.width(); int h = pref.newFile.height(); int bg = pref.newFile.backgroundColor(); bg = MID(0, bg, 2); // If the clipboard contains an image, we can show the size of the // clipboard as default image size. gfx::Size clipboardSize; if (clipboard::get_image_size(clipboardSize)) { w = clipboardSize.w; h = clipboardSize.h; } window.width()->setTextf("%d", MAX(1, w)); window.height()->setTextf("%d", MAX(1, h)); // Select image-type window.colorMode()->setSelectedItem(format); // Select background color window.bgColor()->setSelectedItem(bg); // Advance options bool advanced = pref.newFile.advanced(); window.advancedCheck()->setSelected(advanced); window.advancedCheck()->Click.connect( base::Bind<void>( [&]{ gfx::Rect bounds = window.bounds(); window.advanced()->setVisible(window.advancedCheck()->isSelected()); window.setBounds(gfx::Rect(window.bounds().origin(), window.sizeHint())); window.layout(); window.manager()->invalidateRect(bounds); })); window.advanced()->setVisible(advanced); if (advanced) window.pixelRatio()->setValue(pref.newFile.pixelRatio()); else window.pixelRatio()->setValue("1:1"); // Open the window window.openWindowInForeground(); if (window.closer() == window.okButton()) { bool ok = false; // Get the options format = (doc::PixelFormat)window.colorMode()->selectedItem(); w = window.width()->textInt(); h = window.height()->textInt(); bg = window.bgColor()->selectedItem(); static_assert(IMAGE_RGB == 0, "RGB pixel format should be 0"); static_assert(IMAGE_INDEXED == 2, "Indexed pixel format should be 2"); format = MID(IMAGE_RGB, format, IMAGE_INDEXED); w = MID(1, w, DOC_SPRITE_MAX_WIDTH); h = MID(1, h, DOC_SPRITE_MAX_HEIGHT); bg = MID(0, bg, 2); // Select the color app::Color color = app::Color::fromMask(); if (bg >= 0 && bg <= 3) { color = bg_table[bg]; ok = true; } if (ok) { // Save the configuration pref.newFile.width(w); pref.newFile.height(h); pref.newFile.colorMode(format); pref.newFile.backgroundColor(bg); pref.newFile.advanced(window.advancedCheck()->isSelected()); pref.newFile.pixelRatio(window.pixelRatio()->getValue()); // Create the new sprite ASSERT(format == IMAGE_RGB || format == IMAGE_GRAYSCALE || format == IMAGE_INDEXED); ASSERT(w > 0 && h > 0); std::unique_ptr<Sprite> sprite(Sprite::createBasicSprite(format, w, h, ncolors)); if (window.advancedCheck()->isSelected()) { sprite->setPixelRatio( base::convert_to<PixelRatio>(window.pixelRatio()->getValue())); } if (sprite->pixelFormat() != IMAGE_GRAYSCALE) get_default_palette()->copyColorsTo(sprite->palette(frame_t(0))); // If the background color isn't transparent, we have to // convert the `Layer 1' in a `Background' if (color.getType() != app::Color::MaskType) { Layer* layer = sprite->root()->firstLayer(); if (layer && layer->isImage()) { LayerImage* layerImage = static_cast<LayerImage*>(layer); layerImage->configureAsBackground(); Image* image = layerImage->cel(frame_t(0))->image(); // TODO Replace this adding a new parameter to color utils Palette oldPal = *get_current_palette(); set_current_palette(get_default_palette(), false); doc::clear_image(image, color_utils::color_for_target(color, ColorTarget( ColorTarget::BackgroundLayer, sprite->pixelFormat(), sprite->transparentColor()))); set_current_palette(&oldPal, false); } } // Show the sprite to the user std::unique_ptr<Doc> doc(new Doc(sprite.get())); sprite.release(); sprintf(buf, "Sprite-%04d", ++_sprite_counter); doc->setFilename(buf); doc->setContext(context); doc.release(); } } }
void Document::copyLayerContent(const Layer* sourceLayer0, Document* destDoc, Layer* destLayer0) const { DocumentUndo* undo = destDoc->getUndo(); // Copy the layer name destLayer0->setName(sourceLayer0->getName()); if (sourceLayer0->isImage() && destLayer0->isImage()) { const LayerImage* sourceLayer = static_cast<const LayerImage*>(sourceLayer0); LayerImage* destLayer = static_cast<LayerImage*>(destLayer0); // copy cels CelConstIterator it = sourceLayer->getCelBegin(); CelConstIterator end = sourceLayer->getCelEnd(); for (; it != end; ++it) { const Cel* sourceCel = *it; UniquePtr<Cel> newCel(new Cel(*sourceCel)); ASSERT((sourceCel->getImage() >= 0) && (sourceCel->getImage() < sourceLayer->getSprite()->getStock()->size())); const Image* sourceImage = sourceLayer->getSprite()->getStock()->getImage(sourceCel->getImage()); ASSERT(sourceImage != NULL); Image* newImage = Image::createCopy(sourceImage); newCel->setImage(destLayer->getSprite()->getStock()->addImage(newImage)); if (undo->isEnabled()) { undo->pushUndoer(new undoers::AddImage(undo->getObjects(), destLayer->getSprite()->getStock(), newCel->getImage())); } destLayer->addCel(newCel); newCel.release(); } } else if (sourceLayer0->isFolder() && destLayer0->isFolder()) { const LayerFolder* sourceLayer = static_cast<const LayerFolder*>(sourceLayer0); LayerFolder* destLayer = static_cast<LayerFolder*>(destLayer0); LayerConstIterator it = sourceLayer->getLayerBegin(); LayerConstIterator end = sourceLayer->getLayerEnd(); for (; it != end; ++it) { Layer* sourceChild = *it; UniquePtr<Layer> destChild(NULL); if (sourceChild->isImage()) { destChild.reset(new LayerImage(destLayer->getSprite())); copyLayerContent(sourceChild, destDoc, destChild); } else if (sourceChild->isFolder()) { destChild.reset(new LayerFolder(destLayer->getSprite())); copyLayerContent(sourceChild, destDoc, destChild); } else { ASSERT(false); } ASSERT(destChild != NULL); // Add the new layer in the sprite. destDoc->getApi().addLayer(destLayer, destChild.release(), destLayer->getLastLayer()); } } else { ASSERT(false && "Trying to copy two incompatible layers"); } }
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; } } }
bool FliFormat::onLoad(FileOp* fop) { #define SETPAL() \ do { \ for (c=0; c<256; c++) { \ pal->setEntry(c, rgba(cmap[c*3], \ cmap[c*3+1], \ cmap[c*3+2], 255)); \ } \ pal->setFrame(frpos_out); \ sprite->setPalette(pal, true); \ } while (0) unsigned char cmap[768]; unsigned char omap[768]; s_fli_header fli_header; int c, w, h; FrameNumber frpos_in; FrameNumber frpos_out; int index = 0; // Open the file to read in binary mode FileHandle f(open_file_with_exception(fop->filename, "rb")); fli_read_header(f, &fli_header); fseek(f, 128, SEEK_SET); if (fli_header.magic == NO_HEADER) { fop_error(fop, "The file doesn't have a FLIC header\n"); return false; } /* size by frame */ w = fli_header.width; h = fli_header.height; // Create the bitmaps base::UniquePtr<Image> bmp(Image::create(IMAGE_INDEXED, w, h)); base::UniquePtr<Image> old(Image::create(IMAGE_INDEXED, w, h)); base::UniquePtr<Palette> pal(new Palette(FrameNumber(0), 256)); // Create the image Sprite* sprite = new Sprite(IMAGE_INDEXED, w, h, 256); LayerImage* layer = new LayerImage(sprite); sprite->folder()->addLayer(layer); layer->configureAsBackground(); // Set frames and speed sprite->setTotalFrames(FrameNumber(fli_header.frames)); sprite->setDurationForAllFrames(fli_header.speed); /* write frame by frame */ for (frpos_in = frpos_out = FrameNumber(0); frpos_in < sprite->totalFrames(); ++frpos_in) { /* read the frame */ fli_read_frame(f, &fli_header, (unsigned char *)old->getPixelAddress(0, 0), omap, (unsigned char *)bmp->getPixelAddress(0, 0), cmap); /* first frame, or the frames changes, or the palette changes */ if ((frpos_in == 0) || (count_diff_between_images(old, bmp)) #ifndef USE_LINK /* TODO this should be configurable through a check-box */ || (memcmp(omap, cmap, 768) != 0) #endif ) { /* the image changes? */ if (frpos_in != 0) ++frpos_out; // Add the new frame Image* image = Image::createCopy(bmp); index = sprite->stock()->addImage(image); Cel* cel = new Cel(frpos_out, index); layer->addCel(cel); /* first frame or the palette changes */ if ((frpos_in == 0) || (memcmp(omap, cmap, 768) != 0)) SETPAL(); } #ifdef USE_LINK /* the palette changes */ else if (memcmp(omap, cmap, 768) != 0) { ++frpos_out; SETPAL(); // Add link Cel* cel = new Cel(frpos_out, index); layer_add_cel(layer, cel); } #endif // The palette and the image don't change: add duration to the last added frame else { sprite->setFrameDuration(frpos_out, sprite->getFrameDuration(frpos_out)+fli_header.speed); } /* update the old image and color-map to the new ones to compare later */ copy_image(old, bmp, 0, 0); memcpy(omap, cmap, 768); /* update progress */ fop_progress(fop, (float)(frpos_in+1) / (float)(sprite->totalFrames())); if (fop_is_stop(fop)) break; /* just one frame? */ if (fop->oneframe) break; } // Update number of frames sprite->setTotalFrames(frpos_out.next()); fop->createDocument(sprite); return true; }
bool FliFormat::onLoad(FileOp* fop) { // Open the file to read in binary mode FileHandle handle(open_file_with_exception(fop->filename, "rb")); FILE* f = handle.get(); flic::StdioFileInterface finterface(f); flic::Decoder decoder(&finterface); flic::Header header; if (!decoder.readHeader(header)) { fop_error(fop, "The file doesn't have a FLIC header\n"); return false; } // Size by frame int w = header.width; int h = header.height; // Create a temporal bitmap ImageRef bmp(Image::create(IMAGE_INDEXED, w, h)); Palette pal(0, 1); Cel* prevCel = nullptr; // Create the sprite Sprite* sprite = new Sprite(IMAGE_INDEXED, w, h, 256); LayerImage* layer = new LayerImage(sprite); sprite->folder()->addLayer(layer); layer->configureAsBackground(); // Set frames and speed sprite->setTotalFrames(frame_t(header.frames)); sprite->setDurationForAllFrames(header.speed); flic::Frame fliFrame; flic::Colormap oldFliColormap; fliFrame.pixels = bmp->getPixelAddress(0, 0); fliFrame.rowstride = IndexedTraits::getRowStrideBytes(bmp->width()); frame_t frame_out = 0; for (frame_t frame_in=0; frame_in<sprite->totalFrames(); ++frame_in) { // Read the frame if (!decoder.readFrame(fliFrame)) { fop_error(fop, "Error reading frame %d\n", frame_in); continue; } // Palette change bool palChange = false; if (frame_out == 0 || oldFliColormap != fliFrame.colormap) { oldFliColormap = fliFrame.colormap; pal.resize(fliFrame.colormap.size()); for (int c=0; c<int(fliFrame.colormap.size()); c++) { pal.setEntry(c, rgba(fliFrame.colormap[c].r, fliFrame.colormap[c].g, fliFrame.colormap[c].b, 255)); } pal.setFrame(frame_out); sprite->setPalette(&pal, true); palChange = true; } // First frame, or the frame changes if (!prevCel || (count_diff_between_images(prevCel->image(), bmp.get()))) { // Add the new frame ImageRef image(Image::createCopy(bmp.get())); Cel* cel = new Cel(frame_out, image); layer->addCel(cel); prevCel = cel; ++frame_out; } else if (palChange) { Cel* cel = Cel::createLink(prevCel); cel->setFrame(frame_out); layer->addCel(cel); ++frame_out; } // The palette and the image don't change: add duration to the last added frame else { sprite->setFrameDuration( frame_out-1, sprite->frameDuration(frame_out-1) + header.speed); } if (header.frames > 0) fop_progress(fop, (float)(frame_in+1) / (float)(header.frames)); if (fop_is_stop(fop)) break; if (fop->oneframe) break; } if (frame_out > 0) sprite->setTotalFrames(frame_out); fop->createDocument(sprite); return true; }
void FlattenLayers::onExecute() { Sprite* sprite = this->sprite(); auto doc = static_cast<Doc*>(sprite->document()); // Create a temporary image. ImageRef image(Image::create(sprite->pixelFormat(), sprite->width(), sprite->height())); LayerImage* flatLayer; // The layer onto which everything will be flattened. color_t bgcolor; // The background color to use for flatLayer. flatLayer = sprite->backgroundLayer(); if (flatLayer && flatLayer->isVisible()) { // There exists a visible background layer, so we will flatten onto that. bgcolor = doc->bgColor(flatLayer); } else { // Create a new transparent layer to flatten everything onto. flatLayer = new LayerImage(sprite); ASSERT(flatLayer->isVisible()); executeAndAdd(new cmd::AddLayer(sprite->root(), flatLayer, nullptr)); executeAndAdd(new cmd::SetLayerName(flatLayer, "Flattened")); bgcolor = sprite->transparentColor(); } render::Render render; render.setBgType(render::BgType::NONE); // Copy all frames to the background. for (frame_t frame(0); frame<sprite->totalFrames(); ++frame) { // Clear the image and render this frame. clear_image(image.get(), bgcolor); render.renderSprite(image.get(), sprite, frame); // TODO Keep cel links when possible ImageRef cel_image; Cel* cel = flatLayer->cel(frame); if (cel) { if (cel->links()) executeAndAdd(new cmd::UnlinkCel(cel)); cel_image = cel->imageRef(); ASSERT(cel_image); executeAndAdd(new cmd::CopyRect(cel_image.get(), image.get(), gfx::Clip(0, 0, image->bounds()))); } else { cel_image.reset(Image::createCopy(image.get())); cel = new Cel(frame, cel_image); flatLayer->addCel(cel); } } // Delete old layers. LayerList layers = sprite->root()->layers(); for (Layer* layer : layers) if (layer != flatLayer) executeAndAdd(new cmd::RemoveLayer(layer)); }
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); }