diff --git a/CMakeLists.txt b/CMakeLists.txt
index f4be54c..d114283 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,6 +28,7 @@ set(SOURCES
src/pack_compact.cpp
src/pack_single.cpp
src/pack_keep.cpp
+ src/pack_lines.cpp
src/output.cpp
src/globbing.cpp
src/debug.cpp
diff --git a/README.md b/README.md
index f0b5257..277b228 100644
--- a/README.md
+++ b/README.md
@@ -222,7 +222,7 @@ The following table contains a list of all definitions, with the subject each af
| Definition |Subject| Arguments | Description |
| -------------- |-------| -------------| ----------- |
| **output** |sprite | path | Sets the output texture's _path_. It can describe an un-/bounded sequence of files (e.g. "sheet{0-}.png").
-| pack |output | pack-method | Sets the method, which is used for placing the sprites on the output textures:
- _binpack_ : Tries to reduce the texture size, while keeping the sprites' (trimmed) rectangles apart (default).
- _compact_ : Tries to reduce the texture size, while keeping the sprites' convex outlines apart.
- _single_ : Put each sprite on its own texture.
+| pack |output | pack-method | Sets the method, which is used for placing the sprites on the output textures:
- _binpack_ : Tries to reduce the texture size, while keeping the sprites' (trimmed) rectangles apart (default).
- _compact_ : Tries to reduce the texture size, while keeping the sprites' convex outlines apart.
- _rows_ : Layout sprites in simple rows.
- _columns_ : Layout sprites in simple columns.
- _single_ : Put each sprite on its own texture.
- _keep_ : Keep sprite at same position as in source (ignores most constraints).
| width |output | width | Sets a fixed output texture width.
| height |output | height | Sets a fixed output texture height.
| max-width |output | width | Sets a maximum output texture width.
@@ -234,7 +234,7 @@ The following table contains a list of all definitions, with the subject each af
| padding |output | [pixels], [pixels] | Sets the space between two sprites / the space between a sprite and the texture's border.
| duplicates |output | dedupe-mode | Sets how identical sprites should be processed:
- _keep_ : Disable duplicate detection (default).
- _share_ : Identical sprites should share pixels on the output texture.
- _drop_ : Duplicates should be dropped.
| alpha |output | alpha-mode
[color] | Sets an operation depending on the pixels' alpha values:
- _keep_ : Keep source color and alpha.
- _clear_ : Set color of fully transparent pixels to black.
- _bleed_ : Set color of fully transparent pixels to their nearest non-fully transparent pixel's color.
- _premultiply_ : Premultiply colors with alpha values.
- _colorkey_ : Replace fully transparent pixels with the specified _color_ and make all others opaque.
-| scalings |output | ([filter-mode] scale)+ | Specifies one or more factors for which a respectively scaled output should be generated, with an optional explicit filter-mode:
- _box_: A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios.
- _bilinear_: On upsampling, produces same results as bilinear texture filtering.
- _cubicspline_: The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque.
- _catmullrom_: An interpolating cubic spline.
- _mitchell_: Mitchell-Netrevalli filter with B=1/3, C=1/3.
+| scalings |output | ([scale-filter] scale)+ | Specifies one or more factors for which a respectively scaled output should be generated, with an optional explicit scale-filter:
- _box_: A trapezoid with 1-pixel wide ramps.
- _triangle_: A triangle function (same results as bilinear texture filtering).
- _cubicspline_: A cubic b-spline (gaussian-esque).
- _catmullrom_: An interpolating cubic spline.
- _mitchell_: Mitchell-Netrevalli filter with B=1/3, C=1/3.
| layers |output/input| suffix+ | Specifies the number of layers and their filename suffixes (e.g. "-diffuse", "-normals", ...). Only the first layer is considered when packing, others get identical _rects_.
| **input** |- | path | Adds a new input file at _path_. It can contain wildcards (e.g. "sprites/**/*.png") or it can describe an un-/bounded sequence of files (e.g. "frames_{0-}.png, frames_{0001-0013}.png").
| path |input | path | A _path_ which should be prepended to the input's path.
@@ -258,7 +258,7 @@ The following table contains a list of all definitions, with the subject each af
| trim-margin |sprite | [pixels] | Sets a number of transparent pixel rows around the sprite, which should not be removed by trimming.
| crop |sprite | [boolean] | Sets whether the sprite's rectangle should be reduced to the trimmed bounds.
| crop-pivot |sprite | [boolean] | Sets whether the sprite's pivot point should be relative to the trimmed bounds.
-| extrude |sprite | [pixels],
[wrap-mode] | Adds a padding around the sprite and fills it depending on the _wrap-mode_:
- _clamp_ (default)
- _mirror_:
- _repeat_: .
+| extrude |sprite | [pixels],
[wrap-mode] | Adds a padding around the sprite and fills it depending on the _wrap-mode_:
- _clamp_: Clamp to border pixels (default).
- _mirror_: Mirror border pixels.
- _repeat_: Repeat border pixels.
| common-divisor |sprite | x, [y] | Restricts the sprite's size to be divisible by a certain number of pixels. Smaller sprites are filled up with transparency.
| group |- | - | Can be used for opening a new scope, to limit for example the effect of a tag.
diff --git a/src/Definition.cpp b/src/Definition.cpp
index 7211bcb..ddf1902 100644
--- a/src/Definition.cpp
+++ b/src/Definition.cpp
@@ -267,7 +267,7 @@ void apply_definition(Definition definition,
case Definition::pack: {
const auto string = check_string();
if (const auto index = index_of(string,
- { "binpack", "compact", "single", "keep" }); index >= 0)
+ { "binpack", "rows", "columns", "compact", "single", "keep" }); index >= 0)
state.pack = static_cast(index);
else
error("invalid pack method '", string, "'");
diff --git a/src/input.h b/src/input.h
index 9be941f..636aa82 100644
--- a/src/input.h
+++ b/src/input.h
@@ -20,7 +20,7 @@ enum class Trim { none, rect, convex };
enum class Alpha { keep, clear, bleed, premultiply, colorkey };
-enum class Pack { binpack, compact, single, keep };
+enum class Pack { binpack, rows, columns, compact, single, keep };
enum class Duplicates { keep, share, drop };
diff --git a/src/pack_lines.cpp b/src/pack_lines.cpp
new file mode 100644
index 0000000..1959e4f
--- /dev/null
+++ b/src/pack_lines.cpp
@@ -0,0 +1,81 @@
+
+#include "packing.h"
+
+namespace spright {
+
+void pack_lines(bool horizontal, const OutputPtr& output,
+ SpriteSpan sprites, std::vector& textures) {
+
+ const auto add_texture = [&](SpriteSpan sprites) {
+ auto texture = Texture{ output,
+ static_cast(textures.size()),
+ 0, 0, sprites,
+ };
+ recompute_texture_size(texture);
+ textures.push_back(std::move(texture));
+ };
+
+ const auto max_width = (output->max_width ?
+ output->max_width- output->border_padding * 2 :
+ std::numeric_limits::max());
+ const auto max_height = (output->max_height ?
+ output->max_height - output->border_padding * 2 :
+ std::numeric_limits::max());
+ const auto max_textures = static_cast(output->filename.count());
+
+ auto pos = Point{ };
+ auto size = Size{ };
+ auto line_size = 0;
+
+ // d = direction, p = perpendicular
+ auto& pos_d = (horizontal ? pos.x : pos.y);
+ auto& pos_p = (horizontal ? pos.y : pos.x);
+ const auto& size_d = (horizontal ? size.x : size.y);
+ const auto& size_p = (horizontal ? size.y : size.x);
+ const auto max_d = (horizontal ? max_width : max_height);
+ const auto max_p = (horizontal ? max_height : max_width);
+
+ auto first_sprite = sprites.begin();
+ auto it = first_sprite;
+ for (; it != sprites.end(); ++it) {
+ auto& sprite = *it;
+ size = get_sprite_size(sprite);
+ size.x += output->shape_padding;
+ size.y += output->shape_padding;
+
+ if (pos_d + size_d > max_d) {
+ pos_d = 0;
+ pos_p += line_size;
+ line_size = 0;
+ }
+ if (pos_p + size_p > max_p) {
+ add_texture({ first_sprite, it });
+ if (textures.size() >= max_textures)
+ break;
+ first_sprite = it;
+ pos.x = 0;
+ pos.y = 0;
+ line_size = 0;
+ }
+ if (pos.x + size.x > max_width ||
+ pos.y + size.y > max_height)
+ break;
+
+ const auto indent = get_sprite_indent(sprite);
+ sprite.trimmed_rect = {
+ pos.x + indent.x + output->border_padding,
+ pos.y + indent.y + output->border_padding,
+ sprite.trimmed_source_rect.w,
+ sprite.trimmed_source_rect.h
+ };
+
+ pos_d += size_d;
+ line_size = std::max(line_size, size_p);
+ }
+ if (it != sprites.end())
+ throw std::runtime_error("not all sprites could be packed");
+
+ add_texture({ first_sprite, it });
+}
+
+} // namespace
diff --git a/src/packing.cpp b/src/packing.cpp
index d5dec29..d1d5cc4 100644
--- a/src/packing.cpp
+++ b/src/packing.cpp
@@ -79,6 +79,8 @@ namespace {
case Pack::compact: return pack_compact(output, sprites, textures);
case Pack::single: return pack_single(output, sprites, textures);
case Pack::keep: return pack_keep(output, sprites, textures);
+ case Pack::rows: return pack_lines(true, output, sprites, textures);
+ case Pack::columns: return pack_lines(false, output, sprites, textures);
}
}
@@ -100,19 +102,24 @@ namespace {
}
}
+ // when dropping duplicates, restore order of sprites before packing
+ if (output->duplicates == Duplicates::drop) {
+ std::sort(unique_sprites.begin(), unique_sprites.end(),
+ [](const Sprite& a, const Sprite& b) { return (a.index < b.index); });
+ for (auto i = unique_sprites.size(); i < sprites.size(); ++i)
+ sprites[i] = { };
+ }
+
pack_texture(output, unique_sprites, textures);
- for (auto i = size_t{ }; i < duplicates.size(); ++i) {
- auto& duplicate = sprites[sprites.size() - 1 - i];
- if (output->duplicates == Duplicates::share) {
+ if (output->duplicates == Duplicates::share) {
+ for (auto i = size_t{ }; i < duplicates.size(); ++i) {
+ auto& duplicate = sprites[sprites.size() - 1 - i];
const auto& sprite = unique_sprites[duplicates[i]];
duplicate.texture_index = sprite.texture_index;
duplicate.trimmed_rect = sprite.trimmed_rect;
duplicate.rotated = sprite.rotated;
}
- else {
- duplicate = { };
- }
}
}
diff --git a/src/packing.h b/src/packing.h
index d437907..2dfbd05 100644
--- a/src/packing.h
+++ b/src/packing.h
@@ -39,6 +39,8 @@ void pack_single(const OutputPtr& output, SpriteSpan sprites,
std::vector& textures);
void pack_keep(const OutputPtr& output, SpriteSpan sprites,
std::vector& textures);
+void pack_lines(bool horizontal, const OutputPtr& output,
+ SpriteSpan sprites, std::vector& textures);
void recompute_texture_size(Texture& texture);