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 #ifndef vtk_m_rendering_Texture2D_h
12 #define vtk_m_rendering_Texture2D_h
13 
14 #include <vtkm/cont/Algorithm.h>
15 #include <vtkm/cont/ArrayHandle.h>
16 #include <vtkm/cont/ExecutionObjectBase.h>
17 
18 namespace vtkm
19 {
20 namespace rendering
21 {
22 
23 enum class TextureFilterMode
24 {
25   NearestNeighbour,
26   Linear,
27 }; // enum TextureFilterMode
28 
29 enum class TextureWrapMode
30 {
31   Clamp,
32   Repeat,
33 }; // enum TextureWrapMode
34 
35 template <vtkm::IdComponent NumComponents>
36 class Texture2D
37 {
38 public:
39   using TextureDataHandle = typename vtkm::cont::ArrayHandle<vtkm::UInt8>;
40   using ColorType = vtkm::Vec<vtkm::Float32, NumComponents>;
41 
42   class Texture2DSampler;
43 
44 #define UV_BOUNDS_CHECK(u, v, NoneType)             \
45   if (u < 0.0f || u > 1.0f || v < 0.0f || v > 1.0f) \
46   {                                                 \
47     return NoneType();                              \
48   }
49 
50   VTKM_CONT
Texture2D()51   Texture2D()
52     : Width(0)
53     , Height(0)
54   {
55   }
56 
57   VTKM_CONT
Texture2D(vtkm::Id width,vtkm::Id height,const TextureDataHandle & data)58   Texture2D(vtkm::Id width, vtkm::Id height, const TextureDataHandle& data)
59     : Width(width)
60     , Height(height)
61     , FilterMode(TextureFilterMode::Linear)
62     , WrapMode(TextureWrapMode::Clamp)
63   {
64     VTKM_ASSERT(data.GetNumberOfValues() == (Width * Height * NumComponents));
65     // We do not know the lifetime of the underlying data source of input `data`. Since it might
66     // be from a shallow copy of the data source, we make a deep copy of the input data and keep
67     // it's portal. The copy operation is very fast.
68     vtkm::cont::Algorithm::Copy(data, Data);
69   }
70 
71   VTKM_CONT
IsValid()72   bool IsValid() const { return Width > 0 && Height > 0; }
73 
74   VTKM_CONT
GetFilterMode()75   TextureFilterMode GetFilterMode() const { return this->FilterMode; }
76 
77   VTKM_CONT
SetFilterMode(TextureFilterMode filterMode)78   void SetFilterMode(TextureFilterMode filterMode) { this->FilterMode = filterMode; }
79 
80   VTKM_CONT
GetWrapMode()81   TextureWrapMode GetWrapMode() const { return this->WrapMode; }
82 
83   VTKM_CONT
SetWrapMode(TextureWrapMode wrapMode)84   void SetWrapMode(TextureWrapMode wrapMode) { this->WrapMode = wrapMode; }
85 
GetExecObjectFactory()86   VTKM_CONT Texture2DSampler GetExecObjectFactory() const
87   {
88     return Texture2DSampler(Width, Height, Data, FilterMode, WrapMode);
89   }
90 
91   template <typename Device>
92   class Texture2DSamplerExecutionObject
93   {
94   public:
95     using TextureExecPortal = typename TextureDataHandle::ReadPortalType;
96 
97     VTKM_CONT
Texture2DSamplerExecutionObject()98     Texture2DSamplerExecutionObject()
99       : Width(0)
100       , Height(0)
101     {
102     }
103 
104     VTKM_CONT
Texture2DSamplerExecutionObject(vtkm::Id width,vtkm::Id height,const TextureDataHandle & data,TextureFilterMode filterMode,TextureWrapMode wrapMode,vtkm::cont::Token & token)105     Texture2DSamplerExecutionObject(vtkm::Id width,
106                                     vtkm::Id height,
107                                     const TextureDataHandle& data,
108                                     TextureFilterMode filterMode,
109                                     TextureWrapMode wrapMode,
110                                     vtkm::cont::Token& token)
111       : Width(width)
112       , Height(height)
113       , Data(data.PrepareForInput(Device(), token))
114       , FilterMode(filterMode)
115       , WrapMode(wrapMode)
116     {
117     }
118 
119     VTKM_EXEC
GetColor(vtkm::Float32 u,vtkm::Float32 v)120     inline ColorType GetColor(vtkm::Float32 u, vtkm::Float32 v) const
121     {
122       v = 1.0f - v;
123       UV_BOUNDS_CHECK(u, v, ColorType);
124       switch (FilterMode)
125       {
126         case TextureFilterMode::NearestNeighbour:
127           return GetNearestNeighbourFilteredColor(u, v);
128 
129         case TextureFilterMode::Linear:
130           return GetLinearFilteredColor(u, v);
131 
132         default:
133           return ColorType();
134       }
135     }
136 
137   private:
138     VTKM_EXEC
GetNearestNeighbourFilteredColor(vtkm::Float32 u,vtkm::Float32 v)139     inline ColorType GetNearestNeighbourFilteredColor(vtkm::Float32 u, vtkm::Float32 v) const
140     {
141       vtkm::Id x = static_cast<vtkm::Id>(vtkm::Round(u * static_cast<vtkm::Float32>(Width - 1)));
142       vtkm::Id y = static_cast<vtkm::Id>(vtkm::Round(v * static_cast<vtkm::Float32>(Height - 1)));
143       return GetColorAtCoords(x, y);
144     }
145 
146     VTKM_EXEC
GetLinearFilteredColor(vtkm::Float32 u,vtkm::Float32 v)147     inline ColorType GetLinearFilteredColor(vtkm::Float32 u, vtkm::Float32 v) const
148     {
149       u = u * static_cast<vtkm::Float32>(Width) - 0.5f;
150       v = v * static_cast<vtkm::Float32>(Height) - 0.5f;
151       vtkm::Id x = static_cast<vtkm::Id>(vtkm::Floor(u));
152       vtkm::Id y = static_cast<vtkm::Id>(vtkm::Floor(v));
153       vtkm::Float32 uRatio = u - static_cast<vtkm::Float32>(x);
154       vtkm::Float32 vRatio = v - static_cast<vtkm::Float32>(y);
155       vtkm::Float32 uOpposite = 1.0f - uRatio;
156       vtkm::Float32 vOpposite = 1.0f - vRatio;
157       vtkm::Id xn, yn;
158       GetNextCoords(x, y, xn, yn);
159       ColorType c1 = GetColorAtCoords(x, y);
160       ColorType c2 = GetColorAtCoords(xn, y);
161       ColorType c3 = GetColorAtCoords(x, yn);
162       ColorType c4 = GetColorAtCoords(xn, yn);
163       return (c1 * uOpposite + c2 * uRatio) * vOpposite + (c3 * uOpposite + c4 * uRatio) * vRatio;
164     }
165 
166     VTKM_EXEC
GetColorAtCoords(vtkm::Id x,vtkm::Id y)167     inline ColorType GetColorAtCoords(vtkm::Id x, vtkm::Id y) const
168     {
169       vtkm::Id idx = (y * Width + x) * NumComponents;
170       ColorType color;
171       for (vtkm::IdComponent i = 0; i < NumComponents; ++i)
172       {
173         color[i] = Data.Get(idx + i) / 255.0f;
174       }
175       return color;
176     }
177 
178     VTKM_EXEC
GetNextCoords(vtkm::Id x,vtkm::Id y,vtkm::Id & xn,vtkm::Id & yn)179     inline void GetNextCoords(vtkm::Id x, vtkm::Id y, vtkm::Id& xn, vtkm::Id& yn) const
180     {
181       switch (WrapMode)
182       {
183         case TextureWrapMode::Clamp:
184           xn = (x + 1) < Width ? (x + 1) : x;
185           yn = (y + 1) < Height ? (y + 1) : y;
186           break;
187         case TextureWrapMode::Repeat:
188         default:
189           xn = (x + 1) % Width;
190           yn = (y + 1) % Height;
191           break;
192       }
193     }
194 
195     vtkm::Id Width;
196     vtkm::Id Height;
197     TextureExecPortal Data;
198     TextureFilterMode FilterMode;
199     TextureWrapMode WrapMode;
200   };
201 
202   class Texture2DSampler : public vtkm::cont::ExecutionObjectBase
203   {
204   public:
205     VTKM_CONT
Texture2DSampler()206     Texture2DSampler()
207       : Width(0)
208       , Height(0)
209     {
210     }
211 
212     VTKM_CONT
Texture2DSampler(vtkm::Id width,vtkm::Id height,const TextureDataHandle & data,TextureFilterMode filterMode,TextureWrapMode wrapMode)213     Texture2DSampler(vtkm::Id width,
214                      vtkm::Id height,
215                      const TextureDataHandle& data,
216                      TextureFilterMode filterMode,
217                      TextureWrapMode wrapMode)
218       : Width(width)
219       , Height(height)
220       , Data(data)
221       , FilterMode(filterMode)
222       , WrapMode(wrapMode)
223     {
224     }
225 
226     template <typename Device>
PrepareForExecution(Device,vtkm::cont::Token & token)227     VTKM_CONT Texture2DSamplerExecutionObject<Device> PrepareForExecution(
228       Device,
229       vtkm::cont::Token& token) const
230     {
231       return Texture2DSamplerExecutionObject<Device>(
232         this->Width, this->Height, this->Data, this->FilterMode, this->WrapMode, token);
233     }
234 
235   private:
236     vtkm::Id Width;
237     vtkm::Id Height;
238     TextureDataHandle Data;
239     TextureFilterMode FilterMode;
240     TextureWrapMode WrapMode;
241   }; // class Texture2DSampler
242 
243 private:
244   vtkm::Id Width;
245   vtkm::Id Height;
246   TextureDataHandle Data;
247   TextureFilterMode FilterMode;
248   TextureWrapMode WrapMode;
249 }; // class Texture2D
250 }
251 } // namespace vtkm::rendering
252 
253 #endif // vtk_m_rendering_Texture2D_h
254