[aseprite] 33/308: Add possibility to import/export sprite sheet matrices by columns
Tobias Hansen
thansen at moszumanska.debian.org
Tue Mar 8 02:44:49 UTC 2016
This is an automated email from the git hooks/post-receive script.
thansen pushed a commit to branch master
in repository aseprite.
commit 4dbee127891a4931a90ddb50731b2a5530ec4009
Author: David Capello <davidcapello at gmail.com>
Date: Thu Dec 3 11:38:59 2015 -0300
Add possibility to import/export sprite sheet matrices by columns
Fix #872
---
data/pref.xml | 10 +-
data/skins/default/skin.xml | 2 +
data/widgets/export_sprite_sheet.xml | 6 +-
data/widgets/import_sprite_sheet.xml | 3 +
src/app/app.cpp | 20 +++-
src/app/app_options.cpp | 3 +-
src/app/app_options.h | 2 +
src/app/commands/cmd_canvas_size.cpp | 9 +-
src/app/commands/cmd_export_sprite_sheet.cpp | 152 ++++++++++++++++++---------
src/app/commands/cmd_import_sprite_sheet.cpp | 122 +++++++++++++++++----
src/app/commands/cmd_new_brush.cpp | 5 +-
src/app/commands/cmd_repeat_last_export.cpp | 2 +-
src/app/document_exporter.cpp | 89 +++++++++++-----
src/app/document_exporter.h | 5 +-
src/app/pref/preferences.h | 1 +
src/app/sprite_sheet_type.h | 25 +++++
src/app/ui/editor/select_box_state.cpp | 72 ++++++++-----
src/app/ui/editor/select_box_state.h | 26 ++++-
18 files changed, 410 insertions(+), 144 deletions(-)
diff --git a/data/pref.xml b/data/pref.xml
index aaff328..c15dade 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -26,12 +26,6 @@
<value id="CHECKED_4x4" value="2" />
<value id="CHECKED_2x2" value="3" />
</enum>
- <enum id="SpriteSheetType">
- <value id="NONE" value="0" />
- <value id="HORIZONTAL_STRIP" value="1" />
- <value id="VERTICAL_STRIP" value="2" />
- <value id="MATRIX" value="3" />
- </enum>
<enum id="StopAtGrid">
<value id="NEVER" value="0" />
<value id="IF_VISIBLE" value="1" />
@@ -241,8 +235,9 @@
<option id="resize_scale" type="double" default="1" />
</section>
<section id="sprite_sheet">
- <option id="type" type="SpriteSheetType" default="SpriteSheetType::NONE" />
+ <option id="type" type="app::SpriteSheetType" default="app::SpriteSheetType::None" />
<option id="columns" type="int" default="0" />
+ <option id="rows" type="int" default="0" />
<option id="width" type="int" default="0" />
<option id="height" type="int" default="0" />
<option id="best_fit" type="bool" default="false" />
@@ -256,6 +251,7 @@
<option id="frame_tag" type="std::string" />
</section>
<section id="import_sprite_sheet">
+ <option id="type" type="app::SpriteSheetType" default="app::SpriteSheetType::Rows" />
<option id="bounds" type="gfx::Rect" default="gfx::Rect(0, 0, 16, 16)" />
</section>
</document>
diff --git a/data/skins/default/skin.xml b/data/skins/default/skin.xml
index 63a5576..203fe57 100644
--- a/data/skins/default/skin.xml
+++ b/data/skins/default/skin.xml
@@ -101,6 +101,8 @@
<color id="flag_normal" value="#d3cbbe" />
<color id="flag_active" value="#ff0000" />
<color id="flag_clicked" value="#7d929e" />
+ <color id="select_box_ruler" value="#0000ff" />
+ <color id="select_box_grid" value="#64c864" />
</colors>
<cursors>
diff --git a/data/widgets/export_sprite_sheet.xml b/data/widgets/export_sprite_sheet.xml
index 2b8aca9..a3a4ffb 100644
--- a/data/widgets/export_sprite_sheet.xml
+++ b/data/widgets/export_sprite_sheet.xml
@@ -6,10 +6,14 @@
<label text="Sheet Type:" />
<combobox id="sheet_type" cell_hspan="3" />
- <label id="columns_label" text="Columns:" />
+ <label id="columns_label" text="# of Columns:" />
<entry id="columns" text="" maxsize="4" />
<boxfiller cell_hspan="2" />
+ <label id="rows_label" text="# of Rows:" />
+ <entry id="rows" text="" maxsize="4" />
+ <boxfiller cell_hspan="2" />
+
<hbox />
<vbox>
<check id="padding_enabled" text="Padding" />
diff --git a/data/widgets/import_sprite_sheet.xml b/data/widgets/import_sprite_sheet.xml
index 12f219d..a6854e6 100644
--- a/data/widgets/import_sprite_sheet.xml
+++ b/data/widgets/import_sprite_sheet.xml
@@ -5,6 +5,9 @@
<grid columns="4">
<button id="select_file" text="Select File" cell_hspan="4" />
+ <label text="Type:" />
+ <combobox id="sheet_type" cell_hspan="3" />
+
<label text="X" />
<entry id="x" text="0" maxsize="4" />
<label text="Width" />
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 7155a02..55d72f2 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -218,6 +218,7 @@ void App::initialize(const AppOptions& options)
bool ignoreEmpty = false;
bool trim = false;
Params cropParams;
+ SpriteSheetType sheetType = SpriteSheetType::None;
// Open file specified in the command line
if (!options.values().empty()) {
@@ -271,9 +272,21 @@ void App::initialize(const AppOptions& options)
m_exporter->setTextureHeight(strtol(value.value().c_str(), NULL, 0));
}
// --sheet-pack
+ else if (opt == &options.sheetType()) {
+ if (value.value() == "horizontal")
+ sheetType = SpriteSheetType::Horizontal;
+ else if (value.value() == "vertical")
+ sheetType = SpriteSheetType::Vertical;
+ else if (value.value() == "rows")
+ sheetType = SpriteSheetType::Rows;
+ else if (value.value() == "columns")
+ sheetType = SpriteSheetType::Columns;
+ else if (value.value() == "packed")
+ sheetType = SpriteSheetType::Packed;
+ }
+ // --sheet-pack
else if (opt == &options.sheetPack()) {
- if (m_exporter)
- m_exporter->setTexturePack(true);
+ sheetType = SpriteSheetType::Packed;
}
// --split-layers
else if (opt == &options.splitLayers()) {
@@ -564,6 +577,9 @@ void App::initialize(const AppOptions& options)
if (m_exporter) {
LOG("Exporting sheet...\n");
+ if (sheetType != SpriteSheetType::None)
+ m_exporter->setSpriteSheetType(sheetType);
+
if (ignoreEmpty)
m_exporter->setIgnoreEmptyCels(true);
diff --git a/src/app/app_options.cpp b/src/app/app_options.cpp
index 8bf3b77..b9abab6 100644
--- a/src/app/app_options.cpp
+++ b/src/app/app_options.cpp
@@ -35,7 +35,8 @@ AppOptions::AppOptions(int argc, const char* argv[])
, m_sheet(m_po.add("sheet").requiresValue("<filename.png>").description("Image file to save the texture"))
, m_sheetWidth(m_po.add("sheet-width").requiresValue("<pixels>").description("Sprite sheet width"))
, m_sheetHeight(m_po.add("sheet-height").requiresValue("<pixels>").description("Sprite sheet height"))
- , m_sheetPack(m_po.add("sheet-pack").description("Use a packing algorithm to avoid waste of space\nin the texture"))
+ , m_sheetType(m_po.add("sheet-type").requiresValue("<type>").description("Algorithm to create the sprite sheet:\n horizontal\n vertical\n rows\n columns\n packed"))
+ , m_sheetPack(m_po.add("sheet-pack").description("Same as --sheet-type packed"))
, m_splitLayers(m_po.add("split-layers").description("Import each layer of the next given sprite as\na separated image in the sheet"))
, m_layer(m_po.add("layer").alias("import-layer").requiresValue("<name>").description("Include just the given layer in the sheet"))
, m_allLayers(m_po.add("all-layers").description("Make all layers visible\nBy default hidden layers will be ignored"))
diff --git a/src/app/app_options.h b/src/app/app_options.h
index 61fd560..dc3df68 100644
--- a/src/app/app_options.h
+++ b/src/app/app_options.h
@@ -43,6 +43,7 @@ public:
const Option& sheet() const { return m_sheet; }
const Option& sheetWidth() const { return m_sheetWidth; }
const Option& sheetHeight() const { return m_sheetHeight; }
+ const Option& sheetType() const { return m_sheetType; }
const Option& sheetPack() const { return m_sheetPack; }
const Option& splitLayers() const { return m_splitLayers; }
const Option& layer() const { return m_layer; }
@@ -82,6 +83,7 @@ private:
Option& m_sheet;
Option& m_sheetWidth;
Option& m_sheetHeight;
+ Option& m_sheetType;
Option& m_sheetPack;
Option& m_splitLayers;
Option& m_layer;
diff --git a/src/app/commands/cmd_canvas_size.cpp b/src/app/commands/cmd_canvas_size.cpp
index ad3307e..45fc8ad 100644
--- a/src/app/commands/cmd_canvas_size.cpp
+++ b/src/app/commands/cmd_canvas_size.cpp
@@ -49,9 +49,12 @@ public:
CanvasSizeWindow()
: m_editor(current_editor)
, m_rect(0, 0, current_editor->sprite()->width(), current_editor->sprite()->height())
- , m_selectBoxState(new SelectBoxState(this, m_rect,
- SelectBoxState::RULERS |
- SelectBoxState::DARKOUTSIDE)) {
+ , m_selectBoxState(
+ new SelectBoxState(
+ this, m_rect,
+ SelectBoxState::Flags(
+ int(SelectBoxState::Flags::Rulers) |
+ int(SelectBoxState::Flags::DarkOutside)))) {
setWidth(m_rect.w);
setHeight(m_rect.h);
setLeft(0);
diff --git a/src/app/commands/cmd_export_sprite_sheet.cpp b/src/app/commands/cmd_export_sprite_sheet.cpp
index 8711066..580e686 100644
--- a/src/app/commands/cmd_export_sprite_sheet.cpp
+++ b/src/app/commands/cmd_export_sprite_sheet.cpp
@@ -54,11 +54,12 @@ namespace {
int width;
int height;
int columns;
+ int rows;
int freearea;
- Fit() : width(0), height(0), columns(0), freearea(0) {
+ Fit() : width(0), height(0), columns(0), rows(0), freearea(0) {
}
- Fit(int width, int height, int columns, int freearea) :
- width(width), height(height), columns(columns), freearea(freearea) {
+ Fit(int width, int height, int columns, int rows, int freearea) :
+ width(width), height(height), columns(columns), rows(rows), freearea(freearea) {
}
};
@@ -67,7 +68,7 @@ namespace {
Fit best_fit(Sprite* sprite, int nframes, int borderPadding, int shapePadding, int innerPadding) {
int framew = sprite->width()+2*innerPadding;
int frameh = sprite->height()+2*innerPadding;
- Fit result(framew*nframes, frameh, nframes, std::numeric_limits<int>::max());
+ Fit result(framew*nframes, frameh, nframes, 1, std::numeric_limits<int>::max());
int w, h;
for (w=2; w < framew; w*=2)
@@ -103,7 +104,7 @@ namespace {
for (const gfx::Rect& rgnRect : rgn)
freearea += rgnRect.w * rgnRect.h;
- Fit fit(w, h, (w / framew), freearea);
+ Fit fit(w, h, (w / framew), (h / frameh), freearea);
if (fit.freearea < result.freearea)
result = fit;
}
@@ -115,15 +116,22 @@ namespace {
return result;
}
- Fit calculate_sheet_size(Sprite* sprite, int nframes, int columns,
+ Fit calculate_sheet_size(Sprite* sprite, int nframes,
+ int columns, int rows,
int borderPadding, int shapePadding, int innerPadding) {
- columns = MID(1, columns, nframes);
- int rows = ((nframes/columns) + ((nframes%columns) > 0 ? 1: 0));
+ if (columns == 0) {
+ rows = MID(1, rows, nframes);
+ columns = ((nframes/rows) + ((nframes%rows) > 0 ? 1: 0));
+ }
+ else {
+ columns = MID(1, columns, nframes);
+ rows = ((nframes/columns) + ((nframes%columns) > 0 ? 1: 0));
+ }
return Fit(
2*borderPadding + (sprite->width()+2*innerPadding)*columns + (columns-1)*shapePadding,
2*borderPadding + (sprite->height()+2*innerPadding)*rows + (rows-1)*shapePadding,
- columns, 0);
+ columns, rows, 0);
}
bool ask_overwrite(bool askFilename, std::string filename,
@@ -270,16 +278,18 @@ public:
, m_dataFilenameAskOverwrite(true)
{
static_assert(
- (int)app::gen::SpriteSheetType::NONE == 0 &&
- (int)app::gen::SpriteSheetType::HORIZONTAL_STRIP == 1 &&
- (int)app::gen::SpriteSheetType::VERTICAL_STRIP == 2 &&
- (int)app::gen::SpriteSheetType::MATRIX == 3,
- "ExportType enum changed");
+ (int)app::SpriteSheetType::None == 0 &&
+ (int)app::SpriteSheetType::Horizontal == 1 &&
+ (int)app::SpriteSheetType::Vertical == 2 &&
+ (int)app::SpriteSheetType::Rows == 3 &&
+ (int)app::SpriteSheetType::Columns == 4,
+ "SpriteSheetType enum changed");
sheetType()->addItem("Horizontal Strip");
sheetType()->addItem("Vertical Strip");
- sheetType()->addItem("Matrix");
- if (m_docPref.spriteSheet.type() != app::gen::SpriteSheetType::NONE)
+ sheetType()->addItem("By Rows");
+ sheetType()->addItem("By Columns");
+ if (m_docPref.spriteSheet.type() != app::SpriteSheetType::None)
sheetType()->setSelectedItemIndex((int)m_docPref.spriteSheet.type()-1);
layers()->addItem("Visible layers");
@@ -329,6 +339,7 @@ public:
}
else {
columns()->setTextf("%d", m_docPref.spriteSheet.columns());
+ rows()->setTextf("%d", m_docPref.spriteSheet.rows());
onColumnsChange();
if (m_docPref.spriteSheet.width() > 0 || m_docPref.spriteSheet.height() > 0) {
@@ -368,6 +379,7 @@ public:
exportButton()->Click.connect(Bind<void>(&ExportSpriteSheetWindow::onExport, this));
sheetType()->Change.connect(&ExportSpriteSheetWindow::onSheetTypeChange, this);
columns()->Change.connect(Bind<void>(&ExportSpriteSheetWindow::onColumnsChange, this));
+ rows()->Change.connect(Bind<void>(&ExportSpriteSheetWindow::onRowsChange, this));
fitWidth()->Change.connect(Bind<void>(&ExportSpriteSheetWindow::onSizeChange, this));
fitHeight()->Change.connect(Bind<void>(&ExportSpriteSheetWindow::onSizeChange, this));
bestFit()->Click.connect(Bind<void>(&ExportSpriteSheetWindow::onBestFit, this));
@@ -391,41 +403,51 @@ public:
return getKiller() == exportButton();
}
- app::gen::SpriteSheetType spriteSheetTypeValue() {
- return (app::gen::SpriteSheetType)(sheetType()->getSelectedItemIndex()+1);
+ app::SpriteSheetType spriteSheetTypeValue() const {
+ return (app::SpriteSheetType)(sheetType()->getSelectedItemIndex()+1);
}
- int columnsValue() {
- return columns()->getTextInt();
+ int columnsValue() const {
+ if (spriteSheetTypeValue() != SpriteSheetType::Columns)
+ return columns()->getTextInt();
+ else
+ return 0;
+ }
+
+ int rowsValue() const {
+ if (spriteSheetTypeValue() == SpriteSheetType::Columns)
+ return rows()->getTextInt();
+ else
+ return 0;
}
- int fitWidthValue() {
+ int fitWidthValue() const {
return fitWidth()->getEntryWidget()->getTextInt();
}
- int fitHeightValue() {
+ int fitHeightValue() const {
return fitHeight()->getEntryWidget()->getTextInt();
}
- bool bestFitValue() {
+ bool bestFitValue() const {
return bestFit()->isSelected();
}
- std::string filenameValue() {
+ std::string filenameValue() const {
if (imageEnabled()->isSelected())
return m_filename;
else
return std::string();
}
- std::string dataFilenameValue() {
+ std::string dataFilenameValue() const {
if (dataEnabled()->isSelected())
return m_dataFilename;
else
return std::string();
}
- int borderPaddingValue() {
+ int borderPaddingValue() const {
if (paddingEnabled()->isSelected()) {
int value = borderPadding()->getTextInt();
return MID(0, value, 100);
@@ -434,7 +456,7 @@ public:
return 0;
}
- int shapePaddingValue() {
+ int shapePaddingValue() const {
if (paddingEnabled()->isSelected()) {
int value = shapePadding()->getTextInt();
return MID(0, value, 100);
@@ -443,7 +465,7 @@ public:
return 0;
}
- int innerPaddingValue() {
+ int innerPaddingValue() const {
if (paddingEnabled()->isSelected()) {
int value = innerPadding()->getTextInt();
return MID(0, value, 100);
@@ -452,11 +474,11 @@ public:
return 0;
}
- bool openGeneratedValue() {
+ bool openGeneratedValue() const {
return openGenerated()->isSelected();
}
- std::string layerValue() {
+ std::string layerValue() const {
if (LayerItem* item = dynamic_cast<LayerItem*>(layers()->getSelectedItem()))
return item->layer()->name();
else if (layers()->getSelectedItemIndex() == 1)
@@ -465,7 +487,7 @@ public:
return kAllLayers;
}
- std::string frameTagValue() {
+ std::string frameTagValue() const {
if (TagItem* item = dynamic_cast<TagItem*>(frames()->getSelectedItem()))
return item->tag()->name();
else if (frames()->getSelectedItemIndex() == 1)
@@ -485,21 +507,33 @@ private:
}
void onSheetTypeChange() {
- bool state = false;
+ bool rowsState = false;
+ bool colsState = false;
+ bool matrixState = false;
switch (spriteSheetTypeValue()) {
- case app::gen::SpriteSheetType::MATRIX:
- state = true;
+ case app::SpriteSheetType::Rows:
+ colsState = true;
+ matrixState = true;
+ break;
+ case app::SpriteSheetType::Columns:
+ rowsState = true;
+ matrixState = true;
break;
}
- columnsLabel()->setVisible(state);
- columns()->setVisible(state);
- fitWidthLabel()->setVisible(state);
- fitWidth()->setVisible(state);
- fitHeightLabel()->setVisible(state);
- fitHeight()->setVisible(state);
- bestFitFiller()->setVisible(state);
- bestFit()->setVisible(state);
+ columnsLabel()->setVisible(colsState);
+ columns()->setVisible(colsState);
+
+ rowsLabel()->setVisible(rowsState);
+ rows()->setVisible(rowsState);
+
+ fitWidthLabel()->setVisible(matrixState);
+ fitWidth()->setVisible(matrixState);
+ fitHeightLabel()->setVisible(matrixState);
+ fitHeight()->setVisible(matrixState);
+ bestFitFiller()->setVisible(matrixState);
+ bestFit()->setVisible(matrixState);
+
resize();
}
@@ -514,8 +548,14 @@ private:
updateSizeFields();
}
+ void onRowsChange() {
+ bestFit()->setSelected(false);
+ updateSizeFields();
+ }
+
void onSizeChange() {
columns()->setTextf("%d", fitWidthValue() / m_sprite->width());
+ rows()->setTextf("%d", fitHeightValue() / m_sprite->height());
bestFit()->setSelected(false);
}
@@ -585,6 +625,7 @@ private:
void resize() {
gfx::Size reqSize = getPreferredSize();
moveWindow(gfx::Rect(getOrigin(), reqSize));
+ layout();
invalidate();
}
@@ -614,13 +655,16 @@ private:
}
else {
fit = calculate_sheet_size(
- m_sprite, nframes, columnsValue(),
+ m_sprite, nframes,
+ columnsValue(),
+ rowsValue(),
borderPaddingValue(),
shapePaddingValue(),
innerPaddingValue());
}
columns()->setTextf("%d", fit.columns);
+ rows()->setTextf("%d", fit.rows);
fitWidth()->getEntryWidget()->setTextf("%d", fit.width);
fitHeight()->getEntryWidget()->setTextf("%d", fit.height);
}
@@ -692,6 +736,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
docPref.spriteSheet.type(window.spriteSheetTypeValue());
docPref.spriteSheet.columns(window.columnsValue());
+ docPref.spriteSheet.rows(window.rowsValue());
docPref.spriteSheet.width(window.fitWidthValue());
docPref.spriteSheet.height(window.fitHeightValue());
docPref.spriteSheet.bestFit(window.bestFitValue());
@@ -716,8 +761,9 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
askOverwrite = false; // Already asked in the ExportSpriteSheetWindow
}
- app::gen::SpriteSheetType type = docPref.spriteSheet.type();
+ app::SpriteSheetType type = docPref.spriteSheet.type();
int columns = docPref.spriteSheet.columns();
+ int rows = docPref.spriteSheet.rows();
int width = docPref.spriteSheet.width();
int height = docPref.spriteSheet.height();
bool bestFit = docPref.spriteSheet.bestFit();
@@ -777,6 +823,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
if (bestFit) {
Fit fit = best_fit(sprite, nframes, borderPadding, shapePadding, innerPadding);
columns = fit.columns;
+ rows = fit.rows;
width = fit.width;
height = fit.height;
}
@@ -785,24 +832,25 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
int sheet_h = 0;
switch (type) {
- case app::gen::SpriteSheetType::HORIZONTAL_STRIP:
+ case app::SpriteSheetType::Horizontal:
columns = sprite->totalFrames();
+ rows = 1;
break;
- case app::gen::SpriteSheetType::VERTICAL_STRIP:
+ case app::SpriteSheetType::Vertical:
columns = 1;
+ rows = nframes;
break;
- case app::gen::SpriteSheetType::MATRIX:
+ case app::SpriteSheetType::Rows:
+ case app::SpriteSheetType::Columns:
if (width > 0) sheet_w = width;
if (height > 0) sheet_h = height;
break;
}
Fit fit = calculate_sheet_size(
- sprite,
- nframes,
- columns,
+ sprite, nframes,
+ columns, rows,
borderPadding, shapePadding, innerPadding);
- columns = fit.columns;
if (sheet_w == 0) sheet_w = fit.width;
if (sheet_h == 0) sheet_h = fit.height;
@@ -813,7 +861,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
exporter.setDataFilename(dataFilename);
exporter.setTextureWidth(sheet_w);
exporter.setTextureHeight(sheet_h);
- exporter.setTexturePack(false);
+ exporter.setSpriteSheetType(type);
exporter.setBorderPadding(borderPadding);
exporter.setShapePadding(shapePadding);
exporter.setInnerPadding(innerPadding);
diff --git a/src/app/commands/cmd_import_sprite_sheet.cpp b/src/app/commands/cmd_import_sprite_sheet.cpp
index fa5572b..c49cc48 100644
--- a/src/app/commands/cmd_import_sprite_sheet.cpp
+++ b/src/app/commands/cmd_import_sprite_sheet.cpp
@@ -54,6 +54,20 @@ public:
, m_docPref(nullptr) {
import()->setEnabled(false);
+ static_assert(
+ (int)app::SpriteSheetType::Horizontal == 1 &&
+ (int)app::SpriteSheetType::Vertical == 2 &&
+ (int)app::SpriteSheetType::Rows == 3 &&
+ (int)app::SpriteSheetType::Columns == 4,
+ "SpriteSheetType enum changed");
+
+ sheetType()->addItem("Horizontal Strip");
+ sheetType()->addItem("Vertical Strip");
+ sheetType()->addItem("By Rows");
+ sheetType()->addItem("By Columns");
+ sheetType()->setSelectedItemIndex((int)app::SpriteSheetType::Rows-1);
+
+ sheetType()->Change.connect(Bind<void>(&ImportSpriteSheetWindow::onSheetTypeChange, this));
x()->Change.connect(Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
y()->Change.connect(Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
width()->Change.connect(Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
@@ -74,6 +88,10 @@ public:
releaseEditor();
}
+ SpriteSheetType sheetTypeValue() const {
+ return (app::SpriteSheetType)(sheetType()->getSelectedItemIndex()+1);
+ }
+
bool ok() const {
return getKiller() == import();
}
@@ -92,6 +110,10 @@ public:
protected:
+ void onSheetTypeChange() {
+ updateGridState();
+ }
+
void onSelectFile() {
Document* oldActiveDocument = m_context->activeDocument();
Command* openFile = CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
@@ -107,8 +129,7 @@ protected:
}
}
- gfx::Rect getRectFromEntries()
- {
+ gfx::Rect getRectFromEntries() {
int w = width()->getTextInt();
int h = height()->getTextInt();
@@ -119,8 +140,7 @@ protected:
std::max<int>(1, h));
}
- void onEntriesChange()
- {
+ void onEntriesChange() {
m_rect = getRectFromEntries();
// Redraw new rulers position
@@ -187,6 +207,12 @@ private:
if (m_document) {
m_docPref = &Preferences::instance().document(m_document);
+ if (m_docPref->importSpriteSheet.type() >= app::SpriteSheetType::Horizontal &&
+ m_docPref->importSpriteSheet.type() <= app::SpriteSheetType::Columns)
+ sheetType()->setSelectedItemIndex((int)m_docPref->importSpriteSheet.type()-1);
+ else
+ sheetType()->setSelectedItemIndex((int)app::SpriteSheetType::Rows-1);
+
onChangeRectangle(m_docPref->importSpriteSheet.bounds());
onEntriesChange();
}
@@ -198,12 +224,38 @@ private:
if (m_document && !m_editor) {
m_rect = getRectFromEntries();
m_editor = current_editor;
+ m_editorState.reset(
+ new SelectBoxState(
+ this, m_rect,
+ SelectBoxState::Flags(
+ int(SelectBoxState::Flags::Rulers) |
+ int(SelectBoxState::Flags::Grid))));
+
+ m_editor->setState(m_editorState);
+ updateGridState();
+ }
+ }
+
+ void updateGridState() {
+ if (!m_editorState)
+ return;
- EditorStatePtr newState(new SelectBoxState(this, m_rect,
- SelectBoxState::RULERS |
- SelectBoxState::GRID));
- m_editor->setState(newState);
+ int flags = int(SelectBoxState::Flags::Rulers);
+ switch (sheetTypeValue()) {
+ case SpriteSheetType::Horizontal:
+ flags |= int(SelectBoxState::Flags::HGrid);
+ break;
+ case SpriteSheetType::Vertical:
+ flags |= int(SelectBoxState::Flags::VGrid);
+ break;
+ case SpriteSheetType::Rows:
+ case SpriteSheetType::Columns:
+ flags |= int(SelectBoxState::Flags::Grid);
+ break;
}
+
+ static_cast<SelectBoxState*>(m_editorState.get())->setFlags(SelectBoxState::Flags(flags));
+ m_editor->invalidate();
}
void releaseEditor() {
@@ -216,6 +268,7 @@ private:
Context* m_context;
Document* m_document;
Editor* m_editor;
+ EditorStatePtr m_editorState;
gfx::Rect m_rect;
// True if the user has been opened the file (instead of selecting
@@ -252,6 +305,7 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
Document* document = window.document();
DocumentPreferences* docPref = window.docPref();
gfx::Rect frameBounds = window.frameBounds();
+ auto sheetType = window.sheetTypeValue();
ASSERT(document);
if (!document)
@@ -265,18 +319,48 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
frame_t currentFrame = context->activeSite().frame();
render::Render render;
+ // Each sprite in the sheet
+ std::vector<gfx::Rect> tileRects;
+
+ switch (sheetType) {
+ case app::SpriteSheetType::Horizontal:
+ for (int x=frameBounds.x; x+frameBounds.w<=sprite->width(); x += frameBounds.w) {
+ tileRects.push_back(gfx::Rect(x, frameBounds.y, frameBounds.w, frameBounds.h));
+ }
+ break;
+ case app::SpriteSheetType::Vertical:
+ for (int y=frameBounds.y; y+frameBounds.h<=sprite->height(); y += frameBounds.h) {
+ tileRects.push_back(gfx::Rect(frameBounds.x, y, frameBounds.w, frameBounds.h));
+ }
+ break;
+ case app::SpriteSheetType::Rows:
+ for (int y=frameBounds.y; y+frameBounds.h<=sprite->height(); y += frameBounds.h) {
+ for (int x=frameBounds.x; x+frameBounds.w<=sprite->width(); x += frameBounds.w) {
+ tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h));
+ }
+ }
+ break;
+ case app::SpriteSheetType::Columns:
+ for (int x=frameBounds.x; x+frameBounds.w<=sprite->width(); x += frameBounds.w) {
+ for (int y=frameBounds.y; y+frameBounds.h<=sprite->height(); y += frameBounds.h) {
+ tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h));
+ }
+ }
+ break;
+ }
+
// As first step, we cut each tile and add them into "animation" list.
- for (int y=frameBounds.y; y<sprite->height(); y += frameBounds.h) {
- for (int x=frameBounds.x; x<sprite->width(); x += frameBounds.w) {
- ImageRef resultImage(
- Image::create(sprite->pixelFormat(), frameBounds.w, frameBounds.h));
+ for (const auto& tileRect : tileRects) {
+ ImageRef resultImage(
+ Image::create(
+ sprite->pixelFormat(), tileRect.w, tileRect.h));
- // Render the portion of sheet.
- render.renderSprite(resultImage.get(), sprite, currentFrame,
- gfx::Clip(0, 0, x, y, frameBounds.w, frameBounds.h));
+ // Render the portion of sheet.
+ render.renderSprite(
+ resultImage.get(), sprite, currentFrame,
+ gfx::Clip(0, 0, tileRect));
- animation.push_back(resultImage);
- }
+ animation.push_back(resultImage);
}
if (animation.size() == 0) {
@@ -324,8 +408,10 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
transaction.commit();
ASSERT(docPref);
- if (docPref)
+ if (docPref) {
+ docPref->importSpriteSheet.type(sheetType);
docPref->importSpriteSheet.bounds(frameBounds);
+ }
}
catch (...) {
throw;
diff --git a/src/app/commands/cmd_new_brush.cpp b/src/app/commands/cmd_new_brush.cpp
index 4f1811e..06509a5 100644
--- a/src/app/commands/cmd_new_brush.cpp
+++ b/src/app/commands/cmd_new_brush.cpp
@@ -87,8 +87,9 @@ void NewBrushCommand::onExecute(Context* context)
EditorStatePtr(
new SelectBoxState(
this, current_editor->sprite()->bounds(),
- SelectBoxState::DARKOUTSIDE |
- SelectBoxState::QUICKBOX)));
+ SelectBoxState::Flags(
+ int(SelectBoxState::Flags::DarkOutside) |
+ int(SelectBoxState::Flags::QuickBox)))));
}
// Create a brush from the active selection
else {
diff --git a/src/app/commands/cmd_repeat_last_export.cpp b/src/app/commands/cmd_repeat_last_export.cpp
index 874cb6d..5b8c13e 100644
--- a/src/app/commands/cmd_repeat_last_export.cpp
+++ b/src/app/commands/cmd_repeat_last_export.cpp
@@ -53,7 +53,7 @@ void RepeatLastExportCommand::onExecute(Context* context)
Preferences::instance().document(document);
params.set("ui",
- (docPref.spriteSheet.type() == app::gen::SpriteSheetType::NONE ? "1": "0"));
+ (docPref.spriteSheet.type() == app::SpriteSheetType::None ? "1": "0"));
}
context->executeCommand(cmd, params);
diff --git a/src/app/document_exporter.cpp b/src/app/document_exporter.cpp
index 8627297..1aaf4eb 100644
--- a/src/app/document_exporter.cpp
+++ b/src/app/document_exporter.cpp
@@ -202,6 +202,10 @@ public:
class DocumentExporter::SimpleLayoutSamples :
public DocumentExporter::LayoutSamples {
public:
+ SimpleLayoutSamples(SpriteSheetType type)
+ : m_type(type) {
+ }
+
void layoutSamples(Samples& samples, int borderPadding, int shapePadding, int& width, int& height) override {
const Sprite* oldSprite = NULL;
const Layer* oldLayer = NULL;
@@ -218,36 +222,67 @@ public:
gfx::Size size = sample.requiredSize();
if (oldSprite) {
- // If the user didn't specify a width for the texture, we put
- // each sprite/layer in a different row.
- if (width == 0) {
- // New sprite or layer, go to next row.
- if (oldSprite != sprite || oldLayer != layer) {
+ if (m_type == SpriteSheetType::Columns) {
+ // If the user didn't specify a height for the texture, we
+ // put each sprite/layer in a different column.
+ if (height == 0) {
+ // New sprite or layer, go to next column.
+ if (oldSprite != sprite || oldLayer != layer) {
+ framePt.x += rowSize.w + shapePadding;
+ framePt.y = borderPadding;
+ rowSize = size;
+ }
+ }
+ // When a texture height is specified, we can put different
+ // sprites/layers in each column until we reach the texture
+ // bottom-border.
+ else if (framePt.y+size.h > height-borderPadding) {
+ framePt.x += rowSize.w + shapePadding;
+ framePt.y = borderPadding;
+ rowSize = size;
+ }
+ }
+ else {
+ // If the user didn't specify a width for the texture, we put
+ // each sprite/layer in a different row.
+ if (width == 0) {
+ // New sprite or layer, go to next row.
+ if (oldSprite != sprite || oldLayer != layer) {
+ framePt.x = borderPadding;
+ framePt.y += rowSize.h + shapePadding;
+ rowSize = size;
+ }
+ }
+ // When a texture width is specified, we can put different
+ // sprites/layers in each row until we reach the texture
+ // right-border.
+ else if (framePt.x+size.w > width-borderPadding) {
framePt.x = borderPadding;
framePt.y += rowSize.h + shapePadding;
rowSize = size;
}
}
- // When a texture width is specified, we can put different
- // sprites/layers in each row until we reach the texture
- // right-border.
- else if (framePt.x+size.w > width-borderPadding) {
- framePt.x = borderPadding;
- framePt.y += rowSize.h + shapePadding;
- rowSize = size;
- }
}
sample.setInTextureBounds(gfx::Rect(framePt, size));
// Next frame position.
- framePt.x += size.w + shapePadding;
+ if (m_type == SpriteSheetType::Columns) {
+ framePt.y += size.h + shapePadding;
+ }
+ else {
+ framePt.x += size.w + shapePadding;
+ }
+
rowSize = rowSize.createUnion(size);
oldSprite = sprite;
oldLayer = layer;
}
}
+
+private:
+ SpriteSheetType m_type;
};
class DocumentExporter::BestFitLayoutSamples :
@@ -290,7 +325,7 @@ DocumentExporter::DocumentExporter()
, m_textureFormat(DefaultTextureFormat)
, m_textureWidth(0)
, m_textureHeight(0)
- , m_texturePack(false)
+ , m_sheetType(SpriteSheetType::None)
, m_scale(1.0)
, m_scaleMode(DefaultScaleMode)
, m_ignoreEmptyCels(false)
@@ -327,15 +362,21 @@ Document* DocumentExporter::exportSheet()
}
// 2) Layout those samples in a texture field.
- if (m_texturePack) {
- BestFitLayoutSamples layout;
- layout.layoutSamples(samples,
- m_borderPadding, m_shapePadding, m_textureWidth, m_textureHeight);
- }
- else {
- SimpleLayoutSamples layout;
- layout.layoutSamples(samples,
- m_borderPadding, m_shapePadding, m_textureWidth, m_textureHeight);
+ switch (m_sheetType) {
+ case SpriteSheetType::Packed: {
+ BestFitLayoutSamples layout;
+ layout.layoutSamples(
+ samples, m_borderPadding, m_shapePadding,
+ m_textureWidth, m_textureHeight);
+ break;
+ }
+ default: {
+ SimpleLayoutSamples layout(m_sheetType);
+ layout.layoutSamples(
+ samples, m_borderPadding, m_shapePadding,
+ m_textureWidth, m_textureHeight);
+ break;
+ }
}
// 3) Create and render the texture.
diff --git a/src/app/document_exporter.h b/src/app/document_exporter.h
index 692e1e2..8a3ca2a 100644
--- a/src/app/document_exporter.h
+++ b/src/app/document_exporter.h
@@ -9,6 +9,7 @@
#define APP_DOCUMENT_EXPORTER_H_INCLUDED
#pragma once
+#include "app/sprite_sheet_type.h"
#include "base/disable_copying.h"
#include "doc/image_buffer.h"
#include "gfx/fwd.h"
@@ -51,7 +52,7 @@ namespace app {
void setTextureFilename(const std::string& filename) { m_textureFilename = filename; }
void setTextureWidth(int width) { m_textureWidth = width; }
void setTextureHeight(int height) { m_textureHeight = height; }
- void setTexturePack(bool state) { m_texturePack = state; }
+ void setSpriteSheetType(SpriteSheetType type) { m_sheetType = type; }
void setScale(double scale) { m_scale = scale; }
void setScaleMode(ScaleMode mode) { m_scaleMode = mode; }
void setIgnoreEmptyCels(bool ignore) { m_ignoreEmptyCels = ignore; }
@@ -112,7 +113,7 @@ namespace app {
std::string m_textureFilename;
int m_textureWidth;
int m_textureHeight;
- bool m_texturePack;
+ SpriteSheetType m_sheetType;
double m_scale;
ScaleMode m_scaleMode;
bool m_ignoreEmptyCels;
diff --git a/src/app/pref/preferences.h b/src/app/pref/preferences.h
index 2c559f7..692b69c 100644
--- a/src/app/pref/preferences.h
+++ b/src/app/pref/preferences.h
@@ -11,6 +11,7 @@
#include "app/color.h"
#include "app/pref/option.h"
+#include "app/sprite_sheet_type.h"
#include "app/tools/freehand_algorithm.h"
#include "app/tools/ink_type.h"
#include "app/tools/rotation_algorithm.h"
diff --git a/src/app/sprite_sheet_type.h b/src/app/sprite_sheet_type.h
new file mode 100644
index 0000000..5788932
--- /dev/null
+++ b/src/app/sprite_sheet_type.h
@@ -0,0 +1,25 @@
+// Aseprite
+// Copyright (C) 2001-2015 David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifndef APP_SPRITE_SHEET_TYPE_H_INCLUDED
+#define APP_SPRITE_SHEET_TYPE_H_INCLUDED
+#pragma once
+
+namespace app {
+
+ enum class SpriteSheetType {
+ None,
+ Horizontal,
+ Vertical,
+ Rows,
+ Columns,
+ Packed
+ };
+
+} // namespace app
+
+#endif
diff --git a/src/app/ui/editor/select_box_state.cpp b/src/app/ui/editor/select_box_state.cpp
index 2865683..ea5fd74 100644
--- a/src/app/ui/editor/select_box_state.cpp
+++ b/src/app/ui/editor/select_box_state.cpp
@@ -12,10 +12,11 @@
#include "app/ui/editor/select_box_state.h"
#include "app/app.h"
-#include "app/ui/main_window.h"
-#include "app/ui/context_bar.h"
#include "app/tools/tool_box.h"
+#include "app/ui/context_bar.h"
#include "app/ui/editor/editor.h"
+#include "app/ui/main_window.h"
+#include "app/ui/skin/skin_theme.h"
#include "doc/image.h"
#include "doc/sprite.h"
#include "gfx/rect.h"
@@ -43,6 +44,11 @@ SelectBoxState::~SelectBoxState()
contextBar->updateForCurrentTool();
}
+void SelectBoxState::setFlags(Flags flags)
+{
+ m_flags = flags;
+}
+
gfx::Rect SelectBoxState::getBoxBounds() const
{
int x1 = std::min(m_rulers[V1].getPosition(), m_rulers[V2].getPosition());
@@ -79,7 +85,7 @@ bool SelectBoxState::onMouseDown(Editor* editor, MouseMessage* msg)
if (msg->left() || msg->right()) {
m_movingRuler = -1;
- if (hasFlag(RULERS)) {
+ if (hasFlag(Flags::Rulers)) {
for (int i=0; i<(int)m_rulers.size(); ++i) {
if (touchRuler(editor, m_rulers[i], msg->position().x, msg->position().y)) {
m_movingRuler = i;
@@ -88,7 +94,7 @@ bool SelectBoxState::onMouseDown(Editor* editor, MouseMessage* msg)
}
}
- if (hasFlag(QUICKBOX) && m_movingRuler == -1) {
+ if (hasFlag(Flags::QuickBox) && m_movingRuler == -1) {
m_selectingBox = true;
m_selectingButtons = msg->buttons();
m_startingPos = editor->screenToEditor(msg->position());
@@ -125,7 +131,7 @@ bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg)
updateContextBar();
- if (hasFlag(RULERS) && m_movingRuler >= 0) {
+ if (hasFlag(Flags::Rulers) && m_movingRuler >= 0) {
gfx::Point pt = editor->screenToEditor(msg->position());
switch (m_rulers[m_movingRuler].getOrientation()) {
@@ -141,7 +147,7 @@ bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg)
used = true;
}
- if (hasFlag(QUICKBOX) && m_selectingBox) {
+ if (hasFlag(Flags::QuickBox) && m_selectingBox) {
gfx::Point p1 = m_startingPos;
gfx::Point p2 = editor->screenToEditor(msg->position());
@@ -167,7 +173,7 @@ bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg)
bool SelectBoxState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
{
- if (hasFlag(RULERS)) {
+ if (hasFlag(Flags::Rulers)) {
if (m_movingRuler >= 0) {
switch (m_rulers[m_movingRuler].getOrientation()) {
@@ -210,7 +216,7 @@ bool SelectBoxState::acceptQuickTool(tools::Tool* tool)
bool SelectBoxState::requireBrushPreview()
{
- if (hasFlag(QUICKBOX))
+ if (hasFlag(Flags::QuickBox))
return true;
// Returns false as it overrides default standby state behavior &
@@ -220,7 +226,7 @@ bool SelectBoxState::requireBrushPreview()
tools::Ink* SelectBoxState::getStateInk()
{
- if (hasFlag(QUICKBOX))
+ if (hasFlag(Flags::QuickBox))
return App::instance()->getToolBox()->getInkById(
tools::WellKnownInks::Selection);
else
@@ -230,7 +236,7 @@ tools::Ink* SelectBoxState::getStateInk()
void SelectBoxState::preRenderDecorator(EditorPreRender* render)
{
// Without black shadow?
- if (!hasFlag(DARKOUTSIDE))
+ if (!hasFlag(Flags::DarkOutside))
return;
gfx::Rect rc = getBoxBounds();
@@ -259,29 +265,43 @@ void SelectBoxState::postRenderDecorator(EditorPostRender* render)
{
Editor* editor = render->getEditor();
render::Zoom zoom = editor->zoom();
+ gfx::Rect sp = editor->sprite()->bounds();
gfx::Rect vp = View::getView(editor)->getViewportBounds();
vp.w += zoom.apply(1);
vp.h += zoom.apply(1);
vp = editor->screenToEditor(vp);
// Paint a grid generated by the box
- if (hasFlag(GRID)) {
- gfx::Color gridColor = gfx::rgba(100, 200, 100);
- gfx::Rect boxBounds = getBoxBounds();
-
- if (boxBounds.w > 0)
- for (int x=boxBounds.x+boxBounds.w*2; x<vp.x+vp.w; x+=boxBounds.w)
- render->drawLine(x, boxBounds.y, x, vp.y+vp.h-1, gridColor);
+ gfx::Color rulerColor = skin::SkinTheme::instance()->colors.selectBoxRuler();
+ gfx::Color gridColor = skin::SkinTheme::instance()->colors.selectBoxGrid();
+ gfx::Rect boxBounds = getBoxBounds();
+
+ if (hasFlag(Flags::Grid)) {
+ if (boxBounds.w > 0) {
+ for (int x=boxBounds.x+boxBounds.w*2; x<=sp.x+sp.w; x+=boxBounds.w)
+ render->drawLine(x, boxBounds.y, x, sp.y+sp.h, gridColor);
+ }
- if (boxBounds.h > 0)
- for (int y=boxBounds.y+boxBounds.h*2; y<vp.y+vp.h; y+=boxBounds.h)
- render->drawLine(boxBounds.x, y, vp.x+vp.w-1, y, gridColor);
+ if (boxBounds.h > 0) {
+ for (int y=boxBounds.y+boxBounds.h*2; y<=sp.y+sp.h; y+=boxBounds.h)
+ render->drawLine(boxBounds.x, y, sp.x+sp.w, y, gridColor);
+ }
+ }
+ else if (hasFlag(Flags::HGrid)) {
+ if (boxBounds.w > 0) {
+ for (int x=boxBounds.x+boxBounds.w*2; x<=sp.x+sp.w; x+=boxBounds.w)
+ render->drawLine(x, boxBounds.y, x, boxBounds.y+boxBounds.h, gridColor);
+ }
+ }
+ else if (hasFlag(Flags::VGrid)) {
+ if (boxBounds.h > 0) {
+ for (int y=boxBounds.y+boxBounds.h*2; y<=sp.y+sp.h; y+=boxBounds.h)
+ render->drawLine(boxBounds.x, y, boxBounds.x+boxBounds.w, y, gridColor);
+ }
}
// Draw the rulers enclosing the box
- if (hasFlag(RULERS)) {
- gfx::Color rulerColor = gfx::rgba(0, 0, 255);
-
+ if (hasFlag(Flags::Rulers)) {
for (Rulers::iterator it = m_rulers.begin(), end = m_rulers.end(); it != end; ++it) {
switch (it->getOrientation()) {
@@ -296,8 +316,8 @@ void SelectBoxState::postRenderDecorator(EditorPostRender* render)
}
}
- if (hasFlag(QUICKBOX)) {
- render->drawRectXor(getBoxBounds());
+ if (hasFlag(Flags::QuickBox)) {
+ render->drawRectXor(boxBounds);
}
}
@@ -322,7 +342,7 @@ bool SelectBoxState::touchRuler(Editor* editor, Ruler& ruler, int x, int y)
bool SelectBoxState::hasFlag(Flags flag) const
{
- return ((m_flags & flag) == flag);
+ return (int(m_flags) & int(flag)) == int(flag);
}
} // namespace app
diff --git a/src/app/ui/editor/select_box_state.h b/src/app/ui/editor/select_box_state.h
index c632dd4..15bb0f4 100644
--- a/src/app/ui/editor/select_box_state.h
+++ b/src/app/ui/editor/select_box_state.h
@@ -41,17 +41,33 @@ namespace app {
enum { H1, H2, V1, V2 };
public:
- typedef int Flags;
- static const int RULERS = 1; // Draw rulers at each edge of the current box
- static const int DARKOUTSIDE = 2; // The outside of the box must be darker
- static const int GRID = 4; // Draw a grid
- static const int QUICKBOX = 8; // Select the box as in selection tool, drawing a boxu
+ enum class Flags {
+ // Draw rulers at each edge of the current box
+ Rulers = 1,
+
+ // The outside of the current box must be darker (used in "Canvas Size" command)
+ DarkOutside = 2,
+
+ // Show a horizontal array of boxes starting from the current box
+ HGrid = 4,
+
+ // Show a vertical array of boxes starting from the current box
+ VGrid = 8,
+
+ // Show a grid starting from the current box
+ Grid = (HGrid | VGrid),
+
+ // Select the box as in selection tool, drawing a boxu
+ QuickBox = 16,
+ };
SelectBoxState(SelectBoxDelegate* delegate,
const gfx::Rect& rc,
Flags flags);
~SelectBoxState();
+ void setFlags(Flags flags);
+
// Returns the bounding box arranged by the rulers.
gfx::Rect getBoxBounds() const;
void setBoxBounds(const gfx::Rect& rc);
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git
More information about the Pkg-games-commits
mailing list