1 /** 2 Copyright: Copyright (c) 2013-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 module voxelman.graphics.textureatlas; 8 9 import voxelman.geometry.rectbinpacker; 10 import voxelman.graphics.bitmap; 11 import voxelman.graphics.color; 12 import voxelman.math; 13 14 15 class InsertException : Exception 16 { 17 this(string msg, string file = __FILE__, size_t line = __LINE__) 18 { 19 super(msg, file, line); 20 } 21 } 22 23 /// Can be used to tightly store small images in big atlas, such as font glyps, or lightmaps etc. 24 class TextureAtlas 25 { 26 public Bitmap bitmap; 27 public bool autoGrow = true; 28 29 private: 30 uint _maxAtlasSize = 8192; // 2^13 31 RectBinPacker binPacker; 32 33 public: 34 35 this(in uint size) 36 { 37 binPacker = RectBinPacker(size, size); 38 bitmap = new Bitmap(size, size); 39 } 40 41 this(in uint width, in uint height) 42 { 43 binPacker = RectBinPacker(width, height); 44 bitmap = new Bitmap(width, height); 45 } 46 47 /// Returns: position of inserted node, or throws if not enough space 48 ivec2 insert(ivec2 size, Color4ub color) 49 { 50 ivec2 pos = insert(size); 51 bitmap.fillSubRect(irect(pos, size), color); 52 return pos; 53 } 54 55 /// ditto 56 ivec2 insert(ivec2 size) 57 { 58 Node* node = binPacker.insert(size); 59 60 if (node is null) // There is no place to put new item. 61 { 62 if (autoGrow) // Atlas can grow. 63 { 64 if (bitmap.width >= bitmap.height) // Growing vertically. 65 { 66 binPacker = RectBinPacker(bitmap.width, bitmap.height, 0, bitmap.height); 67 if (bitmap.height >= _maxAtlasSize) 68 throw new InsertException("Texture atlas is full. Max atlas size reached"); 69 bitmap.resize(ivec2(bitmap.width, bitmap.height*2)); 70 } 71 else // Growing horizontally. 72 { 73 binPacker = RectBinPacker(bitmap.width, bitmap.height, bitmap.width, 0); 74 bitmap.resize(ivec2(bitmap.width*2, bitmap.height)); 75 } 76 77 node = binPacker.insert(size); 78 } 79 else 80 { 81 throw new InsertException("Texture atlas is full"); 82 } 83 } 84 85 return ivec2(node.rect.x, node.rect.y); 86 } 87 88 /// ditto 89 ivec2 insert(in Bitmap source, in irect sourceSubRect) 90 { 91 ivec2 pos = insert(sourceSubRect.size); 92 bitmap.putSubRect(source, sourceSubRect, pos); 93 return pos; 94 } 95 }