Support --ignore-empty for --save-as (fix #551)

This commit is contained in:
David Capello 2018-11-07 16:56:32 -03:00
parent e13d424ced
commit c657038b1a
10 changed files with 108 additions and 32 deletions

View File

@ -92,6 +92,9 @@ void DefaultCliDelegate::saveFile(Context* ctx, const CliOpenFile& cof)
params.set("slice", cof.slice.c_str()); params.set("slice", cof.slice.c_str());
} }
if (cof.ignoreEmpty)
params.set("ignoreEmpty", "true");
ctx->executeCommand(saveAsCommand, params); ctx->executeCommand(saveAsCommand, params);
} }

View File

@ -98,6 +98,10 @@ void PreviewCliDelegate::saveFile(Context* ctx, const CliOpenFile& cof)
std::cout << " - Trim\n"; std::cout << " - Trim\n";
} }
if (cof.ignoreEmpty) {
std::cout << " - Ignore empty frames\n";
}
std::cout << " - Size: " std::cout << " - Size: "
<< cof.document->sprite()->width() << "x" << cof.document->sprite()->width() << "x"
<< cof.document->sprite()->height() << "\n"; << cof.document->sprite()->height() << "\n";
@ -136,7 +140,8 @@ void PreviewCliDelegate::saveFile(Context* ctx, const CliOpenFile& cof)
ctx, ctx,
cof.roi(), cof.roi(),
cof.filename, cof.filename,
cof.filenameFormat)); cof.filenameFormat,
cof.ignoreEmpty));
if (fop) { if (fop) {
base::paths files; base::paths files;

View File

@ -89,6 +89,7 @@ SaveFileBaseCommand::SaveFileBaseCommand(const char* id, CommandFlags flags)
: Command(id, flags) : Command(id, flags)
{ {
m_useUI = true; m_useUI = true;
m_ignoreEmpty = false;
} }
void SaveFileBaseCommand::onLoadParams(const Params& params) void SaveFileBaseCommand::onLoadParams(const Params& params)
@ -113,6 +114,9 @@ void SaveFileBaseCommand::onLoadParams(const Params& params)
std::string useUI = params.get("useUI"); std::string useUI = params.get("useUI");
m_useUI = (useUI.empty() || (useUI == "true")); 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. // Returns true if there is a current sprite to save.
@ -194,7 +198,8 @@ void SaveFileBaseCommand::saveDocumentInBackground(
context, context,
roi, roi,
filename, filename,
m_filenameFormat)); m_filenameFormat,
m_ignoreEmpty));
if (!fop) if (!fop)
return; return;

View File

@ -45,6 +45,7 @@ namespace app {
doc::SelectedFrames m_selFrames; doc::SelectedFrames m_selFrames;
bool m_adjustFramesByFrameTag; bool m_adjustFramesByFrameTag;
bool m_useUI; bool m_useUI;
bool m_ignoreEmpty;
}; };
} // namespace app } // namespace app

View File

