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