Add 'Extrude' option to 'Export Sprite Sheet' command (fix #1890)

Merged #1978 (squashed). The only solved conflict was that now
ExportSpriteSheet use params, so we've added the "extrude" param to
ExportSpriteSheetParams.
This commit is contained in:
David N Campo 2019-01-10 07:44:58 -03:00 committed by David Capello
parent 3dc76d08c4
commit cf1711c2af
6 changed files with 74 additions and 15 deletions

View File

@ -425,6 +425,7 @@
<option id="shape_padding" type="int" default="0" /> <option id="shape_padding" type="int" default="0" />
<option id="inner_padding" type="int" default="0" /> <option id="inner_padding" type="int" default="0" />
<option id="trim" type="bool" default="false" /> <option id="trim" type="bool" default="false" />
<option id="extrude" type="bool" default="false" />
<option id="open_generated" type="bool" default="false" /> <option id="open_generated" type="bool" default="false" />
<option id="layer" type="std::string" /> <option id="layer" type="std::string" />
<option id="frame_tag" type="std::string" /> <option id="frame_tag" type="std::string" />

View File

@ -521,6 +521,7 @@ border = Border:
shape = Shape: shape = Shape:
inner = Inner: inner = Inner:
trim = Trim trim = Trim
extrude = Extrude
width = Width: width = Width:
height = Height: height = Height:
best_fit = Best fit for texture best_fit = Best fit for texture

View File

@ -18,6 +18,7 @@
<vbox> <vbox>
<check id="padding_enabled" text="@.padding" /> <check id="padding_enabled" text="@.padding" />
<check id="trim_enabled" text="@.trim" /> <check id="trim_enabled" text="@.trim" />
<check id="extrude_enabled" text="@.extrude" />
</vbox> </vbox>
<grid columns="2" id="padding_container" cell_hspan="2"> <grid columns="2" id="padding_container" cell_hspan="2">
<label text="@.border" /> <label text="@.border" />

View File

@ -179,6 +179,7 @@ struct ExportSpriteSheetParams : public NewParams {
Param<int> shapePadding { this, 0, "shapePadding" }; Param<int> shapePadding { this, 0, "shapePadding" };
Param<int> innerPadding { this, 0, "innerPadding" }; Param<int> innerPadding { this, 0, "innerPadding" };
Param<bool> trim { this, false, "trim" }; Param<bool> trim { this, false, "trim" };
Param<bool> extrude { this, false, "extrude" };
Param<bool> openGenerated { this, false, "openGenerated" }; Param<bool> openGenerated { this, false, "openGenerated" };
Param<std::string> layer { this, std::string(), "layer" }; Param<std::string> layer { this, std::string(), "layer" };
Param<std::string> tag { this, std::string(), "tag" }; Param<std::string> tag { this, std::string(), "tag" };
@ -218,6 +219,7 @@ public:
openGenerated()->setSelected(params.openGenerated()); openGenerated()->setSelected(params.openGenerated());
trimEnabled()->setSelected(params.trim()); trimEnabled()->setSelected(params.trim());
extrudeEnabled()->setSelected(params.extrude());
borderPadding()->setTextf("%d", params.borderPadding()); borderPadding()->setTextf("%d", params.borderPadding());
shapePadding()->setTextf("%d", params.shapePadding()); shapePadding()->setTextf("%d", params.shapePadding());
@ -392,6 +394,10 @@ public:
return trimEnabled()->isSelected(); return trimEnabled()->isSelected();
} }
bool extrudeValue() const {
return extrudeEnabled()->isSelected();
}
bool openGeneratedValue() const { bool openGeneratedValue() const {
return openGenerated()->isSelected(); return openGenerated()->isSelected();
} }
@ -680,6 +686,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
docPref.spriteSheet.shapePadding (params.shapePadding (window.shapePaddingValue())); docPref.spriteSheet.shapePadding (params.shapePadding (window.shapePaddingValue()));
docPref.spriteSheet.innerPadding (params.innerPadding (window.innerPaddingValue())); docPref.spriteSheet.innerPadding (params.innerPadding (window.innerPaddingValue()));
docPref.spriteSheet.trim (params.trim (window.trimValue())); docPref.spriteSheet.trim (params.trim (window.trimValue()));
docPref.spriteSheet.extrude (params.extrude (window.extrudeValue()));
docPref.spriteSheet.openGenerated (params.openGenerated (window.openGeneratedValue())); docPref.spriteSheet.openGenerated (params.openGenerated (window.openGeneratedValue()));
docPref.spriteSheet.layer (params.layer (window.layerValue())); docPref.spriteSheet.layer (params.layer (window.layerValue()));
docPref.spriteSheet.frameTag (params.tag (window.frameTagValue())); docPref.spriteSheet.frameTag (params.tag (window.frameTagValue()));
@ -716,6 +723,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
const int shapePadding = base::clamp(params.shapePadding(), 0, 100); const int shapePadding = base::clamp(params.shapePadding(), 0, 100);
const int innerPadding = base::clamp(params.innerPadding(), 0, 100); const int innerPadding = base::clamp(params.innerPadding(), 0, 100);
const bool trimCels = params.trim(); const bool trimCels = params.trim();
const bool extrude = params.extrude();
const bool listLayers = params.listLayers(); const bool listLayers = params.listLayers();
const bool listTags = params.listTags(); const bool listTags = params.listTags();
const bool listSlices = params.listSlices(); const bool listSlices = params.listSlices();
@ -799,6 +807,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
exporter.setShapePadding(shapePadding); exporter.setShapePadding(shapePadding);
exporter.setInnerPadding(innerPadding); exporter.setInnerPadding(innerPadding);
exporter.setTrimCels(trimCels); exporter.setTrimCels(trimCels);
exporter.setExtrude(extrude);
if (listLayers) exporter.setListLayers(true); if (listLayers) exporter.setListLayers(true);
if (listTags) exporter.setListFrameTags(true); if (listTags) exporter.setListFrameTags(true);
if (listSlices) exporter.setListSlices(true); if (listSlices) exporter.setListSlices(true);

