221 lines
7.0 KiB
C++
221 lines
7.0 KiB
C++
|
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||
|
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||
|
|
// SPDX-License-Identifier: MIT
|
||
|
|
|
||
|
|
#include <TestFramework.h>
|
||
|
|
|
||
|
|
#include <Image/Surface.h>
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
// FormatDescription
|
||
|
|
//
|
||
|
|
// Description of a surface format
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
// Format descriptions
|
||
|
|
static FormatDescription sFormats[] =
|
||
|
|
{
|
||
|
|
// Description BPP #CMP Closest 8 Bit Closest Alpha Red Mask Green Mask Blue Mask Alpha Mask
|
||
|
|
FormatDescription("A4L4", 8, 2, ESurfaceFormat::A8L8, ESurfaceFormat::A4L4, 0x0000000f, 0x0000000f, 0x0000000f, 0x000000f0),
|
||
|
|
FormatDescription("L8", 8, 1, ESurfaceFormat::L8, ESurfaceFormat::A8L8, 0x000000ff, 0x000000ff, 0x000000ff, 0x00000000),
|
||
|
|
FormatDescription("A8", 8, 1, ESurfaceFormat::A8, ESurfaceFormat::A8, 0x00000000, 0x00000000, 0x00000000, 0x000000ff),
|
||
|
|
FormatDescription("A8L8", 16, 2, ESurfaceFormat::A8L8, ESurfaceFormat::A8L8, 0x000000ff, 0x000000ff, 0x000000ff, 0x0000ff00),
|
||
|
|
FormatDescription("R5G6B5", 16, 3, ESurfaceFormat::R8G8B8, ESurfaceFormat::A1R5G5B5, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000),
|
||
|
|
FormatDescription("X1R5G5B5", 16, 3, ESurfaceFormat::R8G8B8, ESurfaceFormat::A1R5G5B5, 0x00007c00, 0x000003e0, 0x0000001f, 0x00000000),
|
||
|
|
FormatDescription("X4R4G4B4", 16, 3, ESurfaceFormat::R8G8B8, ESurfaceFormat::A4R4G4B4, 0x00000f00, 0x000000f0, 0x0000000f, 0x00000000),
|
||
|
|
FormatDescription("A1R5G5B5", 16, 4, ESurfaceFormat::A8R8G8B8, ESurfaceFormat::A1R5G5B5, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000),
|
||
|
|
FormatDescription("A4R4G4B4", 16, 4, ESurfaceFormat::A8R8G8B8, ESurfaceFormat::A4R4G4B4, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000),
|
||
|
|
FormatDescription("R8G8B8", 24, 3, ESurfaceFormat::R8G8B8, ESurfaceFormat::A8R8G8B8, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000),
|
||
|
|
FormatDescription("B8G8R8", 24, 3, ESurfaceFormat::B8G8R8, ESurfaceFormat::A8B8G8R8, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000),
|
||
|
|
FormatDescription("X8R8G8B8", 32, 3, ESurfaceFormat::X8R8G8B8, ESurfaceFormat::A8R8G8B8, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000),
|
||
|
|
FormatDescription("X8B8G8R8", 32, 3, ESurfaceFormat::X8B8G8R8, ESurfaceFormat::A8B8G8R8, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000),
|
||
|
|
FormatDescription("A8R8G8B8", 32, 4, ESurfaceFormat::A8R8G8B8, ESurfaceFormat::A8R8G8B8, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000),
|
||
|
|
FormatDescription("A8B8G8R8", 32, 4, ESurfaceFormat::A8B8G8R8, ESurfaceFormat::A8B8G8R8, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000),
|
||
|
|
FormatDescription("Invalid", 0, 0, ESurfaceFormat::Invalid, ESurfaceFormat::Invalid, 0x00000000, 0x00000000, 0x00000000, 0x00000000),
|
||
|
|
};
|
||
|
|
|
||
|
|
FormatDescription::FormatDescription(const char *inFormatName, int inBitsPerPixel, int inNumberOfComponents, ESurfaceFormat inClosest8BitFormat, ESurfaceFormat inClosestAlphaFormat, uint32 inRedMask, uint32 inGreenMask, uint32 inBlueMask, uint32 inAlphaMask) :
|
||
|
|
mFormatName(inFormatName),
|
||
|
|
mBitsPerPixel(inBitsPerPixel),
|
||
|
|
mNumberOfComponents(inNumberOfComponents),
|
||
|
|
mClosest8BitFormat(inClosest8BitFormat),
|
||
|
|
mClosestAlphaFormat(inClosestAlphaFormat),
|
||
|
|
mRedMask(inRedMask),
|
||
|
|
mGreenMask(inGreenMask),
|
||
|
|
mBlueMask(inBlueMask),
|
||
|
|
mAlphaMask(inAlphaMask)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
uint32 FormatDescription::Encode(ColorArg inColor) const
|
||
|
|
{
|
||
|
|
uint32 col = 0;
|
||
|
|
uint32 written_mask = 0;
|
||
|
|
|
||
|
|
// Loop through all components
|
||
|
|
for (int c = 0; c < 4; ++c)
|
||
|
|
{
|
||
|
|
// Check that we have not yet written this part of the color yet
|
||
|
|
uint32 mask = GetComponentMask(c);
|
||
|
|
if ((written_mask & mask) != 0) continue;
|
||
|
|
written_mask |= mask;
|
||
|
|
|
||
|
|
// Or in this component
|
||
|
|
col |= int(round((1.0f / 255.0f) * mask * inColor(c))) & mask;
|
||
|
|
}
|
||
|
|
|
||
|
|
return col;
|
||
|
|
}
|
||
|
|
|
||
|
|
const Color FormatDescription::Decode(uint32 inColor) const
|
||
|
|
{
|
||
|
|
Color col(0, 0, 0, 0);
|
||
|
|
|
||
|
|
// Loop through all components
|
||
|
|
for (int c = 0; c < 4; ++c)
|
||
|
|
{
|
||
|
|
uint32 mask = GetComponentMask(c);
|
||
|
|
if (mask != 0)
|
||
|
|
{
|
||
|
|
uint32 shift = CountTrailingZeros(mask);
|
||
|
|
uint32 shifted_color = (inColor & mask) >> shift;
|
||
|
|
uint32 shifted_mask = mask >> shift;
|
||
|
|
col(c) = uint8((255 * shifted_color + 127) / shifted_mask);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
col(c) = 255;
|
||
|
|
}
|
||
|
|
|
||
|
|
return col;
|
||
|
|
}
|
||
|
|
|
||
|
|
const FormatDescription &GetFormatDescription(ESurfaceFormat inFormat)
|
||
|
|
{
|
||
|
|
if (inFormat <= ESurfaceFormat::Invalid)
|
||
|
|
return sFormats[uint(inFormat)];
|
||
|
|
|
||
|
|
return sFormats[uint(ESurfaceFormat::Invalid)];
|
||
|
|
}
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Surface
|
||
|
|
//
|
||
|
|
// Class that contains an image in arbitrary format
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
Surface::Surface(int inWidth, int inHeight, ESurfaceFormat inFormat) :
|
||
|
|
mFormat(inFormat),
|
||
|
|
mWidth(inWidth),
|
||
|
|
mHeight(inHeight),
|
||
|
|
mLength(0),
|
||
|
|
mLockMode(ESurfaceLockMode::None),
|
||
|
|
mStride(0),
|
||
|
|
mData(nullptr)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
Surface::~Surface()
|
||
|
|
{
|
||
|
|
JPH_ASSERT(!IsLocked());
|
||
|
|
JPH_ASSERT(mData == nullptr);
|
||
|
|
JPH_ASSERT(mStride == 0);
|
||
|
|
JPH_ASSERT(mLength == 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Surface::Lock(ESurfaceLockMode inMode) const
|
||
|
|
{
|
||
|
|
// Check if this resource can be locked
|
||
|
|
JPH_ASSERT(!IsLocked());
|
||
|
|
JPH_ASSERT((uint(inMode) & uint(ESurfaceLockMode::ReadWrite)) != 0);
|
||
|
|
|
||
|
|
// Store mode
|
||
|
|
mLockMode = inMode;
|
||
|
|
|
||
|
|
// Lock the buffer
|
||
|
|
HardwareLock();
|
||
|
|
|
||
|
|
// Check that data and stride were filled in
|
||
|
|
JPH_ASSERT(mData != nullptr);
|
||
|
|
JPH_ASSERT(mStride > 0);
|
||
|
|
JPH_ASSERT(mLength > 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Surface::UnLock() const
|
||
|
|
{
|
||
|
|
// Check if this resource was locked
|
||
|
|
JPH_ASSERT(IsLocked());
|
||
|
|
|
||
|
|
// Unlock the hardware resource
|
||
|
|
HardwareUnLock();
|
||
|
|
|
||
|
|
// Reset members, so we are sure they will be set next time
|
||
|
|
mLockMode = ESurfaceLockMode::None;
|
||
|
|
mStride = 0;
|
||
|
|
mLength = 0;
|
||
|
|
mData = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Surface::Clear(ColorArg inColor)
|
||
|
|
{
|
||
|
|
Lock(ESurfaceLockMode::Write);
|
||
|
|
|
||
|
|
// Get image properties
|
||
|
|
int bpp = GetBytesPerPixel();
|
||
|
|
int width = GetWidth();
|
||
|
|
int height = GetHeight();
|
||
|
|
|
||
|
|
// Determine clear color
|
||
|
|
uint32 col = GetFormatDescription().Encode(inColor);
|
||
|
|
|
||
|
|
// Clear the image
|
||
|
|
for (int y = 0; y < height; ++y)
|
||
|
|
{
|
||
|
|
uint8 *d = GetScanLine(y);
|
||
|
|
uint8 *d_end = GetScanLine(y) + width * bpp;
|
||
|
|
|
||
|
|
while (d < d_end)
|
||
|
|
{
|
||
|
|
memcpy(d, &col, bpp);
|
||
|
|
d += bpp;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
UnLock();
|
||
|
|
}
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
// SoftwareSurface
|
||
|
|
//
|
||
|
|
// Class that contains an image in arbitrary format
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
SoftwareSurface::SoftwareSurface(int inWidth, int inHeight, ESurfaceFormat inFormat, int inStride) :
|
||
|
|
Surface(inWidth, inHeight, inFormat)
|
||
|
|
{
|
||
|
|
// Determine stride and length
|
||
|
|
mPixelStride = inStride == 0? ((mWidth * GetBytesPerPixel() + 3) & ~3) : inStride;
|
||
|
|
mPixelLength = mPixelStride * inHeight;
|
||
|
|
|
||
|
|
// Allocate pixel data
|
||
|
|
JPH_ASSERT(mPixelLength > 0);
|
||
|
|
mPixelData = new uint8 [mPixelLength];
|
||
|
|
}
|
||
|
|
|
||
|
|
SoftwareSurface::~SoftwareSurface()
|
||
|
|
{
|
||
|
|
delete [] mPixelData;
|
||
|
|
}
|
||
|
|
|
||
|
|
void SoftwareSurface::HardwareLock() const
|
||
|
|
{
|
||
|
|
// Get pointer to data
|
||
|
|
mData = mPixelData;
|
||
|
|
mStride = mPixelStride;
|
||
|
|
mLength = mPixelLength;
|
||
|
|
}
|
||
|
|
|
||
|
|
void SoftwareSurface::HardwareUnLock() const
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|