1 /** 2 * Implement the gradient class. dplug:canvas internals. 3 * 4 * Copyright: Copyright Chris Jones 2020. 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module dplug.canvas.gradient; 8 9 import dplug.core.nogc; 10 import dplug.core.vec; 11 import dplug.canvas.misc; 12 import dplug.graphics.color; 13 14 /* 15 Gradient class, 16 The gradient is defined as a list of colours and positions (known as stops) along 17 a single dimension from 0 to 1. 18 It has a precomputed lookup table for the rasterizer, currently fixed at 256 19 entries. Its a "just get it working for now" solution tbh 20 */ 21 class Gradient 22 { 23 nothrow: 24 @nogc: 25 // colour is 32 bit ARGB, pos runs from 0..1 26 27 struct ColorStop 28 { 29 uint color; 30 float pos; 31 } 32 33 size_t length() 34 { 35 return m_stops.length; 36 } 37 38 bool hasChanged() 39 { 40 return m_changed; 41 } 42 43 void reset() 44 { 45 m_stops.clearContents(); 46 m_changed = true; 47 } 48 49 Gradient addStop(float pos, uint color) 50 { 51 m_stops.pushBack(ColorStop(color,clip(pos,0.0,1.0))); 52 m_changed = true; 53 return this; 54 } 55 56 uint[] getLookup() 57 { 58 if (m_changed) initLookup(); 59 return m_lookup[0..lookupLen]; 60 } 61 62 int lutLength() 63 { 64 return lookupLen; 65 } 66 67 private: 68 69 // fixed size lookup for now, could probably have lookup tables cached 70 // by the rasterizer rather than stuck in here/ 71 72 enum lookupLen = 256; 73 74 void initLookup() 75 { 76 import std.algorithm : sort; 77 m_stops[].sort!("a.pos < b.pos")(); 78 79 if (m_stops.length == 0) 80 { 81 foreach(ref c; m_lookup) c = 0; 82 } 83 else if (m_stops.length == 1) 84 { 85 foreach(ref c; m_lookup) c = m_stops[0].color; 86 } 87 else 88 { 89 int idx = cast(int) (m_stops[0].pos*lookupLen); 90 m_lookup[0..idx] = m_stops[0].color; 91 92 foreach(size_t i; 1.. m_stops.length) 93 { 94 int next = cast(int) (m_stops[i].pos*lookupLen); 95 96 foreach(int j; idx..next) 97 { 98 uint a = (256*(j-idx))/(next-idx); 99 uint c0 = m_stops[i-1].color; 100 uint c1 = m_stops[i].color; 101 uint t0 = (c0 & 0xFF00FF)*(256-a) + (c1 & 0xFF00FF)*a; 102 uint t1 = ((c0 >> 8) & 0xFF00FF)*(256-a) + ((c1 >> 8) & 0xFF00FF)*a; 103 m_lookup[j] = ((t0 >> 8) & 0xFF00FF) | (t1 & 0xFF00FF00); 104 } 105 idx = next; 106 } 107 m_lookup[idx..$] = m_stops[$-1].color; 108 } 109 m_changed = false; 110 } 111 112 Vec!ColorStop m_stops; 113 uint[lookupLen] m_lookup; 114 bool m_changed = true; 115 } 116