Lumitronix_Iflex_Pro_Workshop
Library to interact with the iFlexPro
NeoGammaDynamicTableMethod.h
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2 NeoGammaDynamicTableMethod class is used to correct RGB colors for human eye gamma levels equally
3 across all color channels
4 
5 Written by Michael C. Miller.
6 
7 I invest time and resources providing this open source code,
8 please support me by dontating (see https://github.com/Makuna)
9 
10 -------------------------------------------------------------------------
11 This file is part of the LUMITRONIX_iFlex_Workshop library.
12 
13 LumitronixIFlexBus is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Lesser General Public License as
15 published by the Free Software Foundation, either version 3 of
16 the License, or (at your option) any later version.
17 
18 LumitronixIFlexBus is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU Lesser General Public License for more details.
22 
23 You should have received a copy of the GNU Lesser General Public
24 License along with LumitronixIFlex. If not, see
25 <http://www.gnu.org/licenses/>.
26 -------------------------------------------------------------------------*/
27 #pragma once
28 
29 #if defined(NEOPIXEBUS_NO_STL)
30 
31 typedef float(*GammaCalcFunction)(float unitValue);
32 
33 #else
34 
35 #undef max
36 #undef min
37 #include <functional>
38 typedef std::function<float(float unitValue)> GammaCalcFunction;
39 
40 #endif
41 
43 {
44 protected:
46  {
47  uint8_t pos;
48  uint8_t count;
49  };
50 
51 public:
52  static uint8_t Correct(uint8_t value)
53  {
54  return _table[value];
55  }
56 
57  static uint16_t Correct(uint16_t value)
58  {
59  // since a single monolithic table would be an unreasonable memory usage
60  // this will use a hybrid of two tables, the base 255 table for the hibyte
61  // and a smaller table with hints on how to use the table for certain values
62  // and the left over values some simple calculations to approximate the final
63  // 16 bit value as compared to the equation
64  uint8_t hi = (value >> 8);
65  uint16_t lo = (value & 0x00ff);
66  uint8_t hiResult = _table[hi];
67  uint16_t lowResult = 0;
68 
69  if (hi < _hintsCount)
70  {
71  // use _hints table to calculate a reasonable lowbyte
72  lowResult = (lo + _hints[hi].pos * 256) / _hints[hi].count;
73  }
74  else if (hi == 255)
75  {
76  // last entry is always linear
77  lowResult = lo;
78  }
79  else
80  {
81  // check the _table for duplicate or jumps to adjust the range of lowbyte
82  if (hiResult == _table[hi - 1])
83  {
84  // this result is an upper duplicate
85  lowResult = (lo >> 1) | 0x80; // lo / 2 + 128
86  }
87  else
88  {
89  uint8_t delta = _table[hi + 1] - hiResult;
90 
91  if (delta == 0)
92  {
93  // this result is a lower duplicate
94  lowResult = (lo >> 1); // lo / 2
95  }
96  else if (delta == 1)
97  {
98  // this result is incremental and thus linear
99  lowResult = lo;
100  }
101  else
102  {
103  // this result jumps by more than one, so need to spread across
104  lowResult = delta * lo;
105  }
106  }
107 
108  }
109 
110  return (static_cast<uint16_t>(hiResult) << 8) + lowResult;
111  }
112 
113  static void Initialize(GammaCalcFunction calc, bool optimize16Bit = false)
114  {
115  if (_hints)
116  {
117  delete [] _hints;
118  _hints = nullptr;
119  }
120 
121  // first, iterate and fill 8 bit table
122  for (uint16_t entry = 0; entry < 256; entry++)
123  {
124  _table[entry] = static_cast<uint8_t>(255.0f * calc(entry / 255.0f) + 0.5f);
125  }
126 
127  // no optimization, so no 16 bit hints table
128  if (optimize16Bit)
129  {
130  NeoGamma16LowHint hints[256];
131 
132  // now walk table creating an optimized hint table for 16bit
133  // approximation
134  // There is an assumption that lower values have series of duplicates and
135  // upper values at worst have two values the same
136  //
137  uint16_t entryStart = 0;
138  uint16_t entryEnd = 0;
139  uint16_t entryLastTriplet = 0;
140 
141  while (entryStart < 255)
142  {
143  uint8_t value = _table[entryStart];
144 
145  while (value == _table[entryEnd] && entryEnd < 255)
146  {
147  entryEnd++;
148  }
149 
150  if (entryEnd == entryStart)
151  {
152  // no more duplicates, no need to continue
153  break;
154  }
155 
156  uint8_t pos = 0;
157  uint8_t count = entryEnd - entryStart;
158 
159  if (count >= 3)
160  {
161  // remember the last triplet + series
162  // as we don't need hints after this
163  // there can be paired duplicates after and before this
164  entryLastTriplet = entryEnd;
165  }
166 
167  // fill hints with known duplicate value
168  while (entryStart != entryEnd)
169  {
170  hints[entryStart].count = count;
171  hints[entryStart].pos = pos;
172  entryStart++;
173  pos++;
174  }
175  }
176  // create static hint table and copy temp table to it
177  _hintsCount = entryLastTriplet; // only need to last triplet
178  _hints = new NeoGamma16LowHint[_hintsCount];
179  memcpy(_hints, hints, sizeof(NeoGamma16LowHint) * _hintsCount);
180  }
181  }
182 
183  // SerialDumpTables is used if you want to generate your own static gamma table class
184  // rather than use this dynamically generated table. Just capture the serial output
185  // and use as your initializers for your tables
186  static void SerialDumpTables()
187  {
188  Serial.println();
189  Serial.println("8 bit:");
190  for (uint16_t entry = 0; entry < 256; entry++)
191  {
192  if (entry % 16 == 0)
193  {
194  Serial.println();
195  }
196  Serial.print(_table[entry]);
197  Serial.print(", ");
198  }
199 
200  Serial.println();
201  Serial.println();
202  Serial.print("16 bit: hintsCount = ");
203  Serial.println(_hintsCount);
204  if (_hints)
205  {
206  for (uint8_t hint = 0; hint < _hintsCount; hint++)
207  {
208  if (hint % 16 == 0)
209  {
210  Serial.println();
211  }
212  Serial.print("{");
213  Serial.print(_hints[hint].pos);
214  Serial.print(",");
215  Serial.print(_hints[hint].count);
216  Serial.print("}, ");
217  }
218  }
219  Serial.println();
220  }
221 
222 private:
223  static uint8_t _table[256];
224  static NeoGamma16LowHint* _hints;
225  static uint8_t _hintsCount;
226 };
std::function< float(float unitValue)> GammaCalcFunction
Definition: NeoGammaDynamicTableMethod.h:38
Definition: NeoGammaDynamicTableMethod.h:43
static void Initialize(GammaCalcFunction calc, bool optimize16Bit=false)
Definition: NeoGammaDynamicTableMethod.h:113
static void SerialDumpTables()
Definition: NeoGammaDynamicTableMethod.h:186
static uint16_t Correct(uint16_t value)
Definition: NeoGammaDynamicTableMethod.h:57
static uint8_t Correct(uint8_t value)
Definition: NeoGammaDynamicTableMethod.h:52
Definition: NeoGammaDynamicTableMethod.h:46
uint8_t pos
Definition: NeoGammaDynamicTableMethod.h:47
uint8_t count
Definition: NeoGammaDynamicTableMethod.h:48