1 /**
2   This module introduces some Image implementations.
3 */
4 module gfm.image.bitmap;
5 
6 import std.c.string, 
7        std.math;
8 
9 import gfm.core.alignedbuffer,
10        gfm.core.memory,
11        gfm.math.vector;
12 
13 import gfm.image.image;
14 
15 /**
16     Simple planar image implementing the Image concept.
17     A Bitmap is mostly a triplet of (base address + dimension + stride).
18     Data can be owned or not.
19  */
20 struct Bitmap(T)
21 {
22 nothrow:
23     public
24     {
25         alias T element_t;
26 
27         /// Creates a Bitmap with owned memory.
28         this(vec2i dimension) nothrow
29         {
30             _data = alignedMalloc(dimension.x * dimension.y * T.sizeof, 64);
31             _dimension = dimension;
32             _stride = dimension.x * T.sizeof;
33             _owned = true;
34         }
35 
36         /// Creates a Bitmap from borrowed memory.
37         this(T* data, vec2i dimension, ptrdiff_t stride) nothrow
38         {
39             _data = data;
40             _dimension = dimension;
41             _stride = stride;
42             _owned = false;
43         }
44 
45         /// Creates a Bitmap from borrowed contiguous memory.
46         this(T* data, vec2i dimension) nothrow
47         {
48             this(data, dimension, dimension.x * T.sizeof);
49         }
50 
51         /// Creates with a buffer whose lifetime should be greater than this.
52         this(AlignedBuffer!ubyte buffer, vec2i dimension) nothrow
53         {
54             size_t bytesNeeded = dimension.x * dimension.y * T.sizeof;
55             buffer.resize(bytesNeeded);
56 
57             this(cast(T*)(buffer.ptr), dimension);
58         }
59 
60         ~this()
61         {
62             if (_owned)
63                 alignedFree(_data);
64         }
65 
66         // postblit needed to duplicate owned data
67         this(this) nothrow
68         {
69             if (_owned)
70             {
71                 size_t sizeInBytes = _dimension.x * _dimension.y * T.sizeof;
72                 void* oldData = _data;
73                 _data = alignedMalloc(sizeInBytes, 64);
74                 memcpy(_data, oldData, sizeInBytes);
75             }
76         }
77 
78         auto opAssign(Bitmap other) nothrow
79         {
80             _data = other._data;
81             _dimension = other._dimension;
82             _stride = other._stride;
83             _owned = other._owned;
84             return this;
85         }
86 
87         /// Returns: A sub-bitmap from a Bitmap.
88         Bitmap subImage(vec2i position, vec2i dimension)
89         {
90             assert(contains(position));
91             assert(contains(position + dimension - 1));
92 
93             return Bitmap(address(position.x, position.y), dimension, _stride);
94         }
95 
96         @property
97         {
98             T* ptr()
99             {
100                 return cast(T*) _data;
101             }
102 
103             const(T)* ptr() const
104             {
105                 return cast(T*) _data;
106             }
107 
108             vec2i dimension() const pure
109             {
110                 return _dimension;
111             }
112 
113             /// Returns: Width of image in pixels.
114             int width() const pure
115             {
116                 return _dimension.x;
117             }
118 
119             /// Returns: Height of image in pixels.
120             int height() const pure
121             {
122                 return _dimension.y;
123             }
124 
125         }
126 
127         T get(int i, int j) const pure
128         {
129             return *(address(i, j));
130         }
131 
132         void set(int i, int j, T e)
133         {
134             *(address(i, j)) = e;
135         }
136 
137         bool isDense() const pure
138         {
139             return (_stride == _dimension.x * T.sizeof);
140         }
141 
142         bool contains(vec2i point)
143         {
144             return (cast(uint)(point.x) < cast(uint)(_dimension.x))
145                 && (cast(uint)(point.y) < cast(uint)(_dimension.y));
146         }
147 
148         /// copy another Bitmap of same type and dimension
149         void copy(Bitmap source)
150         {
151             assert(dimension == source.dimension);
152             if (isDense() && source.isDense())
153             {
154                 size_t bytes = dimension.x * dimension.y * T.sizeof;
155                 memcpy(_data, source._data, bytes);
156             }
157             else if(_stride == source._stride)
158             {
159                 size_t bytes = _stride * dimension.y;
160                 memcpy(_data, source._data, bytes);
161             }
162             else
163             {
164                 void* dest = _data;
165                 void* src = source._data;
166                 size_t lineSize = abs(_stride);
167 
168                 for (size_t j = 0; j < dimension.y; ++j)
169                 {
170                     memcpy(dest, src, lineSize);
171                     dest += _stride;
172                     src += source._stride;
173                 }
174             }
175         }
176     }
177 
178     private
179     {
180         vec2i _dimension;
181         void* _data;
182         ptrdiff_t _stride;       // in bytes
183         bool _owned;
184 
185         T* address(int i, int j) pure
186         {
187             return cast(T*)(_data + _stride * j + T.sizeof * i);
188         }
189 
190         const(T)* address(int i, int j) const pure // :| where is inout(this)?
191         {
192             return cast(T*)(_data + _stride * j + T.sizeof * i);
193         }
194     }
195 }
196 
197 static assert(isImage!(Bitmap!int));
198 static assert(isImage!(Bitmap!vec4ub));
199 
200 unittest
201 {
202     {
203         int[] b;
204         b.length = 10 * 10;
205         b[] = 0;
206         auto bitmap = Bitmap!int(b.ptr, vec2i(10, 5), 20 * int.sizeof);
207 
208         fillImage(bitmap, 1);
209         assert(bitmap.dimension.x == 10);
210         assert(bitmap.dimension.y == 5);
211 
212         for (int j = 0; j < 5; ++j)
213             for (int i = 0; i < 10; ++i)
214                 assert(bitmap.get(i, j) == 1);
215 
216         for (int j = 0; j < 5; ++j)
217             for (int i = 0; i < 10; ++i)
218             {
219                 assert(b[i + (2 * j) * 10] == 1);
220                 assert(b[i + (2 * j + 1) * 10] == 0);
221             }
222     }
223 }
224 
225 
226 /**
227   A TiledBitmap is like a Bitmap but pixels are organized in tiles.
228  */
229 struct TiledBitmap(T, size_t tileWidth, size_t tileHeight)
230 {    
231     static assert(tileWidth >= 1 && isPowerOf2(tileWidth));
232     static assert(tileHeight >= 1 && isPowerOf2(tileHeight));
233 
234 nothrow:
235     public
236     {
237         enum tileSize = tileWidth * tileHeight;
238         alias T element_t;
239         alias T[tileSize] tile_t;
240 
241         Bitmap!tile_t tiles; // a Bitmap of tiles
242 
243         /// Create with owned memory, dimension is given in tiles
244         this(vec2i dimension)
245         {
246             tiles = Bitmap!tile_t(dimension);
247         }
248 
249         T get(int i, int j) const pure
250         {
251             return *(address(i, j));
252         }
253 
254         void set(int i, int j, T e)
255         {
256             *(address(i, j)) = e;
257         }
258 
259         @property
260         {
261             T* ptr()
262             {
263                 return tiles.ptr;
264             }
265 
266             const(T)* ptr() const
267             {
268                 return tiles.ptr;
269             }
270 
271             vec2i dimension() const pure
272             {
273                 return tiles.dimension * vec2i(tileWidth, tileHeight);
274             }
275 
276             int width() const pure
277             {
278                 return tiles.width * tileWidth;
279             }
280 
281             int height() const pure
282             {
283                 return tiles.height * tileHeight;
284             }
285         }
286     }
287 
288     private
289     {
290         enum X_MASK = tileWidth - 1,
291              Y_MASK = tileHeight - 1,
292              X_SHIFT = ilog2(tileWidth),
293              Y_SHIFT = ilog2(tileHeight);
294         
295         T* address(int i, int j) pure
296         {
297             tile_t* tile = tiles.address(i >> X_SHIFT, j >> Y_SHIFT);
298             size_t tileIndex = tileWeight * (i & X_MASK) + (j & Y_MASK);
299             return tile.ptr + tileIndex;
300         }
301 
302         const(T)* address(int i, int j) const pure
303         {
304             tile_t* tile = tiles.address(i >> X_SHIFT, j >> Y_SHIFT);
305             size_t tileIndex = tileWeight * (i & X_MASK) + (j & Y_MASK);
306             return tile.ptr + tileIndex;
307         }
308     }
309 }