finish CKBitmapData writer.

- finish spec fmt image writer and raw bitmap writer.
- fix a possible fatal issue in ReadBuffer(). return nullptr simply rather than allocate a blank buf. otherwise the process of use nullptr to check result will be broken.
- add CanSaveAlpha() method in CKBitmapHandler.
This commit is contained in:
yyc12345 2023-09-29 23:09:01 +08:00
parent 1575186c69
commit 3195a9682f
5 changed files with 195 additions and 9 deletions

View File

@ -10,6 +10,9 @@ namespace LibCmo::CK2 {
#pragma region Assist RW Functions
constexpr const CKDWORD c_SpecificFmtHasTransparent = 2;
constexpr const CKDWORD c_SpecificFmtNoTransparent = 1;
bool CKBitmapData::ReadSpecificFormatBitmap(CKStateChunk* chk, VxMath::VxImageDescEx* slot) {
// read transparent prop
CKDWORD transprop;
@ -48,7 +51,7 @@ namespace LibCmo::CK2 {
VxMath::VxDoBlit(&cache, slot);
// proc image alpha
if (transprop == 2) {
if (transprop == c_SpecificFmtHasTransparent) {
CKDWORD alphacount;
chk->ReadStruct(alphacount);
if (alphacount == 1) {
@ -125,11 +128,179 @@ namespace LibCmo::CK2 {
}
void CKBitmapData::WriteSpecificFormatBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* slot, const CKBitmapProperties* savefmt) {
// check image validation
if (slot->IsValid()) {
// get reader
auto reader = DataHandlers::CKBitmapHandler::GetBitmapHandlerWrapper(savefmt->m_Ext, savefmt->m_ReaderGuid);
if (reader != nullptr) {
// prepare file data
CKDWORD expectedSize = reader->SaveMemory(nullptr, slot, *savefmt);
std::unique_ptr<CKBYTE[]> filebuf(new CKBYTE[expectedSize]);
reader->SaveMemory(filebuf.get(), slot, *savefmt);
reader.reset();
// in original Virtools design, only save alpha data when raw data can not represent alpha data
bool canSaveAlpha = reader->CanSaveAlpha();
// write basic data
// whether has transparent
chk->WriteStruct(canSaveAlpha ? c_SpecificFmtNoTransparent : c_SpecificFmtHasTransparent);
// write ext and guid
chk->WriteBufferNoSize(savefmt->m_Ext.GetExt(), savefmt->m_Ext.GetSize());
chk->WriteStruct(savefmt->m_ReaderGuid);
// write file data len and self
chk->WriteStruct(expectedSize);
chk->WriteBufferNoSize(filebuf.get(), expectedSize);
// free filebuf
filebuf.reset();
// write alpha if necessary
if (!canSaveAlpha) {
// prepare alpha list
CKDWORD pixelCount = slot->GetPixelCount();
std::unique_ptr<CKBYTE[]> alphabuf(new CKBYTE[pixelCount * VxMath::VxImageDescEx::ColorFactorSize]);
VxMath::VxCopyStructure(
pixelCount,
alphabuf.get(),
VxMath::VxImageDescEx::ColorFactorSize,
VxMath::VxImageDescEx::ColorFactorSize,
slot->GetImage() + 3 * VxMath::VxImageDescEx::ColorFactorSize, // move to A factor
VxMath::VxImageDescEx::PixelSize
);
// check whether alpha are the same value
bool isSameAlpha = true;
CKDWORD sameAlpha = 0;
for (CKDWORD i = 0; i < pixelCount; ++i) {
if (i == 0) {
sameAlpha = static_cast<CKDWORD>(alphabuf[i * VxMath::VxImageDescEx::ColorFactorSize]);
} else {
if (sameAlpha != static_cast<CKDWORD>(alphabuf[i * VxMath::VxImageDescEx::ColorFactorSize])) {
isSameAlpha = false;
break;
}
}
}
// write alpha count
// MARK: originally, the written value is how many different alpha value in this image.
// so obviously its range is from 1 - 255 and 1 mean all alpha are the same value.
// thus we write 2 here because Virtools do not depend this value in reader,
// we only just let virtools to know there is a alpha list.
chk->WriteStruct(isSameAlpha ? 1 : 2);
if (isSameAlpha) {
chk->WriteStruct(sameAlpha);
} else {
chk->WriteBuffer(alphabuf.get(), pixelCount * VxMath::VxImageDescEx::ColorFactorSize);
}
// free alphabuf
alphabuf.reset();
}
// explicitly return to skip fallback
return;
}
}
// fallback
// if image is invalid, or get reader failed.
// write a zero
chk->WriteStruct(0);
}
void CKBitmapData::WriteRawBitmap(CKStateChunk* chk, const VxMath::VxImageDescEx* slot) {
// check image validation
if (slot->IsValid()) {
// write basic data
chk->WriteStruct(32); // write bytePerPixel. always is 32 bpp.
chk->WriteStruct(slot->GetWidth());
chk->WriteStruct(slot->GetHeight());
// alpha, red, green, blue mask, we always use ARGB8888 fmt.
chk->WriteStruct(0xFF000000);
chk->WriteStruct(0x00FF0000);
chk->WriteStruct(0x0000FF00);
chk->WriteStruct(0x000000FF);
// write bufopt
// write 0 to indicate there is no shitty jpeg compression
// see ReadRawBitmap for more info.
chk->WriteStruct(0);
// MARK: originally there is a bunch of code to convert non-ARGB data into ARGB fmt.
// but in my project design, all data is ARGB fmt, so we can simply write them.
// all convertion code removed.
// create 4 channel buf
// we always write alpha channel data.
CKDWORD pixelCount = slot->GetPixelCount();
CKDWORD bufSize = pixelCount * VxMath::VxImageDescEx::ColorFactorSize;
std::unique_ptr<CKBYTE[]> redbuf(new CKBYTE[bufSize]);
std::unique_ptr<CKBYTE[]> greenbuf(new CKBYTE[bufSize]);
std::unique_ptr<CKBYTE[]> bluebuf(new CKBYTE[bufSize]);
std::unique_ptr<CKBYTE[]> alphabuf(new CKBYTE[bufSize]);
// copy channel data
// copy a
VxMath::VxCopyStructure(
pixelCount,
alphabuf.get(),
VxMath::VxImageDescEx::ColorFactorSize,
VxMath::VxImageDescEx::ColorFactorSize,
slot->GetImage() + (3 * VxMath::VxImageDescEx::ColorFactorSize),
VxMath::VxImageDescEx::PixelSize
);
// copy r
VxMath::VxCopyStructure(
pixelCount,
redbuf.get(),
VxMath::VxImageDescEx::ColorFactorSize,
VxMath::VxImageDescEx::ColorFactorSize,
slot->GetImage() + (2 * VxMath::VxImageDescEx::ColorFactorSize),
VxMath::VxImageDescEx::PixelSize
);
// copy g
VxMath::VxCopyStructure(
pixelCount,
greenbuf.get(),
VxMath::VxImageDescEx::ColorFactorSize,
VxMath::VxImageDescEx::ColorFactorSize,
slot->GetImage() + (1 * VxMath::VxImageDescEx::ColorFactorSize),
VxMath::VxImageDescEx::PixelSize
);
// copy b
VxMath::VxCopyStructure(
pixelCount,
bluebuf.get(),
VxMath::VxImageDescEx::ColorFactorSize,
VxMath::VxImageDescEx::ColorFactorSize,
slot->GetImage() + (0 * VxMath::VxImageDescEx::ColorFactorSize),
VxMath::VxImageDescEx::PixelSize
);
// write 4 buf
chk->WriteBuffer(bluebuf.get(), bufSize);
chk->WriteBuffer(greenbuf.get(), bufSize);
chk->WriteBuffer(redbuf.get(), bufSize);
chk->WriteBuffer(alphabuf.get(), bufSize);
// free 4 buf
redbuf.reset();
greenbuf.reset();
bluebuf.reset();
alphabuf.reset();
// explicitly return to skip fallback
return;
}
// fallback
// if image is invalid, write a zero
chk->WriteStruct(0);
}
#pragma endregion
@ -243,14 +414,14 @@ namespace LibCmo::CK2 {
}
// movie info
if (chunk->SeekIdentifierDword(identifiers.m_MovieFileName)) {
// MARK: movie is not implemented here.
}
// MARK: movie is not implemented here.
return true;
}
bool CKBitmapData::DumpToChunk(CKStateChunk* chunk, CKFileVisitor* file, const CKBitmapDataWriteIdentifiers& identifiers) {
// MARK: movie is not supported in this project in plan.
// resolve save fmt and save opt
CK_TEXTURE_SAVEOPTIONS saveopt = GetSaveOptions();
if (saveopt == CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_USEGLOBAL) {

View File

@ -617,6 +617,8 @@ namespace LibCmo::CK2 {
WriteBufferNoSize_LEndian16(int, void*) Write buffer without size. -> WriteBufferNoSize(const void*, CKDWORD)
*/
// MARK: buf can be nullptr, but buf size must is 0. and WriteBuffer will only write a zero to indicate there is a zero buffer.
bool WriteBuffer(const void* buf, CKDWORD size_in_byte);
bool WriteBufferNoSize(const void* buf, CKDWORD size_in_byte);

View File

@ -310,10 +310,9 @@ namespace LibCmo::CK2 {
return false;
}
// if it is empty buffer, create a fake buffer
// and simply return it.
// if it is empty buffer,
// simply return it but need return true to notify read okey.
if (*size_in_byte == 0) {
*ppData = new CKBYTE[1];
return true;
}

View File

@ -256,6 +256,10 @@ namespace LibCmo::CK2::DataHandlers {
});
}
bool CKBitmapBMPHandler::CanSaveAlpha() {
return false;
}
#pragma endregion
#pragma region CKBitmapTGAHandler
@ -293,6 +297,10 @@ namespace LibCmo::CK2::DataHandlers {
});
}
bool CKBitmapTGAHandler::CanSaveAlpha() {
return true;
}
#pragma endregion
#pragma region General Getter Freer

View File

@ -91,7 +91,11 @@ namespace LibCmo::CK2::DataHandlers {
@see SaveFile
*/
virtual CKDWORD SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) = 0;
/**
* @brief Check whether this bitmap handler can save alpha data.
* @return True if this bitmap handler can save alpha data.
*/
virtual bool CanSaveAlpha() = 0;
};
class CKBitmapBMPHandler : public CKBitmapHandler {
@ -106,6 +110,7 @@ namespace LibCmo::CK2::DataHandlers {
virtual bool ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) override;
virtual bool SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
virtual CKDWORD SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
virtual bool CanSaveAlpha() override;
};
@ -121,6 +126,7 @@ namespace LibCmo::CK2::DataHandlers {
virtual bool ReadMemory(const void* memory, CKDWORD size, VxMath::VxImageDescEx* read_image) override;
virtual bool SaveFile(CKSTRING u8filename, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
virtual CKDWORD SaveMemory(void* memory, const VxMath::VxImageDescEx* write_image, const CKBitmapProperties& codec_param) override;
virtual bool CanSaveAlpha() override;
};