diff --git a/src/app/cli/cli_open_file.cpp b/src/app/cli/cli_open_file.cpp index a77284a01..8999b9ea9 100644 --- a/src/app/cli/cli_open_file.cpp +++ b/src/app/cli/cli_open_file.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2019 Igara Studio S.A. +// Copyright (C) 2019-2022 Igara Studio S.A. // Copyright (C) 2016-2017 David Capello // // This program is distributed under the terms of @@ -47,6 +47,7 @@ FileOpROI CliOpenFile::roi() const selFrames.insert(fromFrame, toFrame); return FileOpROI(document, + gfx::Rect(), slice, tag, selFrames, diff --git a/src/app/commands/cmd_save_file.cpp b/src/app/commands/cmd_save_file.cpp index 92bf030bb..71109cd60 100644 --- a/src/app/commands/cmd_save_file.cpp +++ b/src/app/commands/cmd_save_file.cpp @@ -37,6 +37,7 @@ #include "base/fs.h" #include "base/scoped_value.h" #include "base/thread.h" +#include "doc/mask.h" #include "doc/sprite.h" #include "doc/tag.h" #include "fmt/format.h" @@ -204,7 +205,12 @@ void SaveFileBaseCommand::saveDocumentInBackground( } } - FileOpROI roi(document, params().slice(), params().tag(), + gfx::Rect bounds; + if (params().bounds.isSet()) + bounds = params().bounds(); + + FileOpROI roi(document, bounds, + params().slice(), params().tag(), m_selFrames, m_adjustFramesByTag); std::unique_ptr fop( @@ -346,6 +352,7 @@ void SaveFileCopyAsCommand::onExecute(Context* context) std::string frames = kAllFrames; bool applyPixelRatio = false; double scale = params().scale(); + gfx::Rect bounds = params().bounds(); doc::AniDir aniDirValue = params().aniDir(); bool isForTwitter = false; @@ -382,6 +389,13 @@ void SaveFileCopyAsCommand::onExecute(Context* context) if (params().scale.isSet()) win.setResizeScale(scale); if (params().aniDir.isSet()) win.setAniDir(aniDirValue); + if (params().slice.isSet()) win.setArea(params().slice()); + else if (params().bounds.isSet() && + doc->isMaskVisible() && + doc->mask()->bounds() == params().bounds()) { + win.setArea(kSelectedCanvas); + } + win.remapWindow(); load_window_pos(&win, "ExportFile"); again:; @@ -410,6 +424,9 @@ void SaveFileCopyAsCommand::onExecute(Context* context) layers = win.layersValue(); frames = win.framesValue(); scale = win.resizeValue(); + params().slice(win.areaValue()); // Set slice + if (win.areaValue() == kSelectedCanvas && doc->isMaskVisible()) + bounds = doc->mask()->bounds(); applyPixelRatio = win.applyPixelRatio(); aniDirValue = win.aniDirValue(); isForTwitter = win.isForTwitter(); @@ -480,8 +497,10 @@ void SaveFileCopyAsCommand::onExecute(Context* context) m_adjustFramesByTag = false; } - // Set ani dir + // Set other parameters params().aniDir(aniDirValue); + if (!bounds.isEmpty()) + params().bounds(bounds); // TODO This should be set as options for the specific encoder GifEncoderDurationFix fixGif(isForTwitter); diff --git a/src/app/commands/cmd_save_file.h b/src/app/commands/cmd_save_file.h index 0de06d712..c2b19510b 100644 --- a/src/app/commands/cmd_save_file.h +++ b/src/app/commands/cmd_save_file.h @@ -14,6 +14,7 @@ #include "doc/anidir.h" #include "doc/selected_frames.h" #include "gfx/point.h" +#include "gfx/rect.h" #include @@ -31,6 +32,7 @@ namespace app { Param toFrame { this, 0, { "toFrame", "to-frame" } }; Param ignoreEmpty { this, false, "ignoreEmpty" }; Param scale { this, 1.0, "scale" }; + Param bounds { this, gfx::Rect(), "bounds" }; }; class SaveFileBaseCommand : public CommandWithNewParams { diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp index 74fd38f3d..b6db6a056 100644 --- a/src/app/file/file.cpp +++ b/src/app/file/file.cpp @@ -65,9 +65,9 @@ public: ASSERT(m_doc && m_sprite); } - void setSliceBounds(const gfx::Rect& sliceBounds) { - m_spec.setWidth(sliceBounds.w * m_scale.x); - m_spec.setHeight(sliceBounds.h * m_scale.y); + void setSpecSize(const gfx::Size& size) { + m_spec.setWidth(size.w * m_scale.x); + m_spec.setHeight(size.h * m_scale.y); } void setUnscaledImage(const doc::frame_t frame, @@ -234,7 +234,7 @@ int save_document(Context* context, Doc* document) std::unique_ptr fop( FileOp::createSaveDocumentOperation( context, - FileOpROI(document, "", "", SelectedFrames(), false), + FileOpROI(document, gfx::Rect(), "", "", SelectedFrames(), false), document->filename(), "", false)); if (!fop) @@ -270,11 +270,13 @@ FileOpROI::FileOpROI() } FileOpROI::FileOpROI(const Doc* doc, + const gfx::Rect& bounds, const std::string& sliceName, const std::string& tagName, const doc::SelectedFrames& selFrames, const bool adjustByTag) : m_document(doc) + , m_bounds(bounds) , m_slice(nullptr) , m_tag(nullptr) , m_selFrames(selFrames) @@ -917,23 +919,35 @@ void FileOp::operate(IFileOpProgress* progress) frame_t outputFrame = 0; for (frame_t frame : m_roi.selectedFrames()) { - // Draw the "frame" in "m_seq.image" + gfx::Rect bounds; + + // Export bounds of specific slice if (m_roi.slice()) { const SliceKey* key = m_roi.slice()->getByFrame(frame); if (!key || key->isEmpty()) continue; // Skip frame because there is no slice key + bounds = key->bounds(); + } + // Export specific bounds + else if (!m_roi.bounds().isEmpty()) { + bounds = m_roi.bounds(); + } + + // Draw the "frame" in "m_seq.image" with the given bounds + // (bounds can be the selection bounds or a slice key bounds) + if (!bounds.isEmpty()) { if (m_abstractImage) - m_abstractImage->setSliceBounds(key->bounds()); + m_abstractImage->setSpecSize(bounds.size()); m_seq.image.reset( Image::create(sprite->pixelFormat(), - key->bounds().w, - key->bounds().h)); + bounds.w, + bounds.h)); render.renderSprite( m_seq.image.get(), sprite, frame, - gfx::Clip(gfx::Point(0, 0), key->bounds())); + gfx::Clip(gfx::Point(0, 0), bounds)); } else { render.renderSprite(m_seq.image.get(), sprite, frame); diff --git a/src/app/file/file.h b/src/app/file/file.h index 5a91d35eb..114b8fdf2 100644 --- a/src/app/file/file.h +++ b/src/app/file/file.h @@ -72,12 +72,14 @@ namespace app { public: FileOpROI(); FileOpROI(const Doc* doc, + const gfx::Rect& bounds, const std::string& sliceName, const std::string& tagName, const doc::SelectedFrames& selFrames, const bool adjustByTag); const Doc* document() const { return m_document; } + const gfx::Rect& bounds() const { return m_bounds; } doc::Slice* slice() const { return m_slice; } doc::Tag* tag() const { return m_tag; } doc::frame_t fromFrame() const { return m_selFrames.firstFrame(); } @@ -90,6 +92,7 @@ namespace app { private: const Doc* m_document; + gfx::Rect m_bounds; doc::Slice* m_slice; doc::Tag* m_tag; doc::SelectedFrames m_selFrames; diff --git a/src/app/ui/export_file_window.cpp b/src/app/ui/export_file_window.cpp index e32635bbd..c8f68d30d 100644 --- a/src/app/ui/export_file_window.cpp +++ b/src/app/ui/export_file_window.cpp @@ -153,6 +153,11 @@ void ExportFileWindow::setResizeScale(double scale) resize()->setValue(fmt::format("{:.2f}", 100.0 * scale)); } +void ExportFileWindow::setArea(const std::string& areaValue) +{ + area()->setValue(areaValue); +} + void ExportFileWindow::setAniDir(const doc::AniDir aniDir) { anidir()->setSelectedItemIndex(int(aniDir)); diff --git a/src/app/ui/export_file_window.h b/src/app/ui/export_file_window.h index 3f2a072f8..8b0552159 100644 --- a/src/app/ui/export_file_window.h +++ b/src/app/ui/export_file_window.h @@ -37,6 +37,7 @@ namespace app { void setOutputFilename(const std::string& pathAndFilename); void setResizeScale(const double scale); + void setArea(const std::string& area); void setAniDir(const doc::AniDir aniDir); obs::signal SelectOutputFile;