1 /** 2 * Provides common data manipulation functions. 3 */ 4 module daffodil.util.data; 5 6 import std.math; 7 import std.meta; 8 import std.stdio; 9 import std.typecons; 10 import std.algorithm; 11 import core.bitop; 12 13 import daffodil; 14 import daffodil.util.range; 15 import daffodil.util.errors; 16 17 private const FILE_CHUNK_SIZE = 4096; 18 19 /** 20 * Converts any sort of possible input/location into a forward ubyte range. 21 * Useful for providing simple overloading of load functions for a variety of inputs. 22 */ 23 DataRange dataLoad(File file) { 24 return file.byChunk(FILE_CHUNK_SIZE).joiner.inputRangeObject; 25 } 26 /// Ditto 27 DataRange dataLoad(string path) { 28 import std.path : expandTilde; 29 return dataLoad(File(path.expandTilde, "r")); 30 } 31 32 alias Loadeable = AliasSeq!(File, string); 33 34 template isLoadeable(E) { 35 enum isLoadeable = staticIndexOf!(E, Loadeable) != -1; 36 } 37 38 /** 39 * Converts any sort of possible output into a output range. 40 * Useful for providing simple overloading of save functions for a variety of outputs. 41 */ 42 OutRange dataSave(File file) { 43 struct F { 44 File f; 45 this(File f) { this.f = f; } 46 void put(ubyte data) { f.rawWrite([data]); } 47 } 48 return F(file).outputRangeObject!ubyte; 49 } 50 /// Ditto 51 OutRange dataSave(string path) { 52 import std.path : expandTilde; 53 return dataSave(File(path.expandTilde, "w")); 54 } 55 56 alias Saveable = AliasSeq!(File, string); 57 58 template isSaveable(E) { 59 enum isSaveable = staticIndexOf!(E, Saveable) != -1; 60 } 61 62 struct PixelData { 63 size_t x, y; 64 real[] data; 65 } 66 67 /// Calculate the padding needed when padding a bpp with a width to a multiple of bytes 68 auto paddingFor(size_t width, size_t bpp, size_t padding) { 69 auto bitsPerRow = width * bpp; 70 auto paddingBits = padding * 8; 71 auto bitRowSize = ((bitsPerRow + paddingBits - 1) / paddingBits) * paddingBits; 72 return (bitRowSize - bitsPerRow) / 8; 73 } 74 75 /** 76 * Documentation 77 */ 78 auto maskedRasterLoad(R, T)( 79 R data, 80 T[] mask, 81 size_t bpp, 82 ptrdiff_t _width, 83 ptrdiff_t _height, 84 const ColorSpace* _colorSpace, 85 size_t padding = 1) if (isInputRange!R && 86 is(ElementType!R == ubyte)) { 87 // currently don't support non multiples of 8 88 assert(bpp % 8 == 0); 89 90 struct Range { 91 R range; 92 size_t x = 0; 93 size_t y = 0; 94 size_t channelCount; 95 real[] loadBuffer; 96 97 this(R range) { 98 this.range = range; 99 this.channelCount = mask.length; 100 this.loadBuffer = new real[channelCount]; 101 } 102 103 @property bool empty() { 104 return y >= height; 105 } 106 107 @property PixelData front() { 108 // TODO: Make this happen in popFront 109 maskedLoad(loadBuffer, data, mask, bpp); 110 111 // Adjust for negative heights 112 auto yReal = _height < 0 ? height - y - 1 : y; 113 return PixelData(x, yReal, loadBuffer); 114 } 115 116 private void popPadding() { 117 auto pad = paddingFor(_width, bpp, padding); 118 range.popFrontExactly(pad); 119 } 120 121 void popFront() { 122 if (x + 1 < _width) x++; 123 else { 124 // perform padding (next multiple of padding for x) 125 popPadding(); 126 x = 0; 127 y++; 128 } 129 } 130 131 @property size_t width() { return _width; } 132 @property size_t height() { return abs(_height); } 133 @property auto colorSpace() const { return _colorSpace; } 134 } 135 136 return Range(data); 137 } 138 139 void maskedLoad(R, T)(real[] target, R range, T[] masks, size_t bpp) { 140 auto data = range.take(bpp / 8).array; 141 enforce!UnexpectedEndOfData(data.length == bpp / 8); 142 143 foreach (maskIndex, mask; masks) { 144 auto bitStart = T.sizeof * 8 - bsr(mask) - 1; 145 auto bitEnd = T.sizeof * 8 - bsf(mask); 146 147 auto max = pow(2f, bitEnd - bitStart) - 1f; 148 target[maskIndex] = 0; 149 // TODO: Optimise 150 foreach (index, value; data) { 151 target[maskIndex] += (value >> (bitStart - index * 8)) / max; 152 } 153 } 154 } 155 156 void maskedRasterSave(R, O, T)( 157 R image, 158 O output, 159 T[] mask, 160 size_t bpp, 161 ptrdiff_t width, 162 ptrdiff_t height, 163 size_t padding = 1) if (isOutputRange!(O, ubyte) && 164 isRandomAccessImageRange!R && 165 is(ElementType!R == real[])) { 166 assert(bpp % 8 == 0); 167 168 ubyte[] saveBuffer = new ubyte[bpp / 8]; 169 170 foreach (y; 0..abs(height)) { 171 auto yReal = height < 0 ? -height - y - 1 : y; 172 173 foreach (x; 0..width) { 174 auto data = image[x, yReal]; 175 maskedSave(data, saveBuffer, mask, bpp); 176 put(output, saveBuffer); 177 } 178 179 // Apply padding 180 auto pad = paddingFor(width, bpp, padding); 181 foreach (_; 0..pad) { 182 output.put(cast(ubyte)0); 183 } 184 } 185 } 186 187 void maskedSave(T)(real[] data, ubyte[] target, T[] masks, size_t bpp) { 188 foreach (index; 0..target.length) { 189 target[index] = 0; 190 } 191 192 foreach (maskIndex, mask; masks) { 193 auto bitStart = T.sizeof * 8 - bsr(mask) - 1; 194 auto bitEnd = T.sizeof * 8 - bsf(mask); 195 196 auto max = pow(2f, bitEnd - bitStart) - 1f; 197 198 // TODO: Optimise 199 foreach (index; 0..target.length) { 200 auto value = cast(size_t)(data[maskIndex] * max); 201 target[index] += cast(ubyte)(value << (bitStart - index * 8)); 202 } 203 } 204 }