1 module cubelut; 2 3 import std.stdio; 4 import std.string; 5 import std.conv; 6 import core.stdc.stdio; 7 import inteli.emmintrin; 8 9 /// A 3D LUT can be applied to floating-point images. 10 struct LUT 11 { 12 string title; 13 float[4][] table; 14 int size; 15 16 void readFromCubeFile(string path) // provide .cube file 17 { 18 auto file = File(path, "r"); 19 20 int point = 0; 21 int numPoints = 0; 22 size = 0; // unknown 23 24 // index to write 25 int ix = 0; 26 int iy = 0; 27 int iz = 0; 28 29 string line; 30 while ((line = file.readln()) !is null) 31 { 32 // chomp end of line 33 if (line.length > 0 && line[$-1] == '\n') 34 line = line[0..$-1]; 35 if (line.length > 0 && line[$-1] == '\r') 36 line = line[0..$-1]; 37 38 if (line.length == 0) 39 continue; 40 else if (line[0] == '#') 41 { 42 // comment 43 continue; 44 } 45 else if (line.startsWith("DOMAIN_MIN")) 46 { 47 // ignore 48 } 49 else if (line.startsWith("DOMAIN_MAX")) 50 { 51 // ignore 52 } 53 else if (line.startsWith("LUT_1D_SIZE")) 54 { 55 // ignored, often does nothing 56 } 57 else if (line.startsWith("LUT_1D_INPUT_RANGE")) 58 { 59 // ignored, often does nothing 60 } 61 else if (line.startsWith("TITLE")) 62 { 63 title = line[6..$]; 64 } 65 else if (line.startsWith("LUT_3D_SIZE")) 66 { 67 // init LUT size 68 size = to!int(line[12..$]); 69 table.length = size * size * size; 70 table[] = [0.0f, 0.0f, 0.0f, 0.0f]; 71 numPoints = size * size * size; 72 } 73 else 74 { 75 if (point < numPoints) 76 { 77 // Read rgb triplet with sncanf 78 79 string lineZT = line ~ '\0'; 80 float r = 0, g = 0, b = 0, a = 0; 81 82 if (3 == sscanf(line.ptr, "%f %f %f".ptr, &r, &g, &b)) 83 { 84 table[point] = [r, g, b, a]; 85 point++; 86 } 87 else 88 assert(false); 89 // else input error, TODO fail 90 } 91 } 92 } 93 file.close(); 94 95 } 96 97 __m128 sampleAt(int r, int g, int b) 98 { 99 if (r >= size) r = size - 1; 100 if (g >= size) g = size - 1; 101 if (b >= size) b = size - 1; 102 return _mm_loadu_ps(cast(float*) &table[r + g * size + b * size * size]); 103 } 104 105 void processScanline(float[4]* pixels, int width) 106 { 107 for (int x = 0; x < width; ++x) 108 { 109 float[4] pixel = pixels[x]; // TODO: clamp somewhere 110 111 float fr = pixel[0] * (size - 1); 112 float fg = pixel[1] * (size - 1); 113 float fb = pixel[2] * (size - 1); 114 if (fr < 0) fr = 0; 115 if (fg < 0) fg = 0; 116 if (fb < 0) fb = 0; 117 if (fr > size - 1.001f) fr = size - 1.001f; 118 if (fg > size - 1.001f) fg = size - 1.001f; 119 if (fb > size - 1.001f) fb = size - 1.001f; 120 121 // TODO linear sampling with 8 integer samples 122 123 int ir = cast(int)(fr); 124 int ig = cast(int)(fg); 125 int ib = cast(int)(fb); 126 assert(ir >= 0); 127 assert(ig >= 0); 128 assert(ib >= 0); 129 130 131 __m128 A = sampleAt(ir, ig, ib); 132 __m128 B = sampleAt(ir+1, ig, ib); 133 __m128 C = sampleAt(ir , ig+1, ib); 134 __m128 D = sampleAt(ir+1, ig+1, ib); 135 136 __m128 E = sampleAt(ir, ig, ib+1); 137 __m128 F = sampleAt(ir+1, ig, ib+1); 138 __m128 G = sampleAt(ir , ig+1, ib+1); 139 __m128 H = sampleAt(ir+1, ig+1, ib+1); 140 141 __m128 ones = _mm_set1_ps(1.0f); 142 __m128 fmr = _mm_set1_ps(fr - ir); 143 __m128 fmg = _mm_set1_ps(fg - ig); 144 __m128 fmb = _mm_set1_ps(fb - ib); 145 146 A = E * fmb + A * (ones - fmb); 147 B = F * fmb + B * (ones - fmb); 148 C = G * fmb + C * (ones - fmb); 149 D = H * fmb + D * (ones - fmb); 150 151 A = C * fmg + A * (ones - fmg); 152 B = D * fmg + B * (ones - fmg); 153 154 A = A * fmr + B * (ones - fmr); 155 156 // clip again 157 alias mapped = A; 158 159 pixel[0] = mapped[0]; 160 pixel[1] = mapped[1]; 161 pixel[2] = mapped[2]; // do not touch original alpha 162 163 if (pixel[0] < 0) pixel[0] = 0; 164 if (pixel[1] < 0) pixel[1] = 0; 165 if (pixel[2] < 0) pixel[2] = 0; 166 if (pixel[0] > 1) pixel[0] = 1; 167 if (pixel[1] > 1) pixel[1] = 1; 168 if (pixel[2] > 1) pixel[2] = 1; 169 170 pixels[x] = pixel; 171 } 172 } 173 }