Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer
This commit is contained in:
356
lib/All/JoltPhysics/TestFramework/Image/BlitSurface.cpp
Normal file
356
lib/All/JoltPhysics/TestFramework/Image/BlitSurface.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/BlitSurface.h>
|
||||
#include <Image/Surface.h>
|
||||
#include <Jolt/Core/Color.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BlitSettings
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const BlitSettings BlitSettings::sDefault;
|
||||
|
||||
BlitSettings::BlitSettings() :
|
||||
mConvertRGBToAlpha(false),
|
||||
mConvertAlphaToRGB(false),
|
||||
mConvertToGrayScale(false),
|
||||
mInvertAlpha(false),
|
||||
mColorKeyAlpha(false),
|
||||
mColorKeyStart(240, 0, 240),
|
||||
mColorKeyEnd(255, 15, 255)
|
||||
{
|
||||
}
|
||||
|
||||
bool BlitSettings::operator == (const BlitSettings &inRHS) const
|
||||
{
|
||||
return mConvertRGBToAlpha == inRHS.mConvertRGBToAlpha
|
||||
&& mConvertAlphaToRGB == inRHS.mConvertAlphaToRGB
|
||||
&& mConvertToGrayScale == inRHS.mConvertToGrayScale
|
||||
&& mInvertAlpha == inRHS.mInvertAlpha
|
||||
&& mColorKeyAlpha == inRHS.mColorKeyAlpha
|
||||
&& mColorKeyStart == inRHS.mColorKeyStart
|
||||
&& mColorKeyEnd == inRHS.mColorKeyEnd
|
||||
&& mZoomSettings == inRHS.mZoomSettings;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Converting from one format to another
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The macro COL(s) converts color s to another color given the mapping tables
|
||||
#define CMP(s, c) map[256 * c + ((s & src_mask[c]) >> src_shift[c])]
|
||||
#define COL(s) (CMP(s, 0) + CMP(s, 1) + CMP(s, 2) + CMP(s, 3))
|
||||
|
||||
static void sComputeTranslationTable(const FormatDescription & inSrcDesc, const FormatDescription & inDstDesc, uint32 *outMask, uint32 *outShift, uint32 *outMap)
|
||||
{
|
||||
JPH_PROFILE("sComputeTranslationTable");
|
||||
|
||||
// Compute translation tables for each color component
|
||||
uint32 written_mask = 0;
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
outMask[c] = inSrcDesc.GetComponentMask(c);
|
||||
outShift[c] = CountTrailingZeros(outMask[c]);
|
||||
uint32 src_shifted_mask = outMask[c] >> outShift[c];
|
||||
|
||||
uint32 dst_mask = inDstDesc.GetComponentMask(c);
|
||||
uint32 dst_shift = CountTrailingZeros(dst_mask);
|
||||
uint32 dst_shifted_mask = dst_mask >> dst_shift;
|
||||
|
||||
if ((written_mask & dst_mask) != 0)
|
||||
{
|
||||
dst_mask = 0;
|
||||
dst_shift = 0;
|
||||
dst_shifted_mask = 0;
|
||||
}
|
||||
else
|
||||
written_mask |= dst_mask;
|
||||
|
||||
float scale = src_shifted_mask > 0? float(dst_shifted_mask) / src_shifted_mask : 0.0f;
|
||||
|
||||
uint32 entry = 0;
|
||||
|
||||
if (src_shifted_mask != 0)
|
||||
for (; entry <= src_shifted_mask; ++entry)
|
||||
outMap[256 * c + entry] = uint32(round(scale * entry)) << dst_shift;
|
||||
|
||||
for (; entry < 256; ++entry)
|
||||
outMap[256 * c + entry] = dst_mask;
|
||||
}
|
||||
}
|
||||
|
||||
static bool sConvertImageDifferentTypes(RefConst<Surface> inSrc, Ref<Surface> ioDst)
|
||||
{
|
||||
JPH_PROFILE("sConvertImageDifferentTypes");
|
||||
|
||||
// Get image properties
|
||||
int sbpp = inSrc->GetBytesPerPixel();
|
||||
int dbpp = ioDst->GetBytesPerPixel();
|
||||
int width = inSrc->GetWidth();
|
||||
int height = inSrc->GetHeight();
|
||||
JPH_ASSERT(width == ioDst->GetWidth());
|
||||
JPH_ASSERT(height == ioDst->GetHeight());
|
||||
|
||||
// Compute conversion map
|
||||
uint32 src_mask[4];
|
||||
uint32 src_shift[4];
|
||||
uint32 map[4 * 256];
|
||||
sComputeTranslationTable(inSrc->GetFormatDescription(), ioDst->GetFormatDescription(), src_mask, src_shift, map);
|
||||
|
||||
inSrc->Lock(ESurfaceLockMode::Read);
|
||||
ioDst->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Convert the image
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const uint8 *s = inSrc->GetScanLine(y);
|
||||
const uint8 *s_end = inSrc->GetScanLine(y) + width * sbpp;
|
||||
uint8 *d = ioDst->GetScanLine(y);
|
||||
|
||||
while (s < s_end)
|
||||
{
|
||||
uint32 src = 0;
|
||||
memcpy(&src, s, sbpp);
|
||||
uint32 dst = COL(src);
|
||||
memcpy(d, &dst, dbpp);
|
||||
s += sbpp;
|
||||
d += dbpp;
|
||||
}
|
||||
}
|
||||
|
||||
inSrc->UnLock();
|
||||
ioDst->UnLock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sConvertImageSameTypes(RefConst<Surface> inSrc, Ref<Surface> ioDst)
|
||||
{
|
||||
JPH_PROFILE("sConvertImageSameTypes");
|
||||
|
||||
// Get image properties
|
||||
int dbpp = ioDst->GetBytesPerPixel();
|
||||
int width = inSrc->GetWidth();
|
||||
int height = inSrc->GetHeight();
|
||||
JPH_ASSERT(inSrc->GetFormat() == ioDst->GetFormat());
|
||||
JPH_ASSERT(dbpp == inSrc->GetBytesPerPixel());
|
||||
JPH_ASSERT(width == ioDst->GetWidth());
|
||||
JPH_ASSERT(height == ioDst->GetHeight());
|
||||
|
||||
inSrc->Lock(ESurfaceLockMode::Read);
|
||||
ioDst->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Copy the image line by line to compensate for stride
|
||||
for (int y = 0; y < height; ++y)
|
||||
memcpy(ioDst->GetScanLine(y), inSrc->GetScanLine(y), width * dbpp);
|
||||
|
||||
inSrc->UnLock();
|
||||
ioDst->UnLock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sConvertImage(RefConst<Surface> inSrc, Ref<Surface> ioDst)
|
||||
{
|
||||
JPH_PROFILE("sConvertImage");
|
||||
|
||||
if (inSrc->GetFormat() == ioDst->GetFormat())
|
||||
return sConvertImageSameTypes(inSrc, ioDst);
|
||||
else
|
||||
return sConvertImageDifferentTypes(inSrc, ioDst);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Special color conversions
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void sConvertRGBToAlpha(Ref<Surface> ioSurface)
|
||||
{
|
||||
JPH_PROFILE("sConvertRGBToAlpha");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Convert RGB values to alpha values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
c->a = c->GetIntensity();
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sConvertAlphaToRGB(Ref<Surface> ioSurface)
|
||||
{
|
||||
JPH_PROFILE("sConvertAlphaToRGB");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Convert alpha values to RGB values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
c->r = c->g = c->b = c->a;
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sConvertToGrayScale(Ref<Surface> ioSurface)
|
||||
{
|
||||
JPH_PROFILE("sConvertToGrayScale");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Convert RGB values to grayscale values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
uint8 intensity = c->GetIntensity();
|
||||
c->r = intensity;
|
||||
c->g = intensity;
|
||||
c->b = intensity;
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sInvertAlpha(Ref<Surface> ioSurface)
|
||||
{
|
||||
JPH_PROFILE("sInvertAlpha");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Invert all alpha values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
c->a = uint8(255 - c->a);
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sColorKeyAlpha(Ref<Surface> ioSurface, ColorArg inStart, ColorArg inEnd)
|
||||
{
|
||||
JPH_PROFILE("sColorKeyAlpha");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Set alpha values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
if (c->r >= inStart.r && c->r <= inEnd.r && c->g >= inStart.g && c->g <= inEnd.g && c->b >= inStart.b && c->b <= inEnd.b)
|
||||
c->a = 0;
|
||||
else
|
||||
c->a = 255;
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BlitSurface
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool BlitSurface(RefConst<Surface> inSrc, Ref<Surface> ioDst, const BlitSettings &inBlitSettings)
|
||||
{
|
||||
JPH_PROFILE("BlitSurface");
|
||||
|
||||
// Do extra conversion options
|
||||
RefConst<Surface> src = inSrc;
|
||||
if (inBlitSettings.mConvertRGBToAlpha || inBlitSettings.mConvertAlphaToRGB || inBlitSettings.mConvertToGrayScale || inBlitSettings.mInvertAlpha || inBlitSettings.mColorKeyAlpha)
|
||||
{
|
||||
// Do them on A8R8G8B8 format so the conversion routines are simple
|
||||
Ref<Surface> tmp = new SoftwareSurface(inSrc->GetWidth(), inSrc->GetHeight(), ESurfaceFormat::A8R8G8B8);
|
||||
sConvertImage(inSrc, tmp);
|
||||
src = tmp;
|
||||
|
||||
// Perform all optional conversions
|
||||
tmp->Lock(ESurfaceLockMode::ReadWrite);
|
||||
|
||||
if (inBlitSettings.mConvertRGBToAlpha)
|
||||
sConvertRGBToAlpha(tmp);
|
||||
|
||||
if (inBlitSettings.mConvertAlphaToRGB)
|
||||
sConvertAlphaToRGB(tmp);
|
||||
|
||||
if (inBlitSettings.mConvertToGrayScale)
|
||||
sConvertToGrayScale(tmp);
|
||||
|
||||
if (inBlitSettings.mInvertAlpha)
|
||||
sInvertAlpha(tmp);
|
||||
|
||||
if (inBlitSettings.mColorKeyAlpha)
|
||||
sColorKeyAlpha(tmp, inBlitSettings.mColorKeyStart, inBlitSettings.mColorKeyEnd);
|
||||
|
||||
tmp->UnLock();
|
||||
}
|
||||
|
||||
if (src->GetWidth() != ioDst->GetWidth() || src->GetHeight() != ioDst->GetHeight())
|
||||
{
|
||||
// Zoom the image if the destination size is not equal to the source size
|
||||
if (!ZoomImage(src, ioDst, inBlitSettings.mZoomSettings))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the image if the sizes are equal
|
||||
if (!sConvertImage(src, ioDst))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
37
lib/All/JoltPhysics/TestFramework/Image/BlitSurface.h
Normal file
37
lib/All/JoltPhysics/TestFramework/Image/BlitSurface.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Image/ZoomImage.h>
|
||||
#include <Jolt/Core/Color.h>
|
||||
|
||||
/// Settings for blitting one surface to another with possibly different formats and dimensions. The blit
|
||||
/// routine can use filtering or blurring on the fly. Also it can perform some other
|
||||
/// basic operations like converting an image to grayscale or alpha only surfaces.
|
||||
class BlitSettings
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
BlitSettings();
|
||||
|
||||
/// Comparison operators
|
||||
bool operator == (const BlitSettings &inRHS) const;
|
||||
|
||||
/// Default settings
|
||||
static const BlitSettings sDefault;
|
||||
|
||||
/// Special operations that can be applied during the blit
|
||||
bool mConvertRGBToAlpha; ///< Convert RGB values to alpha values (RGB values remain untouched)
|
||||
bool mConvertAlphaToRGB; ///< Convert alpha values to grayscale RGB values (Alpha values remain untouched)
|
||||
bool mConvertToGrayScale; ///< Convert RGB values to grayscale values (Alpha values remain untouched)
|
||||
bool mInvertAlpha; ///< Invert alpha values
|
||||
bool mColorKeyAlpha; ///< If true, colors in the range mColorKeyStart..mColorKeyEnd will get an alpha of 0, other colors will get an alpha of 255
|
||||
Color mColorKeyStart;
|
||||
Color mColorKeyEnd;
|
||||
ZoomSettings mZoomSettings; ///< Settings for resizing the image
|
||||
};
|
||||
|
||||
/// Copies an image from inSrc to inDst, converting it on the fly as defined by inBlitSettings
|
||||
bool BlitSurface(RefConst<Surface> inSrc, Ref<Surface> ioDst, const BlitSettings &inBlitSettings = BlitSettings::sDefault);
|
||||
199
lib/All/JoltPhysics/TestFramework/Image/LoadBMP.cpp
Normal file
199
lib/All/JoltPhysics/TestFramework/Image/LoadBMP.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/LoadBMP.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
#include <Image/Surface.h>
|
||||
|
||||
#pragma pack (1)
|
||||
|
||||
struct BitmapFileHeader
|
||||
{
|
||||
char mTypeB;
|
||||
char mTypeM;
|
||||
uint32 mSize;
|
||||
uint16 mReserved1;
|
||||
uint16 mReserved2;
|
||||
uint32 mOffBits;
|
||||
};
|
||||
|
||||
struct BitmapInfoHeader
|
||||
{
|
||||
uint32 mSize;
|
||||
uint32 mWidth;
|
||||
uint32 mHeight;
|
||||
uint16 mPlanes;
|
||||
uint16 mBitCount;
|
||||
uint32 mCompression;
|
||||
uint32 mSizeImage;
|
||||
uint32 mXPelsPerMeter;
|
||||
uint32 mYPelsPerMeter;
|
||||
uint32 mClrUsed;
|
||||
uint32 mClrImportant;
|
||||
};
|
||||
|
||||
#pragma pack ()
|
||||
|
||||
Ref<Surface> LoadBMP(istream &inStream)
|
||||
{
|
||||
bool loaded = true;
|
||||
|
||||
// Read bitmap info
|
||||
BitmapFileHeader bfh;
|
||||
BitmapInfoHeader bih;
|
||||
inStream.read((char *)&bfh, sizeof(bfh));
|
||||
if (inStream.fail())
|
||||
return nullptr;
|
||||
inStream.read((char *)&bih, sizeof(bih));
|
||||
if (inStream.fail())
|
||||
return nullptr;
|
||||
|
||||
// Get properties
|
||||
int bpp = (bih.mBitCount + 7) >> 3;
|
||||
int scan_width = (bih.mWidth * bpp + 3) & (~3);
|
||||
|
||||
// Check if it is a bitmap
|
||||
if (bfh.mTypeB != 'B' || bfh.mTypeM != 'M')
|
||||
{
|
||||
Trace("Not a BMP");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if bitmap is bottom-up
|
||||
if (bih.mHeight <= 0)
|
||||
{
|
||||
Trace("Not bottom-up");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if it is not compressed
|
||||
if (bih.mCompression != 0)
|
||||
{
|
||||
Trace("Is compressed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<Surface> surface;
|
||||
|
||||
if (bih.mBitCount == 8)
|
||||
{
|
||||
// Load palette
|
||||
uint32 *palette = new uint32 [256];
|
||||
int pal_bytes = 4 * (bih.mClrUsed != 0? bih.mClrUsed : 256);
|
||||
inStream.read((char *)palette, pal_bytes);
|
||||
loaded = loaded && !inStream.fail();
|
||||
|
||||
// Seek to image data
|
||||
inStream.seekg(bfh.mOffBits);
|
||||
|
||||
// Convert pixel data to a surface
|
||||
surface = new SoftwareSurface(bih.mWidth, bih.mHeight, ESurfaceFormat::X8R8G8B8);
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
uint8 *scan_line = new uint8 [scan_width];
|
||||
for (int y = bih.mHeight - 1; y >= 0; --y)
|
||||
{
|
||||
// Load one scan line
|
||||
inStream.read((char *)scan_line, scan_width);
|
||||
loaded = loaded && !inStream.fail();
|
||||
|
||||
// Copy one scan line
|
||||
uint8 *in_pixel = scan_line;
|
||||
uint32 *out_pixel = (uint32 *)surface->GetScanLine(y);
|
||||
for (uint x = 0; x < bih.mWidth; ++x, ++in_pixel, ++out_pixel)
|
||||
*out_pixel = palette[*in_pixel];
|
||||
}
|
||||
surface->UnLock();
|
||||
|
||||
// Release temporaries
|
||||
delete [] palette;
|
||||
delete [] scan_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine pixel format
|
||||
ESurfaceFormat format;
|
||||
switch (bih.mBitCount)
|
||||
{
|
||||
case 16: format = ESurfaceFormat::X1R5G5B5; break;
|
||||
case 24: format = ESurfaceFormat::R8G8B8; break;
|
||||
default: Trace("Has invalid format"); return nullptr;
|
||||
}
|
||||
|
||||
// Seek to image data
|
||||
inStream.seekg(bfh.mOffBits);
|
||||
|
||||
// Convert pixel data to a surface
|
||||
surface = new SoftwareSurface(bih.mWidth, bih.mHeight, format, scan_width);
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
for (int y = bih.mHeight - 1; y >= 0; --y)
|
||||
{
|
||||
inStream.read((char *)surface->GetScanLine(y), scan_width);
|
||||
loaded = loaded && !inStream.fail();
|
||||
}
|
||||
surface->UnLock();
|
||||
}
|
||||
|
||||
return loaded? surface : Ref<Surface>(nullptr);
|
||||
}
|
||||
|
||||
bool SaveBMP(RefConst<Surface> inSurface, ostream &inStream)
|
||||
{
|
||||
bool stored = true;
|
||||
|
||||
// Convert surface if required
|
||||
const Surface *src = inSurface;
|
||||
Ref<Surface> tmp_src;
|
||||
if (inSurface->GetFormat() != ESurfaceFormat::R8G8B8)
|
||||
{
|
||||
tmp_src = new SoftwareSurface(inSurface->GetWidth(), inSurface->GetHeight(), ESurfaceFormat::R8G8B8);
|
||||
BlitSurface(inSurface, tmp_src);
|
||||
src = tmp_src.GetPtr();
|
||||
}
|
||||
|
||||
// Lock the surface
|
||||
src->Lock(ESurfaceLockMode::Read);
|
||||
JPH_ASSERT(src->GetStride() % 4 == 0);
|
||||
|
||||
BitmapFileHeader bfh;
|
||||
BitmapInfoHeader bih;
|
||||
|
||||
// Fill in headers
|
||||
bfh.mTypeB = 'B';
|
||||
bfh.mTypeM = 'M';
|
||||
bfh.mSize = sizeof(bfh) + sizeof(bih) + src->GetHeight() * src->GetStride();
|
||||
bfh.mReserved1 = 0;
|
||||
bfh.mReserved2 = 0;
|
||||
bfh.mOffBits = sizeof(bfh) + sizeof(bih);
|
||||
|
||||
bih.mSize = sizeof(bih);
|
||||
bih.mWidth = src->GetWidth();
|
||||
bih.mHeight = src->GetHeight();
|
||||
bih.mPlanes = 1;
|
||||
bih.mBitCount = 24;
|
||||
bih.mCompression = 0;
|
||||
bih.mSizeImage = src->GetHeight() * src->GetStride();
|
||||
bih.mXPelsPerMeter = 300;
|
||||
bih.mYPelsPerMeter = 300;
|
||||
bih.mClrUsed = 0;
|
||||
bih.mClrImportant = 0;
|
||||
|
||||
// Write headers
|
||||
inStream.write((char *)&bfh, sizeof(bfh));
|
||||
stored = stored && !inStream.fail();
|
||||
inStream.write((char *)&bih, sizeof(bih));
|
||||
stored = stored && !inStream.fail();
|
||||
|
||||
// Write image data
|
||||
for (int y = src->GetHeight() - 1; y >= 0; --y)
|
||||
{
|
||||
inStream.write((const char *)src->GetScanLine(y), src->GetStride());
|
||||
stored = stored && !inStream.fail();
|
||||
}
|
||||
|
||||
src->UnLock();
|
||||
|
||||
return stored;
|
||||
}
|
||||
15
lib/All/JoltPhysics/TestFramework/Image/LoadBMP.h
Normal file
15
lib/All/JoltPhysics/TestFramework/Image/LoadBMP.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
class Surface;
|
||||
|
||||
/// Load a windows BMP file
|
||||
Ref<Surface> LoadBMP(istream &inStream);
|
||||
|
||||
/// Write a windows BMP file
|
||||
bool SaveBMP(RefConst<Surface> inSurface, ostream &inStream);
|
||||
130
lib/All/JoltPhysics/TestFramework/Image/LoadTGA.cpp
Normal file
130
lib/All/JoltPhysics/TestFramework/Image/LoadTGA.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/LoadTGA.h>
|
||||
#include <Image/Surface.h>
|
||||
|
||||
#pragma pack (1)
|
||||
|
||||
struct TGAHeader
|
||||
{
|
||||
uint8 mIDLength;
|
||||
uint8 mColorMapType;
|
||||
uint8 mImageType;
|
||||
uint16 mColorMapFirstEntryIndex;
|
||||
uint16 mColorMapLength;
|
||||
uint8 mColorMapEntrySize;
|
||||
uint16 mXOrigin;
|
||||
uint16 mYOrigin;
|
||||
uint16 mWidth;
|
||||
uint16 mHeight;
|
||||
uint8 mPixelDepth;
|
||||
uint8 mImageDescriptor;
|
||||
};
|
||||
|
||||
#pragma pack ()
|
||||
|
||||
Ref<Surface> LoadTGA(istream &inStream)
|
||||
{
|
||||
bool loaded = true;
|
||||
|
||||
// Read header
|
||||
TGAHeader header;
|
||||
inStream.read((char *)&header, sizeof(header));
|
||||
if (inStream.fail())
|
||||
return nullptr;
|
||||
|
||||
// Get properties
|
||||
int bytes_per_pixel = (header.mPixelDepth + 7) >> 3;
|
||||
int scan_width = bytes_per_pixel * header.mWidth;
|
||||
|
||||
// Check type
|
||||
if (header.mImageType < 1 || header.mImageType > 2)
|
||||
{
|
||||
Trace("Not a readable TGA");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check compression
|
||||
if ((header.mImageType == 1 && header.mColorMapType != 1) || (header.mImageType == 2 && header.mColorMapType != 0))
|
||||
{
|
||||
Trace("Not an uncompressed TGA");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<Surface> surface;
|
||||
|
||||
if (header.mPixelDepth == 8)
|
||||
{
|
||||
// Determine pixel format
|
||||
ESurfaceFormat format;
|
||||
int pixel_size;
|
||||
switch (header.mColorMapEntrySize)
|
||||
{
|
||||
case 15: format = ESurfaceFormat::X1R5G5B5; pixel_size = 2; break;
|
||||
case 16: format = ESurfaceFormat::X1R5G5B5; pixel_size = 2; break;
|
||||
case 24: format = ESurfaceFormat::R8G8B8; pixel_size = 3; break;
|
||||
case 32: format = ESurfaceFormat::A8R8G8B8; pixel_size = 4; break;
|
||||
default: Trace("Has invalid format"); return nullptr;
|
||||
}
|
||||
|
||||
// Seek to beginning of palette
|
||||
inStream.seekg(sizeof(TGAHeader) + header.mIDLength);
|
||||
|
||||
// Load palette
|
||||
int pal_bytes = pixel_size * header.mColorMapLength;
|
||||
uint8 *palette = new uint8 [pal_bytes];
|
||||
inStream.read((char *)palette, pal_bytes);
|
||||
loaded = loaded && !inStream.fail();
|
||||
|
||||
// Convert pixel data to a surface
|
||||
surface = new SoftwareSurface(header.mWidth, header.mHeight, format);
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
uint8 *scan_line = new uint8 [scan_width];
|
||||
for (int y = header.mHeight - 1; y >= 0; --y)
|
||||
{
|
||||
// Load one scan line
|
||||
inStream.read((char *)scan_line, scan_width);
|
||||
loaded = loaded && !inStream.fail();
|
||||
|
||||
// Copy one scan line
|
||||
uint8 *in_pixel = scan_line;
|
||||
uint8 *out_pixel = (uint8 *)surface->GetScanLine(y);
|
||||
for (int x = 0; x < header.mWidth; ++x, ++in_pixel, out_pixel += pixel_size)
|
||||
memcpy(out_pixel, palette + (*in_pixel - header.mColorMapFirstEntryIndex) * pixel_size, pixel_size);
|
||||
}
|
||||
surface->UnLock();
|
||||
|
||||
// Release temporaries
|
||||
delete [] palette;
|
||||
delete [] scan_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine pixel format
|
||||
ESurfaceFormat format;
|
||||
switch (header.mPixelDepth)
|
||||
{
|
||||
case 15: format = ESurfaceFormat::X1R5G5B5; break;
|
||||
case 16: format = ESurfaceFormat::X1R5G5B5; break;
|
||||
case 24: format = ESurfaceFormat::R8G8B8; break;
|
||||
case 32: format = ESurfaceFormat::A8R8G8B8; break;
|
||||
default: Trace("Invalid format"); return nullptr;
|
||||
}
|
||||
|
||||
// Convert pixel data to a surface
|
||||
surface = new SoftwareSurface(header.mWidth, header.mHeight, format, scan_width);
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
for (int y = header.mHeight - 1; y >= 0; --y)
|
||||
{
|
||||
inStream.read((char *)surface->GetScanLine(y), scan_width);
|
||||
loaded = loaded && !inStream.fail();
|
||||
}
|
||||
surface->UnLock();
|
||||
}
|
||||
|
||||
return loaded? surface : Ref<Surface>(nullptr);
|
||||
}
|
||||
12
lib/All/JoltPhysics/TestFramework/Image/LoadTGA.h
Normal file
12
lib/All/JoltPhysics/TestFramework/Image/LoadTGA.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
class Surface;
|
||||
|
||||
/// Image routines, loads a Targa (TGA) file.
|
||||
Ref<Surface> LoadTGA(istream &inStream);
|
||||
220
lib/All/JoltPhysics/TestFramework/Image/Surface.cpp
Normal file
220
lib/All/JoltPhysics/TestFramework/Image/Surface.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
// 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
|
||||
{
|
||||
}
|
||||
|
||||
181
lib/All/JoltPhysics/TestFramework/Image/Surface.h
Normal file
181
lib/All/JoltPhysics/TestFramework/Image/Surface.h
Normal file
@@ -0,0 +1,181 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
#include <Jolt/Core/Color.h>
|
||||
#include <Jolt/Core/StringTools.h>
|
||||
|
||||
/// Possible lock modes of a Surface
|
||||
enum class ESurfaceLockMode : uint
|
||||
{
|
||||
None = 0 << 0, ///< Not locked, cannot be used as a parameter
|
||||
Read = 1 << 0,
|
||||
Write = 2 << 0,
|
||||
ReadWrite = Read | Write,
|
||||
};
|
||||
|
||||
/// Possible surface formats, most significant bit (MSB) first
|
||||
enum class ESurfaceFormat : uint
|
||||
{
|
||||
A4L4, ///< 4 bit alpha, 4 bit luminance (grayscale)
|
||||
L8, ///< 8 bit luminance (grayscale)
|
||||
A8, ///< 8 bit alpha
|
||||
A8L8, ///< 8 bit luminance and 8 bit alpha
|
||||
R5G6B5, ///< 16 bit RGB
|
||||
X1R5G5B5, ///< 16 bit RGB
|
||||
X4R4G4B4, ///< 16 bit RGB
|
||||
A1R5G5B5, ///< 16 bit RGBA
|
||||
A4R4G4B4, ///< 16 bit RGBA
|
||||
R8G8B8, ///< 24 bit RGB
|
||||
B8G8R8, ///< 24 bit BGR
|
||||
X8R8G8B8, ///< 32 bit RGB
|
||||
X8B8G8R8, ///< 32 bit RGB
|
||||
A8R8G8B8, ///< 32 bit RGBA
|
||||
A8B8G8R8, ///< 32 bit BGRA
|
||||
Invalid, ///< Invalid value
|
||||
Count = Invalid, ///< Number of pixel formats
|
||||
};
|
||||
|
||||
/// Description of a surface format
|
||||
class FormatDescription
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
FormatDescription(const char *inFormatName, int inBitsPerPixel, int inNumberOfComponents, ESurfaceFormat inClosest8BitFormat, ESurfaceFormat inClosestAlphaFormat, uint32 inRedMask, uint32 inGreenMask, uint32 inBlueMask, uint32 inAlphaMask);
|
||||
|
||||
/// General properties
|
||||
const string_view & GetFormatName() const { return mFormatName; }
|
||||
int GetBytesPerPixel() const { return (mBitsPerPixel + 7) >> 3; }
|
||||
int GetNumberOfComponents() const { return mNumberOfComponents; }
|
||||
ESurfaceFormat GetClosest8BitFormat() const { return mClosest8BitFormat; }
|
||||
ESurfaceFormat GetClosestAlphaFormat() const { return mClosestAlphaFormat; }
|
||||
|
||||
/// Bitcounts for the various components of the image
|
||||
int GetBitsPerPixel() const { return mBitsPerPixel; }
|
||||
int GetRedBitsPerPixel() const { return CountBits(mRedMask); }
|
||||
int GetGreenBitsPerPixel() const { return CountBits(mGreenMask); }
|
||||
int GetBlueBitsPerPixel() const { return CountBits(mBlueMask); }
|
||||
int GetAlphaBitsPerPixel() const { return CountBits(mAlphaMask); }
|
||||
int GetComponentBitCount(int inComponent) const { return CountBits(GetComponentMask(inComponent)); }
|
||||
|
||||
/// Bitmasks indicating the various components of the image
|
||||
uint32 GetRedMask() const { return mRedMask; }
|
||||
uint32 GetGreenMask() const { return mGreenMask; }
|
||||
uint32 GetBlueMask() const { return mBlueMask; }
|
||||
uint32 GetAlphaMask() const { return mAlphaMask; }
|
||||
uint32 GetComponentMask(int inComponent) const { return *(&mRedMask + inComponent); }
|
||||
|
||||
/// Convert a single color
|
||||
uint32 Encode(ColorArg inColor) const;
|
||||
const Color Decode(uint32 inColor) const;
|
||||
|
||||
private:
|
||||
string_view mFormatName; ///< User displayable String describing the format
|
||||
int mBitsPerPixel; ///< Number of bits per pixel
|
||||
int mNumberOfComponents; ///< Number of color components per pixel
|
||||
ESurfaceFormat mClosest8BitFormat; ///< Closest matching format that has 8 bit color components
|
||||
ESurfaceFormat mClosestAlphaFormat; ///< Closest matching format that has an alpha channel
|
||||
|
||||
uint32 mRedMask; ///< Bitmasks indicating which bits are used by which color components
|
||||
uint32 mGreenMask;
|
||||
uint32 mBlueMask;
|
||||
uint32 mAlphaMask;
|
||||
};
|
||||
|
||||
/// Get the description for a specific surface format
|
||||
const FormatDescription & GetFormatDescription(ESurfaceFormat inFormat);
|
||||
|
||||
/// Class that contains an image in arbitrary format
|
||||
class Surface : public RefTarget<Surface>
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Surface(int inWidth, int inHeight, ESurfaceFormat inFormat);
|
||||
virtual ~Surface();
|
||||
|
||||
/// Type of the image data
|
||||
const FormatDescription & GetFormatDescription() const { return ::GetFormatDescription(mFormat); }
|
||||
|
||||
const string_view & GetFormatName() const { return GetFormatDescription().GetFormatName(); }
|
||||
int GetBytesPerPixel() const { return GetFormatDescription().GetBytesPerPixel(); }
|
||||
int GetNumberOfComponents() const { return GetFormatDescription().GetNumberOfComponents(); }
|
||||
ESurfaceFormat GetClosest8BitFormat() const { return GetFormatDescription().GetClosest8BitFormat(); }
|
||||
|
||||
int GetBitsPerPixel() const { return GetFormatDescription().GetBitsPerPixel(); }
|
||||
int GetRedBitsPerPixel() const { return GetFormatDescription().GetRedBitsPerPixel(); }
|
||||
int GetGreenBitsPerPixel() const { return GetFormatDescription().GetGreenBitsPerPixel(); }
|
||||
int GetBlueBitsPerPixel() const { return GetFormatDescription().GetBlueBitsPerPixel(); }
|
||||
int GetAlphaBitsPerPixel() const { return GetFormatDescription().GetAlphaBitsPerPixel(); }
|
||||
int GetComponentBitCount(int inComponent) const { return GetFormatDescription().GetComponentBitCount(inComponent); }
|
||||
|
||||
uint32 GetRedMask() const { return GetFormatDescription().GetRedMask(); }
|
||||
uint32 GetGreenMask() const { return GetFormatDescription().GetGreenMask(); }
|
||||
uint32 GetBlueMask() const { return GetFormatDescription().GetBlueMask(); }
|
||||
uint32 GetAlphaMask() const { return GetFormatDescription().GetAlphaMask(); }
|
||||
uint32 GetComponentMask(int inComponent) const { return GetFormatDescription().GetComponentMask(inComponent); }
|
||||
|
||||
/// Get properties of this surface
|
||||
inline ESurfaceFormat GetFormat() const { return mFormat; }
|
||||
inline int GetWidth() const { return mWidth; }
|
||||
inline int GetHeight() const { return mHeight; }
|
||||
|
||||
/// Sets the image to a specific color
|
||||
void Clear(ColorArg inColor = Color::sBlack);
|
||||
|
||||
/// Locking functions
|
||||
void Lock(ESurfaceLockMode inMode) const;
|
||||
void UnLock() const;
|
||||
|
||||
/// Current lock state
|
||||
inline ESurfaceLockMode GetLockMode() const { return mLockMode; }
|
||||
inline bool IsLocked() const { return mLockMode != ESurfaceLockMode::None; }
|
||||
inline bool IsLockedForRead() const { return (uint(mLockMode) & uint(ESurfaceLockMode::Read)) != 0; }
|
||||
inline bool IsLockedForWrite() const { return (uint(mLockMode) & uint(ESurfaceLockMode::Write)) != 0; }
|
||||
inline bool IsLockedForReadWrite() const { return IsLockedForRead() && IsLockedForWrite(); }
|
||||
|
||||
/// Access to the image data
|
||||
inline const uint8 * GetData() const { JPH_ASSERT(IsLockedForRead()); return mData; }
|
||||
inline uint8 * GetData() { JPH_ASSERT(IsLockedForWrite()); return mData; }
|
||||
inline int GetStride() const { JPH_ASSERT(IsLocked()); return mStride; }
|
||||
inline int GetLength() const { JPH_ASSERT(IsLocked()); return mLength; }
|
||||
|
||||
/// Get start of a specific scanline
|
||||
inline const uint8 * GetScanLine(int inScanLine) const { JPH_ASSERT(inScanLine >= 0 && inScanLine < GetHeight()); return GetData() + inScanLine * GetStride(); }
|
||||
inline uint8 * GetScanLine(int inScanLine) { JPH_ASSERT(inScanLine >= 0 && inScanLine < GetHeight()); return GetData() + inScanLine * GetStride(); }
|
||||
|
||||
protected:
|
||||
/// These functions must be overridden by the hardware buffer
|
||||
virtual void HardwareLock() const = 0;
|
||||
virtual void HardwareUnLock() const = 0;
|
||||
|
||||
/// Data
|
||||
ESurfaceFormat mFormat; ///< Pixel format of the surface
|
||||
int mWidth; ///< Width of the image
|
||||
int mHeight; ///< Height of the image
|
||||
mutable int mLength; ///< Length in bytes of the image
|
||||
|
||||
mutable ESurfaceLockMode mLockMode;
|
||||
mutable int mStride; ///< Width of one scanline in bytes
|
||||
mutable uint8 * mData; ///< Pointer to image data, starting at top-left of locked rectangle
|
||||
};
|
||||
|
||||
/// Class that contains an image in arbitrary format, backed by normal memory (not device specific)
|
||||
class SoftwareSurface : public Surface
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
SoftwareSurface(int inWidth, int inHeight, ESurfaceFormat inFormat, int inStride = 0);
|
||||
virtual ~SoftwareSurface() override;
|
||||
|
||||
protected:
|
||||
/// These functions must be overridden by the hardware buffer
|
||||
virtual void HardwareLock() const override;
|
||||
virtual void HardwareUnLock() const override;
|
||||
|
||||
uint8 * mPixelData;
|
||||
int mPixelStride;
|
||||
int mPixelLength;
|
||||
};
|
||||
526
lib/All/JoltPhysics/TestFramework/Image/ZoomImage.cpp
Normal file
526
lib/All/JoltPhysics/TestFramework/Image/ZoomImage.cpp
Normal file
@@ -0,0 +1,526 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/ZoomImage.h>
|
||||
#include <Image/Surface.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ImageFilter
|
||||
//
|
||||
// Abstract class and some implementations of a filter, essentially an 1D weighting function
|
||||
// which is not zero for t e [-GetSupport(), GetSupport()] and zero for all other t
|
||||
// The integrand is usually 1 although it is not required for this implementation,
|
||||
// since the filter is renormalized when it is sampled.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ImageFilter
|
||||
{
|
||||
public:
|
||||
// Destructor
|
||||
virtual ~ImageFilter() = default;
|
||||
|
||||
// Get support of this filter (+/- the range the filter function is not zero)
|
||||
virtual float GetSupport() const = 0;
|
||||
|
||||
// Sample filter function at a certain point
|
||||
virtual float GetValue(float t) const = 0;
|
||||
};
|
||||
|
||||
class ImageFilterBox : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
if (abs(t) <= 0.5f)
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class ImageFilterTriangle : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
t = abs(t);
|
||||
|
||||
if (t < 1.0f)
|
||||
return 1.0f - t;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class ImageFilterBell : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 1.5f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
t = abs(t);
|
||||
|
||||
if (t < 0.5f)
|
||||
return 0.75f - t * t;
|
||||
else if (t < 1.5f)
|
||||
{
|
||||
t = t - 1.5f;
|
||||
return 0.5f * t * t;
|
||||
}
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class ImageFilterBSpline : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 2.0f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
t = abs(t);
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
float tt = t * t;
|
||||
return (0.5f * tt * t) - tt + (2.0f / 3.0f);
|
||||
}
|
||||
else if (t < 2.0f)
|
||||
{
|
||||
t = 2.0f - t;
|
||||
return (1.0f / 6.0f) * (t * t * t);
|
||||
}
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class ImageFilterLanczos3 : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 3.0f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
t = abs(t);
|
||||
|
||||
if (t < 3.0f)
|
||||
return Sinc(t) * Sinc(t / 3.0f);
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
private:
|
||||
static float Sinc(float x)
|
||||
{
|
||||
x *= JPH_PI;
|
||||
|
||||
if (abs(x) < 1.0e-5f)
|
||||
return 1.0f;
|
||||
|
||||
return Sin(x) / x;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class ImageFilterMitchell : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 2.0f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
float tt = t * t;
|
||||
t = abs(t);
|
||||
|
||||
if (t < 1.0f)
|
||||
return (7.0f * (t * tt) - 12.0f * tt + (16.0f / 3.0f)) / 6.0f;
|
||||
else if (t < 2.0f)
|
||||
return ((-7.0f / 3.0f) * (t * tt) + 12.0f * tt + -20.0f * t + (32.0f / 3.0f)) / 6.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
static const ImageFilter &GetFilter(EFilter inFilter)
|
||||
{
|
||||
static ImageFilterBox box;
|
||||
static ImageFilterTriangle triangle;
|
||||
static ImageFilterBell bell;
|
||||
static ImageFilterBSpline bspline;
|
||||
static ImageFilterLanczos3 lanczos3;
|
||||
static ImageFilterMitchell mitchell;
|
||||
|
||||
switch (inFilter)
|
||||
{
|
||||
case FilterBox: return box;
|
||||
case FilterTriangle: return triangle;
|
||||
case FilterBell: return bell;
|
||||
case FilterBSpline: return bspline;
|
||||
case FilterLanczos3: return lanczos3;
|
||||
case FilterMitchell: return mitchell;
|
||||
default: JPH_ASSERT(false); return mitchell;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ZoomSettings
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const ZoomSettings ZoomSettings::sDefault;
|
||||
|
||||
ZoomSettings::ZoomSettings() :
|
||||
mFilter(FilterMitchell),
|
||||
mWrapFilter(true),
|
||||
mBlur(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
bool ZoomSettings::operator == (const ZoomSettings &inRHS) const
|
||||
{
|
||||
return mFilter == inRHS.mFilter
|
||||
&& mWrapFilter == inRHS.mWrapFilter
|
||||
&& mBlur == inRHS.mBlur;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Resizing a surface
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Structure used for zooming
|
||||
struct Contrib
|
||||
{
|
||||
int mOffset; // Offset of this pixel (relative to start of scanline)
|
||||
int mWeight; // Weight of this pixel in 0.12 fixed point format
|
||||
};
|
||||
|
||||
static void sPrecalculateFilter(const ZoomSettings &inZoomSettings, int inOldLength, int inNewLength, int inOffsetFactor, Array<Array<Contrib>> &outContrib)
|
||||
{
|
||||
JPH_PROFILE("PrecalculateFilter");
|
||||
|
||||
// Get filter
|
||||
const ImageFilter &filter = GetFilter(inZoomSettings.mFilter);
|
||||
|
||||
// Get scale
|
||||
float scale = float(inNewLength) / inOldLength;
|
||||
|
||||
float fwidth, fscale;
|
||||
if (scale < 1.0f)
|
||||
{
|
||||
// Minify, broaden filter
|
||||
fwidth = filter.GetSupport() / scale;
|
||||
fscale = scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enlarge, filter is always used as is
|
||||
fwidth = filter.GetSupport();
|
||||
fscale = 1.0f;
|
||||
}
|
||||
|
||||
// Adjust filter for blur
|
||||
fwidth *= inZoomSettings.mBlur;
|
||||
fscale /= inZoomSettings.mBlur;
|
||||
float min_fwidth = 1.0f;
|
||||
if (fwidth < min_fwidth)
|
||||
{
|
||||
fwidth = min_fwidth;
|
||||
fscale = filter.GetSupport() / min_fwidth;
|
||||
}
|
||||
|
||||
// Make room for a whole scanline
|
||||
outContrib.resize(inNewLength);
|
||||
|
||||
// Loop over the whole scanline
|
||||
for (int i = 0; i < inNewLength; ++i)
|
||||
{
|
||||
// Compute center and left- and rightmost pixels affected
|
||||
float center = float(i) / scale;
|
||||
int left = int(floor(center - fwidth));
|
||||
int right = int(ceil(center + fwidth));
|
||||
|
||||
// Reserve required elements
|
||||
Array<Contrib> &a = outContrib[i];
|
||||
a.reserve(right - left + 1);
|
||||
|
||||
// Total sum of all weights, for renormalization of the filter
|
||||
int filter_sum = 0;
|
||||
|
||||
// Compute the contributions for each
|
||||
for (int source = left; source <= right; ++source)
|
||||
{
|
||||
Contrib c;
|
||||
|
||||
// Initialize the offset
|
||||
c.mOffset = source;
|
||||
|
||||
// Compute weight at this position in 0.12 fixed point
|
||||
c.mWeight = int(4096.0f * filter.GetValue(fscale * (center - source)));
|
||||
if (c.mWeight == 0) continue;
|
||||
|
||||
// Add weight to filter total
|
||||
filter_sum += c.mWeight;
|
||||
|
||||
// Reflect the filter at the edges if the filter is not to be wrapped (clamp)
|
||||
if (!inZoomSettings.mWrapFilter && (c.mOffset < 0 || c.mOffset >= inOldLength))
|
||||
c.mOffset = -c.mOffset - 1;
|
||||
|
||||
// Wrap the offset so that it falls within the image
|
||||
c.mOffset = (c.mOffset % inOldLength + inOldLength) % inOldLength;
|
||||
|
||||
// Check that the offset falls within the image
|
||||
JPH_ASSERT(c.mOffset >= 0 && c.mOffset < inOldLength);
|
||||
|
||||
// Multiply the offset with the specified factor
|
||||
c.mOffset *= inOffsetFactor;
|
||||
|
||||
// Add the filter element
|
||||
a.push_back(c);
|
||||
}
|
||||
|
||||
// Normalize the filter to 0.12 fixed point
|
||||
if (filter_sum != 0)
|
||||
for (uint n = 0; n < a.size(); ++n)
|
||||
a[n].mWeight = (a[n].mWeight * 4096) / filter_sum;
|
||||
}
|
||||
}
|
||||
|
||||
static void sZoomHorizontal(RefConst<Surface> inSrc, Ref<Surface> ioDst, const ZoomSettings &inZoomSettings)
|
||||
{
|
||||
JPH_PROFILE("ZoomHorizontal");
|
||||
|
||||
// Check zoom parameters
|
||||
JPH_ASSERT(inSrc->GetHeight() == ioDst->GetHeight());
|
||||
JPH_ASSERT(inSrc->GetFormat() == ioDst->GetFormat());
|
||||
|
||||
const int width = ioDst->GetWidth();
|
||||
const int height = ioDst->GetHeight();
|
||||
const int components = ioDst->GetNumberOfComponents();
|
||||
const int delta_s = -components;
|
||||
const int delta_d = ioDst->GetBytesPerPixel() - components;
|
||||
|
||||
// Pre-calculate filter contributions for a row
|
||||
Array<Array<Contrib>> contrib;
|
||||
sPrecalculateFilter(inZoomSettings, inSrc->GetWidth(), ioDst->GetWidth(), inSrc->GetBytesPerPixel(), contrib);
|
||||
|
||||
// Do the zoom
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const uint8 *s = inSrc->GetScanLine(y);
|
||||
uint8 *d = ioDst->GetScanLine(y);
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
const Array<Contrib> &line = contrib[x];
|
||||
const size_t line_size_min_one = line.size() - 1;
|
||||
|
||||
int c = components;
|
||||
do
|
||||
{
|
||||
int pixel = 0;
|
||||
|
||||
// Apply the filter for one color component
|
||||
size_t j = line_size_min_one;
|
||||
do
|
||||
{
|
||||
const Contrib &cmp = line[j];
|
||||
pixel += cmp.mWeight * s[cmp.mOffset];
|
||||
}
|
||||
while (j--);
|
||||
|
||||
// Clamp the pixel value
|
||||
if (pixel <= 0)
|
||||
*d = 0;
|
||||
else if (pixel >= (255 << 12))
|
||||
*d = 255;
|
||||
else
|
||||
*d = uint8(pixel >> 12);
|
||||
|
||||
++s;
|
||||
++d;
|
||||
}
|
||||
while (--c);
|
||||
|
||||
// Skip unused components if there are any
|
||||
s += delta_s;
|
||||
d += delta_d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sZoomVertical(RefConst<Surface> inSrc, Ref<Surface> ioDst, const ZoomSettings &inZoomSettings)
|
||||
{
|
||||
JPH_PROFILE("ZoomVertical");
|
||||
|
||||
// Check zoom parameters
|
||||
JPH_ASSERT(inSrc->GetWidth() == ioDst->GetWidth());
|
||||
JPH_ASSERT(inSrc->GetFormat() == ioDst->GetFormat());
|
||||
|
||||
const int width = ioDst->GetWidth();
|
||||
const int height = ioDst->GetHeight();
|
||||
const int components = ioDst->GetNumberOfComponents();
|
||||
const int delta_s = inSrc->GetBytesPerPixel() - components;
|
||||
const int delta_d = ioDst->GetBytesPerPixel() - components;
|
||||
|
||||
// Pre-calculate filter contributions for a row
|
||||
Array<Array<Contrib>> contrib;
|
||||
sPrecalculateFilter(inZoomSettings, inSrc->GetHeight(), ioDst->GetHeight(), inSrc->GetStride(), contrib);
|
||||
|
||||
// Do the zoom
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const uint8 *s = inSrc->GetScanLine(0);
|
||||
uint8 *d = ioDst->GetScanLine(y);
|
||||
const Array<Contrib> &line = contrib[y];
|
||||
const size_t line_size_min_one = line.size() - 1;
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
int c = components;
|
||||
do
|
||||
{
|
||||
int pixel = 0;
|
||||
|
||||
// Apply the filter for one color component
|
||||
size_t j = line_size_min_one;
|
||||
do
|
||||
{
|
||||
const Contrib &cmp = line[j];
|
||||
pixel += cmp.mWeight * s[cmp.mOffset];
|
||||
}
|
||||
while (j--);
|
||||
|
||||
// Clamp the pixel value
|
||||
if (pixel <= 0)
|
||||
*d = 0;
|
||||
else if (pixel >= (255 << 12))
|
||||
*d = 255;
|
||||
else
|
||||
*d = uint8(pixel >> 12);
|
||||
|
||||
++s;
|
||||
++d;
|
||||
}
|
||||
while (--c);
|
||||
|
||||
// Skip unused components if there are any
|
||||
s += delta_s;
|
||||
d += delta_d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ZoomImage(RefConst<Surface> inSrc, Ref<Surface> ioDst, const ZoomSettings &inZoomSettings)
|
||||
{
|
||||
JPH_PROFILE("ZoomImage");
|
||||
|
||||
// Get filter
|
||||
const ImageFilter &filter = GetFilter(inZoomSettings.mFilter);
|
||||
|
||||
// Determine the temporary format that will require the least amount of components to be zoomed and the least amount of bytes pushed around
|
||||
ESurfaceFormat tmp_format;
|
||||
ESurfaceFormat src_format = inSrc->GetClosest8BitFormat();
|
||||
ESurfaceFormat dst_format = ioDst->GetClosest8BitFormat();
|
||||
const FormatDescription &src_desc = GetFormatDescription(src_format);
|
||||
const FormatDescription &dst_desc = GetFormatDescription(dst_format);
|
||||
if (src_desc.GetNumberOfComponents() < dst_desc.GetNumberOfComponents())
|
||||
tmp_format = src_format;
|
||||
else if (src_desc.GetNumberOfComponents() > dst_desc.GetNumberOfComponents())
|
||||
tmp_format = dst_format;
|
||||
else if (src_desc.GetBytesPerPixel() < dst_desc.GetBytesPerPixel())
|
||||
tmp_format = src_format;
|
||||
else
|
||||
tmp_format = dst_format;
|
||||
|
||||
// Create temporary source buffer if necessary
|
||||
RefConst<Surface> src = inSrc;
|
||||
if (inSrc->GetFormat() != tmp_format)
|
||||
{
|
||||
Ref<Surface> tmp = new SoftwareSurface(inSrc->GetWidth(), inSrc->GetHeight(), tmp_format);
|
||||
if (!BlitSurface(inSrc, tmp))
|
||||
return false;
|
||||
src = tmp;
|
||||
}
|
||||
|
||||
// Create temporary destination buffer if necessary
|
||||
Ref<Surface> dst = ioDst;
|
||||
if (ioDst->GetFormat() != tmp_format)
|
||||
dst = new SoftwareSurface(ioDst->GetWidth(), ioDst->GetHeight(), tmp_format);
|
||||
|
||||
src->Lock(ESurfaceLockMode::Read);
|
||||
dst->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
if (src->GetWidth() == dst->GetWidth())
|
||||
{
|
||||
// Only vertical zoom required
|
||||
sZoomVertical(src, dst, inZoomSettings);
|
||||
}
|
||||
else if (src->GetHeight() == dst->GetHeight())
|
||||
{
|
||||
// Only horizontal zoom required
|
||||
sZoomHorizontal(src, dst, inZoomSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine most optimal order
|
||||
float operations_vh = float(dst->GetWidth()) * (filter.GetSupport() * src->GetHeight() + filter.GetSupport() * dst->GetHeight());
|
||||
float operations_hv = float(dst->GetHeight()) * (filter.GetSupport() * src->GetWidth() + filter.GetSupport() * dst->GetWidth());
|
||||
if (operations_vh < operations_hv)
|
||||
{
|
||||
// Create temporary buffer to hold the vertical scale
|
||||
Ref<Surface> tmp = new SoftwareSurface(src->GetWidth(), dst->GetHeight(), tmp_format);
|
||||
tmp->Lock(ESurfaceLockMode::ReadWrite);
|
||||
|
||||
// First scale vertically then horizontally
|
||||
sZoomVertical(src, tmp, inZoomSettings);
|
||||
sZoomHorizontal(tmp, dst, inZoomSettings);
|
||||
|
||||
tmp->UnLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create temporary buffer to hold the horizontal scale
|
||||
Ref<Surface> tmp = new SoftwareSurface(dst->GetWidth(), src->GetHeight(), tmp_format);
|
||||
tmp->Lock(ESurfaceLockMode::ReadWrite);
|
||||
|
||||
// First scale horizontally then vertically
|
||||
sZoomHorizontal(src, tmp, inZoomSettings);
|
||||
sZoomVertical(tmp, dst, inZoomSettings);
|
||||
|
||||
tmp->UnLock();
|
||||
}
|
||||
}
|
||||
|
||||
src->UnLock();
|
||||
dst->UnLock();
|
||||
|
||||
// Convert to destination if required
|
||||
if (dst != ioDst)
|
||||
if (!BlitSurface(dst, ioDst))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
41
lib/All/JoltPhysics/TestFramework/Image/ZoomImage.h
Normal file
41
lib/All/JoltPhysics/TestFramework/Image/ZoomImage.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
class Surface;
|
||||
|
||||
/// Filter function used to rescale the image
|
||||
enum EFilter
|
||||
{
|
||||
FilterBox,
|
||||
FilterTriangle,
|
||||
FilterBell,
|
||||
FilterBSpline,
|
||||
FilterLanczos3,
|
||||
FilterMitchell,
|
||||
};
|
||||
|
||||
/// Zoom settings for ZoomImage
|
||||
class ZoomSettings
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
ZoomSettings();
|
||||
|
||||
/// Comparison operators
|
||||
bool operator == (const ZoomSettings &inRHS) const;
|
||||
|
||||
/// Default settings
|
||||
static const ZoomSettings sDefault;
|
||||
|
||||
EFilter mFilter; ///< Filter function for image scaling
|
||||
bool mWrapFilter; ///< If true, the filter will be applied wrapping around the image, this provides better results for repeating textures
|
||||
float mBlur; ///< If > 1 then the image will be blurred, if < 1 the image will be sharpened
|
||||
};
|
||||
|
||||
/// Function to resize an image
|
||||
bool ZoomImage(RefConst<Surface> inSrc, Ref<Surface> ioDst, const ZoomSettings &inZoomSettings = ZoomSettings::sDefault);
|
||||
Reference in New Issue
Block a user