1 /** 2 * A `View` is the base abstraction for images. Port of ae.utils.graphics. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 * Guillaume Piolat <contact@auburnsounds.com> 13 */ 14 15 module dplug.graphics.view; 16 17 import std.functional; 18 import std.typetuple; 19 import std.algorithm.mutation: swap; 20 import std.math; 21 22 import dplug.core.math; 23 24 public import dplug.graphics.color; 25 26 /// A view is any type which provides a width, height, 27 /// and can be indexed to get the color at a specific 28 /// coordinate. 29 enum isView(T) = 30 is(typeof(T.init.w) : size_t) && // width 31 is(typeof(T.init.h) : size_t) && // height 32 is(typeof(T.init[0, 0]) ); // color information 33 34 /// Returns the color type of the specified view. 35 /// By convention, colors are structs with numeric 36 /// fields named after the channel they indicate. 37 alias ViewColor(T) = typeof(T.init[0, 0]); 38 39 /// Views can be read-only or writable. 40 enum isWritableView(T) = 41 isView!T && 42 is(typeof(T.init[0, 0] = ViewColor!T.init)); 43 44 /// Optionally, a view can also provide direct pixel 45 /// access. We call these "direct views". 46 enum isDirectView(T) = 47 isView!T && 48 is(typeof(T.init.scanline(0)) : ViewColor!T[]); 49 50 /// Mixin which implements view primitives on top of 51 /// existing direct view primitives. 52 mixin template DirectView() 53 { 54 alias COLOR = typeof(scanline(0)[0]); 55 56 /// Implements the view[x, y] operator. 57 ref COLOR opIndex(int x, int y) 58 { 59 return scanline(y)[x]; 60 } 61 62 /// Implements the view[x, y] = c operator. 63 COLOR opIndexAssign(COLOR value, int x, int y) 64 { 65 return scanline(y)[x] = value; 66 } 67 } 68 69 // *************************************************************************** 70 71 /// Returns a view which calculates pixels 72 /// on-demand using the specified formula. 73 template procedural(alias formula) 74 { 75 alias fun = binaryFun!(formula, "x", "y"); 76 alias COLOR = typeof(fun(0, 0)); 77 78 auto procedural(int w, int h) 79 { 80 struct Procedural 81 { 82 int w, h; 83 84 auto ref COLOR opIndex(int x, int y) 85 { 86 assert(x >= 0 && y >= 0 && x < w && y < h); 87 return fun(x, y); 88 } 89 } 90 return Procedural(w, h); 91 } 92 } 93 94 /// Returns a view of the specified dimensions 95 /// and same solid color. 96 auto solid(COLOR)(COLOR c, int w, int h) 97 { 98 return procedural!((x, y) => c)(w, h); 99 } 100 101 /// Return a 1x1 view of the specified color. 102 /// Useful for testing. 103 auto onePixel(COLOR)(COLOR c) 104 { 105 return solid(c, 1, 1); 106 } 107 108 unittest 109 { 110 assert(onePixel(42)[0, 0] == 42); 111 } 112 113 // *************************************************************************** 114 115 /// Blits a view onto another. 116 /// The views must have the same size. 117 void blitTo(SRC, DST)(auto ref SRC src, auto ref DST dst) 118 if (isView!SRC && isWritableView!DST) 119 { 120 assert(src.w == dst.w && src.h == dst.h, "View size mismatch"); 121 foreach (y; 0..src.h) 122 { 123 static if (isDirectView!SRC && isDirectView!DST) 124 dst.scanline(y)[] = src.scanline(y)[]; 125 else 126 { 127 foreach (x; 0..src.w) 128 dst[x, y] = src[x, y]; 129 } 130 } 131 } 132 133 /// Helper function to blit an image onto another at a specified location. 134 void blitTo(SRC, DST)(auto ref SRC src, auto ref DST dst, int x, int y) 135 { 136 src.blitTo(dst.crop(x, y, x+src.w, y+src.h)); 137 } 138 139 /// Default implementation for the .size method. 140 /// Asserts that the view has the desired size. 141 void size(V)(auto ref V src, int w, int h) 142 if (isView!V) 143 { 144 assert(src.w == w && src.h == h, "Wrong size for " ~ V.stringof); 145 } 146 147 // *************************************************************************** 148 149 /// Mixin which implements view primitives on top of 150 /// another view, using a coordinate transform function. 151 mixin template Warp(V) 152 if (isView!V) 153 { 154 V src; 155 156 auto ref ViewColor!V opIndex(int x, int y) 157 { 158 warp(x, y); 159 return src[x, y]; 160 } 161 162 static if (isWritableView!V) 163 ViewColor!V opIndexAssign(ViewColor!V value, int x, int y) 164 { 165 warp(x, y); 166 return src[x, y] = value; 167 } 168 } 169 170 /// Crop a view to the specified rectangle. 171 auto crop(V)(auto ref V src, int x0, int y0, int x1, int y1) 172 if (isView!V) 173 { 174 assert( 0 <= x0 && 0 <= y0); 175 assert(x0 <= x1 && y0 <= y1); 176 assert(x1 <= src.w && y1 <= src.h); 177 178 static struct Crop 179 { 180 mixin Warp!V; 181 182 int x0, y0, x1, y1; 183 184 @property int w() { return x1-x0; } 185 @property int h() { return y1-y0; } 186 187 void warp(ref int x, ref int y) 188 { 189 x += x0; 190 y += y0; 191 } 192 193 static if (isDirectView!V) 194 ViewColor!V[] scanline(int y) 195 { 196 return src.scanline(y0+y)[x0..x1]; 197 } 198 } 199 200 static assert(isDirectView!V == isDirectView!Crop); 201 202 return Crop(src, x0, y0, x1, y1); 203 } 204 205 unittest 206 { 207 auto g = procedural!((x, y) => y)(1, 256); 208 auto c = g.crop(0, 10, 1, 20); 209 assert(c[0, 0] == 10); 210 }