1 module tatlas
is aliced
;
9 //version = tatlas_brect;
12 // ////////////////////////////////////////////////////////////////////////// //
13 public final class TexAtlas
{
20 @property bool valid () const pure { pragma(inline
, true); return (x
>= 0 && y
>= 0 && w
> 0 && h
> 0); }
21 @property int area () const pure { pragma(inline
, true); return w
*h
; }
22 @property int x1 () const pure { pragma(inline
, true); return x
+w
; }
23 @property int y1 () const pure { pragma(inline
, true); return y
+h
; }
24 static @property Rect
Invalid () pure { Rect res
; return res
; }
27 // texture coords in atlas
33 public TrueColorImage img
;
37 enum BadRect
= uint.max
;
40 this (int awdt
, int ahgt
) {
41 assert(awdt
> 0 && ahgt
> 0);
42 img
= new TrueColorImage(awdt
, ahgt
);
43 rects
~= Rect(0, 0, awdt
, ahgt
); // one big rect
46 @property const /*pure*/ /*nothrow*/ @safe /*@nogc*/ {
47 int width () { pragma(inline
, true); return img
.width
; }
48 int height () { pragma(inline
, true); return img
.height
; }
49 bool hasTexture () { pragma(inline
, true); return (tex
!is null); }
52 void updateTexture () {
53 version(tatlas_brect
) foreach (const ref rc
; rects
) imgRect(rc
.x
, rc
.y
, rc
.w
, rc
.h
, Color(0, 255, 0, 255));
55 tex
= new Texture(img
, Texture
.Option
.Clamp
, Texture
.Option
.Nearest
);
57 tex
.setFromImage(img
);
61 FRect
texCoords (in Rect rc
) {
63 res
.x0
= cast(float)rc
.x
/cast(float)(width
);
64 res
.y0
= cast(float)rc
.y
/cast(float)(height
);
65 res
.x1
= cast(float)(rc
.x
+rc
.w
)/cast(float)(width
);
66 res
.y1
= cast(float)(rc
.y
+rc
.h
)/cast(float)(height
);
71 private uint findBestFit (int w
, int h
) {
72 uint fitW
= BadRect
, fitH
= BadRect
, biggest
= BadRect
;
74 foreach (immutable idx
, const ref r
; rects
) {
75 if (r
.w
< w || r
.h
< h
) continue; // absolutely can't fit
76 if (r
.w
== w
&& r
.h
== h
) return cast(uint)idx
; // perfect fit
79 if (fitW
== BadRect || rects
.ptr
[fitW
].h
< r
.h
) fitW
= cast(uint)idx
;
80 } else if (r
.h
== h
) {
82 if (fitH
== BadRect || rects
.ptr
[fitH
].w
< r
.w
) fitH
= cast(uint)idx
;
85 if (biggest
== BadRect || rects
.ptr
[biggest
].area
> r
.area
) biggest
= cast(uint)idx
;
89 if (fitW
!= BadRect
&& fitH
!= BadRect
) return (rects
.ptr
[fitW
].area
> rects
.ptr
[fitH
].area ? fitW
: fitH
);
90 if (fitW
!= BadRect
) return fitW
;
91 if (fitH
!= BadRect
) return fitH
;
95 private void imgPutPixel (int x
, int y
, Color c
) {
96 if (x
>= 0 && y
>= 0 && x
< img
.width
&& y
< img
.height
) img
.imageData
.colors
.ptr
[y
*img
.width
+x
] = c
;
99 private void imgRect (int x
, int y
, int w
, int h
, Color c
) {
100 foreach (int d
; 0..w
) { imgPutPixel(x
+d
, y
, c
); imgPutPixel(x
+d
, y
+h
-1, c
); }
101 foreach (int d
; 1..h
-1) { imgPutPixel(x
, y
+d
, c
); imgPutPixel(x
+w
-1, y
+d
, c
); }
104 private void imgRect4 (int x
, int y
, int w
, int h
, Color ct
, Color cb
, Color cl
, Color cr
) {
105 foreach (int d
; 0..w
) { imgPutPixel(x
+d
, y
, ct
); imgPutPixel(x
+d
, y
+h
-1, cb
); }
106 foreach (int d
; 1..h
-1) { imgPutPixel(x
, y
+d
, cl
); imgPutPixel(x
+w
-1, y
+d
, cr
); }
109 // returns invalid rect if there's no room
110 Rect
insert (D2DImage
di) {
111 assert(di !is null && di.width
> 0 && di.height
> 0);
112 auto res
= insert(di.img
);
116 Rect
insert (TrueColorImage ximg
) {
117 assert(ximg
!is null && ximg
.width
> 0 && ximg
.height
> 0);
118 auto ri
= findBestFit(ximg
.width
, ximg
.height
);
119 if (ri
== BadRect
) return Rect
.Invalid
;
120 auto rc
= rects
.ptr
[ri
];
121 auto res
= Rect(rc
.x
, rc
.y
, ximg
.width
, ximg
.height
);
123 if (rc
.w
== res
.w
&& rc
.h
== res
.h
) {
124 // best fit, simply remove this rect
125 foreach (immutable cidx
; ri
+1..rects
.length
) rects
.ptr
[cidx
-1] = rects
.ptr
[cidx
];
127 rects
.assumeSafeAppend
; // for future; we probably won't have alot of best-fitting nodes initially
133 } else if (rc
.h
== res
.h
) {
134 // split horizontally
139 // split in both directions (by longer edge)
140 if (rc
.w
-res
.w
> rc
.h
-res
.h
) {
141 // cut the right part
144 // cut the bottom part
149 // cut the bottom part
152 // cut the right part
162 auto sp
= ximg
.imageData
.colors
.ptr
;
163 auto dp
= img
.imageData
.colors
.ptr
+res
.y
*img
.width
+res
.x
;
164 foreach (immutable dy
; 0..ximg
.height
) {
165 dp
[0..ximg
.width
] = sp
[0..ximg
.width
];
169 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));