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 | |
11 | namespace 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 | |