koi moved to separate module
[dd2d.git] / tatlas.d
blob63675fc256b2c7db448f070972a12808bb9ed841
1 module tatlas is aliced;
2 private:
4 import arsd.color;
6 import d2dgfx;
7 import glutils;
9 //version = tatlas_brect;
12 // ////////////////////////////////////////////////////////////////////////// //
13 public final class TexAtlas {
14 public:
15 static struct Rect {
16 int x, y, w, h;
17 nothrow @safe @nogc:
18 @property bool valid () const pure { pragma(inline, true); return (x >= 0 && y >= 0 && w > 0 && h > 0); }
19 @property int area () const pure { pragma(inline, true); return w*h; }
20 static @property Rect Invalid () pure { Rect res; return res; }
23 private:
24 public TrueColorImage img;
25 Rect[] rects;
26 public Texture tex;
28 enum BadRect = uint.max;
30 public:
31 this (int awdt, int ahgt) {
32 assert(awdt > 0 && ahgt > 0);
33 img = new TrueColorImage(awdt, ahgt);
34 rects ~= Rect(0, 0, awdt, ahgt); // one big rect
37 @property const /*pure*/ /*nothrow*/ @safe /*@nogc*/ {
38 int width () { pragma(inline, true); return img.width; }
39 int height () { pragma(inline, true); return img.height; }
40 bool hasTexture () { pragma(inline, true); return (tex !is null); }
43 void updateTexture () {
44 version(tatlas_brect) foreach (const ref rc; rects) imgRect(rc.x, rc.y, rc.w, rc.h, Color(0, 255, 0, 255));
45 if (tex is null) {
46 tex = new Texture(img, Texture.Option.Clamp, Texture.Option.Nearest);
47 } else {
48 tex.setFromImage(img);
52 // node id or BadRect
53 private uint findBestFit (int w, int h) {
54 uint fitW = BadRect, fitH = BadRect, biggest = BadRect;
56 foreach (immutable idx, const ref r; rects) {
57 if (r.w < w || r.h < h) continue; // absolutely can't fit
58 if (r.w == w && r.h == h) return cast(uint)idx; // perfect fit
59 if (r.w == w) {
60 // width fit
61 if (fitW == BadRect || rects.ptr[fitW].h < r.h) fitW = cast(uint)idx;
62 } else if (r.h == h) {
63 // height fit
64 if (fitH == BadRect || rects.ptr[fitH].w < r.w) fitH = cast(uint)idx;
65 } else {
66 // get biggest rect
67 if (biggest == BadRect || rects.ptr[biggest].area > r.area) biggest = cast(uint)idx;
70 // both?
71 if (fitW != BadRect && fitH != BadRect) return (rects.ptr[fitW].area > rects.ptr[fitH].area ? fitW : fitH);
72 if (fitW != BadRect) return fitW;
73 if (fitH != BadRect) return fitH;
74 return biggest;
77 private void imgPutPixel (int x, int y, Color c) {
78 if (x >= 0 && y >= 0 && x < img.width && y < img.height) img.imageData.colors.ptr[y*img.width+x] = c;
81 private void imgRect (int x, int y, int w, int h, Color c) {
82 foreach (int d; 0..w) { imgPutPixel(x+d, y, c); imgPutPixel(x+d, y+h-1, c); }
83 foreach (int d; 1..h-1) { imgPutPixel(x, y+d, c); imgPutPixel(x+w-1, y+d, c); }
86 private void imgRect4 (int x, int y, int w, int h, Color ct, Color cb, Color cl, Color cr) {
87 foreach (int d; 0..w) { imgPutPixel(x+d, y, ct); imgPutPixel(x+d, y+h-1, cb); }
88 foreach (int d; 1..h-1) { imgPutPixel(x, y+d, cl); imgPutPixel(x+w-1, y+d, cr); }
91 // returns invalid rect if there's no room
92 Rect insert (D2DImage di) {
93 assert(di !is null && di.width > 0 && di.height > 0);
94 auto ri = findBestFit(di.width, di.height);
95 if (ri == BadRect) return Rect.Invalid;
96 auto rc = rects.ptr[ri];
97 auto res = Rect(rc.x, rc.y, di.width, di.height);
98 // split this rect
99 if (rc.w == res.w && rc.h == res.h) {
100 // best fit, simply remove this rect
101 foreach (immutable cidx; ri+1..rects.length) rects.ptr[cidx-1] = rects.ptr[cidx];
102 rects.length -= 1;
103 rects.assumeSafeAppend; // for future; we probably won't have alot of best-fitting nodes initially
104 } else {
105 if (rc.w == res.w) {
106 // split vertically
107 rc.y += res.h;
108 rc.h -= res.h;
109 } else if (rc.h == res.h) {
110 // split horizontally
111 rc.x += res.w;
112 rc.w -= res.w;
113 } else {
114 Rect nr = rc;
115 // split in both directions (by longer edge)
116 if (rc.w-res.w > rc.h-res.h) {
117 // cut the right part
118 nr.x += res.w;
119 nr.w -= res.w;
120 // cut the bottom part
121 rc.y += res.h;
122 rc.h -= res.h;
123 rc.w = res.w;
124 } else {
125 // cut the bottom part
126 nr.y += res.h;
127 nr.h -= res.h;
128 // cut the right part
129 rc.x += res.w;
130 rc.w -= res.w;
131 rc.h = res.h;
133 rects ~= nr;
135 rects.ptr[ri] = rc;
137 // copy image data
138 auto sp = di.asTCImage.imageData.colors.ptr;
139 auto dp = img.imageData.colors.ptr+res.y*img.width+res.x;
140 foreach (immutable dy; 0..di.height) {
141 dp[0..di.width] = sp[0..di.width];
142 sp += di.width;
143 dp += img.width;
145 version(tatlas_brect) imgRect4(res.x, res.y, res.w, res.h, Color(255, 0, 0), Color(255, 255, 0), Color(0, 0, 255), Color(0, 255, 0));
146 return res;