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