2023-09-07 16:27:41 +08:00
|
|
|
#include "CKBitmapHandler.hpp"
|
2023-09-24 20:56:23 +08:00
|
|
|
#include "stb_image.h"
|
|
|
|
#include "stb_image_write.h"
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
namespace LibCmo::CK2::DataHandlers {
|
|
|
|
|
2023-09-07 21:57:48 +08:00
|
|
|
#pragma region Help Functions
|
|
|
|
|
2023-09-12 20:49:19 +08:00
|
|
|
/*
|
|
|
|
ABGR8888 is format used by std image.
|
|
|
|
The data is placed in buffer with RGBA order, so the format is ABGR.
|
|
|
|
ARGB8888 is Virtools standard.
|
|
|
|
The data is placed in buffer with BGRA order.
|
|
|
|
*/
|
|
|
|
|
2023-09-16 18:31:25 +08:00
|
|
|
static void ABGRToARGB(CKDWORD count, const void* _abgr, void* _argb) {
|
2023-09-17 10:38:46 +08:00
|
|
|
const CKBYTE* abgr = static_cast<const CKBYTE*>(_abgr);
|
|
|
|
CKBYTE* argb = static_cast<CKBYTE*>(_argb);
|
2023-09-12 20:49:19 +08:00
|
|
|
// copy R
|
2023-09-07 16:27:41 +08:00
|
|
|
VxMath::VxCopyStructure(
|
|
|
|
count,
|
2023-09-12 20:49:19 +08:00
|
|
|
argb + (2u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize,
|
|
|
|
VxMath::VxImageDescEx::ColorFactorSize,
|
|
|
|
abgr + (0u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize
|
|
|
|
);
|
|
|
|
// copy G
|
|
|
|
VxMath::VxCopyStructure(
|
|
|
|
count,
|
|
|
|
argb + (1u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize,
|
|
|
|
VxMath::VxImageDescEx::ColorFactorSize,
|
|
|
|
abgr + (1u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize
|
|
|
|
);
|
|
|
|
// copy B
|
|
|
|
VxMath::VxCopyStructure(
|
|
|
|
count,
|
|
|
|
argb + (0u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize,
|
|
|
|
VxMath::VxImageDescEx::ColorFactorSize,
|
|
|
|
abgr + (2u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize
|
2023-09-07 16:27:41 +08:00
|
|
|
);
|
|
|
|
// copy A
|
|
|
|
VxMath::VxCopyStructure(
|
|
|
|
count,
|
2023-09-12 20:49:19 +08:00
|
|
|
argb + (3u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize,
|
|
|
|
VxMath::VxImageDescEx::ColorFactorSize,
|
|
|
|
abgr + (3u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize
|
2023-09-07 16:27:41 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-09-16 18:31:25 +08:00
|
|
|
static void ARGBToABGR(CKDWORD count, const void* _argb, void* _abgr) {
|
2023-09-17 10:38:46 +08:00
|
|
|
const CKBYTE* argb = static_cast<const CKBYTE*>(_argb);
|
|
|
|
CKBYTE* abgr = static_cast<CKBYTE*>(_abgr);
|
2023-09-12 20:49:19 +08:00
|
|
|
// copy R
|
|
|
|
VxMath::VxCopyStructure(
|
|
|
|
count,
|
|
|
|
abgr + (0u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize,
|
|
|
|
VxMath::VxImageDescEx::ColorFactorSize,
|
|
|
|
argb + (2u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize
|
|
|
|
);
|
|
|
|
// copy G
|
|
|
|
VxMath::VxCopyStructure(
|
|
|
|
count,
|
|
|
|
abgr + (1u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize,
|
|
|
|
VxMath::VxImageDescEx::ColorFactorSize,
|
|
|
|
argb + (1u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize
|
|
|
|
);
|
|
|
|
// copy B
|
2023-09-07 16:27:41 +08:00
|
|
|
VxMath::VxCopyStructure(
|
|
|
|
count,
|
2023-09-12 20:49:19 +08:00
|
|
|
abgr + (2u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize,
|
|
|
|
VxMath::VxImageDescEx::ColorFactorSize,
|
|
|
|
argb + (0u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize
|
2023-09-07 16:27:41 +08:00
|
|
|
);
|
|
|
|
// copy A
|
|
|
|
VxMath::VxCopyStructure(
|
|
|
|
count,
|
2023-09-12 20:49:19 +08:00
|
|
|
abgr + (3u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize,
|
|
|
|
VxMath::VxImageDescEx::ColorFactorSize,
|
|
|
|
argb + (3u * VxMath::VxImageDescEx::ColorFactorSize),
|
|
|
|
VxMath::VxImageDescEx::PixelSize
|
2023-09-07 16:27:41 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool StbReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) {
|
|
|
|
if (u8filename == nullptr || read_image == nullptr) return false;
|
|
|
|
FILE* fs = EncodingHelper::U8FOpen(u8filename, "rb");
|
|
|
|
if (fs == nullptr) return false;
|
|
|
|
|
|
|
|
// read data
|
|
|
|
int x, y, channels_in_file;
|
|
|
|
stbi_uc* data = stbi_load_from_file(fs, &x, &y, &channels_in_file, 4); // 4 == RGBA8888
|
|
|
|
std::fclose(fs);
|
|
|
|
if (data == nullptr) return false;
|
|
|
|
|
2023-09-11 14:39:07 +08:00
|
|
|
// create read image
|
|
|
|
read_image->CreateImage(static_cast<CKDWORD>(x), static_cast<CKDWORD>(y));
|
|
|
|
|
|
|
|
// copy data
|
2023-09-12 20:49:19 +08:00
|
|
|
ABGRToARGB(read_image->GetPixelCount(), data, read_image->GetMutableImage());
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
// clear data
|
|
|
|
stbi_image_free(data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
static bool StbReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) {
|
|
|
|
// same like ReadFile
|
|
|
|
if (memory == nullptr || read_image == nullptr) return false;
|
|
|
|
|
|
|
|
// read data
|
|
|
|
int x, y, channels_in_file;
|
|
|
|
stbi_uc* data = stbi_load_from_memory(
|
2023-09-20 10:49:32 +08:00
|
|
|
static_cast<const stbi_uc*>(memory),
|
2023-09-07 16:27:41 +08:00
|
|
|
static_cast<int>(size),
|
|
|
|
&x, &y, &channels_in_file, 4 // 4 == RGBA8888
|
|
|
|
);
|
|
|
|
if (data == nullptr) return false;
|
2023-09-11 14:39:07 +08:00
|
|
|
|
|
|
|
// create read image
|
|
|
|
read_image->CreateImage(static_cast<CKDWORD>(x), static_cast<CKDWORD>(y));
|
2023-09-07 16:27:41 +08:00
|
|
|
|
2023-09-11 14:39:07 +08:00
|
|
|
// copy data
|
2023-09-12 20:49:19 +08:00
|
|
|
ABGRToARGB(read_image->GetPixelCount(), data, read_image->GetMutableImage());
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
// clear data
|
|
|
|
stbi_image_free(data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FileSaveContext {
|
|
|
|
FileSaveContext(FILE* fs) :
|
|
|
|
m_Fs(fs), m_Counter(0) {}
|
|
|
|
FILE* m_Fs;
|
|
|
|
size_t m_Counter;
|
|
|
|
};
|
|
|
|
static void FileWriteFunction(void* context, void* data, int size) {
|
2023-09-20 10:49:32 +08:00
|
|
|
FileSaveContext* ctx = static_cast<FileSaveContext*>(context);
|
2023-09-07 16:27:41 +08:00
|
|
|
if (ctx->m_Fs != nullptr) {
|
2023-09-17 10:38:46 +08:00
|
|
|
std::fwrite(data, sizeof(CKBYTE), size, ctx->m_Fs);
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
ctx->m_Counter += size;
|
|
|
|
}
|
|
|
|
struct MemorySaveContext {
|
|
|
|
MemorySaveContext(void* mem) :
|
|
|
|
m_Mem(mem), m_Counter(0) {}
|
|
|
|
void* m_Mem;
|
|
|
|
size_t m_Counter;
|
|
|
|
};
|
|
|
|
static void MemoryWriteFunction(void* context, void* data, int size) {
|
2023-09-20 10:49:32 +08:00
|
|
|
MemorySaveContext* ctx = static_cast<MemorySaveContext*>(context);
|
2023-09-07 16:27:41 +08:00
|
|
|
if (ctx->m_Mem != nullptr) {
|
|
|
|
std::memcpy(ctx->m_Mem, data, size);
|
2023-09-17 10:38:46 +08:00
|
|
|
ctx->m_Mem = static_cast<CKBYTE*>(ctx->m_Mem) + size;
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
ctx->m_Counter += size;
|
|
|
|
}
|
|
|
|
|
|
|
|
using SaveOperation = std::function<int(stbi_write_func*, void*, int, int, int, const void*)>;
|
|
|
|
static bool StbSaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, SaveOperation oper) {
|
|
|
|
if (u8filename == nullptr || write_image == nullptr) return false;
|
2023-09-11 22:20:45 +08:00
|
|
|
if (!write_image->IsValid()) return false;
|
2023-09-07 16:27:41 +08:00
|
|
|
FILE* fs = EncodingHelper::U8FOpen(u8filename, "wb");
|
2023-09-07 21:57:48 +08:00
|
|
|
if (fs == nullptr) return false;
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
// allocate buffer and convert data from ARGB to RGBA
|
|
|
|
CKBYTE* data = new CKBYTE[write_image->GetImageSize()];
|
2023-09-12 20:49:19 +08:00
|
|
|
ARGBToABGR(write_image->GetPixelCount(), write_image->GetImage(), data);
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
// write data
|
|
|
|
FileSaveContext* ctx = new FileSaveContext(fs);
|
|
|
|
int ret = oper(
|
|
|
|
&FileWriteFunction, ctx,
|
2023-09-07 21:57:48 +08:00
|
|
|
static_cast<int>(write_image->GetWidth()), static_cast<int>(write_image->GetHeight()),
|
2023-09-07 16:27:41 +08:00
|
|
|
4, data // 4 == RGBA8888
|
|
|
|
);
|
|
|
|
|
|
|
|
// free data
|
|
|
|
delete ctx;
|
|
|
|
delete[] data;
|
|
|
|
std::fclose(fs);
|
|
|
|
|
2023-11-10 14:58:07 +08:00
|
|
|
// ret is 0 mean failed.
|
|
|
|
return ret != 0;
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
static CKDWORD StbSaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, SaveOperation oper) {
|
|
|
|
if (write_image == nullptr) return 0;
|
2023-09-11 22:20:45 +08:00
|
|
|
if (!write_image->IsValid()) return 0;
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
// allocate buffer and convert data from ARGB to RGBA
|
|
|
|
CKBYTE* data = new CKBYTE[write_image->GetImageSize()];
|
2023-09-12 20:49:19 +08:00
|
|
|
ARGBToABGR(write_image->GetPixelCount(), write_image->GetImage(), data);
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
// write data
|
|
|
|
MemorySaveContext* ctx = new MemorySaveContext(memory);
|
|
|
|
int ret = oper(
|
2023-09-07 22:10:42 +08:00
|
|
|
&MemoryWriteFunction, ctx,
|
2023-09-07 21:57:48 +08:00
|
|
|
static_cast<int>(write_image->GetWidth()), static_cast<int>(write_image->GetHeight()),
|
2023-09-07 16:27:41 +08:00
|
|
|
4, data // 4 == RGBA8888
|
|
|
|
);
|
|
|
|
|
|
|
|
// free data
|
|
|
|
CKDWORD expected = static_cast<CKDWORD>(ctx->m_Counter);
|
|
|
|
delete ctx;
|
|
|
|
delete[] data;
|
|
|
|
|
2023-11-10 14:58:07 +08:00
|
|
|
// ret is 0 mean failed. return zero size.
|
2023-09-07 16:27:41 +08:00
|
|
|
if (ret == 0) return 0;
|
|
|
|
else return expected;
|
|
|
|
}
|
2023-09-07 21:57:48 +08:00
|
|
|
|
|
|
|
#pragma endregion
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
#pragma region CKBitmapBMPHandler
|
|
|
|
|
2023-09-07 21:57:48 +08:00
|
|
|
static const CKBitmapProperties g_BMPProperties(CKGUID(0xBCA97223u, 0x48578BCAu), "Bmp");
|
|
|
|
|
2023-09-07 16:27:41 +08:00
|
|
|
CKBitmapBMPHandler::CKBitmapBMPHandler() :
|
2023-09-07 21:57:48 +08:00
|
|
|
CKBitmapHandler() {}
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
CKBitmapBMPHandler::~CKBitmapBMPHandler() {}
|
|
|
|
|
|
|
|
const CKBitmapProperties& CKBitmapBMPHandler::GetBitmapDefaultProperties() {
|
2023-09-07 21:57:48 +08:00
|
|
|
return g_BMPProperties;
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CKBitmapBMPHandler::ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) {
|
2023-09-07 22:10:42 +08:00
|
|
|
return StbReadFile(u8filename, read_image);
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CKBitmapBMPHandler::ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) {
|
2023-09-07 22:10:42 +08:00
|
|
|
return StbReadMemory(memory, size, read_image);
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CKBitmapBMPHandler::SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
|
|
|
return StbSaveFile(u8filename, write_image,
|
|
|
|
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
|
|
|
return stbi_write_bmp_to_func(func, context, w, h, comp, data);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
CKDWORD CKBitmapBMPHandler::SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
|
|
|
return StbSaveMemory(memory, write_image,
|
|
|
|
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
|
|
|
return stbi_write_bmp_to_func(func, context, w, h, comp, data);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-09-29 23:09:01 +08:00
|
|
|
bool CKBitmapBMPHandler::CanSaveAlpha() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-09-07 16:27:41 +08:00
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
#pragma region CKBitmapTGAHandler
|
2023-09-07 21:57:48 +08:00
|
|
|
|
|
|
|
static const CKBitmapProperties g_TGAProperties(CKGUID(0x585C7216u, 0x33302657u), "Tga");
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
CKBitmapTGAHandler::CKBitmapTGAHandler() :
|
2023-09-07 21:57:48 +08:00
|
|
|
CKBitmapHandler() {}
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
CKBitmapTGAHandler::~CKBitmapTGAHandler() {}
|
|
|
|
|
|
|
|
const CKBitmapProperties& CKBitmapTGAHandler::GetBitmapDefaultProperties() {
|
2023-09-07 21:57:48 +08:00
|
|
|
return g_TGAProperties;
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CKBitmapTGAHandler::ReadFile(CKSTRING u8filename, VxMath::VxImageDescEx* read_image) {
|
2023-09-07 22:10:42 +08:00
|
|
|
return StbReadFile(u8filename, read_image);
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CKBitmapTGAHandler::ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) {
|
2023-09-07 22:10:42 +08:00
|
|
|
return StbReadMemory(memory, size, read_image);
|
2023-09-07 16:27:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CKBitmapTGAHandler::SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
|
|
|
return StbSaveFile(u8filename, write_image,
|
|
|
|
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
2023-09-07 21:57:48 +08:00
|
|
|
return stbi_write_tga_to_func(func, context, w, h, comp, data);
|
2023-09-07 16:27:41 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
CKDWORD CKBitmapTGAHandler::SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) {
|
|
|
|
return StbSaveMemory(memory, write_image,
|
|
|
|
[&codec_param](stbi_write_func* func, void* context, int w, int h, int comp, const void* data) -> int {
|
2023-09-07 21:57:48 +08:00
|
|
|
return stbi_write_tga_to_func(func, context, w, h, comp, data);
|
2023-09-07 16:27:41 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-09-29 23:09:01 +08:00
|
|
|
bool CKBitmapTGAHandler::CanSaveAlpha() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-09-07 16:27:41 +08:00
|
|
|
#pragma endregion
|
2023-09-07 21:57:48 +08:00
|
|
|
|
|
|
|
#pragma region General Getter Freer
|
|
|
|
|
|
|
|
static CKBitmapHandler* FindHandlerByExt(const CKFileExtension& ext) {
|
|
|
|
if (ext == g_BMPProperties.m_Ext) return new CKBitmapBMPHandler();
|
|
|
|
if (ext == g_TGAProperties.m_Ext) return new CKBitmapTGAHandler();
|
|
|
|
return nullptr;
|
|
|
|
}
|
2023-09-07 16:27:41 +08:00
|
|
|
|
2023-09-07 21:57:48 +08:00
|
|
|
static CKBitmapHandler* FindHandlerByGuid(const CKGUID& guid) {
|
|
|
|
if (guid == g_BMPProperties.m_ReaderGuid) return new CKBitmapBMPHandler();
|
|
|
|
if (guid == g_TGAProperties.m_ReaderGuid) return new CKBitmapTGAHandler();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CKBitmapHandler* CKBitmapHandler::GetBitmapHandler(const CKFileExtension& ext, const CKGUID& guid) {
|
|
|
|
CKBitmapHandler* handler = nullptr;
|
|
|
|
|
|
|
|
// check ext first
|
|
|
|
handler = FindHandlerByExt(ext);
|
|
|
|
if (handler != nullptr) return handler;
|
|
|
|
|
|
|
|
// check guid
|
|
|
|
handler = FindHandlerByGuid(guid);
|
|
|
|
if (handler != nullptr) return handler;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-09-28 23:29:52 +08:00
|
|
|
CKBitmapHandlerWrapper_t CKBitmapHandler::GetBitmapHandlerWrapper(const CKFileExtension& ext, const CKGUID& guid) {
|
|
|
|
return CKBitmapHandlerWrapper_t(GetBitmapHandler(ext, guid));
|
2023-09-11 22:20:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CKBitmapHandlerDeleter::operator()(CKBitmapHandler* handler) {
|
|
|
|
CKBitmapHandler::ReleaseBitmapHandler(handler);
|
|
|
|
}
|
|
|
|
|
2023-09-07 21:57:48 +08:00
|
|
|
void CKBitmapHandler::ReleaseBitmapHandler(CKBitmapHandler* handler) {
|
|
|
|
if (handler != nullptr) delete handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
2023-09-07 16:27:41 +08:00
|
|
|
|
|
|
|
}
|