void RemoveFrameCommand::onExecute(Context* context) { ContextWriter writer(context); Document* document(writer.document()); Sprite* sprite(writer.sprite()); { Transaction transaction(writer.context(), "Remove Frame"); DocumentApi api = document->getApi(transaction); // TODO the range of selected frames should be in doc::Site. auto range = App::instance()->timeline()->range(); if (range.enabled()) { for (frame_t frame = range.frameEnd(), begin = range.frameBegin()-1; frame != begin; --frame) { api.removeFrame(sprite, frame); } } else { api.removeFrame(sprite, writer.frame()); } transaction.commit(); } update_screen_for_document(document); }
void CanvasSizeCommand::onExecute(Context* context) { const ContextReader reader(context); const Sprite* sprite(reader.sprite()); if (context->isUiAvailable()) { // load the window widget base::UniquePtr<CanvasSizeWindow> window(new CanvasSizeWindow(0, 0, 0, 0)); window->remapWindow(); window->centerWindow(); load_window_pos(window, "CanvasSize"); window->setVisible(true); window->openWindowInForeground(); save_window_pos(window, "CanvasSize"); if (!window->pressedOk()) return; m_left = window->getLeft(); m_right = window->getRight(); m_top = window->getTop(); m_bottom = window->getBottom(); } // Resize canvas int x1 = -m_left; int y1 = -m_top; int x2 = sprite->width() + m_right; int y2 = sprite->height() + m_bottom; if (x2 <= x1) x2 = x1+1; if (y2 <= y1) y2 = y1+1; { ContextWriter writer(reader); Document* document = writer.document(); Sprite* sprite = writer.sprite(); UndoTransaction undoTransaction(writer.context(), "Canvas Size"); DocumentApi api = document->getApi(); raster::color_t bgcolor = color_utils::color_for_target( context->settings()->getBgColor(), ColorTarget( ColorTarget::BackgroundLayer, sprite->pixelFormat(), sprite->transparentColor())); api.cropSprite(sprite, gfx::Rect(x1, y1, x2-x1, y2-y1), bgcolor); undoTransaction.commit(); document->generateMaskBoundaries(); update_screen_for_document(document); } }
void reverse_frames(Document* doc, const DocumentRange& range) { const app::Context* context = static_cast<app::Context*>(doc->context()); const ContextReader reader(context); ContextWriter writer(reader); Transaction transaction(writer.context(), "Reverse Frames"); DocumentApi api = doc->getApi(transaction); Sprite* sprite = doc->sprite(); frame_t frameBegin, frameEnd; int layerBegin, layerEnd; bool moveFrames = false; switch (range.type()) { case DocumentRange::kCels: frameBegin = range.frameBegin(); frameEnd = range.frameEnd(); layerBegin = range.layerBegin(); layerEnd = range.layerEnd() + 1; break; case DocumentRange::kFrames: frameBegin = range.frameBegin(); frameEnd = range.frameEnd(); moveFrames = true; break; case DocumentRange::kLayers: frameBegin = frame_t(0); frameEnd = sprite->totalFrames()-1; layerBegin = range.layerBegin(); layerEnd = range.layerEnd() + 1; break; } if (moveFrames) { for (frame_t frameRev = frameEnd+1; frameRev > frameBegin; --frameRev) { api.moveFrame(sprite, frameBegin, frameRev); } } else { std::vector<Layer*> layers; sprite->getLayersList(layers); for (int layerIdx = layerBegin; layerIdx != layerEnd; ++layerIdx) { for (frame_t frame = frameBegin, frameRev = frameEnd; frame != (frameBegin+frameEnd)/2+1; ++frame, --frameRev) { LayerImage* layer = static_cast<LayerImage*>(layers[layerIdx]); api.swapCel(layer, frame, frameRev); } } } transaction.commit(); }
bool MovingCelState::onMouseUp(Editor* editor, MouseMessage* msg) { Document* document = editor->document(); // Here we put back the cel into its original coordinate (so we can // add an undoer before). if (m_celOffset != gfx::Point(0, 0)) { // Put the cels in the original position. for (size_t i=0; i<m_celList.size(); ++i) { Cel* cel = m_celList[i]; const gfx::Point& celStart = m_celStarts[i]; cel->setPosition(celStart); } // If the user didn't cancel the operation... if (!m_canceled) { ContextWriter writer(UIContext::instance(), 500); Transaction transaction(writer.context(), "Cel Movement", ModifyDocument); DocumentApi api = document->getApi(transaction); // And now we move the cel (or all selected range) to the new position. for (Cel* cel : m_celList) { api.setCelPosition(writer.sprite(), cel, cel->x() + m_celOffset.x, cel->y() + m_celOffset.y); } // Move selection if it was visible if (m_maskVisible) api.setMaskPosition(document->mask()->bounds().x + m_celOffset.x, document->mask()->bounds().y + m_celOffset.y); transaction.commit(); } // Redraw all editors. We've to notify all views about this // general update because MovingCelState::onMouseMove() redraws // only the cels in the current editor. And at this point we'd // like to update all the editors. document->notifyGeneralUpdate(); } // Restore the mask visibility. if (m_maskVisible) { document->setMaskVisible(m_maskVisible); document->generateMaskBoundaries(); } editor->backToPreviousState(); editor->releaseMouse(); return true; }
void RemoveLayerCommand::onExecute(Context* context) { std::string layerName; ContextWriter writer(context); Document* document(writer.document()); Sprite* sprite(writer.sprite()); { Transaction transaction(writer.context(), "Remove Layer"); DocumentApi api = document->getApi(transaction); const Site* site = writer.site(); if (site->inTimeline() && !site->selectedLayers().empty()) { SelectedLayers selLayers = site->selectedLayers(); selLayers.removeChildrenIfParentIsSelected(); layer_t deletedTopLevelLayers = 0; for (Layer* layer : selLayers) { if (layer->parent() == sprite->root()) ++deletedTopLevelLayers; } if (deletedTopLevelLayers == sprite->root()->layersCount()) { ui::Alert::show("Error<<You cannot delete all layers.||&OK"); return; } for (Layer* layer : selLayers) { api.removeLayer(layer); } } else { if (sprite->allLayersCount() == 1) { ui::Alert::show("Error<<You cannot delete the last layer.||&OK"); return; } Layer* layer = writer.layer(); layerName = layer->name(); api.removeLayer(layer); } transaction.commit(); } update_screen_for_document(document); StatusBar::instance()->invalidate(); if (!layerName.empty()) StatusBar::instance()->showTip(1000, "Layer '%s' removed", layerName.c_str()); else StatusBar::instance()->showTip(1000, "Layers removed"); }
void NewLayerCommand::onExecute(Context* context) { ContextWriter writer(context); Document* document(writer.document()); Sprite* sprite(writer.sprite()); std::string name; // Default name (m_name is a name specified in params) if (!m_name.empty()) name = m_name; else name = get_unique_layer_name(sprite); // If params specify to ask the user about the name... if (m_ask) { // We open the window to ask the name base::UniquePtr<Window> window(app::load_widget<Window>("new_layer.xml", "new_layer")); Widget* name_widget = app::find_widget<Widget>(window, "name"); name_widget->setText(name.c_str()); name_widget->setMinSize(gfx::Size(128, 0)); window->openWindowInForeground(); if (window->closer() != window->findChild("ok")) return; name = window->findChild("name")->text(); } Layer* activeLayer = writer.layer(); Layer* layer; { Transaction transaction(writer.context(), "New Layer"); DocumentApi api = document->getApi(transaction); layer = api.newLayer(sprite, name); // If "top" parameter is false, create the layer above the active // one. if (activeLayer && !m_top) api.restackLayerAfter(layer, activeLayer); transaction.commit(); } update_screen_for_document(document); StatusBar::instance()->invalidate(); StatusBar::instance()->showTip(1000, "Layer `%s' created", name.c_str()); App::instance()->getMainWindow()->popTimeline(); }
/** * [working thread] */ virtual void onJob() { UndoTransaction undoTransaction(m_writer.context(), "Rotate Canvas"); DocumentApi api = m_document->getApi(); // get all sprite cels CelList cels; m_sprite->getCels(cels); // for each cel... for (CelIterator it = cels.begin(); it != cels.end(); ++it) { Cel* cel = *it; Image* image = m_sprite->getStock()->getImage(cel->getImage()); // change it location switch (m_angle) { case 180: api.setCelPosition(m_sprite, cel, m_sprite->getWidth() - cel->getX() - image->getWidth(), m_sprite->getHeight() - cel->getY() - image->getHeight()); break; case 90: api.setCelPosition(m_sprite, cel, m_sprite->getHeight() - cel->getY() - image->getHeight(), cel->getX()); break; case -90: api.setCelPosition(m_sprite, cel, cel->getY(), m_sprite->getWidth() - cel->getX() - image->getWidth()); break; } } // for each stock's image for (int i=0; i<m_sprite->getStock()->size(); ++i) { Image* image = m_sprite->getStock()->getImage(i); if (!image) continue; // rotate the image Image* new_image = Image::create(image->getPixelFormat(), m_angle == 180 ? image->getWidth(): image->getHeight(), m_angle == 180 ? image->getHeight(): image->getWidth()); raster::rotate_image(image, new_image, m_angle); api.replaceStockImage(m_sprite, i, new_image); jobProgress((float)i / m_sprite->getStock()->size()); // cancel all the operation? if (isCanceled()) return; // UndoTransaction destructor will undo all operations } // rotate mask if (m_document->isMaskVisible()) { Mask* origMask = m_document->getMask(); base::UniquePtr<Mask> new_mask(new Mask()); const gfx::Rect& origBounds = origMask->getBounds(); int x = 0, y = 0; switch (m_angle) { case 180: x = m_sprite->getWidth() - origBounds.x - origBounds.w; y = m_sprite->getHeight() - origBounds.y - origBounds.h; break; case 90: x = m_sprite->getHeight() - origBounds.y - origBounds.h; y = origBounds.x; break; case -90: x = origBounds.y; y = m_sprite->getWidth() - origBounds.x - origBounds.w; break; } // create the new rotated mask new_mask->replace(x, y, m_angle == 180 ? origBounds.w: origBounds.h, m_angle == 180 ? origBounds.h: origBounds.w); raster::rotate_image(origMask->getBitmap(), new_mask->getBitmap(), m_angle); // Copy new mask api.copyToCurrentMask(new_mask); // Regenerate mask m_document->resetTransformation(); m_document->generateMaskBoundaries(); } // change the sprite's size if (m_angle != 180) api.setSpriteSize(m_sprite, m_sprite->getHeight(), m_sprite->getWidth()); // commit changes undoTransaction.commit(); }
void SpritePropertiesCommand::onExecute(Context* context) { std::string imgtype_text; char buf[256]; ColorButton* color_button = NULL; // Load the window widget app::gen::SpriteProperties window; // Get sprite properties and fill frame fields { const ContextReader reader(context); const Document* document(reader.document()); const Sprite* sprite(reader.sprite()); // Update widgets values switch (sprite->pixelFormat()) { case IMAGE_RGB: imgtype_text = "RGB"; break; case IMAGE_GRAYSCALE: imgtype_text = "Grayscale"; break; case IMAGE_INDEXED: std::sprintf(buf, "Indexed (%d colors)", sprite->palette(0)->size()); imgtype_text = buf; break; default: ASSERT(false); imgtype_text = "Unknown"; break; } // Filename window.name()->setText(document->filename()); // Color mode window.type()->setText(imgtype_text.c_str()); // Sprite size (width and height) window.size()->setTextf( "%dx%d (%s)", sprite->width(), sprite->height(), base::get_pretty_memory_size(sprite->getMemSize()).c_str()); // How many frames window.frames()->setTextf("%d", (int)sprite->totalFrames()); if (sprite->pixelFormat() == IMAGE_INDEXED) { color_button = new ColorButton(app::Color::fromIndex(sprite->transparentColor()), IMAGE_INDEXED); window.transparentColorPlaceholder()->addChild(color_button); } else { window.transparentColorPlaceholder()->addChild(new Label("(only for indexed images)")); } } window.remapWindow(); window.centerWindow(); load_window_pos(&window, "SpriteProperties"); window.setVisible(true); window.openWindowInForeground(); if (window.closer() == window.ok()) { if (color_button) { ContextWriter writer(context); Sprite* sprite(writer.sprite()); // If the transparent color index has changed, we update the // property in the sprite. int index = color_button->getColor().getIndex(); if (color_t(index) != sprite->transparentColor()) { Transaction transaction(writer.context(), "Set Transparent Color"); DocumentApi api = writer.document()->getApi(transaction); api.setSpriteTransparentColor(sprite, index); transaction.commit(); update_screen_for_document(writer.document()); } } } save_window_pos(&window, "SpriteProperties"); }
static DocumentRange drop_range_op( Document* doc, Op op, const DocumentRange& from, DocumentRangePlace place, const DocumentRange& to) { if (place != kDocumentRangeBefore && place != kDocumentRangeAfter) { 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 DocumentRange::kCels: if (from == to) return from; break; case DocumentRange::kFrames: if (op == Move) { if ((to.frameBegin() >= from.frameBegin() && to.frameEnd() <= from.frameEnd()) || (place == kDocumentRangeBefore && to.frameBegin() == from.frameEnd()+1) || (place == kDocumentRangeAfter && to.frameEnd() == from.frameBegin()-1)) return from; } break; case DocumentRange::kLayers: if (op == Move) { if ((to.layerBegin() >= from.layerBegin() && to.layerEnd() <= from.layerEnd()) || (place == kDocumentRangeBefore && to.layerBegin() == from.layerEnd()+1) || (place == kDocumentRangeAfter && to.layerEnd() == from.layerBegin()-1)) return from; // We cannot move the background for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) if (sprite->indexToLayer(i)->isBackground()) throw std::runtime_error("The background layer cannot be moved"); // Before background if (place == kDocumentRangeBefore) { Layer* background = sprite->indexToLayer(to.layerBegin()); if (background && background->isBackground()) throw std::runtime_error("You cannot move 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"); } DocumentRange resultRange; { const app::Context* context = static_cast<app::Context*>(doc->context()); const ContextReader reader(context); ContextWriter writer(reader); Transaction transaction(writer.context(), undoLabel, ModifyDocument); DocumentApi api = doc->getApi(transaction); // TODO Try to add the range with just one call to DocumentApi // methods, to avoid generating a lot of SetCelFrame undoers (see // DocumentApi::setCelFramePosition). switch (from.type()) { case DocumentRange::kCels: { std::vector<Layer*> layers; sprite->getLayersList(layers); int srcLayerBegin, srcLayerStep, srcLayerEnd; int dstLayerBegin, dstLayerStep; frame_t srcFrameBegin, srcFrameStep, srcFrameEnd; frame_t dstFrameBegin, dstFrameStep; if (to.layerBegin() <= from.layerBegin()) { srcLayerBegin = from.layerBegin(); srcLayerStep = 1; srcLayerEnd = from.layerEnd()+1; dstLayerBegin = to.layerBegin(); dstLayerStep = 1; } else { srcLayerBegin = from.layerEnd(); srcLayerStep = -1; srcLayerEnd = from.layerBegin()-1; dstLayerBegin = to.layerEnd(); dstLayerStep = -1; } if (to.frameBegin() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(1); srcFrameEnd = from.frameEnd()+1; dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameEnd(); dstFrameStep = frame_t(-1); } for (int srcLayerIdx = srcLayerBegin, dstLayerIdx = dstLayerBegin; srcLayerIdx != srcLayerEnd; ) { for (frame_t srcFrame = srcFrameBegin, dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) { LayerImage* srcLayer = static_cast<LayerImage*>(layers[srcLayerIdx]); LayerImage* dstLayer = static_cast<LayerImage*>(layers[dstLayerIdx]); switch (op) { case Move: api.moveCel(srcLayer, srcFrame, dstLayer, dstFrame); break; case Copy: api.copyCel(srcLayer, srcFrame, dstLayer, dstFrame); break; } srcFrame += srcFrameStep; dstFrame += dstFrameStep; } srcLayerIdx += srcLayerStep; dstLayerIdx += dstLayerStep; } resultRange = to; } break; case DocumentRange::kFrames: { frame_t srcFrameBegin = 0, srcFrameStep, srcFrameEnd = 0; frame_t dstFrameBegin = 0, dstFrameStep; switch (op) { case Move: if (place == kDocumentRangeBefore) { if (to.frameBegin() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(1); srcFrameEnd = from.frameEnd()+1; dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(-1); } } else if (place == kDocumentRangeAfter) { if (to.frameEnd() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(1); srcFrameEnd = from.frameEnd()+1; dstFrameBegin = to.frameEnd()+1; dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameEnd()+1; dstFrameStep = frame_t(-1); } } break; case Copy: if (place == kDocumentRangeBefore) { if (to.frameBegin() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(2); srcFrameEnd = from.frameBegin() + 2*from.frames(); dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(0); } } else if (place == kDocumentRangeAfter) { if (to.frameEnd() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(2); srcFrameEnd = from.frameBegin() + 2*from.frames(); dstFrameBegin = to.frameEnd()+1; dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameEnd()+1; dstFrameStep = frame_t(0); } } break; } for (frame_t srcFrame = srcFrameBegin, dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) { switch (op) { case Move: api.moveFrame(sprite, srcFrame, dstFrame); break; case Copy: api.copyFrame(sprite, srcFrame, dstFrame); break; } srcFrame += srcFrameStep; dstFrame += dstFrameStep; } if (place == kDocumentRangeBefore) { resultRange.startRange(LayerIndex::NoLayer, frame_t(to.frameBegin()), from.type()); resultRange.endRange(LayerIndex::NoLayer, frame_t(to.frameBegin()+from.frames()-1)); } else if (place == kDocumentRangeAfter) { resultRange.startRange(LayerIndex::NoLayer, frame_t(to.frameEnd()+1), from.type()); resultRange.endRange(LayerIndex::NoLayer, frame_t(to.frameEnd()+1+from.frames()-1)); } if (op == Move && from.frameBegin() < to.frameBegin()) resultRange.displace(0, -from.frames()); } break; case DocumentRange::kLayers: { std::vector<Layer*> layers; sprite->getLayersList(layers); if (layers.empty()) break; switch (op) { case Move: if (place == kDocumentRangeBefore) { for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) { api.restackLayerBefore( layers[i], layers[to.layerBegin()]); } } else if (place == kDocumentRangeAfter) { for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) { api.restackLayerAfter( layers[i], layers[to.layerEnd()]); } } break; case Copy: if (place == kDocumentRangeBefore) { for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) { api.duplicateLayerBefore( layers[i], layers[to.layerBegin()]); } } else if (place == kDocumentRangeAfter) { for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) { api.duplicateLayerAfter( layers[i], layers[to.layerEnd()]); } } break; } if (place == kDocumentRangeBefore) { resultRange.startRange(LayerIndex(to.layerBegin()), frame_t(-1), from.type()); resultRange.endRange(LayerIndex(to.layerBegin()+from.layers()-1), frame_t(-1)); } else if (place == kDocumentRangeAfter) { resultRange.startRange(LayerIndex(to.layerEnd()+1), frame_t(-1), from.type()); resultRange.endRange(LayerIndex(to.layerEnd()+1+from.layers()-1), frame_t(-1)); } if (op == Move && from.layerBegin() < to.layerBegin()) resultRange.displace(-from.layers(), 0); } break; } transaction.commit(); } return resultRange; }
// [working thread] void onJob() override { DocumentApi api = writer().document()->getApi(transaction()); int cels_count = 0; for (Cel* cel : sprite()->uniqueCels()) { // TODO add size() member function to CelsRange (void)cel; ++cels_count; } // For each cel... int progress = 0; for (Cel* cel : sprite()->uniqueCels()) { // Get cel's image Image* image = cel->image(); if (image && !cel->link()) { // Resize the cel bounds only if it's from a reference layer if (cel->layer()->isReference()) { gfx::RectF newBounds = scale_rect<double>(cel->boundsF()); transaction().execute(new cmd::SetCelBoundsF(cel, newBounds)); } else { // Change its location api.setCelPosition(sprite(), cel, scale_x(cel->x()), scale_y(cel->y())); // Resize the image int w = scale_x(image->width()); int h = scale_y(image->height()); ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h))); new_image->setMaskColor(image->maskColor()); doc::algorithm::fixup_image_transparent_colors(image); doc::algorithm::resize_image( image, new_image.get(), m_resize_method, sprite()->palette(cel->frame()), sprite()->rgbMap(cel->frame()), (cel->layer()->isBackground() ? -1: sprite()->transparentColor())); api.replaceImage(sprite(), cel->imageRef(), new_image); } } jobProgress((float)progress / cels_count); ++progress; // Cancel all the operation? if (isCanceled()) return; // Transaction destructor will undo all operations } // Resize mask if (document()->isMaskVisible()) { ImageRef old_bitmap (crop_image(document()->mask()->bitmap(), -1, -1, document()->mask()->bitmap()->width()+2, document()->mask()->bitmap()->height()+2, 0)); int w = scale_x(old_bitmap->width()); int h = scale_y(old_bitmap->height()); base::UniquePtr<Mask> new_mask(new Mask); new_mask->replace( gfx::Rect( scale_x(document()->mask()->bounds().x-1), scale_y(document()->mask()->bounds().y-1), MAX(1, w), MAX(1, h))); algorithm::resize_image( old_bitmap.get(), new_mask->bitmap(), m_resize_method, sprite()->palette(0), // Ignored sprite()->rgbMap(0), // Ignored -1); // Ignored // Reshrink new_mask->intersect(new_mask->bounds()); // Copy new mask api.copyToCurrentMask(new_mask); // Regenerate mask document()->resetTransformation(); document()->generateMaskBoundaries(); } // Resize slices for (auto& slice : sprite()->slices()) { for (auto& k : *slice) { const SliceKey& key = *k.value(); if (key.isEmpty()) continue; SliceKey newKey = key; newKey.setBounds(scale_rect(newKey.bounds())); if (newKey.hasCenter()) newKey.setCenter(scale_rect(newKey.center())); if (newKey.hasPivot()) newKey.setPivot(gfx::Point(scale_x(newKey.pivot().x), scale_y(newKey.pivot().y))); transaction().execute( new cmd::SetSliceKey(slice, k.frame(), newKey)); } } // Resize Sprite api.setSpriteSize(sprite(), m_new_width, m_new_height); }
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); }
/** * [working thread] */ virtual void onJob() { UndoTransaction undoTransaction(m_writer.context(), "Sprite Size"); DocumentApi api = m_writer.document()->getApi(); // Get all sprite cels CelList cels; m_sprite->getCels(cels); // For each cel... int progress = 0; for (CelIterator it = cels.begin(); it != cels.end(); ++it, ++progress) { Cel* cel = *it; // Change its location api.setCelPosition(m_sprite, cel, scale_x(cel->x()), scale_y(cel->y())); // Get cel's image Image* image = cel->image(); if (!image) continue; // Resize the image int w = scale_x(image->width()); int h = scale_y(image->height()); Image* new_image = Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)); doc::algorithm::fixup_image_transparent_colors(image); doc::algorithm::resize_image(image, new_image, m_resize_method, m_sprite->getPalette(cel->frame()), m_sprite->getRgbMap(cel->frame())); api.replaceStockImage(m_sprite, cel->imageIndex(), new_image); jobProgress((float)progress / cels.size()); // cancel all the operation? if (isCanceled()) return; // UndoTransaction destructor will undo all operations } // Resize mask if (m_document->isMaskVisible()) { base::UniquePtr<Image> old_bitmap (crop_image(m_document->mask()->bitmap(), -1, -1, m_document->mask()->bitmap()->width()+2, m_document->mask()->bitmap()->height()+2, 0)); int w = scale_x(old_bitmap->width()); int h = scale_y(old_bitmap->height()); base::UniquePtr<Mask> new_mask(new Mask); new_mask->replace(scale_x(m_document->mask()->bounds().x-1), scale_y(m_document->mask()->bounds().y-1), MAX(1, w), MAX(1, h)); algorithm::resize_image(old_bitmap, new_mask->bitmap(), m_resize_method, m_sprite->getPalette(FrameNumber(0)), // Ignored m_sprite->getRgbMap(FrameNumber(0))); // Ignored // Reshrink new_mask->intersect(new_mask->bounds()); // Copy new mask api.copyToCurrentMask(new_mask); // Regenerate mask m_document->resetTransformation(); m_document->generateMaskBoundaries(); } // resize sprite api.setSpriteSize(m_sprite, m_new_width, m_new_height); // commit changes undoTransaction.commit(); }
// [working thread] virtual void onJob() { Transaction transaction(m_writer.context(), "Rotate Canvas"); DocumentApi api = m_document->getApi(transaction); // 1) Rotate cel positions for (Cel* cel : m_cels) { Image* image = cel->image(); if (!image) continue; switch (m_angle) { case 180: api.setCelPosition(m_sprite, cel, m_sprite->width() - cel->x() - image->width(), m_sprite->height() - cel->y() - image->height()); break; case 90: api.setCelPosition(m_sprite, cel, m_sprite->height() - cel->y() - image->height(), cel->x()); break; case -90: api.setCelPosition(m_sprite, cel, cel->y(), m_sprite->width() - cel->x() - image->width()); break; } } // 2) Rotate images int i = 0; for (Cel* cel : m_cels) { Image* image = cel->image(); if (image) { ImageRef new_image(Image::create(image->pixelFormat(), m_angle == 180 ? image->width(): image->height(), m_angle == 180 ? image->height(): image->width())); doc::rotate_image(image, new_image.get(), m_angle); api.replaceImage(m_sprite, cel->imageRef(), new_image); } jobProgress((float)i / m_cels.size()); ++i; // cancel all the operation? if (isCanceled()) return; // Transaction destructor will undo all operations } // rotate mask if (m_document->isMaskVisible()) { Mask* origMask = m_document->mask(); base::UniquePtr<Mask> new_mask(new Mask()); const gfx::Rect& origBounds = origMask->bounds(); int x = 0, y = 0; switch (m_angle) { case 180: x = m_sprite->width() - origBounds.x - origBounds.w; y = m_sprite->height() - origBounds.y - origBounds.h; break; case 90: x = m_sprite->height() - origBounds.y - origBounds.h; y = origBounds.x; break; case -90: x = origBounds.y; y = m_sprite->width() - origBounds.x - origBounds.w; break; } // create the new rotated mask new_mask->replace( gfx::Rect(x, y, m_angle == 180 ? origBounds.w: origBounds.h, m_angle == 180 ? origBounds.h: origBounds.w)); doc::rotate_image(origMask->bitmap(), new_mask->bitmap(), m_angle); // Copy new mask api.copyToCurrentMask(new_mask); // Regenerate mask m_document->resetTransformation(); m_document->generateMaskBoundaries(); } // change the sprite's size if (m_rotateSprite && m_angle != 180) api.setSpriteSize(m_sprite, m_sprite->height(), m_sprite->width()); // commit changes transaction.commit(); }
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 FlipCommand::onExecute(Context* context) { ContextWriter writer(context); Document* document = writer.document(); Sprite* sprite = writer.sprite(); DocumentApi api = document->getApi(); { UndoTransaction undoTransaction(writer.context(), m_flipMask ? (m_flipType == raster::algorithm::FlipHorizontal ? "Flip Horizontal": "Flip Vertical"): (m_flipType == raster::algorithm::FlipHorizontal ? "Flip Canvas Horizontal": "Flip Canvas Vertical")); if (m_flipMask) { int x, y; Image* image = writer.image(&x, &y); if (!image) return; Mask* mask = NULL; bool alreadyFlipped = false; // This variable will be the area to be flipped inside the image. gfx::Rect bounds(image->getBounds()); // If there is some portion of sprite selected, we flip the // selected region only. If the mask isn't visible, we flip the // whole image. if (document->isMaskVisible()) { mask = document->getMask(); // Intersect the full area of the image with the mask's // bounds, so we don't request to flip an area outside the // image's bounds. bounds = bounds.createIntersect(gfx::Rect(mask->getBounds()).offset(-x, -y)); // If the mask isn't a rectangular area, we've to flip the mask too. if (mask->getBitmap() != NULL && !mask->isRectangular()) { int bgcolor = app_get_color_to_clear_layer(writer.layer()); // Flip the portion of image specified by the mask. mask->offsetOrigin(-x, -y); api.flipImageWithMask(image, mask, m_flipType, bgcolor); mask->offsetOrigin(x, y); alreadyFlipped = true; // Flip the mask. Image* maskBitmap = mask->getBitmap(); if (maskBitmap != NULL) { // Create a flipped copy of the current mask. base::UniquePtr<Mask> newMask(new Mask(*mask)); newMask->freeze(); raster::algorithm::flip_image(newMask->getBitmap(), maskBitmap->getBounds(), m_flipType); newMask->unfreeze(); // Change the current mask and generate the new boundaries. api.copyToCurrentMask(newMask); document->generateMaskBoundaries(); } } } // Flip the portion of image specified by "bounds" variable. if (!alreadyFlipped) { api.flipImage(image, bounds, m_flipType); } } else { // get all sprite cels CelList cels; sprite->getCels(cels); // for each cel... for (CelIterator it = cels.begin(); it != cels.end(); ++it) { Cel* cel = *it; Image* image = sprite->getStock()->getImage(cel->getImage()); api.setCelPosition (sprite, cel, (m_flipType == raster::algorithm::FlipHorizontal ? sprite->getWidth() - image->getWidth() - cel->getX(): cel->getX()), (m_flipType == raster::algorithm::FlipVertical ? sprite->getHeight() - image->getHeight() - cel->getY(): cel->getY())); api.flipImage(image, image->getBounds(), m_flipType); } } undoTransaction.commit(); } update_screen_for_document(document); }
void FlipCommand::onExecute(Context* context) { ContextWriter writer(context); Document* document = writer.document(); Sprite* sprite = writer.sprite(); { Transaction transaction(writer.context(), m_flipMask ? (m_flipType == doc::algorithm::FlipHorizontal ? "Flip Horizontal": "Flip Vertical"): (m_flipType == doc::algorithm::FlipHorizontal ? "Flip Canvas Horizontal": "Flip Canvas Vertical")); DocumentApi api = document->getApi(transaction); CelList cels; if (m_flipMask) { auto range = App::instance()->timeline()->range(); if (range.enabled()) cels = get_unique_cels(sprite, range); else if (writer.cel()) cels.push_back(writer.cel()); } else { for (Cel* cel : sprite->uniqueCels()) cels.push_back(cel); } Mask* mask = document->mask(); if (m_flipMask && document->isMaskVisible()) { Site site = *writer.site(); for (Cel* cel : cels) { site.frame(cel->frame()); site.layer(cel->layer()); int x, y; Image* image = site.image(&x, &y); if (!image) continue; // When the mask is inside the cel, we can try to flip the // pixels inside the image. if (cel->bounds().contains(mask->bounds())) { gfx::Rect flipBounds = mask->bounds(); flipBounds.offset(-x, -y); flipBounds &= image->bounds(); if (flipBounds.isEmpty()) continue; if (mask->bitmap() && !mask->isRectangular()) transaction.execute(new cmd::FlipMaskedCel(cel, m_flipType)); else api.flipImage(image, flipBounds, m_flipType); if (cel->layer()->isTransparent()) transaction.execute(new cmd::TrimCel(cel)); } // When the mask is bigger than the cel bounds, we have to // expand the cel, make the flip, and shrink it again. else { gfx::Rect flipBounds = (sprite->bounds() & mask->bounds()); if (flipBounds.isEmpty()) continue; ExpandCelCanvas expand( site, cel->layer(), TiledMode::NONE, transaction, ExpandCelCanvas::None); expand.validateDestCanvas(gfx::Region(flipBounds)); if (mask->bitmap() && !mask->isRectangular()) doc::algorithm::flip_image_with_mask( expand.getDestCanvas(), mask, m_flipType, document->bgColor(cel->layer())); else doc::algorithm::flip_image( expand.getDestCanvas(), flipBounds, m_flipType); expand.commit(); } } } else { for (Cel* cel : cels) { Image* image = cel->image(); api.setCelPosition (sprite, cel, (m_flipType == doc::algorithm::FlipHorizontal ? sprite->width() - image->width() - cel->x(): cel->x()), (m_flipType == doc::algorithm::FlipVertical ? sprite->height() - image->height() - cel->y(): cel->y())); api.flipImage(image, image->bounds(), m_flipType); } } // Flip the mask. Image* maskBitmap = mask->bitmap(); if (maskBitmap) { transaction.execute(new cmd::FlipMask(document, m_flipType)); // Flip the mask position because the if (!m_flipMask) transaction.execute( new cmd::SetMaskPosition( document, gfx::Point( (m_flipType == doc::algorithm::FlipHorizontal ? sprite->width() - mask->bounds().x2(): mask->bounds().x), (m_flipType == doc::algorithm::FlipVertical ? sprite->height() - mask->bounds().y2(): mask->bounds().y)))); document->generateMaskBoundaries(); } transaction.commit(); } update_screen_for_document(document); }
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); }