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