200 lines
4.6 KiB
C++
200 lines
4.6 KiB
C++
|
|
// 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;
|
||
|
|
}
|