View File

@ -169,13 +169,14 @@ doc::SelectedFrames DocExporter::Item::getSelectedFrames() const
class DocExporter::Sample { class DocExporter::Sample {
public: public:
Sample(Doc* document, Sprite* sprite, SelectedLayers* selLayers, Sample(Doc* document, Sprite* sprite, SelectedLayers* selLayers,
frame_t frame, const std::string& filename, int innerPadding) : frame_t frame, const std::string& filename, int innerPadding, bool extrude) :
m_document(document), m_document(document),
m_sprite(sprite), m_sprite(sprite),
m_selLayers(selLayers), m_selLayers(selLayers),
m_frame(frame), m_frame(frame),
m_filename(filename), m_filename(filename),
m_innerPadding(innerPadding), m_innerPadding(innerPadding),
m_extrude(extrude),
m_bounds(new SampleBounds(sprite)), m_bounds(new SampleBounds(sprite)),
m_isDuplicated(false) { m_isDuplicated(false) {
} }
@ -194,9 +195,12 @@ public:
const gfx::Rect& inTextureBounds() const { return m_bounds->inTextureBounds(); } const gfx::Rect& inTextureBounds() const { return m_bounds->inTextureBounds(); }
gfx::Size requiredSize() const { gfx::Size requiredSize() const {
// if extrude option is enabled, an extra pixel is needed for each side
// left+right borders and top+bottom borders
int extraExtrudePixels = m_extrude ? 2 : 0;
gfx::Size size = m_bounds->trimmedBounds().size(); gfx::Size size = m_bounds->trimmedBounds().size();
size.w += 2*m_innerPadding; size.w += 2*m_innerPadding + extraExtrudePixels;
size.h += 2*m_innerPadding; size.h += 2*m_innerPadding + extraExtrudePixels;
return size; return size;
} }
@ -223,6 +227,7 @@ private:
frame_t m_frame; frame_t m_frame;
std::string m_filename; std::string m_filename;
int m_innerPadding; int m_innerPadding;
bool m_extrude;
SampleBoundsPtr m_bounds; SampleBoundsPtr m_bounds;
bool m_isDuplicated; bool m_isDuplicated;
}; };
@ -391,6 +396,7 @@ DocExporter::DocExporter()
, m_shapePadding(0) , m_shapePadding(0)
, m_innerPadding(0) , m_innerPadding(0)
, m_trimCels(false) , m_trimCels(false)
, m_extrude(false)
, m_listFrameTags(false) , m_listFrameTags(false)
, m_listLayers(false) , m_listLayers(false)
, m_listSlices(false) , m_listSlices(false)
@ -510,7 +516,7 @@ void DocExporter::captureSamples(Samples& samples)
std::string filename = filename_formatter(format, fnInfo); std::string filename = filename_formatter(format, fnInfo);
Sample sample(doc, sprite, item.selLayers, frame, filename, m_innerPadding); Sample sample(doc, sprite, item.selLayers, frame, filename, m_innerPadding, m_extrude);
Cel* cel = nullptr; Cel* cel = nullptr;
Cel* link = nullptr; Cel* link = nullptr;
bool done = false; bool done = false;
@ -552,7 +558,7 @@ void DocExporter::captureSamples(Samples& samples)
sampleRender->setMaskColor(sprite->transparentColor()); sampleRender->setMaskColor(sprite->transparentColor());
clear_image(sampleRender.get(), sprite->transparentColor()); clear_image(sampleRender.get(), sprite->transparentColor());
renderSample(sample, sampleRender.get(), 0, 0); renderSample(sample, sampleRender.get(), 0, 0, false);
gfx::Rect frameBounds; gfx::Rect frameBounds;
doc::color_t refColor = 0; doc::color_t refColor = 0;
@ -730,7 +736,8 @@ void DocExporter::renderTexture(Context* ctx, const Samples& samples, Image* tex
renderSample(sample, textureImage, renderSample(sample, textureImage,
sample.inTextureBounds().x+m_innerPadding, sample.inTextureBounds().x+m_innerPadding,
sample.inTextureBounds().y+m_innerPadding); sample.inTextureBounds().y+m_innerPadding,
m_extrude);
} }
} }
@ -740,6 +747,16 @@ void DocExporter::createDataFile(const Samples& samples, std::ostream& os, Image
std::string frames_end; std::string frames_end;
bool filename_as_key = false; bool filename_as_key = false;
bool filename_as_attr = false; bool filename_as_attr = false;
int nonExtrudedPosition = 0;
int nonExtrudedSize = 0;
// if the the image was extruded then the exported meta-information (JSON)
// should inform where start the real image (+1 displaced) and its
// size (-2 pixels: one per each dimension compared the extruded image)
if (m_extrude) {
nonExtrudedPosition += 1;
nonExtrudedSize -= 2;
}
// TODO we should use some string templates system here // TODO we should use some string templates system here
switch (m_dataFormat) { switch (m_dataFormat) {
@ -773,10 +790,10 @@ void DocExporter::createDataFile(const Samples& samples, std::ostream& os, Image
<< " \"filename\": \"" << escape_for_json(sample.filename()) << "\",\n"; << " \"filename\": \"" << escape_for_json(sample.filename()) << "\",\n";
os << " \"frame\": { " os << " \"frame\": { "
<< "\"x\": " << frameBounds.x << ", " << "\"x\": " << frameBounds.x + nonExtrudedPosition << ", "
<< "\"y\": " << frameBounds.y << ", " << "\"y\": " << frameBounds.y + nonExtrudedPosition << ", "
<< "\"w\": " << frameBounds.w << ", " << "\"w\": " << frameBounds.w + nonExtrudedSize << ", "
<< "\"h\": " << frameBounds.h << " },\n" << "\"h\": " << frameBounds.h + nonExtrudedSize << " },\n"
<< " \"rotated\": false,\n" << " \"rotated\": false,\n"
<< " \"trimmed\": " << (sample.trimmed() ? "true": "false") << ",\n" << " \"trimmed\": " << (sample.trimmed() ? "true": "false") << ",\n"
<< " \"spriteSourceSize\": { " << " \"spriteSourceSize\": { "
@ -977,17 +994,44 @@ void DocExporter::createDataFile(const Samples& samples, std::ostream& os, Image
<< "}\n"; << "}\n";
} }
void DocExporter::renderSample(const Sample& sample, doc::Image* dst, int x, int y) const void DocExporter::renderSample(const Sample& sample, doc::Image* dst, int x, int y, bool extrude) const
{ {
gfx::Clip clip(x, y, sample.trimmedBounds());
RestoreVisibleLayers layersVisibility; RestoreVisibleLayers layersVisibility;
if (sample.selectedLayers()) if (sample.selectedLayers())
layersVisibility.showSelectedLayers(sample.sprite(), layersVisibility.showSelectedLayers(sample.sprite(),
*sample.selectedLayers()); *sample.selectedLayers());
render::Render render; render::Render render;
if (extrude) {
const gfx::Rect& trim = sample.trimmedBounds();
// Displaced position onto the destination texture
int dx[] = {0, 1, trim.w+1};
int dy[] = {0, 1, trim.h+1};
// Starting point of the area to be copied from the original image
// taking into account the size of the trimmed sprite
int srcx[] = {trim.x, trim.x, trim.x2()-1};
int srcy[] = {trim.y, trim.y, trim.y2()-1};
// Size of the area to be copied from original image, starting at
// the point (srcx[i], srxy[j])
int szx[] = {1, trim.w, 1};
int szy[] = {1, trim.h, 1};
// Render a 9-patch image extruding the sample one pixel on each
// side.
for(int j=0; j<3; ++j) {
for(int i=0; i<3; ++i) {
gfx::Clip clip(x+dx[i], y+dy[j], gfx::RectT<int>(srcx[i], srcy[j], szx[i], szy[j]));
render.renderSprite(dst, sample.sprite(), sample.frame(), clip); render.renderSprite(dst, sample.sprite(), sample.frame(), clip);
} }
}
}
else {
gfx::Clip clip(x, y, sample.trimmedBounds());
render.renderSprite(dst, sample.sprite(), sample.frame(), clip);
}
}
} // namespace app } // namespace app

View File

@ -54,6 +54,7 @@ namespace app {
int shapePadding() const { return m_shapePadding; } int shapePadding() const { return m_shapePadding; }
int innerPadding() const { return m_innerPadding; } int innerPadding() const { return m_innerPadding; }
bool trimCels() const { return m_trimCels; } bool trimCels() const { return m_trimCels; }
bool extrude() const { return m_extrude; }
const std::string& filenameFormat() const { return m_filenameFormat; } const std::string& filenameFormat() const { return m_filenameFormat; }
bool listFrameTags() const { return m_listFrameTags; } bool listFrameTags() const { return m_listFrameTags; }
bool listLayers() const { return m_listLayers; } bool listLayers() const { return m_listLayers; }
@ -69,6 +70,7 @@ namespace app {
void setShapePadding(int padding) { m_shapePadding = padding; } void setShapePadding(int padding) { m_shapePadding = padding; }
void setInnerPadding(int padding) { m_innerPadding = padding; } void setInnerPadding(int padding) { m_innerPadding = padding; }
void setTrimCels(bool trim) { m_trimCels = trim; } void setTrimCels(bool trim) { m_trimCels = trim; }
void setExtrude(bool extrude) { m_extrude = extrude; }
void setFilenameFormat(const std::string& format) { m_filenameFormat = format; } void setFilenameFormat(const std::string& format) { m_filenameFormat = format; }
void setListFrameTags(bool value) { m_listFrameTags = value; } void setListFrameTags(bool value) { m_listFrameTags = value; }
void setListLayers(bool value) { m_listLayers = value; } void setListLayers(bool value) { m_listLayers = value; }
@ -97,7 +99,7 @@ namespace app {
Doc* createEmptyTexture(const Samples& samples) const; Doc* createEmptyTexture(const Samples& samples) const;
void renderTexture(Context* ctx, const Samples& samples, doc::Image* textureImage) const; void renderTexture(Context* ctx, const Samples& samples, doc::Image* textureImage) const;
void createDataFile(const Samples& samples, std::ostream& os, doc::Image* textureImage); void createDataFile(const Samples& samples, std::ostream& os, doc::Image* textureImage);
void renderSample(const Sample& sample, doc::Image* dst, int x, int y) const; void renderSample(const Sample& sample, doc::Image* dst, int x, int y, bool extrude) const;
class Item { class Item {
public: public:
@ -133,6 +135,7 @@ namespace app {
int m_shapePadding; int m_shapePadding;
int m_innerPadding; int m_innerPadding;
bool m_trimCels; bool m_trimCels;
bool m_extrude;
Items m_documents; Items m_documents;
std::string m_filenameFormat; std::string m_filenameFormat;
doc::ImageBufferPtr m_sampleRenderBuf; doc::ImageBufferPtr m_sampleRenderBuf;