1 /** 2 * In-memory images. 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.image; 16 17 import std.conv : to; 18 import std..string : format; 19 20 import dplug.graphics.view; 21 22 /// Represents a reference to COLOR data 23 /// already existing elsewhere in memory. 24 /// Assumes that pixels are stored row-by-row, 25 /// with a known distance between each row. 26 struct ImageRef(COLOR) 27 { 28 int w, h; 29 size_t pitch; /// In bytes, not COLORs 30 COLOR* pixels; 31 32 /// Returns an array for the pixels at row y. 33 COLOR[] scanline(int y) 34 { 35 assert(y>=0 && y<h); 36 assert(pitch); 37 auto row = cast(COLOR*)(cast(ubyte*)pixels + y*pitch); 38 return row[0..w]; 39 } 40 41 mixin DirectView; 42 } 43 44 unittest 45 { 46 static assert(isDirectView!(ImageRef!ubyte)); 47 } 48 49 /// Convert a direct view to an ImageRef. 50 /// Assumes that the rows are evenly spaced. 51 ImageRef!(ViewColor!SRC) toRef(SRC)(auto ref SRC src) 52 if (isDirectView!SRC) 53 { 54 return ImageRef!(ViewColor!SRC)(src.w, src.h, 55 src.h > 1 ? cast(ubyte*)src.scanline(1) - cast(ubyte*)src.scanline(0) : src.w, 56 src.scanline(0).ptr); 57 } 58 59 60 // *************************************************************************** 61 62 /// Performs linear downscale by a constant factor 63 template downscale(int HRX, int HRY=HRX) 64 { 65 auto downscale(SRC, TARGET)(auto ref SRC src, auto ref TARGET target) 66 if (isDirectView!SRC && isWritableView!TARGET) 67 { 68 alias lr = target; 69 alias hr = src; 70 71 assert(hr.w % HRX == 0 && hr.h % HRY == 0, "Size mismatch"); 72 73 lr.size(hr.w / HRX, hr.h / HRY); 74 75 foreach (y; 0..lr.h) 76 foreach (x; 0..lr.w) 77 { 78 static if (HRX*HRY <= 0x100) 79 enum EXPAND_BYTES = 1; 80 else 81 static if (HRX*HRY <= 0x10000) 82 enum EXPAND_BYTES = 2; 83 else 84 static assert(0); 85 static if (is(typeof(COLOR.init.a))) // downscale with alpha 86 { 87 ExpandType!(COLOR, EXPAND_BYTES+COLOR.init.a.sizeof) sum; 88 ExpandType!(typeof(COLOR.init.a), EXPAND_BYTES) alphaSum; 89 auto start = y*HRY*hr.stride + x*HRX; 90 foreach (j; 0..HRY) 91 { 92 foreach (p; hr.pixels[start..start+HRX]) 93 { 94 foreach (i, f; p.tupleof) 95 static if (p.tupleof[i].stringof != "p.a") 96 { 97 enum FIELD = p.tupleof[i].stringof[2..$]; 98 mixin("sum."~FIELD~" += cast(typeof(sum."~FIELD~"))p."~FIELD~" * p.a;"); 99 } 100 alphaSum += p.a; 101 } 102 start += hr.stride; 103 } 104 if (alphaSum) 105 { 106 auto result = cast(COLOR)(sum / alphaSum); 107 result.a = cast(typeof(result.a))(alphaSum / (HRX*HRY)); 108 lr[x, y] = result; 109 } 110 else 111 { 112 static assert(COLOR.init.a == 0); 113 lr[x, y] = COLOR.init; 114 } 115 } 116 else 117 { 118 ExpandChannelType!(ViewColor!SRC, EXPAND_BYTES) sum; 119 auto x0 = x*HRX; 120 auto x1 = x0+HRX; 121 foreach (j; y*HRY..(y+1)*HRY) 122 foreach (p; hr.scanline(j)[x0..x1]) 123 sum += p; 124 lr[x, y] = cast(ViewColor!SRC)(sum / (HRX*HRY)); 125 } 126 } 127 128 return target; 129 } 130 131 auto downscale(SRC)(auto ref SRC src) 132 if (isView!SRC) 133 { 134 ViewImage!SRC target; 135 return src.downscale(target); 136 } 137 } 138 139 // *************************************************************************** 140 141 /// Copy the indicated row of src to a COLOR buffer. 142 void copyScanline(SRC, COLOR)(auto ref SRC src, int y, COLOR[] dst) 143 if (isView!SRC && is(COLOR == ViewColor!SRC)) 144 { 145 static if (isDirectView!SRC) 146 dst[] = src.scanline(y)[]; 147 else 148 { 149 assert(src.w == dst.length); 150 foreach (x; 0..src.w) 151 dst[x] = src[x, y]; 152 } 153 } 154 155 /// Copy a view's pixels (top-to-bottom) to a COLOR buffer. 156 void copyPixels(SRC, COLOR)(auto ref SRC src, COLOR[] dst) 157 if (isView!SRC && is(COLOR == ViewColor!SRC)) 158 { 159 assert(dst.length == src.w * src.h); 160 foreach (y; 0..src.h) 161 src.copyScanline(y, dst[y*src.w..(y+1)*src.w]); 162 } 163 164 // *************************************************************************** 165 166 // Workaround for https://d.puremagic.com/issues/show_bug.cgi?id=12433 167 168 struct InputColor {} 169 alias GetInputColor(COLOR, INPUT) = Select!(is(COLOR == InputColor), INPUT, COLOR); 170 171 struct TargetColor {} 172 enum isTargetColor(C, TARGET) = is(C == TargetColor) || is(C == ViewColor!TARGET); 173 174 // ***************************************************************************