Changed up how resources import/export files. Added Model codecs.

This commit is contained in:
2024-02-20 22:47:52 -08:00
parent 54012df3a1
commit 93f881cf03
20 changed files with 998 additions and 882 deletions

View File

@@ -1,4 +1,7 @@
#include "ehs/io/img/Img.h"
#include "ehs/io/img/PNG.h"
#include <zlib.h>
namespace ehs
{
@@ -53,6 +56,30 @@ namespace ehs
AddType("Img");
}
Img::Img(const Str_8& filePath)
: byteDepth(0), channels(0), size(0), data(nullptr)
{
AddType("Img");
File file(filePath, Mode::READ, Disposition::OPEN);
Str_8 ext = file.GetExtension();
hashId = file.GetName().Hash_64();
id = file.GetName();
const ImgCodec* codec = GetCodec(ext);
if (!codec)
{
EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\".");
return;
}
Serializer<UInt_64> inData = file.ReadSerializer_64(codec->GetEndianness(), file.Size());
file.Release();
codec->Decode(inData, this);
}
Img::Img(Str_8 id, const UInt_8 byteDepth, const UInt_8 channels, const Vec2_u64& resolution, const Byte* const data)
: hashId(id.Hash_64()), id((Str_8&&)id), byteDepth(byteDepth), channels(channels), resolution(resolution),
size(resolution.x * byteDepth * channels * resolution.y), data(new Byte[size])
@@ -69,12 +96,6 @@ namespace ehs
AddType("Img");
}
Img::Img(Str_8 id)
: hashId(id.Hash_64()), id((Str_8&&)id), byteDepth(0), channels(0), size(0), data(nullptr)
{
AddType("Img");
}
Img::Img(Img&& img) noexcept
: BaseObj((BaseObj&&)img), hashId(img.hashId), id((Str_8&&)img.id), byteDepth(img.byteDepth),
channels(img.channels), resolution(img.resolution), size(img.size), data(img.data)
@@ -849,7 +870,7 @@ namespace ehs
return size;
}
bool Img::ToFile(const Str_8& filePath) const
bool Img::Export(const Str_8& filePath) const
{
Str_8 ext = File::ParseExt_8(filePath);
@@ -870,76 +891,6 @@ namespace ehs
return true;
}
Img Img::FromFile(const Str_8& filePath)
{
File file(filePath, Mode::READ, Disposition::OPEN);
Str_8 ext = file.GetExtension();
Img result(file.GetName());
const ImgCodec* codec = GetCodec(ext);
if (!codec)
{
EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\".");
return result;
}
Serializer<UInt_64> data = file.ReadSerializer_64(codec->GetEndianness(), file.Size());
file.Release();
if (!codec->Decode(data, &result))
return {};
return result;
}
Img* Img::FromFile_Heap(const Str_8& filePath)
{
File file(filePath, Mode::READ, Disposition::OPEN);
Str_8 ext = file.GetExtension();
Img* result = nullptr;
const ImgCodec* codec = GetCodec(ext);
if (!codec)
{
EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\".");
return result;
}
result = new Img(file.GetName());
Serializer<UInt_64> data = file.ReadSerializer_64(codec->GetEndianness(), file.Size());
file.Release();
if (!codec->Decode(data, result))
{
delete result;
return nullptr;
}
return result;
}
Img Img::FromData(Str_8 id, const Str_8& ext, Serializer<UInt_64>& data)
{
Img result((Str_8&&)id);
const ImgCodec* codec = GetCodec(ext);
if (!codec)
{
EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\".");
return result;
}
if (!codec->Decode(data, &result))
return {};
return result;
}
Img Img::GetNearestNeighbor(const Vec2_u64& newResolution) const
{
Img result(id, byteDepth, channels, newResolution);
@@ -1485,4 +1436,320 @@ namespace ehs
for (UInt_64 i = 0, n = 0; i < newSize; ++i, n += 2)
buffer[i] = (Byte)((float)*(UInt_16*)&data[n] / (float)EHS_UINT_16_MAX * (float)EHS_UINT_8_MAX);
}
bool EncodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer<ehs::UInt_64>& out, const ehs::Img* in)
{
UInt_8 channels = in->GetChannels();
Vec2_u64 resolution = in->GetResolution();
UInt_32 px_len = resolution.x * resolution.y * channels;
UInt_32 px_end = px_len - channels;
Byte index[256];
for (UInt_64 i = 0; i < 64; ++i)
*(UInt_32*)&index[i * 4] = 0;
Byte prevPixel[4] = {0, 0, 0, 255};
Byte pixel[4] = {0, 0, 0, 255};
Serializer<UInt_32> result(Endianness::BE, resolution.x * resolution.y * (channels + 1) + 22);
result.Write('q');
result.Write('o');
result.Write('i');
result.Write('f');
result.Write<UInt_32>(resolution.x);
result.Write<UInt_32>(resolution.y);
result.Write(in->GetChannels());
result.Write(1);
for (UInt_32 px_pos = 0, run = 0; px_pos < px_len; px_pos += channels)
{
if (channels == 4)
{
*(UInt_32*)pixel = *(UInt_32*)&(*in)[px_pos];
}
else
{
pixel[0] = (*in)[px_pos];
pixel[1] = (*in)[px_pos + 1];
pixel[2] = (*in)[px_pos + 2];
}
if (*(UInt_32*)pixel == *(UInt_32*)prevPixel)
{
run++;
if (run == 62 || px_pos == px_end)
{
result.Write<UInt_8>(0xc0 | (run - 1));
run = 0;
}
}
else
{
if (run > 0)
{
result.Write<UInt_8>(0xc0 | (run - 1));
run = 0;
}
UInt_32 index_pos = (prevPixel[0] * 3 + prevPixel[1] * 5 + prevPixel[2] * 7 + prevPixel[3] * 11) % 64 * channels;
if (*(UInt_32*)&index[index_pos] == *(UInt_32*)pixel)
{
result.Write<UInt_8>(0x00 | (index_pos / channels));
}
else
{
*(UInt_32*)&index[index_pos] = *(UInt_32*)pixel;
if (pixel[3] == prevPixel[3])
{
SInt_8 vr = pixel[0] - prevPixel[0];
SInt_8 vg = pixel[1] - prevPixel[1];
SInt_8 vb = pixel[2] - prevPixel[2];
SInt_8 vg_r = vr - vg;
SInt_8 vg_b = vb - vg;
if (
vr > -3 && vr < 2 &&
vg > -3 && vg < 2 &&
vb > -3 && vb < 2
)
{
result.Write<UInt_8>(0x40 | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2));
}
else if (
vg_r > -9 && vg_r < 8 &&
vg > -33 && vg < 32 &&
vg_b > -9 && vg_b < 8
)
{
result.Write<UInt_8>(0x80 | (vg + 32));
result.Write<UInt_8>((vg_r + 8) << 4 | (vg_b + 8));
}
else
{
result.Write<UInt_8>(0xfe);
result.Write(pixel[0]);
result.Write(pixel[1]);
result.Write(pixel[2]);
}
}
else
{
result.Write<UInt_8>(0xff);
result.SetEndianness(CPU::GetEndianness());
result.Write(*(UInt_32*)pixel);
result.SetEndianness(Endianness::BE);
}
}
}
*(UInt_32*)prevPixel = *(UInt_32*)pixel;
}
result.Write(0x100000000000000);
return true;
}
bool DecodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer<ehs::UInt_64>& in, ehs::Img* out)
{
Str_8 imgType = in.ReadStr<Char_8, UInt_64>(4);
if (imgType != "qoif")
{
EHS_LOG_INT("Error", 0, "Given data is not in the qoif format.");
return false;
}
UInt_64 width = in.Read<UInt_32>();
UInt_64 height = in.Read<UInt_32>();
UInt_8 channels = in.Read<UInt_8>();
channels = 4;
UInt_8 space = in.Read<UInt_8>();
UInt_8 bitDepth = 8;
UInt_64 size = width * channels * height;
*out = Img(out->GetId(), bitDepth / 8, channels, {width, height});
Byte prevPixel[4] = {0, 0, 0, 255};
Byte index[256];
for (UInt_64 i = 0; i < 64; ++i)
*(UInt_32*)&index[i * 4] = 0;
UInt_32 chunksLen = in.Size() - 8;
for (UInt_32 pos = 0, run = 0; pos < size; pos += channels)
{
if (run > 0)
--run;
else if (in.GetOffset() < chunksLen)
{
UInt_32 chunkType = (UInt_32)in.Read<UInt_8>();
if (chunkType == 0xfe) // RGB
{
prevPixel[0] = in.Read<UInt_8>(); // R-value
prevPixel[1] = in.Read<UInt_8>(); // G-value
prevPixel[2] = in.Read<UInt_8>(); // B-value
}
else if (chunkType == 0xff) // RGBA
{
*(UInt_32*)prevPixel = in.Read<UInt_32>();
}
else if ((chunkType & 0xc0) == 0x00) // Index
{
*(UInt_32*)prevPixel = *(UInt_32*)&index[chunkType * channels];
}
else if ((chunkType & 0xc0) == 0x40) // Diff
{
prevPixel[0] += ((chunkType >> 4) & 0x03) - 2; // R-value
prevPixel[1] += ((chunkType >> 2) & 0x03) - 2; // G-value
prevPixel[2] += (chunkType & 0x03) - 2; // B-value
}
else if ((chunkType & 0xc0) == 0x80) // Luma
{
UInt_32 mod = (UInt_32)in.Read<UInt_8>();
UInt_32 vg = (chunkType & 0x3f) - 32;
prevPixel[0] += vg - 8 + ((mod >> 4) & 0x0f); // R-value
prevPixel[1] += vg; // G-value
prevPixel[2] += vg - 8 + (mod & 0x0f); // B-value
}
else if ((chunkType & 0xc0) == 0xc0) // Run
run = (chunkType & 0x3f);
*(UInt_32*)&index[(prevPixel[0] * 3 + prevPixel[1] * 5 + prevPixel[2] * 7 + prevPixel[3] * 11) % 64 * channels] = *(UInt_32*)prevPixel;
}
if (channels == 4)
{
*((UInt_32*)&(*out)[pos]) = *(UInt_32*)prevPixel;
}
else
{
(*out)[pos] = prevPixel[0];
(*out)[pos + 1] = prevPixel[1];
(*out)[pos + 2] = prevPixel[2];
}
}
return true;
}
bool DecodePNG(const ehs::ImgCodec* const codec, ehs::Serializer<ehs::UInt_64>& in, ehs::Img* out)
{
PNG png(out->GetId(), in);
PNG_Chunk* ihdr = png.GetChunk("IHDR");
Serializer<UInt_64>* ihdrData = ihdr->GetData();
UInt_32 width = ihdrData->Read<UInt_32>();
UInt_32 height = ihdrData->Read<UInt_32>();
UInt_8 bitDepth = ihdrData->Read<UInt_8>();
UInt_8 colorType = ihdrData->Read<UInt_8>();
if (colorType == 3)
{
EHS_LOG_INT("Error", 1, "Color type of " + Str_8::FromNum(colorType) + " is unsupported.");
return false;
}
UInt_8 channels = 1;
if (colorType == 2)
channels = 3;
else if (colorType == 4)
channels = 2;
else if (colorType == 6)
channels = 4;
*out = Img(out->GetId(), bitDepth / 8, channels, {width, height});
UInt_8 compression = ihdrData->Read<UInt_8>();
if (compression)
{
EHS_LOG_INT("Error", 2, "Compression method of " + Str_8::FromNum(compression) + " is unsupported.");
return false;
}
UInt_8 filter = ihdrData->Read<UInt_8>();
if (filter)
{
EHS_LOG_INT("Error", 3, "Filter method of " + Str_8::FromNum(filter) + " is unsupported.");
return false;
}
UInt_8 interlaced = ihdrData->Read<UInt_8>();
if (interlaced)
{
EHS_LOG_INT("Error", 4, "Interlacing method of " + Str_8::FromNum(interlaced) + " is unsupported.");
return false;
}
UInt_32 scanline = width * (bitDepth / 8) * channels;
UInt_32 scanLineF = scanline + 1;
UInt_32 bufferSize = scanline * height + height;
Byte* buffer = new Byte[bufferSize];
PNG_Chunk* idat = png.GetChunk("IDAT");
Serializer<UInt_64>* idatData = idat->GetData();
z_stream strm = {};
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = idatData->Size();
strm.next_in = *idatData;
strm.avail_out = bufferSize;
strm.next_out = buffer;
int code = inflateInit(&strm);
if (code != Z_OK)
{
EHS_LOG_INT("Error", 5, "Failed to initialize zlib inflate with error #" + Str_8::FromNum(code) + ".");
delete[] buffer;
return false;
}
do
{
code = inflate(&strm, Z_NO_FLUSH);
if (code != Z_STREAM_END && code != Z_OK)
{
EHS_LOG_INT("Error", 6, "Failed to zlib inflate with error #" + Str_8::FromNum(code) + ".");
delete[] buffer;
return false;
}
} while (strm.avail_out);
code = inflateEnd(&strm);
if (code != Z_OK)
{
EHS_LOG_INT("Error", 7, "Failed to uninitialize zlib inflate with error #" + Str_8::FromNum(code) + ".");
delete[] buffer;
return false;
}
for (UInt_32 i = 0, o = 0; i < bufferSize; i += scanLineF, o += scanline)
{
UInt_8 fCode = buffer[i];
if (fCode == 0)
PNG::FilterNone(&buffer[i + 1], &(*out)[o], bitDepth, channels, scanline);
else if (fCode == 1)
PNG::FilterSub(&buffer[i + 1], &(*out)[o], bitDepth, channels, scanline);
else if (fCode == 2)
PNG::FilterUp(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline);
else if (fCode == 3)
PNG::FilterAverage(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline);
else if (fCode == 4)
PNG::FilterPaeth(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline);
}
delete[] buffer;
return true;
}
}