First commit.
This commit is contained in:
1488
src/io/img/Img.cpp
Normal file
1488
src/io/img/Img.cpp
Normal file
File diff suppressed because it is too large
Load Diff
110
src/io/img/ImgCodec.cpp
Normal file
110
src/io/img/ImgCodec.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "ehs/io/img/ImgCodec.h"
|
||||
#include "ehs/io/img/Img.h"
|
||||
|
||||
namespace ehs
|
||||
{
|
||||
ImgCodec::ImgCodec()
|
||||
: hashExt(0), endianness(Endianness::LE), encodeCb(nullptr), decodeCb(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ImgCodec::ImgCodec(Str_8 id, Str_8 ext, const Endianness end,
|
||||
bool (* encodeCb)(const ImgCodec* const, Serializer<UInt_64>&, const Img*),
|
||||
bool (* decodeCb)(const ImgCodec* const, Serializer<UInt_64>&, Img*))
|
||||
: id(std::move(id)), hashExt(ext.Hash_64()), ext(std::move(ext)), endianness(end), encodeCb(encodeCb), decodeCb(decodeCb)
|
||||
{
|
||||
}
|
||||
|
||||
ImgCodec::ImgCodec(ImgCodec&& codec) noexcept
|
||||
: id(std::move(codec.id)), hashExt(codec.hashExt), ext(std::move(codec.ext)), endianness(codec.endianness),
|
||||
encodeCb(codec.encodeCb), decodeCb(codec.decodeCb)
|
||||
{
|
||||
codec.hashExt = 0;
|
||||
codec.endianness = Endianness::LE;
|
||||
codec.encodeCb = nullptr;
|
||||
codec.decodeCb = nullptr;
|
||||
}
|
||||
|
||||
ImgCodec::ImgCodec(const ImgCodec& codec)
|
||||
: id(codec.id), hashExt(codec.hashExt), ext(codec.ext), endianness(codec.endianness), encodeCb(codec.encodeCb),
|
||||
decodeCb(codec.decodeCb)
|
||||
{
|
||||
}
|
||||
|
||||
ImgCodec& ImgCodec::operator=(ImgCodec&& codec) noexcept
|
||||
{
|
||||
if (this == &codec)
|
||||
return *this;
|
||||
|
||||
id = std::move(codec.id);
|
||||
hashExt = codec.hashExt;
|
||||
ext = std::move(codec.ext);
|
||||
endianness = codec.endianness;
|
||||
encodeCb = codec.encodeCb;
|
||||
decodeCb = codec.decodeCb;
|
||||
|
||||
codec.hashExt = 0;
|
||||
codec.endianness = Endianness::LE;
|
||||
codec.encodeCb = nullptr;
|
||||
codec.decodeCb = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImgCodec& ImgCodec::operator=(const ImgCodec& codec)
|
||||
{
|
||||
if (this == &codec)
|
||||
return *this;
|
||||
|
||||
id = codec.id;
|
||||
hashExt = codec.hashExt;
|
||||
ext = codec.ext;
|
||||
endianness = codec.endianness;
|
||||
encodeCb = codec.encodeCb;
|
||||
decodeCb = codec.decodeCb;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Str_8 ImgCodec::GetId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
UInt_64 ImgCodec::GetHashExt() const
|
||||
{
|
||||
return hashExt;
|
||||
}
|
||||
|
||||
Str_8 ImgCodec::GetExt() const
|
||||
{
|
||||
return ext;
|
||||
}
|
||||
|
||||
Endianness ImgCodec::GetEndianness() const
|
||||
{
|
||||
return endianness;
|
||||
}
|
||||
|
||||
bool ImgCodec::Encode(Serializer<UInt_64>& out, const Img* in) const
|
||||
{
|
||||
if (!encodeCb)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Encoding is not supported for the " + id + " format.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return encodeCb(this, out, in);
|
||||
}
|
||||
|
||||
bool ImgCodec::Decode(Serializer<UInt_64>& in, Img* out) const
|
||||
{
|
||||
if (!decodeCb)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Decoding is not supported for the " + id + " format.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return decodeCb(this, in, out);
|
||||
}
|
||||
}
|
232
src/io/img/PNG.cpp
Normal file
232
src/io/img/PNG.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
#include "ehs/io/img/PNG.h"
|
||||
|
||||
namespace ehs
|
||||
{
|
||||
Array<Byte, UInt_64> pngSeq = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
|
||||
PNG::PNG()
|
||||
: hashId(0)
|
||||
{
|
||||
}
|
||||
|
||||
PNG::PNG(const Str_8& filePath)
|
||||
{
|
||||
id = File::ParseName_8(filePath);
|
||||
hashId = id.Hash_64();
|
||||
|
||||
File file(filePath, Mode::READ, Disposition::OPEN);
|
||||
|
||||
Serializer<UInt_64> data = file.ReadSerializer_64(Endianness::BE, file.Size());
|
||||
|
||||
file.Release();
|
||||
|
||||
Array<Byte, UInt_64> seq = data.ReadArray<Byte, UInt_64>(8);
|
||||
if (seq != pngSeq)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "File at file path, \"" + filePath + "\", is not a valid PNG file.");
|
||||
return;
|
||||
}
|
||||
|
||||
while (data.GetOffset() < data.Size())
|
||||
{
|
||||
UInt_32 length = data.Read<UInt_32>();
|
||||
|
||||
Str_8 id = data.ReadStr<Char_8, UInt_64>(4);
|
||||
|
||||
Serializer<UInt_64> chunkData(Endianness::BE, &data[data.GetOffset()], length);
|
||||
data.SetOffset(data.GetOffset() + length);
|
||||
|
||||
Byte crc[4];
|
||||
UInt_64 crcSize = 4;
|
||||
|
||||
data.ReadArray(crc, &crcSize);
|
||||
|
||||
PNG_Chunk* chunk = GetChunk(id);
|
||||
if (chunk)
|
||||
{
|
||||
chunk->GetData()->SetOffset(chunk->GetData()->Size());
|
||||
chunk->GetData()->WriteSer(chunkData);
|
||||
chunk->GetData()->SetOffset(0);
|
||||
}
|
||||
else
|
||||
chunks.Push(PNG_Chunk(id, chunkData, crc));
|
||||
}
|
||||
}
|
||||
|
||||
PNG::PNG(const Str_8& id, Serializer<UInt_64>& data)
|
||||
: id(id), hashId(id.Hash_64())
|
||||
{
|
||||
Array<Byte, UInt_64> seq = data.ReadArray<Byte, UInt_64>(8);
|
||||
if (seq != pngSeq)
|
||||
return;
|
||||
|
||||
while (data.GetOffset() < data.Size())
|
||||
{
|
||||
UInt_32 length = data.Read<UInt_32>();
|
||||
|
||||
Str_8 id = data.ReadStr<Char_8, UInt_64>(4);
|
||||
|
||||
Serializer<UInt_64> chunkData(Endianness::BE, &data[data.GetOffset()], length);
|
||||
data.SetOffset(data.GetOffset() + length);
|
||||
|
||||
Byte crc[4];
|
||||
UInt_64 crcSize = 4;
|
||||
|
||||
data.ReadArray(crc, &crcSize);
|
||||
|
||||
PNG_Chunk* chunk = GetChunk(id);
|
||||
if (chunk)
|
||||
{
|
||||
chunk->GetData()->SetOffset(chunk->GetData()->Size());
|
||||
chunk->GetData()->WriteSer(chunkData);
|
||||
chunk->GetData()->SetOffset(0);
|
||||
}
|
||||
else
|
||||
chunks.Push(PNG_Chunk(id, chunkData, crc));
|
||||
}
|
||||
}
|
||||
|
||||
PNG::PNG(const PNG& png)
|
||||
: id(png.id), hashId(png.hashId), chunks(png.chunks)
|
||||
{
|
||||
}
|
||||
|
||||
PNG& PNG::operator=(const PNG& png)
|
||||
{
|
||||
if (this == &png)
|
||||
return *this;
|
||||
|
||||
id = png.id;
|
||||
hashId = png.hashId;
|
||||
chunks = png.chunks;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool PNG::HasChunk(const UInt_64 hashId) const
|
||||
{
|
||||
for (UInt_64 i = 0; i < chunks.Size(); ++i)
|
||||
if (chunks[i].GetHashId() == hashId)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PNG::HasChunk(const Str_8& id) const
|
||||
{
|
||||
return HasChunk(id.Hash_64());
|
||||
}
|
||||
|
||||
PNG_Chunk* PNG::GetChunk(const UInt_64 hashId)
|
||||
{
|
||||
for (UInt_64 i = 0; i < chunks.Size(); ++i)
|
||||
if (chunks[i].GetHashId() == hashId)
|
||||
return &chunks[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PNG_Chunk* PNG::GetChunk(const Str_8& id)
|
||||
{
|
||||
return GetChunk(id.Hash_64());
|
||||
}
|
||||
|
||||
bool PNG::IsPNG(Serializer<UInt_32>& data)
|
||||
{
|
||||
UInt_32 oldOffset = data.GetOffset();
|
||||
data.SetOffset(0);
|
||||
|
||||
Array<Byte, UInt_32> seq = data.ReadArray<Byte, UInt_32>(8);
|
||||
if (seq != pngSeq)
|
||||
return false;
|
||||
|
||||
data.SetOffset(oldOffset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PNG::FilterNone(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline)
|
||||
{
|
||||
UInt_8 bytes = bitDepth / 8;
|
||||
|
||||
for (UInt_32 i = 0; i < scanline; i += bytes)
|
||||
for (UInt_8 b = 0; b < bytes; ++b)
|
||||
out[i + b] = in[i + bytes - 1 - b];
|
||||
}
|
||||
|
||||
void PNG::FilterSub(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline)
|
||||
{
|
||||
UInt_8 bytes = bitDepth / 8;
|
||||
UInt_8 pxSize = bytes * channels;
|
||||
|
||||
for (UInt_32 i = 0; i < scanline; i += bytes)
|
||||
{
|
||||
for (UInt_8 b = 0; b < bytes; ++b)
|
||||
{
|
||||
if (i >= pxSize)
|
||||
out[i + b] = in[i + bytes - 1 - b] + out[i - pxSize + b];
|
||||
else
|
||||
out[i + b] = in[i + bytes - 1 - b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PNG::FilterUp(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline)
|
||||
{
|
||||
UInt_8 bytes = bitDepth / 8;
|
||||
|
||||
for (UInt_32 i = 0; i < scanline; i += bytes)
|
||||
for (UInt_8 b = 0; b < bytes; ++b)
|
||||
out[scanline + i + b] = in[i + bytes - 1 - b] + out[i + b];
|
||||
}
|
||||
|
||||
void PNG::FilterAverage(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline)
|
||||
{
|
||||
UInt_8 bytes = bitDepth / 8;
|
||||
UInt_8 pxSize = bytes * channels;
|
||||
|
||||
for (UInt_32 i = 0; i < scanline; i += bytes)
|
||||
{
|
||||
for (UInt_8 b = 0; b < bytes; ++b)
|
||||
{
|
||||
Byte prevPx = 0;
|
||||
if (i >= pxSize)
|
||||
prevPx = out[scanline + i - pxSize + b];
|
||||
|
||||
out[scanline + i + b] = in[i + bytes - 1 - b] + (Byte)Math::Floor(((float)prevPx + (float)out[i + b]) / 2.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PNG::FilterPaeth(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline)
|
||||
{
|
||||
UInt_8 bytes = bitDepth / 8;
|
||||
UInt_8 pxSize = bytes * channels;
|
||||
|
||||
for (UInt_32 i = 0; i < scanline; i += bytes)
|
||||
{
|
||||
for (UInt_8 b = 0; b < bytes; ++b)
|
||||
{
|
||||
if (i >= pxSize)
|
||||
out[scanline + i + b] = in[i + bytes - 1 - b] + PaethPredictor(out[scanline + i - pxSize + b], out[i + b], out[i - pxSize + b]);
|
||||
else
|
||||
out[scanline + i + b] = in[i + bytes - 1 - b] + out[i + b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Byte PNG::PaethPredictor(const Byte a, const Byte b, const Byte c)
|
||||
{
|
||||
SInt_16 p = a + b - c;
|
||||
SInt_16 pa = Math::Abs(p - a);
|
||||
SInt_16 pb = Math::Abs(p - b);
|
||||
SInt_16 pc = Math::Abs(p - c);
|
||||
|
||||
if (pa <= pb && pa <= pc)
|
||||
return a;
|
||||
else if (pb <= pc)
|
||||
return b;
|
||||
else
|
||||
return c;
|
||||
}
|
||||
}
|
55
src/io/img/PNG_Chunk.cpp
Normal file
55
src/io/img/PNG_Chunk.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "ehs/io/img/PNG_Chunk.h"
|
||||
|
||||
namespace ehs
|
||||
{
|
||||
PNG_Chunk::PNG_Chunk()
|
||||
: hashId(0), crc{0x0, 0x0, 0x0, 0x0}
|
||||
{
|
||||
}
|
||||
|
||||
PNG_Chunk::PNG_Chunk(const Str_8& id, const Serializer<UInt_64>& data, const Byte crc[4])
|
||||
: id(id), hashId(id.Hash_64()), data(data), crc{crc[0], crc[1], crc[2], crc[3]}
|
||||
{
|
||||
}
|
||||
|
||||
PNG_Chunk::PNG_Chunk(const PNG_Chunk& chunk)
|
||||
: id(chunk.id), hashId(chunk.hashId), data(chunk.data), crc{chunk.crc[0], chunk.crc[1], chunk.crc[2], chunk.crc[3]}
|
||||
{
|
||||
}
|
||||
|
||||
PNG_Chunk& PNG_Chunk::operator=(const PNG_Chunk& chunk)
|
||||
{
|
||||
if (this == &chunk)
|
||||
return *this;
|
||||
|
||||
id = chunk.id;
|
||||
hashId = chunk.hashId;
|
||||
data = chunk.data;
|
||||
crc[0] = chunk.crc[0];
|
||||
crc[1] = chunk.crc[1];
|
||||
crc[2] = chunk.crc[2];
|
||||
crc[3] = chunk.crc[3];
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Str_8 PNG_Chunk::GetId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
UInt_64 PNG_Chunk::GetHashId() const
|
||||
{
|
||||
return hashId;
|
||||
}
|
||||
|
||||
Serializer<UInt_64>* PNG_Chunk::GetData()
|
||||
{
|
||||
return &data;
|
||||
}
|
||||
|
||||
const unsigned char* PNG_Chunk::GetCRC() const
|
||||
{
|
||||
return crc;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user