[aseprite] 45/134: Add gfx::PackingRects class for packing multiple rectangles in a texture
Tobias Hansen
thansen at moszumanska.debian.org
Sat Mar 14 17:10:02 UTC 2015
This is an automated email from the git hooks/post-receive script.
thansen pushed a commit to branch master
in repository aseprite.
commit c0d35b16e007fc00644389b28980b75fdccbcd72
Author: David Capello <davidcapello at gmail.com>
Date: Fri Nov 7 10:32:14 2014 -0300
Add gfx::PackingRects class for packing multiple rectangles in a texture
---
src/gfx/CMakeLists.txt | 3 +-
src/gfx/packing_rects.cpp | 98 +++++++++++++++++++++++++++++++++++++++++
src/gfx/packing_rects.h | 53 ++++++++++++++++++++++
src/gfx/packing_rects_tests.cpp | 96 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 249 insertions(+), 1 deletion(-)
diff --git a/src/gfx/CMakeLists.txt b/src/gfx/CMakeLists.txt
index 954e247..337b5eb 100644
--- a/src/gfx/CMakeLists.txt
+++ b/src/gfx/CMakeLists.txt
@@ -1,8 +1,9 @@
-# ASEPRITE
+# Aseprite
# Copyright (C) 2001-2014 David Capello
add_library(gfx-lib
hsv.cpp
+ packing_rects.cpp
region.cpp
rgb.cpp
transformation.cpp)
diff --git a/src/gfx/packing_rects.cpp b/src/gfx/packing_rects.cpp
new file mode 100644
index 0000000..e1e15be
--- /dev/null
+++ b/src/gfx/packing_rects.cpp
@@ -0,0 +1,98 @@
+// Aseprite Gfx Library
+// Copyright (C) 2001-2014 David Capello
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gfx/packing_rects.h"
+
+#include "gfx/region.h"
+#include "gfx/size.h"
+
+namespace gfx {
+
+void PackingRects::add(const Size& sz)
+{
+ m_rects.push_back(Rect(sz));
+}
+
+void PackingRects::add(const Rect& rc)
+{
+ m_rects.push_back(rc);
+}
+
+Size PackingRects::bestFit()
+{
+ Size size(0, 0);
+
+ // Calculate the amount of pixels that we need, the texture cannot
+ // be smaller than that.
+ int neededArea = 0;
+ for (const auto& rc : m_rects) {
+ neededArea += rc.w * rc.h;
+ }
+
+ int w = 1;
+ int h = 1;
+ int z = 0;
+ bool fit = false;
+ while (true) {
+ if (w*h >= neededArea) {
+ fit = pack(Size(w, h));
+ if (fit) {
+ size = Size(w, h);
+ break;
+ }
+ }
+
+ if ((++z) & 1)
+ w *= 2;
+ else
+ h *= 2;
+ }
+
+ return size;
+}
+
+static bool by_area(const Rect* a, const Rect* b) {
+ return a->w*a->h > b->w*b->h;
+}
+
+bool PackingRects::pack(const Size& size)
+{
+ m_bounds = Rect(size);
+
+ // We cannot sort m_rects because we want to
+ std::vector<Rect*> rectPtrs(m_rects.size());
+ int i = 0;
+ for (auto& rc : m_rects)
+ rectPtrs[i++] = &rc;
+ std::sort(rectPtrs.begin(), rectPtrs.end(), by_area);
+
+ gfx::Region rgn(m_bounds);
+ for (auto rcPtr : rectPtrs) {
+ gfx::Rect& rc = *rcPtr;
+
+ for (int v=0; v<=m_bounds.h-rc.h; ++v) {
+ for (int u=0; u<=m_bounds.w-rc.w; ++u) {
+ gfx::Rect possible(u, v, rc.w, rc.h);
+ Region::Overlap overlap = rgn.contains(possible);
+ if (overlap == Region::In) {
+ rc = possible;
+ rgn.createSubtraction(rgn, gfx::Region(rc));
+ goto next_rc;
+ }
+ }
+ }
+ return false; // There is not enough room for "rc"
+ next_rc:;
+ }
+
+ return true;
+}
+
+} // namespace gfx
diff --git a/src/gfx/packing_rects.h b/src/gfx/packing_rects.h
new file mode 100644
index 0000000..7729a0d
--- /dev/null
+++ b/src/gfx/packing_rects.h
@@ -0,0 +1,53 @@
+// Aseprite Gfx Library
+// Copyright (C) 2001-2014 David Capello
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#ifndef GFX_TEXTURE_SIZE_H_INCLUDED
+#define GFX_TEXTURE_SIZE_H_INCLUDED
+#pragma once
+
+#include "gfx/fwd.h"
+#include "gfx/rect.h"
+#include <vector>
+
+namespace gfx {
+
+ // TODO add support for rotations
+ class PackingRects {
+ public:
+ typedef std::vector<Rect> Rects;
+ typedef Rects::const_iterator const_iterator;
+
+ // Iterate over all given rectangles (in the same order they where
+ // given in addSize() calls).
+ const_iterator begin() const { return m_rects.begin(); }
+ const_iterator end() const { return m_rects.end(); }
+
+ size_t size() const { return m_rects.size(); }
+ const Rect& operator[](int i) const { return m_rects[i]; }
+
+ // Adds a new rectangle.
+ void add(const Size& sz);
+ void add(const Rect& rc);
+
+ // Returns the best size for the texture.
+ Size bestFit();
+
+ // Rearrange all given rectangles to best fit a texture size.
+ // Returns true if all rectangles were correctly arranged or false
+ // if there is not enough space.
+ bool pack(const Size& size);
+
+ // Returns the bounds of the packed area.
+ const Rect& bounds() const { return m_bounds; }
+
+ private:
+ Rect m_bounds;
+ Rects m_rects;
+ };
+
+} // namespace gfx
+
+#endif
diff --git a/src/gfx/packing_rects_tests.cpp b/src/gfx/packing_rects_tests.cpp
new file mode 100644
index 0000000..0bf24fb
--- /dev/null
+++ b/src/gfx/packing_rects_tests.cpp
@@ -0,0 +1,96 @@
+// Aseprite Gfx Library
+// Copyright (C) 2001-2014 David Capello
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#include <gtest/gtest.h>
+
+#include "gfx/packing_rects.h"
+#include "gfx/rect_io.h"
+#include "gfx/size.h"
+
+using namespace gfx;
+
+TEST(PackingRects, Simple)
+{
+ PackingRects pr;
+ pr.add(Size(256, 128));
+ EXPECT_FALSE(pr.pack(Size(256, 120)));
+ EXPECT_TRUE(pr.pack(Size(256, 128)));
+
+ EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
+ EXPECT_EQ(Rect(0, 0, 256, 128), pr.bounds());
+}
+
+TEST(PackingRects, SimpleTwoRects)
+{
+ PackingRects pr;
+ pr.add(Size(256, 128));
+ pr.add(Size(256, 120));
+ EXPECT_TRUE(pr.pack(Size(256, 256)));
+
+ EXPECT_EQ(Rect(0, 0, 256, 256), pr.bounds());
+ EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
+ EXPECT_EQ(Rect(0, 128, 256, 120), pr[1]);
+}
+
+TEST(PackingRects, BestFit)
+{
+ PackingRects pr;
+ pr.add(Size(10, 12));
+ pr.bestFit();
+ EXPECT_EQ(Rect(0, 0, 16, 16), pr.bounds());
+}
+
+TEST(PackingRects, BestFitTwoRects)
+{
+ PackingRects pr;
+ pr.add(Size(256, 128));
+ pr.add(Size(256, 127));
+ pr.bestFit();
+
+ EXPECT_EQ(Rect(0, 0, 256, 256), pr.bounds());
+ EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
+ EXPECT_EQ(Rect(0, 128, 256, 127), pr[1]);
+}
+
+TEST(PackingRects, BestFit6Frames100x100)
+{
+ PackingRects pr;
+ pr.add(Size(100, 100));
+ pr.add(Size(100, 100));
+ pr.add(Size(100, 100));
+ pr.add(Size(100, 100));
+ pr.add(Size(100, 100));
+ pr.add(Size(100, 100));
+ pr.bestFit();
+
+ EXPECT_EQ(Rect(0, 0, 512, 256), pr.bounds());
+ EXPECT_EQ(Rect(0, 0, 100, 100), pr[0]);
+ EXPECT_EQ(Rect(100, 0, 100, 100), pr[1]);
+ EXPECT_EQ(Rect(200, 0, 100, 100), pr[2]);
+ EXPECT_EQ(Rect(300, 0, 100, 100), pr[3]);
+ EXPECT_EQ(Rect(400, 0, 100, 100), pr[4]);
+ EXPECT_EQ(Rect(0, 100, 100, 100), pr[5]);
+}
+
+TEST(PackingRects, KeepSameRectsOrder)
+{
+ PackingRects pr;
+ pr.add(Size(10, 10));
+ pr.add(Size(20, 20));
+ pr.add(Size(30, 30));
+ pr.bestFit();
+
+ EXPECT_EQ(Rect(0, 0, 64, 32), pr.bounds());
+ EXPECT_EQ(Rect(50, 0, 10, 10), pr[0]);
+ EXPECT_EQ(Rect(30, 0, 20, 20), pr[1]);
+ EXPECT_EQ(Rect(0, 0, 30, 30), pr[2]);
+}
+
+int main(int argc, char** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
--
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