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());
}
if (cof.ignoreEmpty)
params.set("ignoreEmpty", "true");
ctx->executeCommand(saveAsCommand, params);
}

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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)

View File

@ -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.

View 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()) ||

View 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.
@ -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);

View File

@ -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

View File

@ -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.