1 //============================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 // This software is distributed WITHOUT ANY WARRANTY; without even
6 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
7 // PURPOSE. See the above copyright notice for more information.
8 //
9 // Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
10 // Copyright 2014 UT-Battelle, LLC.
11 // Copyright 2014 Los Alamos National Security.
12 //
13 // Under the terms of Contract DE-NA0003525 with NTESS,
14 // the U.S. Government retains certain rights in this software.
15 //
16 // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
17 // Laboratory (LANL), the U.S. Government retains certain rights in
18 // this software.
19 //============================================================================
20
21 #include <vtkm/cont/testing/Testing.h>
22 #include <vtkm/worklet/ScalarsToColors.h>
23
24 namespace
25 {
26
27 // data we want are valid values between 0 and 1 that represent the fraction
28 // of the range we want to map into.
29 std::vector<float> test_values = { 0.0f, 0.125f, 0.25f, 0.5f, 0.625, 0.75f, 1.0f };
30 std::vector<vtkm::Vec<vtkm::UInt8, 3>> rgb_result = {
31 { 0, 0, 0 }, { 32, 32, 32 }, { 64, 64, 64 }, { 128, 128, 128 },
32 { 159, 159, 159 }, { 191, 191, 191 }, { 255, 255, 255 },
33 };
34
35 template <typename T>
as_color(vtkm::Float32 v,vtkm::Float32)36 T as_color(vtkm::Float32 v, vtkm::Float32)
37 {
38 return static_cast<T>(v);
39 }
40
41 template <>
as_color(vtkm::Float32 v,vtkm::Float32)42 vtkm::UInt8 as_color<vtkm::UInt8>(vtkm::Float32 v, vtkm::Float32)
43 {
44 return static_cast<vtkm::UInt8>(v * 255.0f + 0.5f);
45 }
46
47 template <>
as_color(vtkm::Float32 v,vtkm::Float32 alpha)48 vtkm::Vec<vtkm::Float32, 2> as_color<vtkm::Vec<vtkm::Float32, 2>>(vtkm::Float32 v,
49 vtkm::Float32 alpha)
50 { //generate luminance+alpha values
51 return vtkm::Vec<vtkm::Float32, 2>(v, alpha);
52 }
53 template <>
as_color(vtkm::Float32 v,vtkm::Float32 alpha)54 vtkm::Vec<vtkm::Float64, 2> as_color<vtkm::Vec<vtkm::Float64, 2>>(vtkm::Float32 v,
55 vtkm::Float32 alpha)
56 { //generate luminance+alpha values
57 return vtkm::Vec<vtkm::Float64, 2>(v, alpha);
58 }
59 template <>
as_color(vtkm::Float32 v,vtkm::Float32 alpha)60 vtkm::Vec<vtkm::UInt8, 2> as_color<vtkm::Vec<vtkm::UInt8, 2>>(vtkm::Float32 v, vtkm::Float32 alpha)
61 { //generate luminance+alpha values
62 return vtkm::Vec<vtkm::UInt8, 2>(static_cast<vtkm::UInt8>(v * 255.0f + 0.5f),
63 static_cast<vtkm::UInt8>(alpha * 255.0f + 0.5f));
64 }
65
66 template <>
as_color(vtkm::Float32 v,vtkm::Float32)67 vtkm::Vec<vtkm::UInt8, 3> as_color<vtkm::Vec<vtkm::UInt8, 3>>(vtkm::Float32 v, vtkm::Float32)
68 { //vec 3 are always rgb
69 return vtkm::Vec<vtkm::UInt8, 3>(static_cast<vtkm::UInt8>(v * 255.0f + 0.5f));
70 }
71
72 template <>
as_color(vtkm::Float32 v,vtkm::Float32 alpha)73 vtkm::Vec<vtkm::Float32, 4> as_color<vtkm::Vec<vtkm::Float32, 4>>(vtkm::Float32 v,
74 vtkm::Float32 alpha)
75 { //generate rgba
76 return vtkm::Vec<vtkm::Float32, 4>(v, v, v, alpha);
77 }
78 template <>
as_color(vtkm::Float32 v,vtkm::Float32 alpha)79 vtkm::Vec<vtkm::Float64, 4> as_color<vtkm::Vec<vtkm::Float64, 4>>(vtkm::Float32 v,
80 vtkm::Float32 alpha)
81 { //generate rgba
82 return vtkm::Vec<vtkm::Float64, 4>(v, v, v, alpha);
83 }
84 template <>
as_color(vtkm::Float32 v,vtkm::Float32 alpha)85 vtkm::Vec<vtkm::UInt8, 4> as_color<vtkm::Vec<vtkm::UInt8, 4>>(vtkm::Float32 v, vtkm::Float32 alpha)
86 { //generate rgba
87 return vtkm::Vec<vtkm::UInt8, 4>(static_cast<vtkm::UInt8>(v * 255.0f + 0.5f),
88 static_cast<vtkm::UInt8>(v * 255.0f + 0.5f),
89 static_cast<vtkm::UInt8>(v * 255.0f + 0.5f),
90 static_cast<vtkm::UInt8>(alpha * 255.0f + 0.5f));
91 }
92
93
94 template <typename T>
make_data(const vtkm::Range & r)95 vtkm::cont::ArrayHandle<T> make_data(const vtkm::Range& r)
96 {
97 using BaseT = typename vtkm::BaseComponent<T>::Type;
98 vtkm::Float32 shift, scale;
99 vtkm::worklet::colorconversion::ComputeShiftScale(r, shift, scale);
100 const bool shiftscale = vtkm::worklet::colorconversion::needShiftScale(BaseT{}, shift, scale);
101
102
103
104 vtkm::cont::ArrayHandle<T> handle;
105 handle.Allocate(static_cast<vtkm::Id>(test_values.size()));
106
107 auto portal = handle.GetPortalControl();
108 vtkm::Id index = 0;
109 if (shiftscale)
110 {
111 const vtkm::Float32 alpha = static_cast<vtkm::Float32>(r.Max);
112 for (const auto& i : test_values)
113 {
114 //we want to apply the shift and scale, and than clamp to the allowed
115 //range of the data. The problem is that we might need to shift and scale the alpha value
116 //for the color
117 //
118 const float c = (i * static_cast<float>(r.Length())) - shift;
119 portal.Set(index++, as_color<T>(c, alpha));
120 }
121 }
122 else
123 {
124 const vtkm::Float32 alpha = 1.0f;
125 //no shift or scale required
126 for (const auto& i : test_values)
127 {
128 portal.Set(index++, as_color<T>(i, alpha));
129 }
130 }
131 return handle;
132 }
133
verify(vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8,3>> output)134 bool verify(vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>> output)
135 {
136 auto portal = output.GetPortalConstControl();
137 vtkm::Id index = 0;
138 for (auto i : rgb_result)
139 {
140 auto v = portal.Get(index);
141 if (v != i)
142 {
143 std::cerr << "failed comparison at index: " << index
144 << " found: " << static_cast<vtkm::Vec<int, 3>>(v)
145 << " was expecting: " << static_cast<vtkm::Vec<int, 3>>(i) << std::endl;
146 break;
147 }
148 index++;
149 }
150 bool valid = static_cast<std::size_t>(index) == rgb_result.size();
151 return valid;
152 }
153
verify(vtkm::Float32 alpha,vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8,4>> output)154 bool verify(vtkm::Float32 alpha, vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>> output)
155 {
156 const vtkm::UInt8 a = vtkm::worklet::colorconversion::ColorToUChar(alpha);
157 auto portal = output.GetPortalConstControl();
158 vtkm::Id index = 0;
159 for (auto i : rgb_result)
160 {
161 auto v = portal.Get(index);
162 auto e = vtkm::make_Vec(i[0], i[1], i[2], a);
163 if (v != e)
164 {
165 std::cerr << "failed comparison at index: " << index
166 << " found: " << static_cast<vtkm::Vec<int, 4>>(v)
167 << " was expecting: " << static_cast<vtkm::Vec<int, 4>>(e) << std::endl;
168 break;
169 }
170 index++;
171 }
172 bool valid = static_cast<std::size_t>(index) == rgb_result.size();
173 return valid;
174 }
175
176 #if defined(VTKM_MSVC)
177 #pragma warning(push)
178 #pragma warning(disable : 4127) // conditional expression is constant
179 #endif
180
181 struct TestToRGB
182 {
183 vtkm::worklet::ScalarsToColors Worklet;
184
TestToRGB__anon9d371f030111::TestToRGB185 TestToRGB()
186 : Worklet()
187 {
188 }
189
TestToRGB__anon9d371f030111::TestToRGB190 TestToRGB(vtkm::Float32 minR, vtkm::Float32 maxR)
191 : Worklet(vtkm::Range(minR, maxR))
192 {
193 }
194
195 using DeviceAdapter = VTKM_DEFAULT_DEVICE_ADAPTER_TAG;
196 template <typename T>
operator ()__anon9d371f030111::TestToRGB197 VTKM_CONT void operator()(T) const
198 {
199 //use each component to generate the output
200 vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>> output;
201 this->Worklet.Run(make_data<T>(this->Worklet.GetRange()), output, DeviceAdapter());
202 bool valid = verify(output);
203 VTKM_TEST_ASSERT(valid, "scalar RGB failed");
204 }
205
206 template <typename U, int N>
operator ()__anon9d371f030111::TestToRGB207 VTKM_CONT void operator()(vtkm::Vec<U, N>) const
208 {
209 bool valid = false;
210 using T = vtkm::Vec<U, N>;
211
212 auto input = make_data<T>(this->Worklet.GetRange());
213 vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>> output;
214
215 //use all components to generate the output
216 this->Worklet.Run(input, output, DeviceAdapter());
217 valid = verify(output);
218 VTKM_TEST_ASSERT(valid, "all components RGB failed");
219
220 //use the magnitude of the vector if vector is 3 components
221 if (N == 3)
222 {
223 //compute the range needed for the magnitude, since the range can span
224 //negative/positive space we need to find the magnitude of each value
225 //and them to the range to get the correct range
226 vtkm::worklet::colorconversion::MagnitudePortal wrapper;
227 vtkm::Range magr;
228 auto portal = input.GetPortalControl();
229 for (vtkm::Id i = 0; i < input.GetNumberOfValues(); ++i)
230 {
231 const auto magv = wrapper(portal.Get(i));
232 magr.Include(static_cast<double>(magv));
233 }
234
235 vtkm::worklet::ScalarsToColors magWorklet(magr);
236 magWorklet.RunMagnitude(input, output, DeviceAdapter());
237 // vtkm::cont::printSummary_ArrayHandle(output, std::cout, true);
238
239 auto portal2 = output.GetPortalControl();
240 for (vtkm::Id i = 0; i < input.GetNumberOfValues(); ++i)
241 {
242 const auto expected = wrapper(portal.Get(i));
243 const auto percent = (portal2.Get(i)[0] / 255.0f);
244 const auto result = (percent * magr.Length()) + magr.Min;
245 if (!test_equal(expected, result, 0.005))
246 {
247 std::cerr << "failed comparison at index: " << i << " found: " << result
248 << " was expecting: " << expected << std::endl;
249 VTKM_TEST_ASSERT(test_equal(expected, result), "magnitude RGB failed");
250 }
251 }
252 }
253
254 //use the components of the vector, if the vector is 2 or 4 we need
255 //to ignore the last component as it is alpha
256 int end = (N % 2 == 0) ? (N - 1) : N;
257 for (int i = 0; i < end; ++i)
258 {
259 this->Worklet.RunComponent(input, i, output, DeviceAdapter());
260 valid = verify(output);
261 VTKM_TEST_ASSERT(valid, "per component RGB failed");
262 }
263 }
264 };
265
266 struct TestToRGBA
267 {
268 vtkm::worklet::ScalarsToColors Worklet;
269
TestToRGBA__anon9d371f030111::TestToRGBA270 TestToRGBA()
271 : Worklet()
272 {
273 }
274
TestToRGBA__anon9d371f030111::TestToRGBA275 TestToRGBA(vtkm::Float32 minR, vtkm::Float32 maxR, vtkm::Float32 alpha)
276 : Worklet(vtkm::Range(minR, maxR), alpha)
277 {
278 }
279
280 using DeviceAdapter = VTKM_DEFAULT_DEVICE_ADAPTER_TAG;
281 template <typename T>
operator ()__anon9d371f030111::TestToRGBA282 VTKM_CONT void operator()(T) const
283 {
284 //use each component to generate the output
285 vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>> output;
286 this->Worklet.Run(make_data<T>(this->Worklet.GetRange()), output, DeviceAdapter());
287
288 bool valid = verify(this->Worklet.GetAlpha(), output);
289 VTKM_TEST_ASSERT(valid, "scalar RGBA failed");
290 }
291
292 template <typename U, int N>
operator ()__anon9d371f030111::TestToRGBA293 VTKM_CONT void operator()(vtkm::Vec<U, N>) const
294 {
295 bool valid = false;
296 using T = vtkm::Vec<U, N>;
297 vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>> output;
298
299 auto input = make_data<T>(this->Worklet.GetRange());
300 // vtkm::cont::printSummary_ArrayHandle(input, std::cout, true);
301
302 //use all components to generate the output
303 this->Worklet.Run(input, output, DeviceAdapter());
304 valid = verify(this->Worklet.GetAlpha(), output);
305 VTKM_TEST_ASSERT(valid, "all components RGBA failed");
306
307 //use the magnitude of the vector if vector is 3 components
308 if (N == 3)
309 {
310 //compute the range needed for the magnitude, since the range can span
311 //negative/positive space we need to find the magnitude of each value
312 //and them to the range to get the correct range
313 vtkm::worklet::colorconversion::MagnitudePortal wrapper;
314 vtkm::Range magr;
315 auto portal = input.GetPortalControl();
316 for (vtkm::Id i = 0; i < input.GetNumberOfValues(); ++i)
317 {
318 const auto magv = wrapper(portal.Get(i));
319 magr.Include(static_cast<double>(magv));
320 }
321
322 vtkm::worklet::ScalarsToColors magWorklet(magr);
323 magWorklet.RunMagnitude(input, output, DeviceAdapter());
324 // vtkm::cont::printSummary_ArrayHandle(output, std::cout, true);
325
326 auto portal2 = output.GetPortalControl();
327 for (vtkm::Id i = 0; i < input.GetNumberOfValues(); ++i)
328 {
329 const auto expected = wrapper(portal.Get(i));
330 const auto percent = (portal2.Get(i)[0] / 255.0f);
331 const auto result = (percent * magr.Length()) + magr.Min;
332 if (!test_equal(expected, result, 0.005))
333 {
334 std::cerr << "failed comparison at index: " << i << " found: " << result
335 << " was expecting: " << expected << std::endl;
336 VTKM_TEST_ASSERT(test_equal(expected, result), "magnitude RGB failed");
337 }
338 }
339 }
340
341 //use the components of the vector, if the vector is 2 or 4 we need
342 //to ignore the last component as it is alpha
343 int end = (N % 2 == 0) ? (N - 1) : N;
344 for (int i = 0; i < end; ++i)
345 {
346 this->Worklet.RunComponent(input, i, output, DeviceAdapter());
347 valid = verify(this->Worklet.GetAlpha(), output);
348 VTKM_TEST_ASSERT(valid, "per component RGB failed");
349 }
350 }
351 };
352
353 #if defined(VTKM_MSVC)
354 #pragma warning(pop)
355 #endif
356
357
358 struct TypeListTagScalarColorTypes : vtkm::ListTagBase<vtkm::Float32,
359 vtkm::Float64,
360 vtkm::Vec<vtkm::Float32, 2>,
361 vtkm::Vec<vtkm::Float64, 2>,
362 vtkm::Vec<vtkm::Float32, 3>,
363 vtkm::Vec<vtkm::Float64, 3>,
364 vtkm::Vec<vtkm::Float32, 4>,
365 vtkm::Vec<vtkm::Float64, 4>>
366 {
367 };
368
369 struct TypeListTagUIntColorTypes : vtkm::ListTagBase<vtkm::UInt8,
370 vtkm::Vec<vtkm::UInt8, 2>,
371 vtkm::Vec<vtkm::UInt8, 3>,
372 vtkm::Vec<vtkm::UInt8, 4>>
373 {
374 };
375
376
TestScalarsToColors()377 void TestScalarsToColors()
378 {
379 std::cout << "Test ConvertToRGB with UInt8 types" << std::endl;
380 vtkm::testing::Testing::TryTypes(TestToRGB(), TypeListTagUIntColorTypes());
381 std::cout << "Test ConvertToRGB with Scalar types" << std::endl;
382 vtkm::testing::Testing::TryTypes(TestToRGB(0.0f, 1.0f), TypeListTagScalarColorTypes());
383 std::cout << "Test ShiftScaleToRGB with scalar types and varying range" << std::endl;
384 vtkm::testing::Testing::TryTypes(TestToRGB(1024.0f, 4096.0f), TypeListTagScalarColorTypes());
385 vtkm::testing::Testing::TryTypes(TestToRGB(-2048.0f, 1024.0f), TypeListTagScalarColorTypes());
386
387 std::cout << "Test ConvertToRGBA with UInt8 types and alpha values=[1.0, 0.5, 0.0]" << std::endl;
388 vtkm::testing::Testing::TryTypes(TestToRGBA(), TypeListTagUIntColorTypes());
389 vtkm::testing::Testing::TryTypes(TestToRGBA(0.0f, 255.0f, 0.5f), TypeListTagUIntColorTypes());
390 vtkm::testing::Testing::TryTypes(TestToRGBA(0.0f, 255.0f, 0.0f), TypeListTagUIntColorTypes());
391
392 std::cout << "Test ConvertToRGBA with Scalar types and alpha values=[0.3, 0.6, 1.0]" << std::endl;
393 vtkm::testing::Testing::TryTypes(TestToRGBA(0.0f, 1.0f, 0.3f), TypeListTagScalarColorTypes());
394 vtkm::testing::Testing::TryTypes(TestToRGBA(0.0f, 1.0f, 0.6f), TypeListTagScalarColorTypes());
395 vtkm::testing::Testing::TryTypes(TestToRGBA(0.0f, 1.0f, 1.0f), TypeListTagScalarColorTypes());
396
397 std::cout
398 << "Test ConvertToRGBA with Scalar types and varying range with alpha values=[0.25, 0.5, 0.75]"
399 << std::endl;
400 vtkm::testing::Testing::TryTypes(TestToRGBA(-0.075f, -0.025f, 0.25f),
401 TypeListTagScalarColorTypes());
402 vtkm::testing::Testing::TryTypes(TestToRGBA(0.0f, 2048.0f, 0.5f), TypeListTagScalarColorTypes());
403 vtkm::testing::Testing::TryTypes(TestToRGBA(-2048.0f, 2048.0f, 0.75f),
404 TypeListTagScalarColorTypes());
405 }
406 }
407
UnitTestScalarsToColors(int,char * [])408 int UnitTestScalarsToColors(int, char* [])
409 {
410 return vtkm::cont::testing::Testing::Run(TestScalarsToColors);
411 }
412