mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
Support --ignore-empty for --save-as (fix #551)
This commit is contained in:
parent
e13d424ced
commit
c657038b1a
@ -92,6 +92,9 @@ void DefaultCliDelegate::saveFile(Context* ctx, const CliOpenFile& cof)
|
||||
params.set("slice", cof.slice.c_str());
|
||||
}
|
||||
|
||||
if (cof.ignoreEmpty)
|
||||
params.set("ignoreEmpty", "true");
|
||||
|
||||
ctx->executeCommand(saveAsCommand, params);
|
||||
}
|
||||
|
||||
|
@ -98,6 +98,10 @@ void PreviewCliDelegate::saveFile(Context* ctx, const CliOpenFile& cof)
|
||||
std::cout << " - Trim\n";
|
||||
}
|
||||
|
||||
if (cof.ignoreEmpty) {
|
||||
std::cout << " - Ignore empty frames\n";
|
||||
}
|
||||
|
||||
std::cout << " - Size: "
|
||||
<< cof.document->sprite()->width() << "x"
|
||||
<< cof.document->sprite()->height() << "\n";
|
||||
@ -136,7 +140,8 @@ void PreviewCliDelegate::saveFile(Context* ctx, const CliOpenFile& cof)
|
||||
ctx,
|
||||
cof.roi(),
|
||||
cof.filename,
|
||||
cof.filenameFormat));
|
||||
cof.filenameFormat,
|
||||
cof.ignoreEmpty));
|
||||
|
||||
if (fop) {
|
||||
base::paths files;
|
||||
|
@ -89,6 +89,7 @@ SaveFileBaseCommand::SaveFileBaseCommand(const char* id, CommandFlags flags)
|
||||
: Command(id, flags)
|
||||
{
|
||||
m_useUI = true;
|
||||
m_ignoreEmpty = false;
|
||||
}
|
||||
|
||||
void SaveFileBaseCommand::onLoadParams(const Params& params)
|
||||
@ -113,6 +114,9 @@ void SaveFileBaseCommand::onLoadParams(const Params& params)
|
||||
|
||||
std::string useUI = params.get("useUI");
|
||||
m_useUI = (useUI.empty() || (useUI == "true"));
|
||||
|
||||
std::string ignoreEmpty = params.get("ignoreEmpty");
|
||||
m_ignoreEmpty = (ignoreEmpty == "true");
|
||||
}
|
||||
|
||||
// Returns true if there is a current sprite to save.
|
||||
@ -194,7 +198,8 @@ void SaveFileBaseCommand::saveDocumentInBackground(
|
||||
context,
|
||||
roi,
|
||||
filename,
|
||||
m_filenameFormat));
|
||||
m_filenameFormat,
|
||||
m_ignoreEmpty));
|
||||
if (!fop)
|
||||
return;
|
||||
|
||||
|
@ -45,6 +45,7 @@ namespace app {
|
||||
doc::SelectedFrames m_selFrames;
|
||||
bool m_adjustFramesByFrameTag;
|
||||
bool m_useUI;
|
||||
bool m_ignoreEmpty;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -104,7 +104,8 @@ int save_document(Context* context, Doc* document)
|
||||
FileOp::createSaveDocumentOperation(
|
||||
context,
|
||||
FileOpROI(document, "", "", SelectedFrames(), false),
|
||||
document->filename(), ""));
|
||||
document->filename(), "",
|
||||
false));
|
||||
if (!fop)
|
||||
return -1;
|
||||
|
||||
@ -317,7 +318,8 @@ done:;
|
||||
FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
||||
const FileOpROI& roi,
|
||||
const std::string& filename,
|
||||
const std::string& filenameFormatArg)
|
||||
const std::string& filenameFormatArg,
|
||||
const bool ignoreEmptyFrames)
|
||||
{
|
||||
std::unique_ptr<FileOp> fop(
|
||||
new FileOp(FileOpSave, const_cast<Context*>(context)));
|
||||
@ -325,6 +327,7 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
||||
// Document to save
|
||||
fop->m_document = const_cast<Doc*>(roi.document());
|
||||
fop->m_roi = roi;
|
||||
fop->m_ignoreEmpty = ignoreEmptyFrames;
|
||||
|
||||
// Get the extension of the filename (in lower case)
|
||||
LOG("FILE: Saving document \"%s\"\n", filename.c_str());
|
||||
@ -744,31 +747,42 @@ void FileOp::operate(IFileOpProgress* progress)
|
||||
render.renderSprite(m_seq.image.get(), sprite, frame);
|
||||
}
|
||||
|
||||
// Setup the palette.
|
||||
sprite->palette(frame)->copyColorsTo(m_seq.palette);
|
||||
bool save = true;
|
||||
|
||||
// Setup the filename to be used.
|
||||
m_filename = m_seq.filename_list[outputFrame];
|
||||
|
||||
// Make directories
|
||||
{
|
||||
std::string dir = base::get_file_path(m_filename);
|
||||
try {
|
||||
if (!base::is_directory(dir))
|
||||
base::make_all_directories(dir);
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
// Ignore errors and make the delegate fail
|
||||
setError("Error creating directory \"%s\"\n%s",
|
||||
dir.c_str(), ex.what());
|
||||
}
|
||||
// Check if we have to ignore empty frames
|
||||
if (m_ignoreEmpty &&
|
||||
!sprite->isOpaque() &&
|
||||
doc::is_empty_image(m_seq.image.get())) {
|
||||
save = false;
|
||||
}
|
||||
|
||||
// Call the "save" procedure... did it fail?
|
||||
if (!m_format->save(this)) {
|
||||
setError("Error saving frame %d in the file \"%s\"\n",
|
||||
outputFrame+1, m_filename.c_str());
|
||||
break;
|
||||
if (save) {
|
||||
// Setup the palette.
|
||||
sprite->palette(frame)->copyColorsTo(m_seq.palette);
|
||||
|
||||
// Setup the filename to be used.
|
||||
m_filename = m_seq.filename_list[outputFrame];
|
||||
|
||||
// Make directories
|
||||
{
|
||||
std::string dir = base::get_file_path(m_filename);
|
||||
try {
|
||||
if (!base::is_directory(dir))
|
||||
base::make_all_directories(dir);
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
// Ignore errors and make the delegate fail
|
||||
setError("Error creating directory \"%s\"\n%s",
|
||||
dir.c_str(), ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
// Call the "save" procedure... did it fail?
|
||||
if (!m_format->save(this)) {
|
||||
setError("Error saving frame %d in the file \"%s\"\n",
|
||||
outputFrame+1, m_filename.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_seq.progress_offset += m_seq.progress_fraction;
|
||||
@ -1153,6 +1167,7 @@ FileOp::FileOp(FileOpType type, Context* context)
|
||||
, m_done(false)
|
||||
, m_stop(false)
|
||||
, m_oneframe(false)
|
||||
, m_ignoreEmpty(false)
|
||||
, m_preserveColorProfile(
|
||||
Preferences::instance().color.manage())
|
||||
, m_embeddedColorProfile(false)
|
||||
|
@ -105,7 +105,8 @@ namespace app {
|
||||
static FileOp* createSaveDocumentOperation(const Context* context,
|
||||
const FileOpROI& roi,
|
||||
const std::string& filename,
|
||||
const std::string& filenameFormat);
|
||||
const std::string& filenameFormat,
|
||||
const bool ignoreEmptyFrames);
|
||||
|
||||
~FileOp();
|
||||
|
||||
@ -196,6 +197,7 @@ namespace app {
|
||||
bool m_oneframe; // Load just one frame (in formats
|
||||
// that support animation like
|
||||
// GIF/FLI/ASE).
|
||||
bool m_ignoreEmpty;
|
||||
|
||||
// Return if we've to save/embed the color space of the document
|
||||
// in the file.
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2018 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -295,6 +296,19 @@ void fill_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t color)
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename ImageTraits>
|
||||
bool is_plain_image_templ(const Image* img, const color_t color)
|
||||
{
|
||||
const LockImageBits<ImageTraits> bits(img);
|
||||
typename LockImageBits<ImageTraits>::const_iterator it, end;
|
||||
for (it=bits.begin(), end=bits.end(); it!=end; ++it) {
|
||||
if (*it != color)
|
||||
return false;
|
||||
}
|
||||
ASSERT(it == end);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename ImageTraits>
|
||||
int count_diff_between_images_templ(const Image* i1, const Image* i2)
|
||||
{
|
||||
@ -316,6 +330,25 @@ int count_diff_between_images_templ(const Image* i1, const Image* i2)
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool is_plain_image(const Image* img, color_t c)
|
||||
{
|
||||
switch (img->pixelFormat()) {
|
||||
case IMAGE_RGB: return is_plain_image_templ<RgbTraits>(img, c);
|
||||
case IMAGE_GRAYSCALE: return is_plain_image_templ<GrayscaleTraits>(img, c);
|
||||
case IMAGE_INDEXED: return is_plain_image_templ<IndexedTraits>(img, c);
|
||||
case IMAGE_BITMAP: return is_plain_image_templ<BitmapTraits>(img, c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_empty_image(const Image* img)
|
||||
{
|
||||
color_t c = 0; // alpha = 0
|
||||
if (img->colorMode() == ColorMode::INDEXED)
|
||||
c = img->maskColor();
|
||||
return is_plain_image(img, 0);
|
||||
}
|
||||
|
||||
int count_diff_between_images(const Image* i1, const Image* i2)
|
||||
{
|
||||
if ((i1->pixelFormat() != i2->pixelFormat()) ||
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2018 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -39,6 +40,9 @@ namespace doc {
|
||||
void draw_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t c);
|
||||
void fill_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t c);
|
||||
|
||||
bool is_plain_image(const Image* img, color_t c);
|
||||
bool is_empty_image(const Image* img);
|
||||
|
||||
int count_diff_between_images(const Image* i1, const Image* i2);
|
||||
|
||||
void remap_image(Image* image, const Remap& remap);
|
||||
|
@ -149,16 +149,21 @@ void Sprite::setColorSpace(const gfx::ColorSpacePtr& colorSpace)
|
||||
cel->image()->setColorSpace(colorSpace);
|
||||
}
|
||||
|
||||
bool Sprite::isOpaque() const
|
||||
{
|
||||
Layer* bg = backgroundLayer();
|
||||
return (bg && bg->isVisible());
|
||||
}
|
||||
|
||||
bool Sprite::needAlpha() const
|
||||
{
|
||||
switch (pixelFormat()) {
|
||||
case IMAGE_RGB:
|
||||
case IMAGE_GRAYSCALE: {
|
||||
Layer* bg = backgroundLayer();
|
||||
return (!bg || !bg->isVisible());
|
||||
}
|
||||
case IMAGE_GRAYSCALE:
|
||||
return !isOpaque();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sprite::supportAlpha() const
|
||||
|
@ -84,6 +84,9 @@ namespace doc {
|
||||
void setSize(int width, int height);
|
||||
void setColorSpace(const gfx::ColorSpacePtr& colorSpace);
|
||||
|
||||
// Returns true if the sprite has a background layer and it's visible
|
||||
bool isOpaque() const;
|
||||
|
||||
// Returns true if the rendered images will contain alpha values less
|
||||
// than 255. Only RGBA and Grayscale images without background needs
|
||||
// alpha channel in the render.
|
||||
|
Loading…
x
Reference in New Issue
Block a user