1 /**
2  * Provides extended functionality to std.range
3  */
4 module daffodil.util.range;
5 
6 public import std.range;
7 
8 import daffodil.colorspace;
9 
10 template isImageRange(R, E) {
11     enum bool isImageRange = isImageRange!R && is(ElementType!R == E);
12 }
13 
14 template isImageRange(R) {
15     enum bool isImageRange = isInputRange!R && is(typeof(
16         (inout int = 0) {
17             R r = R.init;
18             size_t w = r.width;
19             size_t h = r.height;
20             size_t c = r.channelCount;
21             const ColorSpace* s = r.colorSpace;
22         }
23     ));
24 }
25 
26 template isRandomAccessImageRange(R) {
27     enum bool isRandomAccessImageRange = isImageRange!R && is(typeof(
28         (inout int = 0) {
29             R r = R.init;
30             ElementType!R e = r[0, 0];
31         }
32     ));
33 }
34 
35 interface ImageRange(E) : InputRange!E {
36     @property size_t width();
37     @property size_t height();
38     @property size_t channelCount();
39     @property const(ColorSpace*) colorSpace();
40 }
41 
42 unittest {
43     static assert(isImageRange!(ImageRange!int));
44 }
45 
46 interface RandomAccessImageRange(E) : ImageRange!E {
47     E opIndex(size_t x, size_t y);
48 }
49 
50 unittest {
51     static assert(isRandomAccessImageRange!(RandomAccessImageRange!int));
52 }
53 
54 template MostDerivedImageRange(R) if (isImageRange!R) {
55     alias E = ElementType!R;
56 
57     static if (isRandomAccessImageRange!R) {
58         alias MostDerivedImageRange = RandomAccessImageRange!E;
59     } else {
60         alias MostDerivedImageRange = ImageRange!E;
61     }
62 }
63 
64 class ImageRangeObject(R) : MostDerivedImageRange!R if (isImageRange!R) {
65     private alias E = ElementType!R;
66 
67     private R _range;
68 
69     this(R range) {
70         _range = range;
71     }
72 
73     @property E front() { return _range.front; }
74     void popFront() { _range.popFront(); }
75     @property bool empty() { return _range.empty; }
76     @property size_t width() { return _range.width; }
77     @property size_t height() { return _range.height; }
78     @property size_t channelCount() { return _range.channelCount; }
79     @property const(ColorSpace*) colorSpace() { return _range.colorSpace; }
80 
81     E moveFront() {
82         return .moveFront(_range);
83     }
84 
85     // Optimization:  One delegate call is faster than three virtual
86     // function calls.  Use opApply for foreach syntax.
87     int opApply(scope int delegate(E) dg) {
88         int res;
89 
90         foreach (i, e; this) {
91             res = dg(e);
92             if (res) break;
93         }
94 
95         return res;
96     }
97 
98     int opApply(scope int delegate(size_t, E) dg) {
99         int res;
100 
101         size_t i = 0;
102         for(; !empty; popFront()) {
103             res = dg(i, front);
104             if (res) break;
105             i++;
106         }
107 
108         return res;
109     }
110 
111     static if (isRandomAccessImageRange!R) {
112 
113         E opIndex(size_t x, size_t y) {
114             return _range[x, y];
115         }
116 
117     }
118 }
119 
120 unittest {
121     static assert(isImageRange!(ImageRangeObject!(ImageRange!int)));
122     static assert(isRandomAccessImageRange!(ImageRangeObject!(RandomAccessImageRange!int)));
123 }
124 
125 ImageRangeObject!R imageRangeObject(R)(R range) if (isImageRange!R) {
126     static if (is(R : ImageRange!(ElementType!R))) {
127         return range;
128     } else {
129         return new ImageRangeObject!R(range);
130     }
131 }
132 
133 class Iter(R) if (isRandomAccessRange!R) {
134     private R range;
135 
136     this(R r) {
137         range = r;
138     }
139 
140     void popFront() {
141         range = range[1..$];
142     }
143 
144     @property bool empty() { return range.length == 0; }
145     @property ubyte front() { return range[0]; }
146 }
147 
148 auto iter(R)(R range) if (isRandomAccessRange!R) {
149     return new Iter!R(range);
150 }