1 /** 2 * This module contains the implementation for the internal pixel storage 3 * mechanisms. 4 */ 5 module daffodil.pixel; 6 7 import std.conv; 8 import std.array; 9 import std.format; 10 import std.traits; 11 import std.algorithm; 12 13 import daffodil.colorspace; 14 15 /** 16 * The storage struct for a color. 17 */ 18 struct Pixel(V) if (isColorValue!V) { 19 /// The type used to store individual values for a color 20 alias Value = V; 21 22 /// The values of the color 23 Value[] values; 24 25 /// The color space used for operations with the color 26 const ColorSpace* colorSpace; 27 28 alias values this; 29 30 /// 31 this(Value[] values, const ColorSpace* colorSpace) { 32 this.values = values; 33 this.colorSpace = colorSpace; 34 } 35 36 /// Ditto 37 static if (!is(V == real)) { 38 this(real[] values, const ColorSpace* colorSpace) { 39 this(values.toColorValues!V, colorSpace); 40 } 41 } 42 43 /// Ditto 44 this(size_t size, const ColorSpace* colorSpace) { 45 this(new Value[size], colorSpace); 46 } 47 48 // TODO: Memory optimisation 49 50 /// 51 Pixel!V opBinary(string op : "*")(const real other) const { 52 real[] target = new real[this.length]; 53 colorSpace.opScalarMul(values.toReals, other, target); 54 return Pixel!V(target, colorSpace); 55 } 56 57 /// 58 Pixel!V opBinary(string op : "+")(const Pixel!V other) const { 59 // TODO: Check other.colorSpace 60 real[] target = new real[this.length]; 61 colorSpace.opColorAdd(values.toReals, other.values.toReals, target); 62 return Pixel!V(target, colorSpace); 63 } 64 65 /// 66 void opOpAssign(string op : "*")(const real other) { 67 colorSpace.opScalarMul(values, other, values); 68 } 69 70 /// 71 void opOpAssign(string op : "+")(const Pixel!V other) { 72 colorSpace.opColorAdd(values, other, values); 73 } 74 75 /// 76 void opAssign(const Pixel!V other) { 77 assert(other.length == this.length); 78 foreach (index; 0..this.length) { 79 this[index] = other[index]; 80 } 81 } 82 83 /// Clear all the color values to 0 84 void clear() { 85 foreach (index; 0..this.length) { 86 this[index] = 0; 87 } 88 } 89 90 /// Return a duplicate color in the same color space 91 @property auto dup() { 92 return Pixel!V(values.dup, colorSpace); 93 } 94 } 95 96 /** 97 * Template for checking whether a type is a valid color value. Color values are 98 * what daffodil stores internally. 99 * 100 * Any floating point type, unsigned integreal or valid 101 * :d:func:`isCustomColorValue` is a valid color value. 102 */ 103 template isColorValue(V) { 104 enum isColorValue = isFloatingPoint!V || 105 isIntegral!V && isUnsigned!V || 106 isCustomColorValue!V; 107 } 108 109 /// 110 @("isColorValue") 111 unittest { 112 assert(isColorValue!ubyte); 113 assert(isColorValue!uint); 114 assert(isColorValue!ulong); 115 assert(!isColorValue!int); 116 assert(isColorValue!float); 117 assert(isColorValue!real); 118 } 119 120 /** 121 * Template for checking whether a type is a valid custom color value. A custom 122 * color value must have a static ``init`` property, a static ``fromReal`` that 123 * converts a real to ``V``, and a ``toReal`` function that converts a ``V`` 124 * back to a real. 125 */ 126 template isCustomColorValue(V) { 127 enum isCustomColorValue = is(typeof( 128 (inout int = 0) { 129 V v = V.init; 130 v = V.fromReal(cast(real)1.0); 131 real r = v.toReal(); 132 } 133 )); 134 } 135 136 /// 137 @("isCustomColorValue") 138 unittest { 139 static struct IntCV { 140 int value = 0; 141 142 static auto fromReal(real v) { 143 return IntCV(cast(int)(v / int.max)); 144 } 145 146 real toReal() { 147 return cast(real)value / int.max; 148 } 149 } 150 151 assert(isCustomColorValue!IntCV); 152 assert(isColorValue!IntCV); 153 } 154 155 /** 156 * Converts a real to a specified color value. 157 */ 158 V toColorValue(V)(const real value) if (isColorValue!V) { 159 static if (isFloatingPoint!V) { 160 return value; 161 } else static if (isIntegral!V) { 162 return cast(V)(V.max * value.clamp(0, 1)); 163 } else { 164 return V.fromReal(value); 165 } 166 } 167 168 /** 169 * Converts an array of reals to an array of specified color values. 170 */ 171 V[] toColorValues(V)(const real[] values) if (isColorValue!V) { 172 return values.map!(toColorValue!V).array; 173 } 174 175 /** 176 * Converts a valid color value to a real. 177 */ 178 real toReal(V)(const V value) if (isColorValue!V) { 179 static if (isFloatingPoint!V) { 180 return value; 181 } else static if (isIntegral!V) { 182 return cast(real)value / V.max; 183 } else { 184 return value.toReal(); 185 } 186 } 187 188 /** 189 * Converts an array of valid color values to an array of reals. 190 */ 191 real[] toReals(V)(const V[] values) if (isColorValue!V) { 192 return values.map!(toReal!V).array; 193 }