mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-06 03:39:51 +00:00
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:
parent
3dc76d08c4
commit
cf1711c2af
@ -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" />
|
||||||
|
@ -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
|
||||||
|
@ -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" />
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user