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