#include "ehs/io/img/Img.h" #include "ehs/io/img/PNG.h" #include namespace ehs { Array 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"); } 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(LogType::ERR, 0, "Codec not found for file extension, \"" + ext + "\"."); return; } Serializer 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]) { 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; } bool Img::Export(const Str_8& filePath) const { Str_8 ext = File::ParseExt_8(filePath); const ImgCodec* codec = GetCodec(ext); if (!codec) { EHS_LOG_INT(LogType::ERR, 0, "Codec not found for file extension, \"" + ext + "\"."); return false; } Serializer 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); } bool EncodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer& 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 result(Endianness::BE, resolution.x * resolution.y * (channels + 1) + 22); result.Write('q'); result.Write('o'); result.Write('i'); result.Write('f'); result.Write(resolution.x); result.Write(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(0xc0 | (run - 1)); run = 0; } } else { if (run > 0) { result.Write(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(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(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(0x80 | (vg + 32)); result.Write((vg_r + 8) << 4 | (vg_b + 8)); } else { result.Write(0xfe); result.Write(pixel[0]); result.Write(pixel[1]); result.Write(pixel[2]); } } else { result.Write(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& in, ehs::Img* out) { Str_8 imgType = in.ReadStr(4); if (imgType != "qoif") { EHS_LOG_INT(LogType::ERR, 0, "Given data is not in the qoif format."); return false; } UInt_64 width = in.Read(); UInt_64 height = in.Read(); UInt_8 channels = in.Read(); channels = 4; UInt_8 space = in.Read(); 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(); if (chunkType == 0xfe) // RGB { prevPixel[0] = in.Read(); // R-value prevPixel[1] = in.Read(); // G-value prevPixel[2] = in.Read(); // B-value } else if (chunkType == 0xff) // RGBA { *(UInt_32*)prevPixel = in.Read(); } 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_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& in, ehs::Img* out) { PNG png(out->GetId(), in); PNG_Chunk* ihdr = png.GetChunk("IHDR"); Serializer* ihdrData = ihdr->GetData(); UInt_32 width = ihdrData->Read(); UInt_32 height = ihdrData->Read(); UInt_8 bitDepth = ihdrData->Read(); UInt_8 colorType = ihdrData->Read(); if (colorType == 3) { EHS_LOG_INT(LogType::ERR, 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(); if (compression) { EHS_LOG_INT(LogType::ERR, 2, "Compression method of " + Str_8::FromNum(compression) + " is unsupported."); return false; } UInt_8 filter = ihdrData->Read(); if (filter) { EHS_LOG_INT(LogType::ERR, 3, "Filter method of " + Str_8::FromNum(filter) + " is unsupported."); return false; } UInt_8 interlaced = ihdrData->Read(); if (interlaced) { EHS_LOG_INT(LogType::ERR, 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* 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(LogType::ERR, 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(LogType::ERR, 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(LogType::ERR, 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; } }