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 }