1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Image/BsPixelData.h"
4#include "Image/BsPixelUtil.h"
5#include "Private/RTTI/BsPixelDataRTTI.h"
6#include "Image/BsColor.h"
7#include "Math/BsVector2.h"
8#include "Math/BsMath.h"
9#include "Debug/BsDebug.h"
10
11namespace bs
12{
13 PixelData::PixelData(const PixelVolume& extents, PixelFormat pixelFormat)
14 :mExtents(extents), mFormat(pixelFormat)
15 {
16 PixelUtil::getPitch(extents.getWidth(), extents.getHeight(), extents.getDepth(), pixelFormat, mRowPitch,
17 mSlicePitch);
18 }
19
20 PixelData::PixelData(UINT32 width, UINT32 height, UINT32 depth, PixelFormat pixelFormat)
21 : mExtents(0, 0, 0, width, height, depth), mFormat(pixelFormat)
22 {
23 PixelUtil::getPitch(width, height, depth, pixelFormat, mRowPitch, mSlicePitch);
24 }
25
26 PixelData::PixelData(const PixelData& copy)
27 :GpuResourceData(copy)
28 {
29 mFormat = copy.mFormat;
30 mRowPitch = copy.mRowPitch;
31 mSlicePitch = copy.mSlicePitch;
32 mExtents = copy.mExtents;
33 }
34
35 PixelData& PixelData::operator=(const PixelData& rhs)
36 {
37 GpuResourceData::operator= (rhs);
38
39 mFormat = rhs.mFormat;
40 mRowPitch = rhs.mRowPitch;
41 mSlicePitch = rhs.mSlicePitch;
42 mExtents = rhs.mExtents;
43
44 return *this;
45 }
46
47 UINT32 PixelData::getConsecutiveSize() const
48 {
49 return PixelUtil::getMemorySize(getWidth(), getHeight(), getDepth(), mFormat);
50 }
51
52 UINT32 PixelData::getSize() const
53 {
54 if(mRowPitch == 0)
55 return 0;
56
57 return PixelUtil::getMemorySize(mRowPitch, mSlicePitch / mRowPitch, getDepth(), getFormat());
58 }
59
60 PixelData PixelData::getSubVolume(const PixelVolume& volume) const
61 {
62 if (PixelUtil::isCompressed(mFormat))
63 {
64 if (volume.left == getLeft() && volume.top == getTop() && volume.front == getFront() &&
65 volume.right == getRight() && volume.bottom == getBottom() && volume.back == getBack())
66 {
67 // Entire buffer is being queried
68 return *this;
69 }
70
71 BS_EXCEPT(InvalidParametersException, "Cannot return subvolume of compressed PixelBuffer");
72 }
73
74 if (!mExtents.contains(volume))
75 {
76 BS_EXCEPT(InvalidParametersException, "Bounds out of range");
77 }
78
79 const size_t elemSize = PixelUtil::getNumElemBytes(mFormat);
80 PixelData rval(volume.getWidth(), volume.getHeight(), volume.getDepth(), mFormat);
81
82 rval.setExternalBuffer(((UINT8*)getData()) + ((volume.left - getLeft())*elemSize)
83 + ((volume.top - getTop())*mRowPitch*elemSize)
84 + ((volume.front - getFront())*mSlicePitch*elemSize));
85
86 rval.mFormat = mFormat;
87 PixelUtil::getPitch(volume.getWidth(), volume.getHeight(), volume.getDepth(), mFormat, rval.mRowPitch,
88 rval.mSlicePitch);
89
90 return rval;
91 }
92
93 Color PixelData::sampleColorAt(const Vector2& coords, TextureFilter filter) const
94 {
95 Vector2 pixelCoords = coords * Vector2((float)mExtents.getWidth(), (float)mExtents.getHeight());
96
97 INT32 maxExtentX = std::max(0, (INT32)mExtents.getWidth() - 1);
98 INT32 maxExtentY = std::max(0, (INT32)mExtents.getHeight() - 1);
99
100 if(filter == TF_BILINEAR)
101 {
102 pixelCoords -= Vector2(0.5f, 0.5f);
103
104 UINT32 x = (UINT32)Math::clamp(Math::floorToInt(pixelCoords.x), 0, maxExtentX);
105 UINT32 y = (UINT32)Math::clamp(Math::floorToInt(pixelCoords.y), 0, maxExtentY);
106
107 float fracX = pixelCoords.x - x;
108 float fracY = pixelCoords.y - y;
109
110 x = Math::clamp(x, 0U, (UINT32)maxExtentX);
111 y = Math::clamp(y, 0U, (UINT32)maxExtentY);
112
113 INT32 x1 = Math::clamp(x + 1, 0U, (UINT32)maxExtentX);
114 INT32 y1 = Math::clamp(y + 1, 0U, (UINT32)maxExtentY);
115
116 Color color = Color::ZERO;
117 color += (1.0f - fracX) * (1.0f - fracY) * getColorAt(x, y);
118 color += fracX * (1.0f - fracY) * getColorAt(x1, y);
119 color += (1.0f - fracX) * fracY * getColorAt(x, y1);
120 color += fracX * fracY * getColorAt(x1, y1);
121
122 return color;
123 }
124 else
125 {
126 UINT32 x = (UINT32)Math::clamp(Math::floorToInt(pixelCoords.x), 0, maxExtentX);
127 UINT32 y = (UINT32)Math::clamp(Math::floorToInt(pixelCoords.y), 0, maxExtentY);
128
129 return getColorAt(x, y);
130 }
131 }
132
133 Color PixelData::getColorAt(UINT32 x, UINT32 y, UINT32 z) const
134 {
135 Color cv;
136
137 UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
138 UINT32 pixelOffset = pixelSize * (z * mSlicePitch + y * mRowPitch + x);
139 PixelUtil::unpackColor(&cv, mFormat, (unsigned char *)getData() + pixelOffset);
140
141 return cv;
142 }
143
144 void PixelData::setColorAt(const Color& color, UINT32 x, UINT32 y, UINT32 z)
145 {
146 UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
147 UINT32 pixelOffset = pixelSize * (z * mSlicePitch + y * mRowPitch + x);
148 PixelUtil::packColor(color, mFormat, (unsigned char *)getData() + pixelOffset);
149 }
150
151 Vector<Color> PixelData::getColors() const
152 {
153 UINT32 depth = mExtents.getDepth();
154 UINT32 height = mExtents.getHeight();
155 UINT32 width = mExtents.getWidth();
156
157 UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
158 UINT8* data = getData();
159
160 Vector<Color> colors(width * height * depth);
161 for (UINT32 z = 0; z < depth; z++)
162 {
163 UINT32 zArrayIdx = z * width * height;
164 UINT32 zDataIdx = z * mSlicePitch * pixelSize;
165
166 for (UINT32 y = 0; y < height; y++)
167 {
168 UINT32 yArrayIdx = y * width;
169 UINT32 yDataIdx = y * mRowPitch * pixelSize;
170
171 for (UINT32 x = 0; x < width; x++)
172 {
173 UINT32 arrayIdx = x + yArrayIdx + zArrayIdx;
174 UINT32 dataIdx = x * pixelSize + yDataIdx + zDataIdx;
175
176 UINT8* dest = data + dataIdx;
177 PixelUtil::unpackColor(&colors[arrayIdx], mFormat, dest);
178 }
179 }
180 }
181
182 return colors;
183 }
184
185 template<class T>
186 void PixelData::setColorsInternal(const T& colors, UINT32 numElements)
187 {
188 UINT32 depth = mExtents.getDepth();
189 UINT32 height = mExtents.getHeight();
190 UINT32 width = mExtents.getWidth();
191
192 UINT32 totalNumElements = width * height * depth;
193 if (numElements != totalNumElements)
194 {
195 LOGERR("Unable to set colors, invalid array size.");
196 return;
197 }
198
199 UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
200 UINT8* data = getData();
201
202 for (UINT32 z = 0; z < depth; z++)
203 {
204 UINT32 zArrayIdx = z * width * height;
205 UINT32 zDataIdx = z * mSlicePitch * pixelSize;
206
207 for (UINT32 y = 0; y < height; y++)
208 {
209 UINT32 yArrayIdx = y * width;
210 UINT32 yDataIdx = y * mRowPitch * pixelSize;
211
212 for (UINT32 x = 0; x < width; x++)
213 {
214 UINT32 arrayIdx = x + yArrayIdx + zArrayIdx;
215 UINT32 dataIdx = x * pixelSize + yDataIdx + zDataIdx;
216
217 UINT8* dest = data + dataIdx;
218 PixelUtil::packColor(colors[arrayIdx], mFormat, dest);
219 }
220 }
221 }
222 }
223
224 template BS_CORE_EXPORT void PixelData::setColorsInternal(Color* const &, UINT32);
225 template BS_CORE_EXPORT void PixelData::setColorsInternal(const Vector<Color>&, UINT32);
226
227 void PixelData::setColors(const Vector<Color>& colors)
228 {
229 setColorsInternal(colors, (UINT32)colors.size());
230 }
231
232 void PixelData::setColors(Color* colors, UINT32 numElements)
233 {
234 setColorsInternal(colors, numElements);
235 }
236
237 void PixelData::setColors(const Color& color)
238 {
239 UINT32 depth = mExtents.getDepth();
240 UINT32 height = mExtents.getHeight();
241 UINT32 width = mExtents.getWidth();
242
243 UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
244 UINT32 packedColor[4];
245 assert(pixelSize <= sizeof(packedColor));
246
247 PixelUtil::packColor(color, mFormat, packedColor);
248
249 UINT8* data = getData();
250 for (UINT32 z = 0; z < depth; z++)
251 {
252 UINT32 zDataIdx = z * mSlicePitch * pixelSize;
253
254 for (UINT32 y = 0; y < height; y++)
255 {
256 UINT32 yDataIdx = y * mRowPitch * pixelSize;
257
258 for (UINT32 x = 0; x < width; x++)
259 {
260 UINT32 dataIdx = x * pixelSize + yDataIdx + zDataIdx;
261
262 UINT8* dest = data + dataIdx;
263 memcpy(dest, packedColor, pixelSize);
264 }
265 }
266 }
267 }
268
269 float PixelData::getDepthAt(UINT32 x, UINT32 y, UINT32 z) const
270 {
271 UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
272 UINT32 pixelOffset = pixelSize * (z * mSlicePitch + y * mRowPitch + x);
273 return PixelUtil::unpackDepth(mFormat, (unsigned char *)getData() + pixelOffset);;
274 }
275
276 Vector<float> PixelData::getDepths() const
277 {
278 UINT32 depth = mExtents.getDepth();
279 UINT32 height = mExtents.getHeight();
280 UINT32 width = mExtents.getWidth();
281
282 UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
283 UINT8* data = getData();
284
285 Vector<float> depths(width * height * depth);
286 for (UINT32 z = 0; z < depth; z++)
287 {
288 UINT32 zArrayIdx = z * width * height;
289 UINT32 zDataIdx = z * mSlicePitch * pixelSize;
290
291 for (UINT32 y = 0; y < height; y++)
292 {
293 UINT32 yArrayIdx = y * width;
294 UINT32 yDataIdx = y * mRowPitch * pixelSize;
295
296 for (UINT32 x = 0; x < width; x++)
297 {
298 UINT32 arrayIdx = x + yArrayIdx + zArrayIdx;
299 UINT32 dataIdx = x * pixelSize + yDataIdx + zDataIdx;
300
301 UINT8* dest = data + dataIdx;
302 depths[arrayIdx] = PixelUtil::unpackDepth(mFormat, dest);
303 }
304 }
305 }
306
307 return depths;
308 }
309
310 SPtr<PixelData> PixelData::create(const PixelVolume &extents, PixelFormat pixelFormat)
311 {
312 SPtr<PixelData> pixelData = bs_shared_ptr_new<PixelData>(extents, pixelFormat);
313 pixelData->allocateInternalBuffer();
314
315 return pixelData;
316 }
317
318 SPtr<PixelData> PixelData::create(UINT32 width, UINT32 height, UINT32 depth, PixelFormat pixelFormat)
319 {
320 SPtr<PixelData> pixelData = bs_shared_ptr_new<PixelData>(width, height, depth, pixelFormat);
321 pixelData->allocateInternalBuffer();
322
323 return pixelData;
324 }
325
326 UINT32 PixelData::getInternalBufferSize() const
327 {
328 return getSize();
329 }
330
331 /************************************************************************/
332 /* SERIALIZATION */
333 /************************************************************************/
334
335 RTTITypeBase* PixelData::getRTTIStatic()
336 {
337 return PixelDataRTTI::instance();
338 }
339
340 RTTITypeBase* PixelData::getRTTI() const
341 {
342 return PixelData::getRTTIStatic();
343 }
344}
345