1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkDiscretizableColorTransferFunction.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkDiscretizableColorTransferFunction.h"
16 
17 #include "vtkCommand.h"
18 #include "vtkLookupTable.h"
19 #include "vtkMath.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkPiecewiseFunction.h"
22 #include "vtkTemplateAliasMacro.h"
23 #include "vtkTuple.h"
24 
25 #include <vector>
26 
27 class vtkDiscretizableColorTransferFunction::vtkInternals
28 {
29 public:
30   std::vector<vtkTuple<double, 4>> IndexedColors;
31 };
32 
33 vtkStandardNewMacro(vtkDiscretizableColorTransferFunction);
34 vtkCxxSetObjectMacro(
35   vtkDiscretizableColorTransferFunction, ScalarOpacityFunction, vtkPiecewiseFunction);
36 //------------------------------------------------------------------------------
vtkDiscretizableColorTransferFunction()37 vtkDiscretizableColorTransferFunction::vtkDiscretizableColorTransferFunction()
38   : Internals(new vtkInternals())
39 {
40   this->LookupTable = vtkLookupTable::New();
41 
42   this->Discretize = 0;
43   this->NumberOfValues = 256;
44 
45   this->UseLogScale = 0;
46 
47   this->ScalarOpacityFunction = nullptr;
48   this->EnableOpacityMapping = false;
49 }
50 
51 //------------------------------------------------------------------------------
~vtkDiscretizableColorTransferFunction()52 vtkDiscretizableColorTransferFunction::~vtkDiscretizableColorTransferFunction()
53 {
54   // this removes any observer we may have setup for the
55   // ScalarOpacityFunction.
56   this->SetScalarOpacityFunction(nullptr);
57   this->LookupTable->Delete();
58 
59   delete this->Internals;
60   this->Internals = nullptr;
61 }
62 
63 //------------------------------------------------------------------------------
GetMTime()64 vtkMTimeType vtkDiscretizableColorTransferFunction::GetMTime()
65 {
66   vtkMTimeType mtime = this->Superclass::GetMTime();
67   if (this->ScalarOpacityFunction)
68   {
69     vtkMTimeType somtime = this->ScalarOpacityFunction->GetMTime();
70     mtime = somtime > mtime ? somtime : mtime;
71   }
72   if (this->LookupTable)
73   {
74     vtkMTimeType ltmtime = this->LookupTable->GetMTime();
75     mtime = ltmtime > mtime ? ltmtime : mtime;
76   }
77 
78   return mtime;
79 }
80 
81 //------------------------------------------------------------------------------
SetNumberOfIndexedColors(unsigned int count)82 void vtkDiscretizableColorTransferFunction::SetNumberOfIndexedColors(unsigned int count)
83 {
84   if (static_cast<unsigned int>(this->Internals->IndexedColors.size()) != count)
85   {
86     this->Internals->IndexedColors.resize(count, vtkTuple<double, 4>(0.0));
87     this->Modified();
88   }
89 }
90 
91 //------------------------------------------------------------------------------
GetNumberOfIndexedColors()92 unsigned int vtkDiscretizableColorTransferFunction::GetNumberOfIndexedColors()
93 {
94   return static_cast<unsigned int>(this->Internals->IndexedColors.size());
95 }
96 
97 //------------------------------------------------------------------------------
SetIndexedColor(unsigned int index,double r,double g,double b,double a)98 void vtkDiscretizableColorTransferFunction::SetIndexedColor(
99   unsigned int index, double r, double g, double b, double a)
100 {
101   if (static_cast<unsigned int>(this->Internals->IndexedColors.size()) <= index)
102   {
103     // resize and fill all new colors with the same color as specified.
104     size_t old_size = this->Internals->IndexedColors.size();
105     size_t new_size = static_cast<size_t>(index + 1);
106     this->Internals->IndexedColors.resize(new_size);
107 
108     for (size_t cc = old_size; cc < new_size; cc++)
109     {
110       double* data = this->Internals->IndexedColors[cc].GetData();
111       data[0] = r;
112       data[1] = g;
113       data[2] = b;
114       data[3] = a;
115     }
116 
117     this->Modified();
118   }
119   else if (this->Internals->IndexedColors[index].GetData()[0] != r ||
120     this->Internals->IndexedColors[index].GetData()[1] != g ||
121     this->Internals->IndexedColors[index].GetData()[2] != b ||
122     this->Internals->IndexedColors[index].GetData()[3] != a)
123   {
124     // color has changed, change it.
125     double* data = this->Internals->IndexedColors[index].GetData();
126     data[0] = r;
127     data[1] = g;
128     data[2] = b;
129     data[3] = a;
130 
131     this->Modified();
132   }
133 }
134 
135 //------------------------------------------------------------------------------
GetIndexedColor(vtkIdType i,double rgba[4])136 void vtkDiscretizableColorTransferFunction::GetIndexedColor(vtkIdType i, double rgba[4])
137 {
138   if (this->IndexedLookup || this->Discretize)
139   {
140     this->LookupTable->GetIndexedColor(i, rgba);
141   }
142   else
143   {
144     this->Superclass::GetIndexedColor(i, rgba);
145   }
146 }
147 
148 //------------------------------------------------------------------------------
SetUseLogScale(int useLogScale)149 void vtkDiscretizableColorTransferFunction::SetUseLogScale(int useLogScale)
150 {
151   if (this->UseLogScale != useLogScale)
152   {
153     this->UseLogScale = useLogScale;
154     if (this->UseLogScale)
155     {
156       this->LookupTable->SetScaleToLog10();
157       this->SetScaleToLog10();
158     }
159     else
160     {
161       this->LookupTable->SetScaleToLinear();
162       this->SetScaleToLinear();
163     }
164 
165     this->Modified();
166   }
167 }
168 
169 //------------------------------------------------------------------------------
IsOpaque()170 int vtkDiscretizableColorTransferFunction::IsOpaque()
171 {
172   return !this->EnableOpacityMapping;
173 }
174 
IsOpaque(vtkAbstractArray * scalars,int colorMode,int component)175 int vtkDiscretizableColorTransferFunction::IsOpaque(
176   vtkAbstractArray* scalars, int colorMode, int component)
177 {
178   // use superclass logic?
179   vtkDataArray* dataArray = vtkArrayDownCast<vtkDataArray>(scalars);
180   if ((colorMode == VTK_COLOR_MODE_DEFAULT &&
181         vtkArrayDownCast<vtkUnsignedCharArray>(dataArray) != nullptr) ||
182     (colorMode == VTK_COLOR_MODE_DIRECT_SCALARS && dataArray))
183   {
184     return this->Superclass::IsOpaque(scalars, colorMode, component);
185   }
186   // otherwise look at our basic approach
187   return this->IsOpaque();
188 }
189 
190 //------------------------------------------------------------------------------
Build()191 void vtkDiscretizableColorTransferFunction::Build()
192 {
193   this->Superclass::Build();
194 
195   if (this->LookupTableUpdateTime > this->GetMTime())
196   {
197     // no need to rebuild anything.
198     return;
199   }
200 
201   this->LookupTable->SetVectorMode(this->VectorMode);
202   this->LookupTable->SetVectorComponent(this->VectorComponent);
203   this->LookupTable->SetIndexedLookup(this->IndexedLookup);
204   this->LookupTable->SetUseBelowRangeColor(this->UseBelowRangeColor);
205   this->LookupTable->SetUseAboveRangeColor(this->UseAboveRangeColor);
206 
207   double rgba[4];
208   this->GetBelowRangeColor(rgba);
209   rgba[3] = 1.0;
210   this->LookupTable->SetBelowRangeColor(rgba);
211 
212   this->GetAboveRangeColor(rgba);
213   rgba[3] = 1.0;
214   this->LookupTable->SetAboveRangeColor(rgba);
215 
216   // this is essential since other the LookupTable doesn't update the
217   // annotations map. That's a bug in the implementation of
218   // vtkScalarsToColors::SetAnnotations(..,..);
219   this->LookupTable->SetAnnotations(nullptr, nullptr);
220   this->LookupTable->SetAnnotations(this->AnnotatedValues, this->Annotations);
221 
222   if (this->IndexedLookup)
223   {
224     if (this->GetNumberOfIndexedColors() > 0)
225     {
226       // Use the specified indexed-colors.
227       vtkIdType count = this->GetNumberOfAnnotatedValues();
228       this->LookupTable->SetNumberOfTableValues(count);
229       for (size_t cc = 0;
230            cc < this->Internals->IndexedColors.size() && cc < static_cast<size_t>(count); cc++)
231       {
232         rgba[0] = this->Internals->IndexedColors[cc].GetData()[0];
233         rgba[1] = this->Internals->IndexedColors[cc].GetData()[1];
234         rgba[2] = this->Internals->IndexedColors[cc].GetData()[2];
235         rgba[3] = this->Internals->IndexedColors[cc].GetData()[3];
236         this->LookupTable->SetTableValue(static_cast<int>(cc), rgba);
237       }
238     }
239     else
240     {
241       // old logic for backwards compatibility.
242       int nv = this->GetSize();
243       this->LookupTable->SetNumberOfTableValues(nv);
244       double nodeVal[6];
245       for (int i = 0; i < nv; ++i)
246       {
247         this->GetNodeValue(i, nodeVal);
248         nodeVal[4] = 1.;
249         this->LookupTable->SetTableValue(i, &nodeVal[1]);
250       }
251     }
252   }
253   else if (this->Discretize)
254   {
255     // Do not omit the LookupTable->SetNumberOfTableValues call:
256     // WritePointer does not update the NumberOfColors ivar.
257     this->LookupTable->SetNumberOfTableValues(this->NumberOfValues);
258     unsigned char* lut_ptr = this->LookupTable->WritePointer(0, this->NumberOfValues);
259     double* table = new double[this->NumberOfValues * 3];
260     double range[2];
261     this->GetRange(range);
262     bool logRangeValid = true;
263     if (this->UseLogScale)
264     {
265       logRangeValid = range[0] > 0.0 || range[1] < 0.0;
266       if (!logRangeValid && this->LookupTable->GetScale() == VTK_SCALE_LOG10)
267       {
268         this->LookupTable->SetScaleToLinear();
269       }
270     }
271 
272     this->LookupTable->SetRange(range);
273     if (this->UseLogScale && logRangeValid && this->LookupTable->GetScale() == VTK_SCALE_LINEAR)
274     {
275       this->LookupTable->SetScaleToLog10();
276     }
277 
278     this->GetTable(range[0], range[1], this->NumberOfValues, table);
279     // Now, convert double to unsigned chars and fill the LUT.
280     for (int cc = 0; cc < this->NumberOfValues; cc++)
281     {
282       lut_ptr[4 * cc] = (unsigned char)(255.0 * table[3 * cc] + 0.5);
283       lut_ptr[4 * cc + 1] = (unsigned char)(255.0 * table[3 * cc + 1] + 0.5);
284       lut_ptr[4 * cc + 2] = (unsigned char)(255.0 * table[3 * cc + 2] + 0.5);
285       lut_ptr[4 * cc + 3] = 255;
286     }
287     delete[] table;
288   }
289 
290   this->LookupTable->BuildSpecialColors();
291 
292   this->LookupTableUpdateTime.Modified();
293 }
294 
295 //------------------------------------------------------------------------------
SetAlpha(double alpha)296 void vtkDiscretizableColorTransferFunction::SetAlpha(double alpha)
297 {
298   this->LookupTable->SetAlpha(alpha);
299   this->Superclass::SetAlpha(alpha);
300 }
301 
302 //------------------------------------------------------------------------------
SetNanColor(double r,double g,double b)303 void vtkDiscretizableColorTransferFunction::SetNanColor(double r, double g, double b)
304 {
305   this->LookupTable->SetNanColor(r, g, b, this->GetNanOpacity());
306   this->Superclass::SetNanColor(r, g, b);
307 }
308 
309 //------------------------------------------------------------------------------
SetNanOpacity(double a)310 void vtkDiscretizableColorTransferFunction::SetNanOpacity(double a)
311 {
312   double color[3];
313   this->GetNanColor(color);
314   this->LookupTable->SetNanColor(color[0], color[1], color[2], a);
315   this->Superclass::SetNanOpacity(a);
316 }
317 
318 //------------------------------------------------------------------------------
MapValue(double v)319 const unsigned char* vtkDiscretizableColorTransferFunction::MapValue(double v)
320 {
321   this->Build();
322   if (this->Discretize || this->IndexedLookup)
323   {
324     return this->LookupTable->MapValue(v);
325   }
326 
327   return this->Superclass::MapValue(v);
328 }
329 
330 //------------------------------------------------------------------------------
GetColor(double v,double rgb[3])331 void vtkDiscretizableColorTransferFunction::GetColor(double v, double rgb[3])
332 {
333   this->Build();
334   if (this->Discretize || this->IndexedLookup)
335   {
336     this->LookupTable->GetColor(v, rgb);
337   }
338   else
339   {
340     this->Superclass::GetColor(v, rgb);
341   }
342 }
343 
344 //------------------------------------------------------------------------------
GetOpacity(double v)345 double vtkDiscretizableColorTransferFunction::GetOpacity(double v)
346 {
347   if (this->IndexedLookup || !this->EnableOpacityMapping || !this->ScalarOpacityFunction)
348   {
349     return this->Superclass::GetOpacity(v);
350   }
351   return this->ScalarOpacityFunction->GetValue(v);
352 }
353 
354 //------------------------------------------------------------------------------
355 // Internal mapping of the opacity value through the lookup table
356 template <class T>
vtkDiscretizableColorTransferFunctionMapOpacity(vtkDiscretizableColorTransferFunction * self,T * input,unsigned char * output,int length,int inIncr,int outFormat)357 static void vtkDiscretizableColorTransferFunctionMapOpacity(
358   vtkDiscretizableColorTransferFunction* self, T* input, unsigned char* output, int length,
359   int inIncr, int outFormat)
360 {
361   double x;
362   int i = length;
363   unsigned char* optr = output;
364   T* iptr = input;
365 
366   if (self->GetScalarOpacityFunction()->GetSize() == 0)
367   {
368     vtkGenericWarningMacro("Transfer Function Has No Points!");
369     return;
370   }
371 
372   if (outFormat != VTK_RGBA && outFormat != VTK_LUMINANCE_ALPHA)
373   {
374     return;
375   }
376 
377   // opacity component stride
378   unsigned int stride = (outFormat == VTK_RGBA ? 4 : 2);
379 
380   optr += stride - 1; // Move to first alpha value
381   // Iterate through color components
382   while (--i >= 0)
383   {
384     x = static_cast<double>(*iptr);
385     double alpha = self->GetScalarOpacityFunction()->GetValue(x);
386     *(optr) = static_cast<unsigned char>(alpha * 255.0 + 0.5);
387     optr += stride;
388     iptr += inIncr;
389   }
390 }
391 
392 //------------------------------------------------------------------------------
MapScalarsThroughTable2(void * input,unsigned char * output,int inputDataType,int numberOfValues,int inputIncrement,int outputFormat)393 void vtkDiscretizableColorTransferFunction::MapScalarsThroughTable2(void* input,
394   unsigned char* output, int inputDataType, int numberOfValues, int inputIncrement,
395   int outputFormat)
396 {
397   // Calculate RGB values
398   if (this->Discretize || this->IndexedLookup)
399   {
400     this->LookupTable->MapScalarsThroughTable2(
401       input, output, inputDataType, numberOfValues, inputIncrement, outputFormat);
402   }
403   else
404   {
405     this->Superclass::MapScalarsThroughTable2(
406       input, output, inputDataType, numberOfValues, inputIncrement, outputFormat);
407   }
408 
409   // Calculate alpha values
410   if (this->IndexedLookup == false && // don't change alpha for IndexedLookup.
411     this->EnableOpacityMapping == true && this->ScalarOpacityFunction.GetPointer() != nullptr)
412   {
413     switch (inputDataType)
414     {
415       vtkTemplateMacro(vtkDiscretizableColorTransferFunctionMapOpacity(
416         this, static_cast<VTK_TT*>(input), output, numberOfValues, inputIncrement, outputFormat));
417       default:
418         vtkErrorMacro(<< "MapImageThroughTable: Unknown input ScalarType");
419         return;
420     }
421   }
422 }
423 
424 //------------------------------------------------------------------------------
GetNumberOfAvailableColors()425 vtkIdType vtkDiscretizableColorTransferFunction::GetNumberOfAvailableColors()
426 {
427   if (this->Discretize == false)
428   {
429     return 16777216; // 2^24
430   }
431   return this->NumberOfValues;
432 }
433 
434 //------------------------------------------------------------------------------
GetScalarOpacityFunction() const435 vtkPiecewiseFunction* vtkDiscretizableColorTransferFunction::GetScalarOpacityFunction() const
436 {
437   return this->ScalarOpacityFunction;
438 }
439 
440 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)441 void vtkDiscretizableColorTransferFunction::PrintSelf(ostream& os, vtkIndent indent)
442 {
443   this->Superclass::PrintSelf(os, indent);
444   os << indent << "Discretize: " << this->Discretize << endl;
445   os << indent << "NumberOfValues: " << this->NumberOfValues << endl;
446   os << indent << "UseLogScale: " << this->UseLogScale << endl;
447   os << indent << "EnableOpacityMapping: " << this->EnableOpacityMapping << endl;
448   os << indent << "ScalarOpacityFunction: " << this->ScalarOpacityFunction << endl;
449 }
450