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