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); } } }
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)); } } }
/** * 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(); } } }
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)); }
/** * 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(); } } }