mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 22:28:10 +00:00
AGS: Picked a sprite file writing code into SpriteFileWriter class
From upstream 62a87664fc9249bad2886b44d10affc4d60071c3
This commit is contained in:
parent
ddb38177f9
commit
fc9473d175
@ -105,7 +105,7 @@ void SpriteCache::Init() {
|
||||
}
|
||||
|
||||
void SpriteCache::Reset() {
|
||||
_file.Reset();
|
||||
_file.Close();
|
||||
// TODO: find out if it's safe to simply always delete _spriteData.Image with array element
|
||||
for (size_t i = 0; i < _spriteData.size(); ++i) {
|
||||
if (_spriteData[i].Image) {
|
||||
@ -434,7 +434,7 @@ int SpriteCache::SaveToFile(const String &filename, bool compressOutput, SpriteF
|
||||
pre_save_sprite(data.Image);
|
||||
sprites.push_back(data.Image);
|
||||
}
|
||||
return _file.SaveToFile(filename, sprites, &_file, compressOutput, index);
|
||||
return SaveSpriteFile(filename, sprites, &_file, compressOutput, index);
|
||||
}
|
||||
|
||||
HError SpriteCache::InitFile(const String &filename, const String &sprindex_filename) {
|
||||
@ -465,7 +465,7 @@ HError SpriteCache::InitFile(const String &filename, const String &sprindex_file
|
||||
}
|
||||
|
||||
void SpriteCache::DetachFile() {
|
||||
_file.Reset();
|
||||
_file.Close();
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/util/compress.h"
|
||||
#include "ags/shared/util/file.h"
|
||||
#include "ags/shared/util/memory_stream.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
@ -109,7 +110,7 @@ HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filen
|
||||
return RebuildSpriteIndex(_stream.get(), topmost, vers, metrics);
|
||||
}
|
||||
|
||||
void SpriteFile::Reset() {
|
||||
void SpriteFile::Close() {
|
||||
_stream.reset();
|
||||
_curPos = -2;
|
||||
}
|
||||
@ -312,14 +313,6 @@ HError SpriteFile::LoadSpriteData(sprkey_t index, Size &metric, int &bpp,
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
sprkey_t SpriteFile::FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
|
||||
sprkey_t topmost = -1;
|
||||
for (sprkey_t i = 0; i < static_cast<sprkey_t>(sprites.size()); ++i)
|
||||
if (sprites[i])
|
||||
topmost = i;
|
||||
return topmost;
|
||||
}
|
||||
|
||||
void SpriteFile::SeekToSprite(sprkey_t index) {
|
||||
// If we didn't just load the previous sprite, seek to it
|
||||
if (index != _curPos) {
|
||||
@ -328,7 +321,16 @@ void SpriteFile::SeekToSprite(sprkey_t index) {
|
||||
}
|
||||
}
|
||||
|
||||
int SpriteFile::SaveToFile(const String &save_to_file,
|
||||
// Finds the topmost occupied slot index. Warning: may be slow.
|
||||
static sprkey_t FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
|
||||
sprkey_t topmost = -1;
|
||||
for (sprkey_t i = 0; i < static_cast<sprkey_t>(sprites.size()); ++i)
|
||||
if (sprites[i])
|
||||
topmost = i;
|
||||
return topmost;
|
||||
}
|
||||
|
||||
int SaveSpriteFile(const String &save_to_file,
|
||||
const std::vector<Bitmap *> &sprites,
|
||||
SpriteFile *read_from_file,
|
||||
bool compressOutput, SpriteFileIndex &index) {
|
||||
@ -336,27 +338,12 @@ int SpriteFile::SaveToFile(const String &save_to_file,
|
||||
if (output == nullptr)
|
||||
return -1;
|
||||
|
||||
int spriteFileIDCheck = g_system->getMillis();
|
||||
|
||||
// sprite file version
|
||||
output->WriteInt16(kSprfVersion_Current);
|
||||
|
||||
output->WriteArray(spriteFileSig, strlen(spriteFileSig), 1);
|
||||
|
||||
output->WriteInt8(compressOutput ? 1 : 0);
|
||||
output->WriteInt32(spriteFileIDCheck);
|
||||
|
||||
sprkey_t lastslot = read_from_file ? read_from_file->GetTopmostSprite() : 0;
|
||||
lastslot = std::max(lastslot, FindTopmostSprite(sprites));
|
||||
output->WriteInt32(lastslot);
|
||||
|
||||
// allocate buffers to store the indexing info
|
||||
sprkey_t numsprits = lastslot + 1;
|
||||
std::vector<int16_t> spritewidths, spriteheights;
|
||||
std::vector<soff_t> spriteoffs;
|
||||
spritewidths.resize(numsprits);
|
||||
spriteheights.resize(numsprits);
|
||||
spriteoffs.resize(numsprits);
|
||||
SpriteFileWriter writer(output);
|
||||
writer.Begin(compressOutput, lastslot);
|
||||
|
||||
std::unique_ptr<Bitmap> temp_bmp; // for disposing temp sprites
|
||||
std::vector<char> membuf; // for loading raw sprite data
|
||||
|
||||
@ -364,8 +351,6 @@ int SpriteFile::SaveToFile(const String &save_to_file,
|
||||
read_from_file && read_from_file->IsFileCompressed() != compressOutput;
|
||||
|
||||
for (sprkey_t i = 0; i <= lastslot; ++i) {
|
||||
soff_t sproff = output->GetPosition();
|
||||
|
||||
Bitmap *image = (size_t)i < sprites.size() ? sprites[i] : nullptr;
|
||||
|
||||
// if compression setting is different, load the sprite into memory
|
||||
@ -377,34 +362,11 @@ int SpriteFile::SaveToFile(const String &save_to_file,
|
||||
|
||||
// if managed to load an image - save it according the new compression settings
|
||||
if (image != nullptr) {
|
||||
// image in memory -- write it out
|
||||
int bpp = image->GetColorDepth() / 8;
|
||||
spriteoffs[i] = sproff;
|
||||
spritewidths[i] = image->GetWidth();
|
||||
spriteheights[i] = image->GetHeight();
|
||||
output->WriteInt16(bpp);
|
||||
output->WriteInt16(spritewidths[i]);
|
||||
output->WriteInt16(spriteheights[i]);
|
||||
|
||||
if (compressOutput) {
|
||||
soff_t lenloc = output->GetPosition();
|
||||
// write some space for the length data
|
||||
output->WriteInt32(0);
|
||||
|
||||
rle_compress(image, output.get());
|
||||
|
||||
soff_t fileSizeSoFar = output->GetPosition();
|
||||
// write the length of the compressed data
|
||||
output->Seek(lenloc, kSeekBegin);
|
||||
output->WriteInt32((fileSizeSoFar - lenloc) - 4);
|
||||
output->Seek(0, kSeekEnd);
|
||||
} else {
|
||||
output->WriteArray(image->GetDataForWriting(), spritewidths[i] * bpp, spriteheights[i]);
|
||||
}
|
||||
writer.WriteBitmap(image);
|
||||
continue;
|
||||
} else if (diff_compress) {
|
||||
// sprite doesn't exist
|
||||
output->WriteInt16(0); // colour depth
|
||||
writer.WriteEmptySlot();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -413,33 +375,19 @@ int SpriteFile::SaveToFile(const String &save_to_file,
|
||||
Size metric;
|
||||
int bpp;
|
||||
read_from_file->LoadSpriteData(i, metric, bpp, membuf);
|
||||
|
||||
output->WriteInt16(bpp);
|
||||
if (bpp == 0)
|
||||
if (bpp == 0) {
|
||||
writer.WriteEmptySlot();
|
||||
continue; // empty slot
|
||||
|
||||
spriteoffs[i] = sproff;
|
||||
spritewidths[i] = metric.Width;
|
||||
spriteheights[i] = metric.Height;
|
||||
output->WriteInt16(metric.Width);
|
||||
output->WriteInt16(metric.Height);
|
||||
if (compressOutput)
|
||||
output->WriteInt32(membuf.size());
|
||||
if (membuf.size() == 0)
|
||||
continue; // bad data?
|
||||
output->Write(&membuf[0], membuf.size());
|
||||
}
|
||||
writer.WriteSpriteData(membuf, metric.Width, metric.Height, bpp);
|
||||
}
|
||||
writer.Finalize();
|
||||
|
||||
index.SpriteFileIDCheck = spriteFileIDCheck;
|
||||
index.LastSlot = lastslot;
|
||||
index.SpriteCount = numsprits;
|
||||
index.Widths = spritewidths;
|
||||
index.Heights = spriteheights;
|
||||
index.Offsets = spriteoffs;
|
||||
index = writer.GetIndex();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SpriteFile::SaveSpriteIndex(const String &filename, const SpriteFileIndex &index) {
|
||||
int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index) {
|
||||
// write the sprite index file
|
||||
Stream *out = File::CreateFile(filename);
|
||||
if (!out)
|
||||
@ -451,9 +399,9 @@ int SpriteFile::SaveSpriteIndex(const String &filename, const SpriteFileIndex &i
|
||||
out->WriteInt32(index.SpriteFileIDCheck);
|
||||
// write last sprite number and num sprites, to verify that
|
||||
// it matches the spr file
|
||||
out->WriteInt32(index.LastSlot);
|
||||
out->WriteInt32(index.SpriteCount);
|
||||
if (index.SpriteCount > 0) {
|
||||
out->WriteInt32(index.GetLastSlot());
|
||||
out->WriteInt32(index.GetCount());
|
||||
if (index.GetCount() > 0) {
|
||||
out->WriteArrayOfInt16(&index.Widths.front(), index.Widths.size());
|
||||
out->WriteArrayOfInt16(&index.Heights.front(), index.Heights.size());
|
||||
out->WriteArrayOfInt64(&index.Offsets.front(), index.Offsets.size());
|
||||
@ -462,6 +410,83 @@ int SpriteFile::SaveSpriteIndex(const String &filename, const SpriteFileIndex &i
|
||||
return 0;
|
||||
}
|
||||
|
||||
SpriteFileWriter::SpriteFileWriter(std::unique_ptr<Stream> &out) : _out(out) {
|
||||
}
|
||||
|
||||
void SpriteFileWriter::Begin(bool compressed, sprkey_t last_slot) {
|
||||
if (!_out) return;
|
||||
_index.SpriteFileIDCheck = g_system->getMillis();
|
||||
_compress = compressed;
|
||||
|
||||
// sprite file version
|
||||
_out->WriteInt16(kSprfVersion_Current);
|
||||
_out->WriteArray(spriteFileSig, strlen(spriteFileSig), 1);
|
||||
_out->WriteInt8(_compress ? 1 : 0);
|
||||
_out->WriteInt32(_index.SpriteFileIDCheck);
|
||||
|
||||
// Remember and write provided "last slot" index,
|
||||
// but if it's not set (< 0) then we will have to return back later
|
||||
// and write correct one; this is done in Finalize().
|
||||
_lastSlotPos = _out->GetPosition();
|
||||
_out->WriteInt32(last_slot);
|
||||
|
||||
if (last_slot >= 0) { // allocate buffers to store the indexing info
|
||||
sprkey_t numsprits = last_slot + 1;
|
||||
_index.Offsets.reserve(numsprits);
|
||||
_index.Widths.reserve(numsprits);
|
||||
_index.Heights.reserve(numsprits);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteFileWriter::WriteBitmap(Bitmap *image) {
|
||||
if (!_out) return;
|
||||
int bpp = image->GetColorDepth() / 8;
|
||||
int w = image->GetWidth();
|
||||
int h = image->GetHeight();
|
||||
if (_compress) {
|
||||
MemoryStream mems(_membuf, kStream_Write);
|
||||
rle_compress(image, &mems);
|
||||
WriteSpriteData(_membuf, w, h, bpp);
|
||||
_membuf.clear();
|
||||
} else {
|
||||
WriteSpriteData((const char *)image->GetData(), w * h * bpp, w, h, bpp);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteFileWriter::WriteEmptySlot() {
|
||||
if (!_out) return;
|
||||
soff_t sproff = _out->GetPosition();
|
||||
_out->WriteInt16(0); // write invalid color depth to mark empty slot
|
||||
_index.Offsets.push_back(sproff);
|
||||
_index.Widths.push_back(0);
|
||||
_index.Heights.push_back(0);
|
||||
}
|
||||
|
||||
void SpriteFileWriter::WriteSpriteData(const char *pbuf, size_t len,
|
||||
int w, int h, int bpp) {
|
||||
if (!_out) return;
|
||||
soff_t sproff = _out->GetPosition();
|
||||
_index.Offsets.push_back(sproff);
|
||||
_index.Widths.push_back(w);
|
||||
_index.Heights.push_back(h);
|
||||
_out->WriteInt16(bpp);
|
||||
_out->WriteInt16(w);
|
||||
_out->WriteInt16(h);
|
||||
// if not compressed, then the data size could be calculated from the
|
||||
// image metrics, therefore no need to write one
|
||||
if (_compress)
|
||||
_out->WriteInt32(len);
|
||||
if (len == 0) return; // bad data?
|
||||
_out->Write(pbuf, len); // write data itself
|
||||
}
|
||||
|
||||
void SpriteFileWriter::Finalize() {
|
||||
if (!_out || _lastSlotPos < 0) return;
|
||||
_out->Seek(_lastSlotPos, kSeekBegin);
|
||||
_out->WriteInt32(_index.GetLastSlot());
|
||||
_out.reset();
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
@ -21,7 +21,10 @@
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// SpriteFile class handles sprite file loading and streaming.
|
||||
// SpriteFile class handles sprite file parsing and streaming sprites.
|
||||
// SpriteFileWriter manages writing sprites into the output stream one by one,
|
||||
// accumulating index information, and may therefore be suitable for a variety
|
||||
// of situations.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
@ -29,7 +32,9 @@
|
||||
#define AGS_SHARED_AC_SPRITE_FILE_H
|
||||
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/lib/std/memory.h"
|
||||
#include "ags/lib/std/vector.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
@ -61,14 +66,16 @@ typedef int32_t sprkey_t;
|
||||
// SpriteFileIndex contains sprite file's table of contents
|
||||
struct SpriteFileIndex {
|
||||
int SpriteFileIDCheck = 0; // tag matching sprite file and index file
|
||||
sprkey_t LastSlot = -1;
|
||||
size_t SpriteCount = 0u;
|
||||
std::vector<int16_t> Widths;
|
||||
std::vector<int16_t> Heights;
|
||||
std::vector<soff_t> Offsets;
|
||||
|
||||
inline size_t GetCount() const { return Offsets.size(); }
|
||||
inline sprkey_t GetLastSlot() const { return (sprkey_t)GetCount() - 1; }
|
||||
};
|
||||
|
||||
|
||||
// SpriteFile opens a sprite file for reading, reports general information,
|
||||
// and lets read sprites in any order.
|
||||
class SpriteFile {
|
||||
public:
|
||||
// Standart sprite file and sprite index names
|
||||
@ -79,7 +86,8 @@ public:
|
||||
// Loads sprite reference information and inits sprite stream
|
||||
HError OpenFile(const String &filename, const String &sprindex_filename,
|
||||
std::vector<Size> &metrics);
|
||||
void Reset();
|
||||
// Closes stream; no reading will be possible unless opened again
|
||||
void Close();
|
||||
|
||||
// Tells if bitmaps in the file are compressed
|
||||
bool IsFileCompressed() const;
|
||||
@ -93,21 +101,12 @@ public:
|
||||
HError RebuildSpriteIndex(Stream *in, sprkey_t topmost, SpriteFileVersion vers,
|
||||
std::vector<Size> &metrics);
|
||||
|
||||
// Loads an image data and creates a ready bitmap
|
||||
HError LoadSprite(sprkey_t index, Bitmap *&sprite);
|
||||
// Loads an image data into the buffer, reports the bitmap metrics and color depth
|
||||
HError LoadSpriteData(sprkey_t index, Size &metric, int &bpp, std::vector<char> &data);
|
||||
|
||||
// Saves all sprites to file; fills in index data for external use
|
||||
// TODO: refactor to be able to save main file and index file separately (separate function for gather data?)
|
||||
static int SaveToFile(const String &save_to_file,
|
||||
const std::vector<Bitmap *> &sprites, // available sprites (may contain nullptrs)
|
||||
SpriteFile *read_from_file, // optional file to read missing sprites from
|
||||
bool compressOutput, SpriteFileIndex &index);
|
||||
// Saves sprite index table in a separate file
|
||||
static int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
|
||||
|
||||
private:
|
||||
// Finds the topmost occupied slot index. Warning: may be slow.
|
||||
static sprkey_t FindTopmostSprite(const std::vector<Bitmap *> &sprites);
|
||||
// Seek stream to sprite
|
||||
void SeekToSprite(sprkey_t index);
|
||||
|
||||
@ -124,6 +123,49 @@ private:
|
||||
sprkey_t _curPos; // current stream position (sprite slot)
|
||||
};
|
||||
|
||||
// SpriteFileWriter class writes a sprite file in a requested format.
|
||||
// Start using it by calling Begin, write ready bitmaps or copy raw sprite data
|
||||
// over slot by slot, then call Finalize to let it close the format correctly.
|
||||
class SpriteFileWriter {
|
||||
public:
|
||||
SpriteFileWriter(std::unique_ptr<Stream> &out);
|
||||
~SpriteFileWriter() {}
|
||||
|
||||
// Get the sprite index, accumulated after write
|
||||
const SpriteFileIndex &GetIndex() const { return _index; }
|
||||
|
||||
// Initializes new sprite file format
|
||||
void Begin(bool compress, sprkey_t last_slot = -1);
|
||||
// Writes a bitmap into file, compressing if necessary
|
||||
void WriteBitmap(Bitmap *image);
|
||||
// Writes an empty slot marker
|
||||
void WriteEmptySlot();
|
||||
// Writes a raw sprite data without additional processing
|
||||
void WriteSpriteData(const char *pbuf, size_t len, int w, int h, int bpp);
|
||||
void WriteSpriteData(const std::vector<char> &buf, int w, int h, int bpp)
|
||||
{ WriteSpriteData(&buf[0], buf.size(), w, h, bpp); }
|
||||
// Finalizes current format; no further writing is possible after this
|
||||
void Finalize();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Stream> &_out;
|
||||
bool _compress = false;
|
||||
soff_t _lastSlotPos = -1; // last slot save position in file
|
||||
// sprite index accumulated on write for reporting back to user
|
||||
SpriteFileIndex _index;
|
||||
// compression buffer
|
||||
std::vector<char> _membuf;
|
||||
};
|
||||
|
||||
// Saves all sprites to file; fills in index data for external use
|
||||
// TODO: refactor to be able to save main file and index file separately (separate function for gather data?)
|
||||
int SaveSpriteFile(const String &save_to_file,
|
||||
const std::vector<Bitmap*> &sprites, // available sprites (may contain nullptrs)
|
||||
SpriteFile *read_from_file, // optional file to read missing sprites from
|
||||
bool compressOutput, SpriteFileIndex &index);
|
||||
// Saves sprite index table in a separate file
|
||||
int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
Loading…
Reference in New Issue
Block a user