[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