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