@ -104,7 +104,8 @@ int save_document(Context* context, Doc* document)
FileOp::createSaveDocumentOperation( FileOp::createSaveDocumentOperation(
context, context,
FileOpROI(document, "", "", SelectedFrames(), false), FileOpROI(document, "", "", SelectedFrames(), false),
document->filename(), "")); document->filename(), "",
false));
if (!fop) if (!fop)
return -1; return -1;
@ -317,7 +318,8 @@ done:;
FileOp* FileOp::createSaveDocumentOperation(const Context* context, FileOp* FileOp::createSaveDocumentOperation(const Context* context,
const FileOpROI& roi, const FileOpROI& roi,
const std::string& filename, const std::string& filename,
const std::string& filenameFormatArg) const std::string& filenameFormatArg,
const bool ignoreEmptyFrames)
{ {
std::unique_ptr<FileOp> fop( std::unique_ptr<FileOp> fop(
new FileOp(FileOpSave, const_cast<Context*>(context))); new FileOp(FileOpSave, const_cast<Context*>(context)));
@ -325,6 +327,7 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
// Document to save // Document to save
fop->m_document = const_cast<Doc*>(roi.document()); fop->m_document = const_cast<Doc*>(roi.document());
fop->m_roi = roi; fop->m_roi = roi;
fop->m_ignoreEmpty = ignoreEmptyFrames;
// Get the extension of the filename (in lower case) // Get the extension of the filename (in lower case)
LOG("FILE: Saving document \"%s\"\n", filename.c_str()); 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); render.renderSprite(m_seq.image.get(), sprite, frame);
} }
// Setup the palette. bool save = true;
sprite->palette(frame)->copyColorsTo(m_seq.palette);
// Setup the filename to be used. // Check if we have to ignore empty frames
m_filename = m_seq.filename_list[outputFrame]; if (m_ignoreEmpty &&
!sprite->isOpaque() &&
// Make directories doc::is_empty_image(m_seq.image.get())) {
{ save = false;
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 (save) {
if (!m_format->save(this)) { // Setup the palette.
setError("Error saving frame %d in the file \"%s\"\n", sprite->palette(frame)->copyColorsTo(m_seq.palette);
outputFrame+1, m_filename.c_str());
break; // 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; m_seq.progress_offset += m_seq.progress_fraction;
@ -1153,6 +1167,7 @@ FileOp::FileOp(FileOpType type, Context* context)
, m_done(false) , m_done(false)
, m_stop(false) , m_stop(false)
, m_oneframe(false) , m_oneframe(false)
, m_ignoreEmpty(false)
, m_preserveColorProfile( , m_preserveColorProfile(
Preferences::instance().color.manage()) Preferences::instance().color.manage())
, m_embeddedColorProfile(false) , m_embeddedColorProfile(false)

View File

@ -105,7 +105,8 @@ namespace app {
static FileOp* createSaveDocumentOperation(const Context* context, static FileOp* createSaveDocumentOperation(const Context* context,
const FileOpROI& roi, const FileOpROI& roi,
const std::string& filename, const std::string& filename,
const std::string& filenameFormat); const std::string& filenameFormat,
const bool ignoreEmptyFrames);
~FileOp(); ~FileOp();
@ -196,6 +197,7 @@ namespace app {
bool m_oneframe; // Load just one frame (in formats bool m_oneframe; // Load just one frame (in formats
// that support animation like // that support animation like
// GIF/FLI/ASE). // GIF/FLI/ASE).
bool m_ignoreEmpty;
// Return if we've to save/embed the color space of the document // Return if we've to save/embed the color space of the document
// in the file. // in the file.

View File

@ -1,4 +1,5 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2018 Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello // Copyright (c) 2001-2016 David Capello
// //
// This file is released under the terms of the MIT license. // 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 { 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> template<typename ImageTraits>
int count_diff_between_images_templ(const Image* i1, const Image* i2) 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 } // 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) int count_diff_between_images(const Image* i1, const Image* i2)
{ {
if ((i1->pixelFormat() != i2->pixelFormat()) || if ((i1->pixelFormat() != i2->pixelFormat()) ||

View File

@ -1,4 +1,5 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2018 Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello // Copyright (c) 2001-2016 David Capello
// //
// This file is released under the terms of the MIT license. // 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 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); 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); int count_diff_between_images(const Image* i1, const Image* i2);
void remap_image(Image* image, const Remap& remap); void remap_image(Image* image, const Remap& remap);

View File

@ -149,16 +149,21 @@ void Sprite::setColorSpace(const gfx::ColorSpacePtr& colorSpace)
cel->image()->setColorSpace(colorSpace); cel->image()->setColorSpace(colorSpace);
} }
bool Sprite::isOpaque() const
{
Layer* bg = backgroundLayer();
return (bg && bg->isVisible());
}
bool Sprite::needAlpha() const bool Sprite::needAlpha() const
{ {
switch (pixelFormat()) { switch (pixelFormat()) {
case IMAGE_RGB: case IMAGE_RGB:
case IMAGE_GRAYSCALE: { case IMAGE_GRAYSCALE:
Layer* bg = backgroundLayer(); return !isOpaque();
return (!bg || !bg->isVisible()); default:
} return false;
} }
return false;
} }
bool Sprite::supportAlpha() const bool Sprite::supportAlpha() const

View File

@ -84,6 +84,9 @@ namespace doc {
void setSize(int width, int height); void setSize(int width, int height);
void setColorSpace(const gfx::ColorSpacePtr& colorSpace); 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 // Returns true if the rendered images will contain alpha values less
// than 255. Only RGBA and Grayscale images without background needs // than 255. Only RGBA and Grayscale images without background needs
// alpha channel in the render. // alpha channel in the render.