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 }