2024-02-05 22:25:30 -08:00
|
|
|
#include "ehs/io/img/Img.h"
|
2024-02-20 22:47:52 -08:00
|
|
|
#include "ehs/io/img/PNG.h"
|
|
|
|
|
|
|
|
#include <zlib.h>
|
2024-02-05 22:25:30 -08:00
|
|
|
|
|
|
|
namespace ehs
|
|
|
|
{
|
|
|
|
Array<ImgCodec> Img::codecs;
|
|
|
|
|
|
|
|
bool Img::HasCodec(const UInt_64 hashExt)
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0; i < codecs.Size(); ++i)
|
|
|
|
if (codecs[i].GetHashExt() == hashExt)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Img::HasCodec(const Str_8& ext)
|
|
|
|
{
|
|
|
|
return HasCodec(ext.Hash_64());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Img::AddCodec(ImgCodec codec)
|
|
|
|
{
|
|
|
|
if (HasCodec(codec.GetHashExt()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
codecs.Push(std::move(codec));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ImgCodec* Img::GetCodec(const UInt_64 hashExt)
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0; i < codecs.Size(); ++i)
|
|
|
|
if (codecs[i].GetHashExt() == hashExt)
|
|
|
|
return &codecs[i];
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ImgCodec* Img::GetCodec(const Str_8& ext)
|
|
|
|
{
|
|
|
|
return GetCodec(ext.Hash_64());
|
|
|
|
}
|
|
|
|
|
|
|
|
Img::~Img()
|
|
|
|
{
|
|
|
|
delete[] data;
|
|
|
|
}
|
|
|
|
|
|
|
|
Img::Img()
|
|
|
|
: hashId(0), data(nullptr), byteDepth(0), channels(0), size(0)
|
|
|
|
{
|
|
|
|
AddType("Img");
|
|
|
|
}
|
|
|
|
|
2024-02-20 22:47:52 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-02-05 22:25:30 -08:00
|
|
|
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])
|
|
|
|
{
|
|
|
|
Util::Copy(this->data, data, size);
|
|
|
|
|
|
|
|
AddType("Img");
|
|
|
|
}
|
|
|
|
|
|
|
|
Img::Img(Str_8 id, const UInt_8 byteDepth, const UInt_8 channels, const Vec2_u64& resolution)
|
|
|
|
: 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])
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
img.byteDepth = 0;
|
|
|
|
img.channels = 0;
|
|
|
|
img.resolution = {};
|
|
|
|
img.size = 0;
|
|
|
|
img.data = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Img::Img(const Img& img)
|
|
|
|
: BaseObj(img), hashId(img.hashId), id(img.id), byteDepth(img.byteDepth), channels(img.channels),
|
|
|
|
resolution(img.resolution), size(img.size), data(new Byte[img.size])
|
|
|
|
{
|
|
|
|
Util::Copy(data, img.data, img.size);
|
|
|
|
}
|
|
|
|
|
|
|
|
Img& Img::operator=(Img&& img) noexcept
|
|
|
|
{
|
|
|
|
if (this == &img)
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
BaseObj::operator=((BaseObj&&)img);
|
|
|
|
|
|
|
|
byteDepth = img.byteDepth;
|
|
|
|
channels = img.channels;
|
|
|
|
resolution = img.resolution;
|
|
|
|
size = img.size;
|
|
|
|
delete[] data;
|
|
|
|
data = img.data;
|
|
|
|
|
|
|
|
img.byteDepth = 0;
|
|
|
|
img.channels = 0;
|
|
|
|
img.resolution = {};
|
|
|
|
img.size = 0;
|
|
|
|
img.data = nullptr;
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
Img& Img::operator=(const Img& img)
|
|
|
|
{
|
|
|
|
if (this == &img)
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
BaseObj::operator=(img);
|
|
|
|
|
|
|
|
byteDepth = img.byteDepth;
|
|
|
|
channels = img.channels;
|
|
|
|
resolution = img.resolution;
|
|
|
|
size = img.size;
|
|
|
|
delete[] data;
|
|
|
|
data = new Byte[img.size];
|
|
|
|
Util::Copy(data, img.data, img.size);
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
Img::operator const Byte* () const
|
|
|
|
{
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
Img::operator Byte* ()
|
|
|
|
{
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::Release()
|
|
|
|
{
|
|
|
|
byteDepth = 0;
|
|
|
|
channels = 0;
|
|
|
|
resolution = {};
|
|
|
|
size = 0;
|
|
|
|
delete[] data;
|
|
|
|
data = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt_64 Img::GetHashId() const
|
|
|
|
{
|
|
|
|
return hashId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::SetId(Str_8 newId)
|
|
|
|
{
|
|
|
|
hashId = newId.Hash_64();
|
|
|
|
id = (Str_8&&)newId;
|
|
|
|
}
|
|
|
|
|
|
|
|
Str_8 Img::GetId() const
|
|
|
|
{
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt_8 Img::GetByteDepth() const
|
|
|
|
{
|
|
|
|
return byteDepth;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt_8 Img::GetBitDepth() const
|
|
|
|
{
|
|
|
|
return byteDepth * 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt_8 Img::GetChannels() const
|
|
|
|
{
|
|
|
|
return channels;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vec2_u64 Img::GetResolution() const
|
|
|
|
{
|
|
|
|
return resolution;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt_64 Img::GetSize() const
|
|
|
|
{
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::SetPixel(const UInt_64 index, const Byte* const pixel)
|
|
|
|
{
|
|
|
|
UInt_64 rIndex = index * byteDepth * channels;
|
|
|
|
|
|
|
|
for (UInt_64 i = 0; i < byteDepth * channels; ++i)
|
|
|
|
data[rIndex + i] = pixel[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::GetPixel(const UInt_64 index, Byte* const pixel) const
|
|
|
|
{
|
|
|
|
UInt_64 rIndex = index * byteDepth * channels;
|
|
|
|
|
|
|
|
for (UInt_64 i = 0; i < byteDepth * channels; ++i)
|
|
|
|
pixel[i] = data[rIndex + i];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::SetPixel(const UInt_64 x, const UInt_64 y, const Byte* const pixel)
|
|
|
|
{
|
|
|
|
UInt_64 rIndex = (y * resolution.x * byteDepth * channels) + (x * byteDepth * channels);
|
|
|
|
|
|
|
|
for (UInt_64 i = 0; i < byteDepth * channels; ++i)
|
|
|
|
data[rIndex + i] = pixel[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::GetPixel(const UInt_64 x, const UInt_64 y, Byte* const pixel) const
|
|
|
|
{
|
|
|
|
UInt_64 rIndex = (y * resolution.x * byteDepth * channels) + (x * byteDepth * channels);
|
|
|
|
|
|
|
|
for (UInt_64 i = 0; i < byteDepth * channels; ++i)
|
|
|
|
pixel[i] = data[rIndex + i];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::Resize(const Resampling method, const Vec2_u64& newResolution)
|
|
|
|
{
|
|
|
|
switch (method)
|
|
|
|
{
|
|
|
|
case Resampling::NEAREST_NEIGHBOR:
|
|
|
|
{
|
|
|
|
NearestNeighbor(newResolution);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetResized(const Resampling method, const Vec2_u64& newResolution) const
|
|
|
|
{
|
|
|
|
switch (method)
|
|
|
|
{
|
|
|
|
case Resampling::NEAREST_NEIGHBOR:
|
|
|
|
return GetNearestNeighbor(newResolution);
|
|
|
|
default:
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::ToRGBA()
|
|
|
|
{
|
|
|
|
switch (channels)
|
|
|
|
{
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 4;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
RGB_To_RGBA(size, result);
|
|
|
|
channels = 4;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 4;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
MonoA_To_RGBA(size, result);
|
|
|
|
channels = 4;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 4;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
Mono_To_RGBA(size, result);
|
|
|
|
channels = 4;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetAsRGBA() const
|
|
|
|
{
|
|
|
|
switch (channels)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 4, resolution);
|
|
|
|
RGB_To_RGBA(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 4, resolution);
|
|
|
|
MonoA_To_RGBA(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 4, resolution);
|
|
|
|
Mono_To_RGBA(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::ToRGB()
|
|
|
|
{
|
|
|
|
switch (channels)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 3;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
RGBA_To_RGB(size, result);
|
|
|
|
channels = 3;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 3;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
MonoA_To_RGB(size, result);
|
|
|
|
channels = 3;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 3;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
Mono_To_RGB(size, result);
|
|
|
|
channels = 3;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetAsRGB() const
|
|
|
|
{
|
|
|
|
switch (channels)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 3, resolution);
|
|
|
|
RGBA_To_RGB(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 3, resolution);
|
|
|
|
MonoA_To_RGB(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 3, resolution);
|
|
|
|
Mono_To_RGB(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::ToMonoA()
|
|
|
|
{
|
|
|
|
switch (channels)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 2;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
RGBA_To_MonoA(size, result);
|
|
|
|
channels = 2;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 2;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
RGB_To_MonoA(size, result);
|
|
|
|
channels = 2;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth * 2;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
Mono_To_MonoA(size, result);
|
|
|
|
channels = 2;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetAsMonoA() const
|
|
|
|
{
|
|
|
|
switch (channels)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 2, resolution);
|
|
|
|
RGBA_To_MonoA(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 2, resolution);
|
|
|
|
RGB_To_MonoA(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 2, resolution);
|
|
|
|
Mono_To_MonoA(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::ToMono()
|
|
|
|
{
|
|
|
|
switch (channels)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
RGBA_To_Mono(size, result);
|
|
|
|
channels = 1;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
RGB_To_Mono(size, result);
|
|
|
|
channels = 1;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * byteDepth;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
MonoA_To_Mono(size, result);
|
|
|
|
channels = 1;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetAsMono() const
|
|
|
|
{
|
|
|
|
switch (channels)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 1, resolution);
|
|
|
|
RGBA_To_Mono(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 1, resolution);
|
|
|
|
RGB_To_Mono(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, 1, resolution);
|
|
|
|
MonoA_To_Mono(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::To32()
|
|
|
|
{
|
|
|
|
switch (byteDepth)
|
|
|
|
{
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 4 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD24_to_BD32(size, result);
|
|
|
|
byteDepth = 4;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 4 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD16_to_BD32(size, result);
|
|
|
|
byteDepth = 4;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 4 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD8_to_BD32(size, result);
|
|
|
|
byteDepth = 4;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetAs32() const
|
|
|
|
{
|
|
|
|
switch (byteDepth)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
Img result(id, 4, channels, resolution);
|
|
|
|
BD24_to_BD32(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
Img result(id, 4, channels, resolution);
|
|
|
|
BD16_to_BD32(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
Img result(id, 4, channels, resolution);
|
|
|
|
BD8_to_BD32(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::To24()
|
|
|
|
{
|
|
|
|
switch (byteDepth)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 3 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD32_to_BD24(size, result);
|
|
|
|
byteDepth = 3;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 3 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD16_to_BD24(size, result);
|
|
|
|
byteDepth = 3;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 3 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD8_to_BD24(size, result);
|
|
|
|
byteDepth = 3;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetAs24() const
|
|
|
|
{
|
|
|
|
switch (byteDepth)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
Img result(id, 3, channels, resolution);
|
|
|
|
BD32_to_BD24(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
Img result(id, 3, channels, resolution);
|
|
|
|
BD16_to_BD24(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
Img result(id, 3, channels, resolution);
|
|
|
|
BD8_to_BD24(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::To16()
|
|
|
|
{
|
|
|
|
switch (byteDepth)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 2 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD32_to_BD16(size, result);
|
|
|
|
byteDepth = 2;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 2 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD24_to_BD16(size, result);
|
|
|
|
byteDepth = 2;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * 2 * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD8_to_BD16(size, result);
|
|
|
|
byteDepth = 2;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetAs16() const
|
|
|
|
{
|
|
|
|
switch (byteDepth)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
Img result(id, 2, channels, resolution);
|
|
|
|
BD32_to_BD16(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
Img result(id, 2, channels, resolution);
|
|
|
|
BD24_to_BD16(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
Img result(id, 2, channels, resolution);
|
|
|
|
BD8_to_BD16(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::To8()
|
|
|
|
{
|
|
|
|
switch (byteDepth)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD32_to_BD8(size, result);
|
|
|
|
byteDepth = 1;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD24_to_BD8(size, result);
|
|
|
|
byteDepth = 1;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
size = resolution.x * resolution.y * channels;
|
|
|
|
Byte* result = new Byte[size];
|
|
|
|
BD16_to_BD8(size, result);
|
|
|
|
byteDepth = 1;
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetAs8() const
|
|
|
|
{
|
|
|
|
switch (byteDepth)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
Img result(id, 1, channels, resolution);
|
|
|
|
BD32_to_BD8(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
Img result(id, 1, channels, resolution);
|
|
|
|
BD24_to_BD8(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
Img result(id, 1, channels, resolution);
|
|
|
|
BD16_to_BD8(result.GetSize(), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
return {*this};
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Img::IsValid() const
|
|
|
|
{
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2024-02-20 22:47:52 -08:00
|
|
|
bool Img::Export(const Str_8& filePath) const
|
2024-02-05 22:25:30 -08:00
|
|
|
{
|
|
|
|
Str_8 ext = File::ParseExt_8(filePath);
|
|
|
|
|
|
|
|
const ImgCodec* codec = GetCodec(ext);
|
|
|
|
if (!codec)
|
|
|
|
{
|
|
|
|
EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\".");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Serializer<UInt_64> result;
|
|
|
|
if (!codec->Encode(result, this))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
File file(filePath, Mode::WRITE, Disposition::CREATE_PERSISTENT);
|
|
|
|
file.WriteSerializer_64(result);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Img Img::GetNearestNeighbor(const Vec2_u64& newResolution) const
|
|
|
|
{
|
|
|
|
Img result(id, byteDepth, channels, newResolution);
|
|
|
|
|
|
|
|
Vec2_d ratio = {
|
|
|
|
(double)resolution.x / (double)newResolution.x,
|
|
|
|
(double)resolution.y / (double)newResolution.x
|
|
|
|
};
|
|
|
|
|
|
|
|
Vec2_d pixel;
|
|
|
|
|
|
|
|
for(UInt_64 y = 0; y < newResolution.y; ++y)
|
|
|
|
{
|
|
|
|
for (UInt_64 x = 0; x < newResolution.x; ++x)
|
|
|
|
{
|
|
|
|
pixel = {
|
|
|
|
Math::Floor((double)x * ratio.x),
|
|
|
|
Math::Floor((double)y * ratio.y)
|
|
|
|
};
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * channels; ++b)
|
|
|
|
result[y * newResolution.x * byteDepth * channels + x * byteDepth * channels + b] = data[(UInt_64)pixel.y * resolution.x * byteDepth * channels + (UInt_64)pixel.x * byteDepth * channels + b];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::NearestNeighbor(const Vec2_u64& newResolution)
|
|
|
|
{
|
|
|
|
Byte* result = new Byte[newResolution.x * byteDepth * channels * newResolution.y];
|
|
|
|
|
|
|
|
Vec2_d ratio = {
|
|
|
|
(double)resolution.x / (double)newResolution.x,
|
|
|
|
(double)resolution.y / (double)newResolution.x
|
|
|
|
};
|
|
|
|
|
|
|
|
Vec2_d pixel;
|
|
|
|
|
|
|
|
for(UInt_64 y = 0; y < newResolution.y; ++y)
|
|
|
|
{
|
|
|
|
for (UInt_64 x = 0; x < newResolution.x; ++x)
|
|
|
|
{
|
|
|
|
pixel = {
|
|
|
|
Math::Floor((double)x * ratio.x),
|
|
|
|
Math::Floor((double)y * ratio.y)
|
|
|
|
};
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * channels; ++b)
|
|
|
|
result[y * newResolution.x * byteDepth * channels + x * byteDepth * channels + b] = data[(UInt_64)pixel.y * resolution.x * byteDepth * channels + (UInt_64)pixel.x * byteDepth * channels + b];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] data;
|
|
|
|
data = result;
|
|
|
|
resolution = newResolution;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::RGB_To_RGBA(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 4, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; ++b)
|
|
|
|
buffer[i + b] = data[n + b];
|
|
|
|
|
|
|
|
if (byteDepth == 1)
|
|
|
|
buffer[i + byteDepth * 3] = EHS_UINT_8_MAX;
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
*(UInt_16*)&buffer[i + byteDepth * 3] = EHS_UINT_16_MAX;
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
UInt_32 value = EHS_UINT_24_MAX;
|
|
|
|
for (UInt_64 b = 0; b < byteDepth; ++b)
|
|
|
|
buffer[i + byteDepth * 3 + b] = ((Byte*)&value)[b];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
*(UInt_32*)&buffer[i + byteDepth * 3] = EHS_UINT_32_MAX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::MonoA_To_RGBA(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 4, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 4; ++b)
|
|
|
|
buffer[i + b] = data[n + b % byteDepth];
|
|
|
|
|
|
|
|
if (byteDepth == 1)
|
|
|
|
buffer[i + byteDepth * 3] = data[n + byteDepth];
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
*(UInt_16*) &buffer[i + byteDepth * 3] = *(UInt_16*) &data[n + byteDepth];
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
for (UInt_64 b = 0; b < byteDepth; ++b)
|
|
|
|
buffer[i + byteDepth * 3 + b] = data[n + byteDepth + b];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
*(UInt_32*) &buffer[i + byteDepth * 3] = *(UInt_32*) &data[n + byteDepth];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::Mono_To_RGBA(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 4, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 4; ++b)
|
|
|
|
buffer[i + b] = data[n + b % byteDepth];
|
|
|
|
|
|
|
|
if (byteDepth == 1)
|
|
|
|
buffer[i + byteDepth * 3] = EHS_UINT_8_MAX;
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
*(UInt_16*)&buffer[i + byteDepth * 3] = EHS_UINT_16_MAX;
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
UInt_32 value = EHS_UINT_24_MAX;
|
|
|
|
for (UInt_64 b = 0; b < byteDepth; ++b)
|
|
|
|
buffer[i + byteDepth * 3 + b] = ((Byte*)&value)[b];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
*(UInt_32*) &buffer[i + byteDepth * 3] = EHS_UINT_32_MAX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::RGBA_To_RGB(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 3, n += byteDepth * channels)
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; ++b)
|
|
|
|
buffer[i + b] = data[n + b];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::MonoA_To_RGB(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 3, n += byteDepth * channels)
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; ++b)
|
|
|
|
buffer[i + b] = data[n + b % byteDepth];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::Mono_To_RGB(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 3, n += byteDepth * channels)
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; ++b)
|
|
|
|
buffer[i + b] = data[n + b % byteDepth];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::RGBA_To_MonoA(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 2, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
if (byteDepth == 1)
|
|
|
|
{
|
|
|
|
UInt_16 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < 3; ++b)
|
|
|
|
average += data[n + b];
|
|
|
|
|
|
|
|
buffer[i] = average / 4;
|
|
|
|
buffer[i + byteDepth] = data[n + 3];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
{
|
|
|
|
UInt_32 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
average += *(UInt_16*)&data[n + b];
|
|
|
|
|
|
|
|
*(UInt_16*)&buffer[i] = average / 4;
|
|
|
|
*(UInt_16*)&buffer[i + byteDepth] = *(UInt_16*)&data[n + byteDepth * 3];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
UInt_64 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
{
|
|
|
|
UInt_32 num = 0;
|
|
|
|
num |= data[n + b];
|
|
|
|
num |= data[n + b + 1] << 8;
|
|
|
|
num |= data[n + b + 2] << 16;
|
|
|
|
|
|
|
|
average += num;
|
|
|
|
}
|
|
|
|
|
|
|
|
average /= 4;
|
|
|
|
|
|
|
|
buffer[i] = ((Byte*)&average)[0];
|
|
|
|
buffer[i + 1] = ((Byte*)&average)[1];
|
|
|
|
buffer[i + 2] = ((Byte*)&average)[2];
|
|
|
|
|
|
|
|
buffer[i + byteDepth] = data[n + byteDepth * 3];
|
|
|
|
buffer[i + byteDepth + 1] = data[n + byteDepth * 3 + 1];
|
|
|
|
buffer[i + byteDepth + 2] = data[n + byteDepth * 3 + 2];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
{
|
|
|
|
UInt_64 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
average += *(UInt_32*)&data[n + b];
|
|
|
|
|
|
|
|
*(UInt_32*)&buffer[i] = average / 4;
|
|
|
|
*(UInt_32*)&buffer[i + byteDepth] = *(UInt_32*)&data[n + byteDepth * 3];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::RGB_To_MonoA(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 2, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
if (byteDepth == 1)
|
|
|
|
{
|
|
|
|
UInt_16 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < 3; ++b)
|
|
|
|
average += data[n + b];
|
|
|
|
|
|
|
|
buffer[i] = average / 4;
|
|
|
|
buffer[i + byteDepth] = EHS_UINT_8_MAX;
|
|
|
|
}
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
{
|
|
|
|
UInt_32 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
average += *(UInt_16*)&data[n + b];
|
|
|
|
|
|
|
|
*(UInt_16*)&buffer[i] = average / 4;
|
|
|
|
*(UInt_16*)&buffer[i + byteDepth] = EHS_UINT_16_MAX;
|
|
|
|
}
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
UInt_64 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
{
|
|
|
|
UInt_32 num = 0;
|
|
|
|
num |= data[n + b];
|
|
|
|
num |= data[n + b + 1] << 8;
|
|
|
|
num |= data[n + b + 2] << 16;
|
|
|
|
|
|
|
|
average += num;
|
|
|
|
}
|
|
|
|
|
|
|
|
average /= 4;
|
|
|
|
|
|
|
|
buffer[i] = ((Byte*)&average)[0];
|
|
|
|
buffer[i + 1] = ((Byte*)&average)[1];
|
|
|
|
buffer[i + 2] = ((Byte*)&average)[2];
|
|
|
|
|
|
|
|
UInt_32 tmp = EHS_UINT_24_MAX;
|
|
|
|
|
|
|
|
buffer[i + byteDepth] = ((Byte*)&tmp)[0];
|
|
|
|
buffer[i + byteDepth + 1] = ((Byte*)&tmp)[1];
|
|
|
|
buffer[i + byteDepth + 2] = ((Byte*)&tmp)[2];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
{
|
|
|
|
UInt_64 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
average += *(UInt_32*)&data[n + b];
|
|
|
|
|
|
|
|
*(UInt_32*)&buffer[i] = average / 4;
|
|
|
|
*(UInt_32*)&buffer[i + byteDepth] = EHS_UINT_32_MAX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::Mono_To_MonoA(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 2, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
if (byteDepth == 1)
|
|
|
|
{
|
|
|
|
buffer[i] = data[n];
|
|
|
|
buffer[i + byteDepth] = EHS_UINT_8_MAX;
|
|
|
|
}
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
{
|
|
|
|
*(UInt_16*)&buffer[i] = *(UInt_16*)&data[n];
|
|
|
|
*(UInt_16*)&buffer[i + byteDepth] = EHS_UINT_16_MAX;
|
|
|
|
}
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
buffer[i] = data[n];
|
|
|
|
buffer[i + 1] = data[n + 1];
|
|
|
|
buffer[i + 2] = data[n + 2];
|
|
|
|
|
|
|
|
UInt_32 tmp = EHS_UINT_24_MAX;
|
|
|
|
|
|
|
|
buffer[i + byteDepth] = ((Byte*)&tmp)[0];
|
|
|
|
buffer[i + byteDepth + 1] = ((Byte*)&tmp)[1];
|
|
|
|
buffer[i + byteDepth + 2] = ((Byte*)&tmp)[2];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
{
|
|
|
|
*(UInt_32*)&buffer[i] = *(UInt_32*)&data[n];
|
|
|
|
*(UInt_32*)&buffer[i + byteDepth] = EHS_UINT_32_MAX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::RGBA_To_Mono(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
if (byteDepth == 1)
|
|
|
|
{
|
|
|
|
UInt_16 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < 3; ++b)
|
|
|
|
average += data[n + b];
|
|
|
|
|
|
|
|
buffer[i] = average / 4;
|
|
|
|
}
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
{
|
|
|
|
UInt_32 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
average += *(UInt_16*)&data[n + b];
|
|
|
|
|
|
|
|
*(UInt_16*)&buffer[i] = average / 4;
|
|
|
|
}
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
UInt_64 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
{
|
|
|
|
UInt_32 num = 0;
|
|
|
|
num |= data[n + b];
|
|
|
|
num |= data[n + b + 1] << 8;
|
|
|
|
num |= data[n + b + 2] << 16;
|
|
|
|
|
|
|
|
average += num;
|
|
|
|
}
|
|
|
|
|
|
|
|
average /= 4;
|
|
|
|
|
|
|
|
buffer[i] = ((Byte*)&average)[0];
|
|
|
|
buffer[i + 1] = ((Byte*)&average)[1];
|
|
|
|
buffer[i + 2] = ((Byte*)&average)[2];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
{
|
|
|
|
UInt_64 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
average += *(UInt_32*)&data[n + b];
|
|
|
|
|
|
|
|
*(UInt_32*)&buffer[i] = average / 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::RGB_To_Mono(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
if (byteDepth == 1)
|
|
|
|
{
|
|
|
|
UInt_16 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < 3; ++b)
|
|
|
|
average += data[n + b];
|
|
|
|
|
|
|
|
buffer[i] = average / 4;
|
|
|
|
}
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
{
|
|
|
|
UInt_32 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
average += *(UInt_16*)&data[n + b];
|
|
|
|
|
|
|
|
*(UInt_16*)&buffer[i] = average / 4;
|
|
|
|
}
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
UInt_64 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
{
|
|
|
|
UInt_32 num = 0;
|
|
|
|
num |= data[n + b];
|
|
|
|
num |= data[n + b + 1] << 8;
|
|
|
|
num |= data[n + b + 2] << 16;
|
|
|
|
|
|
|
|
average += num;
|
|
|
|
}
|
|
|
|
|
|
|
|
average /= 4;
|
|
|
|
|
|
|
|
buffer[i] = ((Byte*)&average)[0];
|
|
|
|
buffer[i + 1] = ((Byte*)&average)[1];
|
|
|
|
buffer[i + 2] = ((Byte*)&average)[2];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
{
|
|
|
|
UInt_64 average = 0;
|
|
|
|
|
|
|
|
for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth)
|
|
|
|
average += *(UInt_32*)&data[n + b];
|
|
|
|
|
|
|
|
*(UInt_32*)&buffer[i] = average / 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::MonoA_To_Mono(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth, n += byteDepth * channels)
|
|
|
|
{
|
|
|
|
if (byteDepth == 1)
|
|
|
|
{
|
|
|
|
buffer[i] = data[n];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 2)
|
|
|
|
{
|
|
|
|
*(UInt_16*)&buffer[i] = *(UInt_16*)&data[n];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 3)
|
|
|
|
{
|
|
|
|
buffer[i] = data[n];
|
|
|
|
buffer[i + 1] = data[n + 1];
|
|
|
|
buffer[i + 2] = data[n + 2];
|
|
|
|
}
|
|
|
|
else if (byteDepth == 4)
|
|
|
|
{
|
|
|
|
*(UInt_32*)&buffer[i] = *(UInt_32*)&data[n];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD24_to_BD32(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 4, n += 3)
|
|
|
|
{
|
|
|
|
UInt_32 inValue = 0;
|
|
|
|
((Byte*)&inValue)[0] = data[n];
|
|
|
|
((Byte*)&inValue)[1] = data[n + 1];
|
|
|
|
((Byte*)&inValue)[2] = data[n + 2];
|
|
|
|
|
|
|
|
*(UInt_32*)&buffer[i] = (UInt_32)((float)inValue / (float)EHS_UINT_24_MAX * (float)EHS_UINT_32_MAX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD16_to_BD32(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 4, n += 2)
|
|
|
|
*(UInt_32*)&buffer[i] = (UInt_32)((float)*(UInt_16*)&data[n] / (float)EHS_UINT_16_MAX * (float)EHS_UINT_32_MAX);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD8_to_BD32(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 4, ++n)
|
|
|
|
*(UInt_32*)&buffer[i] = (UInt_32)((float)data[n] / (float)EHS_UINT_8_MAX * (float)EHS_UINT_32_MAX);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD32_to_BD24(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 3, n += 4)
|
|
|
|
{
|
|
|
|
UInt_32 rValue = (UInt_32)((float)*(UInt_32*)&data[n] / (float)EHS_UINT_32_MAX * (float)EHS_UINT_24_MAX);
|
|
|
|
|
|
|
|
buffer[i] = ((Byte*)&rValue)[0];
|
|
|
|
buffer[i + 1] = ((Byte*)&rValue)[1];
|
|
|
|
buffer[i + 2] = ((Byte*)&rValue)[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD16_to_BD24(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 3, n += 2)
|
|
|
|
{
|
|
|
|
UInt_32 rValue = (UInt_32)((float)*(UInt_16*)&data[n] / (float)EHS_UINT_16_MAX * (float)EHS_UINT_24_MAX);
|
|
|
|
|
|
|
|
buffer[i] = ((Byte*)&rValue)[0];
|
|
|
|
buffer[i + 1] = ((Byte*)&rValue)[1];
|
|
|
|
buffer[i + 2] = ((Byte*)&rValue)[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD8_to_BD24(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 3, ++n)
|
|
|
|
{
|
|
|
|
UInt_32 rValue = (UInt_32)((float)data[n] / (float)EHS_UINT_8_MAX * (float)EHS_UINT_24_MAX);
|
|
|
|
|
|
|
|
buffer[i] = ((Byte*)&rValue)[0];
|
|
|
|
buffer[i + 1] = ((Byte*)&rValue)[1];
|
|
|
|
buffer[i + 2] = ((Byte*)&rValue)[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD32_to_BD16(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 2, n += 4)
|
|
|
|
*(UInt_16*)&buffer[i] = (UInt_16)((float)*(UInt_32*)&data[n] / (float)EHS_UINT_32_MAX * (float)EHS_UINT_16_MAX);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD24_to_BD16(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 2, n += 3)
|
|
|
|
{
|
|
|
|
UInt_32 inValue = 0;
|
|
|
|
((Byte*)&inValue)[0] = data[n];
|
|
|
|
((Byte*)&inValue)[1] = data[n + 1];
|
|
|
|
((Byte*)&inValue)[2] = data[n + 2];
|
|
|
|
|
|
|
|
*(UInt_16*)&buffer[i] = (UInt_16)((float)inValue / (float)EHS_UINT_24_MAX * (float)EHS_UINT_16_MAX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD8_to_BD16(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; i += 2, ++n)
|
|
|
|
*(UInt_16*)&buffer[i] = (UInt_16)((float)data[n] / (float)EHS_UINT_8_MAX * (float)EHS_UINT_16_MAX);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD32_to_BD8(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; ++i, n += 4)
|
|
|
|
buffer[i] = (Byte)((float)*(UInt_32*)&data[n] / (float)EHS_UINT_32_MAX * (float)EHS_UINT_8_MAX);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD24_to_BD8(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
for (UInt_64 i = 0, n = 0; i < newSize; ++i, n += 3)
|
|
|
|
{
|
|
|
|
UInt_32 inValue = 0;
|
|
|
|
((Byte*)&inValue)[0] = data[n];
|
|
|
|
((Byte*)&inValue)[1] = data[n + 1];
|
|
|
|
((Byte*)&inValue)[2] = data[n + 2];
|
|
|
|
|
|
|
|
buffer[i] = (Byte)((float)inValue / (float)EHS_UINT_24_MAX * (float)EHS_UINT_8_MAX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Img::BD16_to_BD8(const UInt_64 newSize, Byte* buffer) const
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2024-02-20 22:47:52 -08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2024-02-05 22:25:30 -08:00
|
|
|
}
|