1 module tatlas
is aliced
;
9 //version = tatlas_brect;
12 // ////////////////////////////////////////////////////////////////////////// //
13 public final class TexAtlas
{
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
; }
24 public TrueColorImage img
;
28 enum BadRect
= uint.max
;
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));
46 tex
= new Texture(img
, Texture
.Option
.Clamp
, Texture
.Option
.Nearest
);
48 tex
.setFromImage(img
);
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
61 if (fitW
== BadRect || rects
.ptr
[fitW
].h
< r
.h
) fitW
= cast(uint)idx
;
62 } else if (r
.h
== h
) {
64 if (fitH
== BadRect || rects
.ptr
[fitH
].w
< r
.w
) fitH
= cast(uint)idx
;
67 if (biggest
== BadRect || rects
.ptr
[biggest
].area
> r
.area
) biggest
= cast(uint)idx
;
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
;
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
);
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
];
103 rects
.assumeSafeAppend
; // for future; we probably won't have alot of best-fitting nodes initially
109 } else if (rc
.h
== res
.h
) {
110 // split horizontally
115 // split in both directions (by longer edge)
116 if (rc
.w
-res
.w
> rc
.h
-res
.h
) {
117 // cut the right part
120 // cut the bottom part
125 // cut the bottom part
128 // cut the right part
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
];
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));