[Scummvm-git-logs] scummvm master -> b615ca27b3d8fd77fb6ed01b630906cd6c306582
dreammaster
noreply at scummvm.org
Thu Mar 31 05:14:10 UTC 2022
This automated email contains information about 12 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
77e4a7f520 AGS: Tidy some code in SpriteFile class
3c81cbaa1c AGS: In SpriteFile split WriteRawData() and WriteSpriteData()
8c17e3cc7e AGS: rle_(de)compress works with arbitrary buffers, not Bitmaps
6912f58a2f AGS: SpriteFile's extended format, let store as indexed bitmaps
4392fa5e2d AGS: Simplified savecompressed_allegro() and renamed for clarity
02540a3b47 AGS: In save_lzw() replaced the use of temp file with memory buffer
dc7609a79c AGS: Removed lzwexpand_to_mem(), tidied load_lzw()
eecd211bb6 AGS: Don't call quit() from lzw functions
897724f110 AGS: Split VectorStream from MemoryStream and allow write C-buffer
6d4a816fb4 AGS: SpriteFile supports compression type selection (RLE/LZW)
0f6ca133d2 AGS: fixed gcc compilation of lzw
b615ca27b3 AGS: Fix String::ClipRight causing access violation for empty string
Commit: 77e4a7f520a61f07b71c0248a3ac524cf760165f
https://github.com/scummvm/scummvm/commit/77e4a7f520a61f07b71c0248a3ac524cf760165f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:17-07:00
Commit Message:
AGS: Tidy some code in SpriteFile class
>From upstream 923c4b3e9ab23359c57233802acf57430843a308
Changed paths:
engines/ags/shared/ac/sprite_cache.h
engines/ags/shared/ac/sprite_file.cpp
engines/ags/shared/ac/sprite_file.h
diff --git a/engines/ags/shared/ac/sprite_cache.h b/engines/ags/shared/ac/sprite_cache.h
index fb830b963e2..fbb94593aae 100644
--- a/engines/ags/shared/ac/sprite_cache.h
+++ b/engines/ags/shared/ac/sprite_cache.h
@@ -153,7 +153,6 @@ private:
void DisposeOldest();
// Information required for the sprite streaming
- // TODO: split into sprite cache and sprite stream data
struct SpriteData {
size_t Size; // to track cache size
uint32_t Flags;
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
index ef08fd907ff..6439293b2b1 100644
--- a/engines/ags/shared/ac/sprite_file.cpp
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -74,18 +74,15 @@ HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filen
return new Error("Uknown spriteset format.");
}
- if (vers == kSprfVersion_Uncompressed) {
- this->_compressed = false;
- } else if (vers == kSprfVersion_Compressed) {
- this->_compressed = true;
- } else if (vers >= kSprfVersion_Last32bit) {
- this->_compressed = (_stream->ReadInt8() == 1);
- spriteFileID = _stream->ReadInt32();
- }
-
if (vers < kSprfVersion_Compressed) {
+ _compressed = false;
// skip the palette
_stream->Seek(256 * 3); // sizeof(RGB) * 256
+ } else if (vers == kSprfVersion_Compressed) {
+ _compressed = true;
+ } else if (vers >= kSprfVersion_Last32bit) {
+ _compressed = (_stream->ReadInt8() == 1);
+ spriteFileID = _stream->ReadInt32();
}
sprkey_t topmost;
@@ -191,37 +188,17 @@ bool SpriteFile::LoadSpriteIndexFile(const String &filename, int expectedFileID,
HError SpriteFile::RebuildSpriteIndex(Stream *in, sprkey_t topmost,
SpriteFileVersion vers, std::vector<Size> &metrics) {
- for (sprkey_t i = 0; i <= topmost; ++i) {
+ topmost = std::min(topmost, (sprkey_t)_spriteData.size() - 1);
+ for (sprkey_t i = 0; !in->EOS() && (i <= topmost); ++i) {
_spriteData[i].Offset = in->GetPosition();
-
- int coldep = in->ReadInt16();
-
- if (coldep == 0) {
- if (in->EOS())
- break;
- continue;
- }
-
- if (in->EOS())
- break;
-
- if ((size_t)i >= _spriteData.size())
- break;
-
- int wdd = in->ReadInt16();
- int htt = in->ReadInt16();
- metrics[i].Width = wdd;
- metrics[i].Height = htt;
-
- size_t spriteDataSize;
- if (vers == kSprfVersion_Compressed) {
- spriteDataSize = in->ReadInt32();
- } else if (vers >= kSprfVersion_Last32bit) {
- spriteDataSize = this->_compressed ? in->ReadInt32() : wdd * coldep * htt;
- } else {
- spriteDataSize = wdd * coldep * htt;
- }
- in->Seek(spriteDataSize);
+ int bpp = in->ReadInt16();
+ if (bpp == 0) continue; // empty slot
+ int w = in->ReadInt16();
+ int h = in->ReadInt16();
+ metrics[i].Width = w;
+ metrics[i].Height = h;
+ size_t data_sz = _compressed ? in->ReadInt32() : w * h * bpp;
+ in->Seek(data_sz); // skip image data
}
return HError::None();
}
@@ -238,17 +215,16 @@ HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
SeekToSprite(index);
_curPos = -2; // mark undefined pos
- int coldep = _stream->ReadInt16();
- if (coldep == 0) { // empty slot, this is normal
+ int bpp = _stream->ReadInt16();
+ if (bpp == 0) { // empty slot, this is normal
return HError::None();
}
-
- int wdd = _stream->ReadInt16();
- int htt = _stream->ReadInt16();
- Bitmap *image = BitmapHelper::CreateBitmap(wdd, htt, coldep * 8);
+ int w = _stream->ReadInt16();
+ int h = _stream->ReadInt16();
+ Bitmap *image = BitmapHelper::CreateBitmap(w, h, bpp * 8);
if (image == nullptr) {
return new Error(String::FromFormat("LoadSprite: failed to allocate bitmap %d (%dx%d%d).",
- index, wdd, htt, coldep * 8));
+ index, w, h, bpp * 8));
}
if (_compressed) {
@@ -258,27 +234,28 @@ HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
return new Error(String::FromFormat("LoadSprite: bad compressed data for sprite %d.", index));
}
rle_decompress(image, _stream.get());
+ // TODO: test that not more than data_size was read!
} else {
- if (coldep == 1) {
- for (int h = 0; h < htt; ++h)
- _stream->ReadArray(&image->GetScanLineForWriting(h)[0], coldep, wdd);
- } else if (coldep == 2) {
- for (int h = 0; h < htt; ++h)
- _stream->ReadArrayOfInt16((int16_t *)&image->GetScanLineForWriting(h)[0], wdd);
- } else {
- for (int h = 0; h < htt; ++h)
- _stream->ReadArrayOfInt32((int32_t *)&image->GetScanLineForWriting(h)[0], wdd);
+ switch (bpp) {
+ case 1: _stream->Read(image->GetDataForWriting(), w * h); break;
+ case 2: _stream->ReadArrayOfInt16(
+ reinterpret_cast<int16_t *>(image->GetDataForWriting()), w *h); break;
+ case 4: _stream->ReadArrayOfInt32(
+ reinterpret_cast<int32_t *>(image->GetDataForWriting()), w *h); break;
+ default: assert(0); break;
}
}
+
sprite = image;
_curPos = index + 1; // mark correct pos
return HError::None();
}
HError SpriteFile::LoadSpriteData(sprkey_t index, Size &metric, int &bpp,
- std::vector<uint8_t> &data) {
+ std::vector<uint8_t> &data) {
metric = Size();
bpp = 0;
+ data.resize(0);
if (index < 0 || (size_t)index >= _spriteData.size())
new Error(String::FromFormat("LoadSprite: slot index %d out of bounds (%d - %d).",
index, 0, _spriteData.size() - 1));
@@ -289,26 +266,17 @@ HError SpriteFile::LoadSpriteData(sprkey_t index, Size &metric, int &bpp,
SeekToSprite(index);
_curPos = -2; // mark undefined pos
- int coldep = _stream->ReadInt16();
- if (coldep == 0) { // empty slot, this is normal
- metric = Size();
- bpp = 0;
- data.resize(0);
+ bpp = _stream->ReadInt16();
+ if (bpp == 0) { // empty slot, this is normal
return HError::None();
}
-
- int width = _stream->ReadInt16();
- int height = _stream->ReadInt16();
-
- size_t data_size;
- if (_compressed)
- data_size = _stream->ReadInt32();
- else
- data_size = width * height * coldep;
+ int w = _stream->ReadInt16();
+ int h = _stream->ReadInt16();
+ size_t data_size = _compressed ? _stream->ReadInt32() : w * h * bpp;
data.resize(data_size);
_stream->Read(&data[0], data_size);
- metric = Size(width, height);
- bpp = coldep;
+ metric = Size(w, h);
+
_curPos = index + 1; // mark correct pos
return HError::None();
}
@@ -402,9 +370,9 @@ int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index) {
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());
+ out->WriteArrayOfInt16(&index.Widths[0], index.Widths.size());
+ out->WriteArrayOfInt16(&index.Heights[0], index.Heights.size());
+ out->WriteArrayOfInt64(&index.Offsets[0], index.Offsets.size());
}
delete out;
return 0;
@@ -440,7 +408,7 @@ void SpriteFileWriter::Begin(bool compressed, sprkey_t last_slot) {
void SpriteFileWriter::WriteBitmap(Bitmap *image) {
if (!_out) return;
- int bpp = image->GetColorDepth() / 8;
+ int bpp = image->GetBPP();
int w = image->GetWidth();
int h = image->GetHeight();
if (_compress) {
diff --git a/engines/ags/shared/ac/sprite_file.h b/engines/ags/shared/ac/sprite_file.h
index 2699964e909..a12ed80ccfd 100644
--- a/engines/ags/shared/ac/sprite_file.h
+++ b/engines/ags/shared/ac/sprite_file.h
@@ -113,7 +113,8 @@ private:
// Internal sprite reference
struct SpriteRef {
soff_t Offset = 0; // data offset
- size_t Size = 0; // cache size of element, in bytes
+ size_t RawSize = 0; // file size of element, in bytes
+ // TODO: RawSize is currently unused, due to incompleteness of spriteindex format
};
// Array of sprite references
Commit: 3c81cbaa1c9bc583c5f9846e9577882ea548340e
https://github.com/scummvm/scummvm/commit/3c81cbaa1c9bc583c5f9846e9577882ea548340e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:17-07:00
Commit Message:
AGS: In SpriteFile split WriteRawData() and WriteSpriteData()
>From upstream eab89574be9a577ea13e4d90e7032a6d6c44c903
Changed paths:
engines/ags/shared/ac/sprite_file.cpp
engines/ags/shared/ac/sprite_file.h
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
index 6439293b2b1..878f65ccec1 100644
--- a/engines/ags/shared/ac/sprite_file.cpp
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -251,10 +251,8 @@ HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
return HError::None();
}
-HError SpriteFile::LoadSpriteData(sprkey_t index, Size &metric, int &bpp,
- std::vector<uint8_t> &data) {
- metric = Size();
- bpp = 0;
+HError SpriteFile::LoadRawData(sprkey_t index, SpriteDatHeader &hdr, std::vector<uint8_t> &data) {
+ hdr = SpriteDatHeader();
data.resize(0);
if (index < 0 || (size_t)index >= _spriteData.size())
new Error(String::FromFormat("LoadSprite: slot index %d out of bounds (%d - %d).",
@@ -266,16 +264,20 @@ HError SpriteFile::LoadSpriteData(sprkey_t index, Size &metric, int &bpp,
SeekToSprite(index);
_curPos = -2; // mark undefined pos
- bpp = _stream->ReadInt16();
+ int bpp = _stream->ReadInt16();
if (bpp == 0) { // empty slot, this is normal
return HError::None();
}
int w = _stream->ReadInt16();
int h = _stream->ReadInt16();
- size_t data_size = _compressed ? _stream->ReadInt32() : w * h * bpp;
+ size_t data_size = w * h * bpp;
+ if (_compressed) {
+ data_size = _stream->ReadInt32() + sizeof(int32_t);
+ _stream->Seek(-4);
+ }
data.resize(data_size);
_stream->Read(&data[0], data_size);
- metric = Size(w, h);
+ hdr = SpriteDatHeader(bpp, w, h);
_curPos = index + 1; // mark correct pos
return HError::None();
@@ -340,14 +342,13 @@ int SaveSpriteFile(const String &save_to_file,
// Not in memory - and same compression option;
// Directly copy the sprite bytes from the input file to the output
- Size metric;
- int bpp;
- read_from_file->LoadSpriteData(i, metric, bpp, membuf);
- if (bpp == 0) {
+ SpriteDatHeader hdr;
+ read_from_file->LoadRawData(i, hdr, membuf);
+ if (hdr.BPP == 0) { // empty slot
writer.WriteEmptySlot();
- continue; // empty slot
+ continue;
}
- writer.WriteSpriteData(&membuf[0], membuf.size(), metric.Width, metric.Height, bpp);
+ writer.WriteRawData(hdr, &membuf[0], membuf.size());
}
writer.Finalize();
@@ -411,13 +412,39 @@ void SpriteFileWriter::WriteBitmap(Bitmap *image) {
int bpp = image->GetBPP();
int w = image->GetWidth();
int h = image->GetHeight();
+ const SpriteDatHeader hdr(bpp, w, h);
if (_compress) {
MemoryStream mems(_membuf, kStream_Write);
rle_compress(image, &mems);
- WriteSpriteData(&_membuf[0], _membuf.size(), w, h, bpp);
+ // write image data as a plain byte array
+ WriteSpriteData(hdr, &_membuf[0], _membuf.size(), 1);
_membuf.clear();
} else {
- WriteSpriteData(image->GetData(), w * h * bpp, w, h, bpp);
+ WriteSpriteData(hdr, image->GetData(), w * h * bpp, bpp);
+ }
+}
+
+void SpriteFileWriter::WriteSpriteData(const SpriteDatHeader &hdr,
+ const uint8_t *im_data, size_t im_data_sz, int im_bpp) {
+ // Add index entry and write resulting data to the stream
+ soff_t sproff = _out->GetPosition();
+ _index.Offsets.push_back(sproff);
+ _index.Widths.push_back(hdr.Width);
+ _index.Heights.push_back(hdr.Height);
+ _out->WriteInt16(hdr.BPP);
+ _out->WriteInt16(hdr.Width);
+ _out->WriteInt16(hdr.Height);
+ // if not compressed, then the data size is supposed to be calculated
+ // from the image metrics
+ if (_compress)
+ _out->WriteInt32(im_data_sz);
+ switch (im_bpp) {
+ case 1: _out->Write(im_data, im_data_sz); break;
+ case 2: _out->WriteArrayOfInt16(reinterpret_cast<const int16_t *>(im_data),
+ im_data_sz / sizeof(int16_t)); break;
+ case 4: _out->WriteArrayOfInt32(reinterpret_cast<const int32_t *>(im_data),
+ im_data_sz / sizeof(int32_t)); break;
+ default: assert(0); break;
}
}
@@ -430,22 +457,16 @@ void SpriteFileWriter::WriteEmptySlot() {
_index.Heights.push_back(0);
}
-void SpriteFileWriter::WriteSpriteData(const uint8_t *pbuf, size_t len,
- int w, int h, int bpp) {
+void SpriteFileWriter::WriteRawData(const SpriteDatHeader &hdr, const uint8_t *data, size_t data_sz) {
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
+ _index.Widths.push_back(hdr.Width);
+ _index.Heights.push_back(hdr.Height);
+ _out->WriteInt16(hdr.BPP);
+ _out->WriteInt16(hdr.Width);
+ _out->WriteInt16(hdr.Height);
+ _out->Write(data, data_sz);
}
void SpriteFileWriter::Finalize() {
diff --git a/engines/ags/shared/ac/sprite_file.h b/engines/ags/shared/ac/sprite_file.h
index a12ed80ccfd..4fde6949e72 100644
--- a/engines/ags/shared/ac/sprite_file.h
+++ b/engines/ags/shared/ac/sprite_file.h
@@ -74,6 +74,18 @@ struct SpriteFileIndex {
inline sprkey_t GetLastSlot() const { return (sprkey_t)GetCount() - 1; }
};
+// Invidual sprite data header (as read from the file)
+struct SpriteDatHeader {
+ int BPP = 0; // color depth (bytes per pixel)
+ int Width = 0;
+ int Height = 0;
+
+ SpriteDatHeader() = default;
+ SpriteDatHeader(int bpp, int w = 0, int h = 0)
+ : BPP(bpp), Width(w), Height(h) {
+ }
+};
+
// SpriteFile opens a sprite file for reading, reports general information,
// and lets read sprites in any order.
class SpriteFile {
@@ -103,8 +115,8 @@ public:
// 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<uint8_t> &data);
+ // Loads a raw sprite element data into the buffer, stores header info separately
+ HError LoadRawData(sprkey_t index, SpriteDatHeader &hdr, std::vector<uint8_t> &data);
private:
// Seek stream to sprite
@@ -141,13 +153,16 @@ public:
void WriteBitmap(Bitmap *image);
// Writes an empty slot marker
void WriteEmptySlot();
- // Writes a raw sprite data without additional processing
- void WriteSpriteData(const uint8_t *pbuf, size_t len, int w, int h, int bpp);
- // Finalizes current format; no further writing is possible after this
- void Finalize();
+ // Writes a raw sprite data without any additional processing
+ void WriteRawData(const SpriteDatHeader &hdr, const uint8_t *data, size_t data_sz);
+ // Finalizes current format; no further writing is possible after this
+ void Finalize();
private:
- std::unique_ptr<Stream> &_out;
+ // Writes prepared image data in a proper file format, following explicit data_bpp rule
+ void WriteSpriteData(const SpriteDatHeader &hdr, const uint8_t *im_data, size_t im_data_sz, int im_bpp);
+
+ 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
Commit: 8c17e3cc7e6cd8e753be5dcf87b7fd415f5ac9af
https://github.com/scummvm/scummvm/commit/8c17e3cc7e6cd8e753be5dcf87b7fd415f5ac9af
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:17-07:00
Commit Message:
AGS: rle_(de)compress works with arbitrary buffers, not Bitmaps
>From upstream d3be1ce673455ed6bf6f715e0adad4be44eee2bd
Changed paths:
engines/ags/shared/ac/sprite_file.cpp
engines/ags/shared/util/compress.cpp
engines/ags/shared/util/compress.h
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
index 878f65ccec1..ba58ef4d186 100644
--- a/engines/ags/shared/ac/sprite_file.cpp
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -233,7 +233,7 @@ HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
delete image;
return new Error(String::FromFormat("LoadSprite: bad compressed data for sprite %d.", index));
}
- rle_decompress(image, _stream.get());
+ rle_decompress(image->GetDataForWriting(), w * h * bpp, bpp, _stream.get());
// TODO: test that not more than data_size was read!
} else {
switch (bpp) {
@@ -415,7 +415,7 @@ void SpriteFileWriter::WriteBitmap(Bitmap *image) {
const SpriteDatHeader hdr(bpp, w, h);
if (_compress) {
MemoryStream mems(_membuf, kStream_Write);
- rle_compress(image, &mems);
+ rle_compress(image->GetData(), w * h * bpp, bpp, &mems);
// write image data as a plain byte array
WriteSpriteData(hdr, &_membuf[0], _membuf.size(), 1);
_membuf.clear();
diff --git a/engines/ags/shared/util/compress.cpp b/engines/ags/shared/util/compress.cpp
index 98154396aa5..268f3fb51c2 100644
--- a/engines/ags/shared/util/compress.cpp
+++ b/engines/ags/shared/util/compress.cpp
@@ -43,17 +43,18 @@ using namespace AGS::Shared;
// RLE
//-----------------------------------------------------------------------------
-void cpackbitl(const uint8_t *line, int size, Stream *out) {
- int cnt = 0; // bytes encoded
+static void cpackbitl(const uint8_t *line, size_t size, Stream *out) {
+ size_t cnt = 0; // bytes encoded
while (cnt < size) {
+ // note that the algorithm below requires signed operations
int i = cnt;
int j = i + 1;
int jmax = i + 126;
- if (jmax >= size)
+ if ((size_t)jmax >= size)
jmax = size - 1;
- if (i == size - 1) { //................last byte alone
+ if (i == (int)size - 1) { //................last byte alone
out->WriteInt8(0);
out->WriteInt8(line[i]);
cnt++;
@@ -71,24 +72,25 @@ void cpackbitl(const uint8_t *line, int size, Stream *out) {
j++;
out->WriteInt8(j - i);
- out->WriteArray(line + i, j - i + 1, 1);
+ out->Write(line + i, j - i + 1);
cnt += j - i + 1;
}
} // end while
}
-void cpackbitl16(const uint16_t *line, int size, Stream *out) {
- int cnt = 0; // bytes encoded
+static void cpackbitl16(const uint16_t *line, size_t size, Stream *out) {
+ size_t cnt = 0; // bytes encoded
while (cnt < size) {
+ // note that the algorithm below requires signed operations
int i = cnt;
int j = i + 1;
int jmax = i + 126;
- if (jmax >= size)
+ if ((size_t)jmax >= size)
jmax = size - 1;
- if (i == size - 1) { //................last byte alone
+ if (i == (int)size - 1) { //................last byte alone
out->WriteInt8(0);
out->WriteInt16(line[i]);
cnt++;
@@ -113,17 +115,18 @@ void cpackbitl16(const uint16_t *line, int size, Stream *out) {
} // end while
}
-void cpackbitl32(const uint32_t *line, int size, Stream *out) {
- int cnt = 0; // bytes encoded
+static void cpackbitl32(const uint32_t *line, size_t size, Stream *out) {
+ size_t cnt = 0; // bytes encoded
while (cnt < size) {
+ // note that the algorithm below requires signed operations
int i = cnt;
int j = i + 1;
int jmax = i + 126;
- if (jmax >= size)
+ if ((size_t)jmax >= size)
jmax = size - 1;
- if (i == size - 1) { //................last byte alone
+ if (i == (int)size - 1) { //................last byte alone
out->WriteInt8(0);
out->WriteInt32(line[i]);
cnt++;
@@ -178,15 +181,15 @@ void csavecompressed(Stream *out, const unsigned char *tobesaved, const RGB pala
free(ress);
}
-int cunpackbitl(uint8_t *line, int size, Stream *in) {
- int n = 0; // number of bytes decoded
+static int cunpackbitl(uint8_t *line, size_t size, Stream *in) {
+ size_t n = 0; // number of bytes decoded
while (n < size) {
int ix = in->ReadByte(); // get index byte
if (in->HasErrors())
break;
- int8_t cx = ix;
+ char cx = ix;
if (cx == -128)
cx = 0;
@@ -215,15 +218,15 @@ int cunpackbitl(uint8_t *line, int size, Stream *in) {
return in->HasErrors() ? -1 : 0;
}
-int cunpackbitl16(uint16_t *line, int size, Stream *in) {
- int n = 0; // number of bytes decoded
+static int cunpackbitl16(uint16_t *line, size_t size, Stream *in) {
+ size_t n = 0; // number of bytes decoded
while (n < size) {
int ix = in->ReadByte(); // get index byte
if (in->HasErrors())
break;
- int8_t cx = ix;
+ char cx = ix;
if (cx == -128)
cx = 0;
@@ -252,15 +255,15 @@ int cunpackbitl16(uint16_t *line, int size, Stream *in) {
return in->HasErrors() ? -1 : 0;
}
-int cunpackbitl32(uint32_t *line, int size, Stream *in) {
- int n = 0; // number of bytes decoded
+static int cunpackbitl32(uint32_t *line, size_t size, Stream *in) {
+ size_t n = 0; // number of bytes decoded
while (n < size) {
int ix = in->ReadByte(); // get index byte
if (in->HasErrors())
break;
- int8_t cx = ix;
+ char cx = ix;
if (cx == -128)
cx = 0;
@@ -289,31 +292,21 @@ int cunpackbitl32(uint32_t *line, int size, Stream *in) {
return in->HasErrors() ? -1 : 0;
}
-void rle_compress(Bitmap *bmp, Shared::Stream *out) {
- const int depth = bmp->GetBPP();
- if (depth == 1) {
- for (int y = 0; y < bmp->GetHeight(); y++)
- cpackbitl(&bmp->GetScanLineForWriting(y)[0], bmp->GetWidth(), out);
- } else if (depth == 2) {
- for (int y = 0; y < bmp->GetHeight(); y++)
- cpackbitl16((uint16_t *)&bmp->GetScanLine(y)[0], bmp->GetWidth(), out);
- } else {
- for (int y = 0; y < bmp->GetHeight(); y++)
- cpackbitl32((uint32_t *)&bmp->GetScanLine(y)[0], bmp->GetWidth(), out);
+void rle_compress(const uint8_t *data, size_t data_sz, int image_bpp, Stream *out) {
+ switch (image_bpp) {
+ case 1: cpackbitl(data, data_sz, out); break;
+ case 2: cpackbitl16(reinterpret_cast<const uint16_t *>(data), data_sz / sizeof(uint16_t), out); break;
+ case 4: cpackbitl32(reinterpret_cast<const uint32_t *>(data), data_sz / sizeof(uint32_t), out); break;
+ default: assert(0); break;
}
}
-void rle_decompress(Bitmap *bmp, Shared::Stream *in) {
- const int depth = bmp->GetBPP();
- if (depth == 1) {
- for (int y = 0; y < bmp->GetHeight(); y++)
- cunpackbitl(&bmp->GetScanLineForWriting(y)[0], bmp->GetWidth(), in);
- } else if (depth == 2) {
- for (int y = 0; y < bmp->GetHeight(); y++)
- cunpackbitl16((uint16_t *)&bmp->GetScanLineForWriting(y)[0], bmp->GetWidth(), in);
- } else {
- for (int y = 0; y < bmp->GetHeight(); y++)
- cunpackbitl32((uint32_t *)&bmp->GetScanLineForWriting(y)[0], bmp->GetWidth(), in);
+void rle_decompress(uint8_t *data, size_t data_sz, int image_bpp, Stream *in) {
+ switch (image_bpp) {
+ case 1: cunpackbitl(data, data_sz, in); break;
+ case 2: cunpackbitl16(reinterpret_cast<uint16_t *>(data), data_sz / sizeof(uint16_t), in); break;
+ case 4: cunpackbitl32(reinterpret_cast<uint32_t *>(data), data_sz / sizeof(uint32_t), in); break;
+ default: assert(0); break;
}
}
diff --git a/engines/ags/shared/util/compress.h b/engines/ags/shared/util/compress.h
index 42223fd443b..d9472c69c7e 100644
--- a/engines/ags/shared/util/compress.h
+++ b/engines/ags/shared/util/compress.h
@@ -36,8 +36,8 @@ class Bitmap;
using namespace AGS; // FIXME later
-void rle_compress(Shared::Bitmap *, Shared::Stream *);
-void rle_decompress(Shared::Bitmap *, Shared::Stream *);
+void rle_compress(const uint8_t *data, size_t data_sz, int image_bpp, Shared::Stream *out);
+void rle_decompress(uint8_t *data, size_t data_sz, int image_bpp, Shared::Stream *in);
// LZW compression
void save_lzw(Shared::Stream *out, const Shared::Bitmap *bmpp, const RGB *pall);
Commit: 6912f58a2fecd49c42be8a94fb89bd8f7b8c87cf
https://github.com/scummvm/scummvm/commit/6912f58a2fecd49c42be8a94fb89bd8f7b8c87cf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:17-07:00
Commit Message:
AGS: SpriteFile's extended format, let store as indexed bitmaps
>From upstream 34f32e3b71b2f8d0f7480aa04a2fcd38d77fb83b
Changed paths:
engines/ags/shared/ac/sprite_cache.cpp
engines/ags/shared/ac/sprite_cache.h
engines/ags/shared/ac/sprite_file.cpp
engines/ags/shared/ac/sprite_file.h
diff --git a/engines/ags/shared/ac/sprite_cache.cpp b/engines/ags/shared/ac/sprite_cache.cpp
index e80de98edee..b03fcb26773 100644
--- a/engines/ags/shared/ac/sprite_cache.cpp
+++ b/engines/ags/shared/ac/sprite_cache.cpp
@@ -423,7 +423,7 @@ void SpriteCache::RemapSpriteToSprite0(sprkey_t index) {
#endif
}
-int SpriteCache::SaveToFile(const String &filename, bool compressOutput, SpriteFileIndex &index) {
+int SpriteCache::SaveToFile(const String &filename, int store_flags, bool compress, SpriteFileIndex &index) {
std::vector<Bitmap *> sprites;
for (const auto &data : _spriteData) {
// NOTE: this is a horrible hack:
@@ -434,7 +434,7 @@ int SpriteCache::SaveToFile(const String &filename, bool compressOutput, SpriteF
pre_save_sprite(data.Image);
sprites.push_back(data.Image);
}
- return SaveSpriteFile(filename, sprites, &_file, compressOutput, index);
+ return SaveSpriteFile(filename, sprites, &_file, store_flags, compress, index);
}
HError SpriteCache::InitFile(const String &filename, const String &sprindex_filename) {
diff --git a/engines/ags/shared/ac/sprite_cache.h b/engines/ags/shared/ac/sprite_cache.h
index fbb94593aae..d759639c5d8 100644
--- a/engines/ags/shared/ac/sprite_cache.h
+++ b/engines/ags/shared/ac/sprite_cache.h
@@ -94,10 +94,13 @@ public:
// Loads sprite reference information and inits sprite stream
HError InitFile(const Shared::String &filename, const Shared::String &sprindex_filename);
// Saves current cache contents to the file
- int SaveToFile(const Shared::String &filename, bool compressOutput, SpriteFileIndex &index);
+ int SaveToFile(const Shared::String &filename, int store_flags, bool compress, SpriteFileIndex &index);
// Closes an active sprite file stream
void DetachFile();
+ inline int GetStoreFlags() const {
+ return _file.GetStoreFlags();
+ }
inline bool IsFileCompressed() const {
return _file.IsFileCompressed();
}
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
index ba58ef4d186..34bc74af5cf 100644
--- a/engines/ags/shared/ac/sprite_file.cpp
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -39,6 +39,106 @@ static const char *spindexid = "SPRINDEX";
const char *SpriteFile::DefaultSpriteFileName = "acsprset.spr";
const char *SpriteFile::DefaultSpriteIndexName = "sprindex.dat";
+// Image buffer pointer, a helper struct that eases switching
+// between intermediate buffers when loading, saving or converting an image.
+template <typename T> struct ImBufferPtrT {
+ T Buf = nullptr;
+ size_t Size = 0;
+ int BPP = 1; // byte per pixel
+
+ ImBufferPtrT() = default;
+ ImBufferPtrT(T buf, size_t sz, int bpp) : Buf(buf), Size(sz), BPP(bpp) {
+ }
+};
+typedef ImBufferPtrT<uint8_t *> ImBufferPtr;
+typedef ImBufferPtrT<const uint8_t *> ImBufferCPtr;
+
+
+// Finds the given color's index in the palette, or returns -1 if such color is not there
+static size_t lookup_palette(uint32_t col, uint32_t palette[256], uint32_t ncols) {
+ for (size_t i = 0; i < ncols; ++i)
+ if (palette[i] == col) return i;
+ return (size_t)-1;
+}
+
+// Converts a 16/32-bit image into the indexed 8-bit pixel data with palette;
+// NOTE: the palette will contain colors in the same format as the source image.
+// only succeeds if the total number of colors used in the image is < 257.
+static bool CreateIndexedBitmap(const Bitmap *image, std::vector<uint8_t> &dst_data,
+ uint32_t palette[256], uint32_t &pal_count) {
+ const int src_bpp = image->GetBPP();
+ const size_t src_size = image->GetWidth() * image->GetHeight() * image->GetBPP();
+ const size_t dst_size = image->GetWidth() * image->GetHeight();
+ dst_data.resize(dst_size);
+ const uint8_t *src = image->GetData(), *src_end = src + src_size;
+ uint8_t *dst = &dst_data[0], *dst_end = dst + dst_size;
+ pal_count = 0;
+
+ for (; src < src_end && dst < dst_end; src += src_bpp) {
+ uint32_t col = 0;
+ size_t pal_n = 0;
+ switch (src_bpp) {
+ case 2:
+ col = *((const uint16_t *)src);
+ pal_n = lookup_palette(col, palette, pal_count);
+ break;
+ case 4:
+ col = *((const uint32_t *)src);
+ pal_n = lookup_palette(col, palette, pal_count);
+ break;
+ default: assert(0); break;
+ }
+
+ if (pal_n == -1) {
+ if (pal_count == 256) return false;
+ pal_n = pal_count;
+ palette[pal_count++] = col;
+ }
+ *(dst++) = (uint8_t)pal_n;
+ }
+ return true;
+}
+
+// Unpacks an indexed image's pixel data into the 16/32-bit image;
+// NOTE: the palette is expected to contain colors in the same format as the destination.
+static void UnpackIndexedBitmap(Bitmap *image, const uint8_t *data, size_t data_size,
+ uint32_t *palette, uint32_t pal_count) {
+ assert(pal_count > 0);
+ if (pal_count == 0) return; // meaningless
+ const uint8_t bpp = image->GetBPP();
+ const size_t dst_size = image->GetWidth() * image->GetHeight() * image->GetBPP();
+ uint8_t *dst = image->GetDataForWriting(), *dst_end = dst + dst_size;
+ for (size_t p = 0; (p < data_size) && (dst < dst_end); ++p, dst += bpp) {
+ uint8_t index = data[p];
+ assert(index < pal_count);
+ uint32_t color = (index < pal_count) ? palette[index] : palette[0];
+ switch (bpp) {
+ case 2: *((uint16_t *)dst) = color; break;
+ case 4: *((uint32_t *)dst) = color; break;
+ default: assert(0); break;
+ }
+ }
+}
+
+
+static inline SpriteFormat PaletteFormatForBPP(int bpp) {
+ switch (bpp) {
+ case 1: return kSprFmt_PaletteRgb888;
+ case 2: return kSprFmt_PaletteRgb565;
+ case 4: return kSprFmt_PaletteArgb8888;
+ }
+ return kSprFmt_Undefined;
+}
+
+static inline uint8_t GetPaletteBPP(SpriteFormat fmt) {
+ switch (fmt) {
+ case kSprFmt_PaletteRgb888: return 3;
+ case kSprFmt_PaletteArgb8888: return 4;
+ case kSprFmt_PaletteRgb565: return 2;
+ }
+ return 0; // means no palette
+}
+
SpriteFile::SpriteFile() {
_compressed = false;
@@ -47,7 +147,6 @@ SpriteFile::SpriteFile() {
HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filename,
std::vector<Size> &metrics) {
- SpriteFileVersion vers;
char buff[20];
soff_t spr_initial_offs = 0;
int spriteFileID = 0;
@@ -58,13 +157,14 @@ HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filen
spr_initial_offs = _stream->GetPosition();
- vers = (SpriteFileVersion)_stream->ReadInt16();
+ _version = (SpriteFileVersion)_stream->ReadInt16();
// read the "Sprite File" signature
_stream->ReadArray(&buff[0], 13, 1);
- if (vers < kSprfVersion_Uncompressed || vers > kSprfVersion_Current) {
+ if (_version < kSprfVersion_Uncompressed || _version > kSprfVersion_Current) {
_stream.reset();
- return new Error(String::FromFormat("Unsupported spriteset format (requested %d, supported %d - %d).", vers, kSprfVersion_Uncompressed, kSprfVersion_Current));
+ return new Error(String::FromFormat("Unsupported spriteset format (requested %d, supported %d - %d).", _version,
+ kSprfVersion_Uncompressed, kSprfVersion_Current));
}
// unknown version
@@ -74,28 +174,37 @@ HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filen
return new Error("Uknown spriteset format.");
}
- if (vers < kSprfVersion_Compressed) {
+ _storeFlags = 0;
+ if (_version < kSprfVersion_Compressed) {
_compressed = false;
// skip the palette
_stream->Seek(256 * 3); // sizeof(RGB) * 256
- } else if (vers == kSprfVersion_Compressed) {
+ } else if (_version == kSprfVersion_Compressed) {
_compressed = true;
- } else if (vers >= kSprfVersion_Last32bit) {
+ } else if (_version >= kSprfVersion_Last32bit) {
_compressed = (_stream->ReadInt8() == 1);
spriteFileID = _stream->ReadInt32();
}
sprkey_t topmost;
- if (vers < kSprfVersion_HighSpriteLimit)
+ if (_version < kSprfVersion_HighSpriteLimit)
topmost = (uint16_t)_stream->ReadInt16();
else
topmost = _stream->ReadInt32();
- if (vers < kSprfVersion_Uncompressed)
+ if (_version < kSprfVersion_Uncompressed)
topmost = 200;
_spriteData.resize(topmost + 1);
metrics.resize(topmost + 1);
+ // Version 12+: read global store flags
+ if (_version >= kSprfVersion_StorageFormats) {
+ _storeFlags = _stream->ReadInt8();
+ _stream->ReadInt8(); // reserved
+ _stream->ReadInt8();
+ _stream->ReadInt8();
+ }
+
// if there is a sprite index file, use it
if (LoadSpriteIndexFile(sprindex_filename, spriteFileID,
spr_initial_offs, topmost, metrics)) {
@@ -104,7 +213,7 @@ HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filen
}
// Failed, index file is invalid; index sprites manually
- return RebuildSpriteIndex(_stream.get(), topmost, vers, metrics);
+ return RebuildSpriteIndex(_stream.get(), topmost, _version, metrics);
}
void SpriteFile::Close() {
@@ -112,6 +221,10 @@ void SpriteFile::Close() {
_curPos = -2;
}
+int SpriteFile::GetStoreFlags() const {
+ return _storeFlags;
+}
+
bool SpriteFile::IsFileCompressed() const {
return _compressed;
}
@@ -186,18 +299,36 @@ bool SpriteFile::LoadSpriteIndexFile(const String &filename, int expectedFileID,
return true;
}
+static inline void ReadSprHeader(SpriteDatHeader &hdr, Stream *in,
+ const SpriteFileVersion ver, int gl_compress) {
+ int bpp = in->ReadInt8();
+ SpriteFormat sformat = (SpriteFormat)in->ReadInt8();
+ // note we MUST read first 2 * int8 before skipping rest
+ if (bpp == 0) {
+ hdr = SpriteDatHeader(); return;
+ } // empty slot
+ int compress = gl_compress, pal_count = 0;
+ if (ver >= kSprfVersion_StorageFormats) {
+ pal_count = (uint8_t)in->ReadInt8() + 1; // saved as (count - 1)
+ compress = in->ReadInt8();
+ }
+ int w = in->ReadInt16();
+ int h = in->ReadInt16();
+ hdr = SpriteDatHeader(bpp, sformat, pal_count, compress, w, h);
+}
+
HError SpriteFile::RebuildSpriteIndex(Stream *in, sprkey_t topmost,
SpriteFileVersion vers, std::vector<Size> &metrics) {
topmost = std::min(topmost, (sprkey_t)_spriteData.size() - 1);
for (sprkey_t i = 0; !in->EOS() && (i <= topmost); ++i) {
_spriteData[i].Offset = in->GetPosition();
- int bpp = in->ReadInt16();
- if (bpp == 0) continue; // empty slot
- int w = in->ReadInt16();
- int h = in->ReadInt16();
- metrics[i].Width = w;
- metrics[i].Height = h;
- size_t data_sz = _compressed ? in->ReadInt32() : w * h * bpp;
+ SpriteDatHeader hdr;
+ ReadSprHeader(hdr, _stream.get(), _version, _compressed ? 1 : 0);
+ if (hdr.BPP == 0) return HError::None(); // empty slot, this is normal
+ int pal_bpp = GetPaletteBPP(hdr.SFormat);
+ if (pal_bpp > 0) in->Seek(hdr.PalCount * pal_bpp); // skip palette
+ size_t data_sz = ((_version >= kSprfVersion_StorageFormats) || _compressed) ?
+ (uint32_t)in->ReadInt32() : hdr.Width * hdr.Height * hdr.BPP;
in->Seek(data_sz); // skip image data
}
return HError::None();
@@ -215,36 +346,55 @@ HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
SeekToSprite(index);
_curPos = -2; // mark undefined pos
- int bpp = _stream->ReadInt16();
- if (bpp == 0) { // empty slot, this is normal
- return HError::None();
- }
- int w = _stream->ReadInt16();
- int h = _stream->ReadInt16();
+ SpriteDatHeader hdr;
+ ReadSprHeader(hdr, _stream.get(), _version, _compressed ? 1 : 0);
+ if (hdr.BPP == 0) return HError::None(); // empty slot, this is normal
+ int bpp = hdr.BPP, w = hdr.Width, h = hdr.Height;
Bitmap *image = BitmapHelper::CreateBitmap(w, h, bpp * 8);
if (image == nullptr) {
return new Error(String::FromFormat("LoadSprite: failed to allocate bitmap %d (%dx%d%d).",
index, w, h, bpp * 8));
}
-
+ ImBufferPtr im_data(image->GetDataForWriting(), w * h * bpp, bpp);
+ // (Optional) Handle storage options, reverse
+ std::vector<uint8_t> indexed_buf;
+ uint32_t palette[256];
+ uint32_t pal_bpp = GetPaletteBPP(hdr.SFormat);
+ if (pal_bpp > 0) { // read palette if format assumes one
+ switch (pal_bpp) {
+ case 2: for (uint32_t i = 0; i < hdr.PalCount; ++i) palette[i] = _stream->ReadInt16(); break;
+ case 4: for (uint32_t i = 0; i < hdr.PalCount; ++i) palette[i] = _stream->ReadInt32(); break;
+ default: assert(0); break;
+ }
+ indexed_buf.resize(w * h);
+ im_data = ImBufferPtr(&indexed_buf[0], indexed_buf.size(), 1);
+ }
+ // (Optional) Decompress the image data into the temp buffer
+ size_t in_data_size = ((_version >= kSprfVersion_StorageFormats) || _compressed) ?
+ (uint32_t)_stream->ReadInt32() : (w * h * bpp);
if (_compressed) {
- size_t data_size = _stream->ReadInt32();
- if (data_size == 0) {
+ if (in_data_size == 0) {
delete image;
return new Error(String::FromFormat("LoadSprite: bad compressed data for sprite %d.", index));
}
- rle_decompress(image->GetDataForWriting(), w * h * bpp, bpp, _stream.get());
+ rle_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get());
// TODO: test that not more than data_size was read!
- } else {
- switch (bpp) {
- case 1: _stream->Read(image->GetDataForWriting(), w * h); break;
+ }
+ // Otherwise (no compression) read directly
+ else {
+ switch (im_data.BPP) {
+ case 1: _stream->Read(im_data.Buf, im_data.Size); break;
case 2: _stream->ReadArrayOfInt16(
- reinterpret_cast<int16_t *>(image->GetDataForWriting()), w *h); break;
+ reinterpret_cast<int16_t *>(im_data.Buf), im_data.Size / sizeof(int16_t)); break;
case 4: _stream->ReadArrayOfInt32(
- reinterpret_cast<int32_t *>(image->GetDataForWriting()), w *h); break;
- default: assert(0); break;
+ reinterpret_cast<int32_t *>(im_data.Buf), im_data.Size / sizeof(int32_t)); break;
+ default: assert(0);
}
}
+ // Finally revert storage options
+ if (pal_bpp > 0) {
+ UnpackIndexedBitmap(image, im_data.Buf, im_data.Size, palette, hdr.PalCount);
+ }
sprite = image;
_curPos = index + 1; // mark correct pos
@@ -264,20 +414,19 @@ HError SpriteFile::LoadRawData(sprkey_t index, SpriteDatHeader &hdr, std::vector
SeekToSprite(index);
_curPos = -2; // mark undefined pos
- int bpp = _stream->ReadInt16();
- if (bpp == 0) { // empty slot, this is normal
- return HError::None();
- }
- int w = _stream->ReadInt16();
- int h = _stream->ReadInt16();
- size_t data_size = w * h * bpp;
- if (_compressed) {
- data_size = _stream->ReadInt32() + sizeof(int32_t);
- _stream->Seek(-4);
- }
+ ReadSprHeader(hdr, _stream.get(), _version, _compressed ? 1 : 0);
+ if (hdr.BPP == 0) return HError::None(); // empty slot, this is normal
+ size_t data_size = 0;
+ soff_t data_pos = _stream->GetPosition();
+ // Optional palette
+ data_size += hdr.PalCount * GetPaletteBPP(hdr.SFormat);
+ if ((_version >= kSprfVersion_StorageFormats) || _compressed)
+ data_size += (uint32_t)_stream->ReadInt32() + sizeof(uint32_t);
+ else
+ data_size += hdr.Width * hdr.Height * hdr.BPP;
data.resize(data_size);
+ _stream->Seek(data_pos);
_stream->Read(&data[0], data_size);
- hdr = SpriteDatHeader(bpp, w, h);
_curPos = index + 1; // mark correct pos
return HError::None();
@@ -291,6 +440,7 @@ void SpriteFile::SeekToSprite(sprkey_t index) {
}
}
+
// Finds the topmost occupied slot index. Warning: may be slow.
static sprkey_t FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
sprkey_t topmost = -1;
@@ -303,7 +453,7 @@ static sprkey_t FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
int SaveSpriteFile(const String &save_to_file,
const std::vector<Bitmap *> &sprites,
SpriteFile *read_from_file,
- bool compressOutput, SpriteFileIndex &index) {
+ int store_flags, bool compress, SpriteFileIndex &index) {
std::unique_ptr<Stream> output(File::CreateFile(save_to_file));
if (output == nullptr)
return -1;
@@ -312,13 +462,16 @@ int SaveSpriteFile(const String &save_to_file,
lastslot = std::max(lastslot, FindTopmostSprite(sprites));
SpriteFileWriter writer(output);
- writer.Begin(compressOutput, lastslot);
+ writer.Begin(store_flags, compress, lastslot);
std::unique_ptr<Bitmap> temp_bmp; // for disposing temp sprites
std::vector<uint8_t> membuf; // for loading raw sprite data
+ std::vector<uint32_t> palette;
const bool diff_compress =
- read_from_file && read_from_file->IsFileCompressed() != compressOutput;
+ read_from_file &&
+ (read_from_file->IsFileCompressed() != compress ||
+ read_from_file->GetStoreFlags() != store_flags);
for (sprkey_t i = 0; i <= lastslot; ++i) {
Bitmap *image = (size_t)i < sprites.size() ? sprites[i] : nullptr;
@@ -382,10 +535,11 @@ int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index) {
SpriteFileWriter::SpriteFileWriter(std::unique_ptr<Stream> &out) : _out(out) {
}
-void SpriteFileWriter::Begin(bool compressed, sprkey_t last_slot) {
+void SpriteFileWriter::Begin(int store_flags, bool compress, sprkey_t last_slot) {
if (!_out) return;
_index.SpriteFileIDCheck = g_system->getMillis();
- _compress = compressed;
+ _storeFlags = store_flags;
+ _compress = compress;
// sprite file version
_out->WriteInt16(kSprfVersion_Current);
@@ -399,6 +553,11 @@ void SpriteFileWriter::Begin(bool compressed, sprkey_t last_slot) {
_lastSlotPos = _out->GetPosition();
_out->WriteInt32(last_slot);
+ _out->WriteInt8(_storeFlags);
+ _out->WriteInt8(0); // reserved
+ _out->WriteInt8(0);
+ _out->WriteInt8(0);
+
if (last_slot >= 0) { // allocate buffers to store the indexing info
sprkey_t numsprits = last_slot + 1;
_index.Offsets.reserve(numsprits);
@@ -412,32 +571,62 @@ void SpriteFileWriter::WriteBitmap(Bitmap *image) {
int bpp = image->GetBPP();
int w = image->GetWidth();
int h = image->GetHeight();
- const SpriteDatHeader hdr(bpp, w, h);
+ ImBufferCPtr im_data(image->GetData(), w * h * bpp, bpp);
+ // (Optional) Handle storage options
+ std::vector<uint8_t> indexed_buf;
+ uint32_t palette[256];
+ uint32_t pal_count = 0;
+ SpriteFormat sformat = kSprFmt_Undefined;
+ int compress = 0;
+ if ((_storeFlags & kSprStore_OptimizeForSize) != 0 && (image->GetBPP() > 1)) { // Try to store this sprite as an indexed bitmap
+ if (CreateIndexedBitmap(image, indexed_buf, palette, pal_count) && pal_count > 0) {
+ sformat = PaletteFormatForBPP(image->GetBPP());
+ im_data = ImBufferCPtr(&indexed_buf[0], indexed_buf.size(), 1);
+ }
+ }
+ // (Optional) Compress the image data into the temp buffer
if (_compress) {
+ compress = 1;
MemoryStream mems(_membuf, kStream_Write);
- rle_compress(image->GetData(), w * h * bpp, bpp, &mems);
- // write image data as a plain byte array
- WriteSpriteData(hdr, &_membuf[0], _membuf.size(), 1);
- _membuf.clear();
- } else {
- WriteSpriteData(hdr, image->GetData(), w * h * bpp, bpp);
+ rle_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems);
+ // mark to write as a plain byte array
+ im_data = ImBufferCPtr(&_membuf[0], _membuf.size(), 1);
}
+ // Write the final data
+ SpriteDatHeader hdr(bpp, sformat, pal_count, compress, w, h);
+ WriteSpriteData(hdr, im_data.Buf, im_data.Size, im_data.BPP, palette);
+ _membuf.clear();
+}
+
+static inline void WriteSprHeader(const SpriteDatHeader &hdr, Stream *out) {
+ out->WriteInt8(hdr.BPP);
+ out->WriteInt8(hdr.SFormat);
+ out->WriteInt8(hdr.PalCount > 0 ? (uint8_t)(hdr.PalCount - 1) : 0);
+ out->WriteInt8(hdr.Compress);
+ out->WriteInt16(hdr.Width);
+ out->WriteInt16(hdr.Height);
}
void SpriteFileWriter::WriteSpriteData(const SpriteDatHeader &hdr,
- const uint8_t *im_data, size_t im_data_sz, int im_bpp) {
+ const uint8_t *im_data, size_t im_data_sz, int im_bpp,
+ const uint32_t palette[256]) {
// Add index entry and write resulting data to the stream
soff_t sproff = _out->GetPosition();
_index.Offsets.push_back(sproff);
_index.Widths.push_back(hdr.Width);
_index.Heights.push_back(hdr.Height);
- _out->WriteInt16(hdr.BPP);
- _out->WriteInt16(hdr.Width);
- _out->WriteInt16(hdr.Height);
- // if not compressed, then the data size is supposed to be calculated
- // from the image metrics
- if (_compress)
- _out->WriteInt32(im_data_sz);
+ WriteSprHeader(hdr, _out.get());
+ // write palette, if available
+ int pal_bpp = GetPaletteBPP(hdr.SFormat);
+ if (pal_bpp > 0) {
+ assert(hdr.PalCount > 0);
+ switch (pal_bpp) {
+ case 2: for (uint32_t i = 0; i < hdr.PalCount; ++i) _out->WriteInt16(palette[i]);
+ case 4: for (uint32_t i = 0; i < hdr.PalCount; ++i) _out->WriteInt32(palette[i]);
+ }
+ }
+ // write the image pixel data
+ _out->WriteInt32(im_data_sz);
switch (im_bpp) {
case 1: _out->Write(im_data, im_data_sz); break;
case 2: _out->WriteArrayOfInt16(reinterpret_cast<const int16_t *>(im_data),
@@ -463,9 +652,7 @@ void SpriteFileWriter::WriteRawData(const SpriteDatHeader &hdr, const uint8_t *d
_index.Offsets.push_back(sproff);
_index.Widths.push_back(hdr.Width);
_index.Heights.push_back(hdr.Height);
- _out->WriteInt16(hdr.BPP);
- _out->WriteInt16(hdr.Width);
- _out->WriteInt16(hdr.Height);
+ WriteSprHeader(hdr, _out.get());
_out->Write(data, data_sz);
}
diff --git a/engines/ags/shared/ac/sprite_file.h b/engines/ags/shared/ac/sprite_file.h
index 4fde6949e72..26e000aa21d 100644
--- a/engines/ags/shared/ac/sprite_file.h
+++ b/engines/ags/shared/ac/sprite_file.h
@@ -50,7 +50,8 @@ enum SpriteFileVersion {
kSprfVersion_Last32bit = 6,
kSprfVersion_64bit = 10,
kSprfVersion_HighSpriteLimit = 11,
- kSprfVersion_Current = kSprfVersion_HighSpriteLimit
+ kSprfVersion_StorageFormats = 12,
+ kSprfVersion_Current = kSprfVersion_StorageFormats
};
enum SpriteIndexFileVersion {
@@ -61,6 +62,24 @@ enum SpriteIndexFileVersion {
kSpridxfVersion_Current = kSpridxfVersion_HighSpriteLimit
};
+// Instructions to how the sprites are allowed to be stored
+enum SpriteStorage {
+ // When possible convert the sprite into another format for less disk space
+ // e.g. save 16/32-bit images as 8-bit colormaps with palette
+ kSprStore_OptimizeForSize = 0x01
+};
+
+// Format in which the sprite's pixel data is stored
+enum SpriteFormat {
+ kSprFmt_Undefined = 0, // undefined, or keep as-is
+ // Encoded as a 8-bit colormap with palette of 24-bit RGB values
+ kSprFmt_PaletteRgb888 = 32,
+ // Encoded as a 8-bit colormap with palette of 32-bit ARGB values
+ kSprFmt_PaletteArgb8888 = 33,
+ // Encoded as a 8-bit colormap with palette of 16-bit RGB565 values
+ kSprFmt_PaletteRgb565 = 34
+};
+
typedef int32_t sprkey_t;
// SpriteFileIndex contains sprite file's table of contents
@@ -76,13 +95,18 @@ struct SpriteFileIndex {
// Invidual sprite data header (as read from the file)
struct SpriteDatHeader {
- int BPP = 0; // color depth (bytes per pixel)
- int Width = 0;
- int Height = 0;
+ int BPP = 0; // color depth (bytes per pixel); or input format
+ SpriteFormat SFormat = kSprFmt_Undefined; // storage format
+ uint32_t PalCount = 0; // palette length, if applicable to storage format
+ int Compress = 0; // compression type
+ int Width = 0; // sprite's width
+ int Height = 0; // sprite's height
SpriteDatHeader() = default;
- SpriteDatHeader(int bpp, int w = 0, int h = 0)
- : BPP(bpp), Width(w), Height(h) {
+ SpriteDatHeader(int bpp, SpriteFormat sformat = kSprFmt_Undefined,
+ uint32_t pal_count = 0, int compress = 0, int w = 0, int h = 0)
+ : BPP(bpp), SFormat(sformat), PalCount(pal_count),
+ Compress(compress), Width(w), Height(h) {
}
};
@@ -101,6 +125,7 @@ public:
// Closes stream; no reading will be possible unless opened again
void Close();
+ int GetStoreFlags() const;
// Tells if bitmaps in the file are compressed
bool IsFileCompressed() const;
// Tells the highest known sprite index
@@ -117,6 +142,8 @@ public:
HError LoadSprite(sprkey_t index, Bitmap *&sprite);
// Loads a raw sprite element data into the buffer, stores header info separately
HError LoadRawData(sprkey_t index, SpriteDatHeader &hdr, std::vector<uint8_t> &data);
+ HError LoadSpriteData(sprkey_t index, SpriteDatHeader &hdr, std::vector<uint8_t> &data,
+ std::vector<uint32_t> &palette);
private:
// Seek stream to sprite
@@ -132,6 +159,8 @@ private:
// Array of sprite references
std::vector<SpriteRef> _spriteData;
std::unique_ptr<Stream> _stream; // the sprite stream
+ SpriteFileVersion _version = kSprfVersion_Current;
+ int _storeFlags = 0; // storage flags, specify how sprites may be stored
bool _compressed; // are sprites compressed
sprkey_t _curPos; // current stream position (sprite slot)
};
@@ -148,7 +177,7 @@ public:
const SpriteFileIndex &GetIndex() const { return _index; }
// Initializes new sprite file format
- void Begin(bool compress, sprkey_t last_slot = -1);
+ void Begin(int store_flags, bool compress, sprkey_t last_slot = -1);
// Writes a bitmap into file, compressing if necessary
void WriteBitmap(Bitmap *image);
// Writes an empty slot marker
@@ -160,9 +189,12 @@ public:
private:
// Writes prepared image data in a proper file format, following explicit data_bpp rule
- void WriteSpriteData(const SpriteDatHeader &hdr, const uint8_t *im_data, size_t im_data_sz, int im_bpp);
+ void WriteSpriteData(const SpriteDatHeader &hdr,
+ const uint8_t *im_data, size_t im_data_sz, int im_bpp,
+ const uint32_t palette[256]);
std::unique_ptr<Stream> &_out;
+ int _storeFlags = 0;
bool _compress = false;
soff_t _lastSlotPos = -1; // last slot save position in file
// sprite index accumulated on write for reporting back to user
@@ -176,7 +208,7 @@ private:
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);
+ int store_flags, bool compress, SpriteFileIndex &index);
// Saves sprite index table in a separate file
int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
Commit: 4392fa5e2d42f3021f6eff5fbf8fb85de2c39379
https://github.com/scummvm/scummvm/commit/4392fa5e2d42f3021f6eff5fbf8fb85de2c39379
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:17-07:00
Commit Message:
AGS: Simplified savecompressed_allegro() and renamed for clarity
>From upstream 9a6b55bfe78bf0e9f24b2e3c2d1b073f850fae50
Changed paths:
engines/ags/shared/game/room_file.cpp
engines/ags/shared/util/compress.cpp
engines/ags/shared/util/compress.h
diff --git a/engines/ags/shared/game/room_file.cpp b/engines/ags/shared/game/room_file.cpp
index f52bfb9c615..9d74ec0e3b7 100644
--- a/engines/ags/shared/game/room_file.cpp
+++ b/engines/ags/shared/game/room_file.cpp
@@ -298,28 +298,28 @@ HError ReadMainBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
if (data_ver >= kRoomVersion_pre114_5)
load_lzw(in, &mask, room->BackgroundBPP, room->Palette);
else
- loadcompressed_allegro(in, &mask, room->Palette);
+ mask = load_rle_bitmap8(in);
room->BgFrames[0].Graphic.reset(mask);
update_polled_stuff_if_runtime();
// Mask bitmaps
if (data_ver >= kRoomVersion_255b) {
- loadcompressed_allegro(in, &mask, room->Palette);
+ mask = load_rle_bitmap8(in);
} else if (data_ver >= kRoomVersion_114) {
// an old version - clear the 'shadow' area into a blank regions bmp
- loadcompressed_allegro(in, &mask, room->Palette);
+ mask = load_rle_bitmap8(in);
delete mask;
mask = nullptr;
}
room->RegionMask.reset(mask);
update_polled_stuff_if_runtime();
- loadcompressed_allegro(in, &mask, room->Palette);
+ mask = load_rle_bitmap8(in);
room->WalkAreaMask.reset(mask);
update_polled_stuff_if_runtime();
- loadcompressed_allegro(in, &mask, room->Palette);
+ mask = load_rle_bitmap8(in);
room->WalkBehindMask.reset(mask);
update_polled_stuff_if_runtime();
- loadcompressed_allegro(in, &mask, room->Palette);
+ mask = load_rle_bitmap8(in);
room->HotspotMask.reset(mask);
return HError::None();
}
@@ -748,10 +748,10 @@ void WriteMainBlock(const RoomStruct *room, Stream *out) {
out->WriteInt32(room->Regions[i].Tint);
save_lzw(out, room->BgFrames[0].Graphic.get(), room->Palette);
- savecompressed_allegro(out, room->RegionMask.get(), room->Palette);
- savecompressed_allegro(out, room->WalkAreaMask.get(), room->Palette);
- savecompressed_allegro(out, room->WalkBehindMask.get(), room->Palette);
- savecompressed_allegro(out, room->HotspotMask.get(), room->Palette);
+ save_rle_bitmap8(out, room->RegionMask.get());
+ save_rle_bitmap8(out, room->WalkAreaMask.get());
+ save_rle_bitmap8(out, room->WalkBehindMask.get());
+ save_rle_bitmap8(out, room->HotspotMask.get());
}
void WriteCompSc3Block(const RoomStruct *room, Stream *out) {
diff --git a/engines/ags/shared/util/compress.cpp b/engines/ags/shared/util/compress.cpp
index 268f3fb51c2..8b93d3e1203 100644
--- a/engines/ags/shared/util/compress.cpp
+++ b/engines/ags/shared/util/compress.cpp
@@ -151,36 +151,6 @@ static void cpackbitl32(const uint32_t *line, size_t size, Stream *out) {
} // end while
}
-
-void csavecompressed(Stream *out, const unsigned char *tobesaved, const RGB pala[256]) {
- int widt, hit;
- widt = *tobesaved++;
- widt += (*tobesaved++) * 256;
- hit = *tobesaved++;
- hit += (*tobesaved++) * 256;
- // Those were originally written as shorts, although they are ints
- out->WriteInt16(widt);
- out->WriteInt16(hit);
-
- unsigned char *ress = (unsigned char *)malloc(widt + 1);
- int ww;
-
- for (ww = 0; ww < hit; ww++) {
- for (int ss = 0; ss < widt; ss++)
- (*ress++) = (*tobesaved++);
-
- ress -= widt;
- cpackbitl(ress, widt, out);
- }
-
- for (ww = 0; ww < 256; ww++) {
- out->WriteInt8(pala[ww].r);
- out->WriteInt8(pala[ww].g);
- out->WriteInt8(pala[ww].b);
- }
- free(ress);
-}
-
static int cunpackbitl(uint8_t *line, size_t size, Stream *in) {
size_t n = 0; // number of bytes decoded
@@ -310,6 +280,46 @@ void rle_decompress(uint8_t *data, size_t data_sz, int image_bpp, Stream *in) {
}
}
+void save_rle_bitmap8(Stream *out, const Bitmap *bmp, const RGB(*pal)[256]) {
+ assert(bmp->GetBPP() == 1);
+ out->WriteInt16(static_cast<uint16_t>(bmp->GetWidth()));
+ out->WriteInt16(static_cast<uint16_t>(bmp->GetHeight()));
+ // Pack the pixels
+ cpackbitl(bmp->GetData(), bmp->GetWidth() * bmp->GetHeight(), out);
+ // Save palette
+ if (!pal) { // if no pal, write dummy palette, because we have to
+ out->WriteByteCount(0, 256 * 3);
+ return;
+ }
+ const RGB *ppal = *pal;
+ for (int i = 0; i < 256; ++i) {
+ out->WriteInt8(ppal[i].r);
+ out->WriteInt8(ppal[i].g);
+ out->WriteInt8(ppal[i].b);
+ }
+}
+
+Shared::Bitmap *load_rle_bitmap8(Stream *in, RGB(*pal)[256]) {
+ int w = in->ReadInt16();
+ int h = in->ReadInt16();
+ Bitmap *bmp = BitmapHelper::CreateBitmap(w, h, 8);
+ if (!bmp) return nullptr;
+ // Unpack the pixels
+ cunpackbitl(bmp->GetDataForWriting(), w * h, in);
+ // Load or skip the palette
+ if (!pal) {
+ in->Seek(3 * 256);
+ return bmp;
+ }
+ RGB *ppal = *pal;
+ for (int i = 0; i < 256; ++i) {
+ ppal[i].r = in->ReadInt8();
+ ppal[i].g = in->ReadInt8();
+ ppal[i].b = in->ReadInt8();
+ }
+ return bmp;
+}
+
//-----------------------------------------------------------------------------
// LZW
//-----------------------------------------------------------------------------
@@ -399,7 +409,7 @@ void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, RGB *pall) {
Bitmap *bmm = BitmapHelper::CreateBitmap((loptr[0] / dst_bpp), loptr[1], dst_bpp * 8);
if (bmm == nullptr)
- quit("!load_room: not enough memory to load room background");
+ quit("load_room: not enough memory to load room background");
update_polled_stuff_if_runtime();
@@ -418,37 +428,4 @@ void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, RGB *pall) {
*dst_bmp = bmm;
}
-void savecompressed_allegro(Stream *out, const Bitmap *bmpp, const RGB *pall) {
- unsigned char *wgtbl = (unsigned char *)malloc(bmpp->GetWidth() * bmpp->GetHeight() + 4);
- short *sss = (short *)wgtbl;
-
- sss[0] = bmpp->GetWidth();
- sss[1] = bmpp->GetHeight();
-
- memcpy(&wgtbl[4], bmpp->GetData(), bmpp->GetWidth() * bmpp->GetHeight());
-
- csavecompressed(out, wgtbl, pall);
- free(wgtbl);
-}
-
-void loadcompressed_allegro(Stream *in, Bitmap **bimpp, RGB *pall) {
- short widd, hitt;
- int ii;
-
- widd = in->ReadInt16();
- hitt = in->ReadInt16();
- Bitmap *bim = BitmapHelper::CreateBitmap(widd, hitt, 8);
- if (bim == nullptr)
- quit("!load_room: not enough memory to decompress masks");
-
- for (ii = 0; ii < hitt; ii++) {
- cunpackbitl(&bim->GetScanLineForWriting(ii)[0], widd, in);
- if (ii % 20 == 0)
- update_polled_stuff_if_runtime();
- }
-
- in->Seek(768); // skip palette
- *bimpp = bim;
-}
-
} // namespace AGS3
diff --git a/engines/ags/shared/util/compress.h b/engines/ags/shared/util/compress.h
index d9472c69c7e..01f4e039375 100644
--- a/engines/ags/shared/util/compress.h
+++ b/engines/ags/shared/util/compress.h
@@ -38,12 +38,14 @@ using namespace AGS; // FIXME later
void rle_compress(const uint8_t *data, size_t data_sz, int image_bpp, Shared::Stream *out);
void rle_decompress(uint8_t *data, size_t data_sz, int image_bpp, Shared::Stream *in);
+// Packs a 8-bit bitmap using RLE compression, and writes into stream along with the palette
+void save_rle_bitmap8(Shared::Stream *out, const Shared::Bitmap *bmp, const RGB(*pal)[256] = nullptr);
+// Reads a 8-bit bitmap with palette from the stream and unpacks from RLE
+Shared::Bitmap *load_rle_bitmap8(Shared::Stream *in, RGB(*pal)[256] = nullptr);
// LZW compression
void save_lzw(Shared::Stream *out, const Shared::Bitmap *bmpp, const RGB *pall);
void load_lzw(Shared::Stream *in, Shared::Bitmap **bmm, int dst_bpp, RGB *pall);
-void savecompressed_allegro(Shared::Stream *out, const Shared::Bitmap *bmpp, const RGB *pall);
-void loadcompressed_allegro(Shared::Stream *in, Shared::Bitmap **bimpp, RGB *pall);
} // namespace AGS3
Commit: 02540a3b4762b7f3bc5b9a8e7679e15014a6abd7
https://github.com/scummvm/scummvm/commit/02540a3b4762b7f3bc5b9a8e7679e15014a6abd7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:17-07:00
Commit Message:
AGS: In save_lzw() replaced the use of temp file with memory buffer
>From upstream 49045c7c5b6550d7724c4ceda822fbf4c3fc05a1
Changed paths:
engines/ags/shared/game/room_file.cpp
engines/ags/shared/util/compress.cpp
engines/ags/shared/util/compress.h
engines/ags/shared/util/lzw.cpp
diff --git a/engines/ags/shared/game/room_file.cpp b/engines/ags/shared/game/room_file.cpp
index 9d74ec0e3b7..f8f235ada69 100644
--- a/engines/ags/shared/game/room_file.cpp
+++ b/engines/ags/shared/game/room_file.cpp
@@ -296,7 +296,7 @@ HError ReadMainBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
// Primary background
Bitmap *mask = nullptr;
if (data_ver >= kRoomVersion_pre114_5)
- load_lzw(in, &mask, room->BackgroundBPP, room->Palette);
+ load_lzw(in, &mask, room->BackgroundBPP, &room->Palette);
else
mask = load_rle_bitmap8(in);
room->BgFrames[0].Graphic.reset(mask);
@@ -390,7 +390,7 @@ HError ReadAnimBgBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
for (size_t i = 1; i < room->BgFrameCount; ++i) {
update_polled_stuff_if_runtime();
Bitmap *frame = nullptr;
- load_lzw(in, &frame, room->BackgroundBPP, room->BgFrames[i].Palette);
+ load_lzw(in, &frame, room->BackgroundBPP, &room->BgFrames[i].Palette);
room->BgFrames[i].Graphic.reset(frame);
}
return HError::None();
@@ -747,7 +747,7 @@ void WriteMainBlock(const RoomStruct *room, Stream *out) {
for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i)
out->WriteInt32(room->Regions[i].Tint);
- save_lzw(out, room->BgFrames[0].Graphic.get(), room->Palette);
+ save_lzw(out, room->BgFrames[0].Graphic.get(), &room->Palette);
save_rle_bitmap8(out, room->RegionMask.get());
save_rle_bitmap8(out, room->WalkAreaMask.get());
save_rle_bitmap8(out, room->WalkBehindMask.get());
@@ -777,7 +777,7 @@ void WriteAnimBgBlock(const RoomStruct *room, Stream *out) {
for (size_t i = 0; i < room->BgFrameCount; ++i)
out->WriteInt8(room->BgFrames[i].IsPaletteShared ? 1 : 0);
for (size_t i = 1; i < room->BgFrameCount; ++i)
- save_lzw(out, room->BgFrames[i].Graphic.get(), room->BgFrames[i].Palette);
+ save_lzw(out, room->BgFrames[i].Graphic.get(), &room->BgFrames[i].Palette);
}
void WritePropertiesBlock(const RoomStruct *room, Stream *out) {
diff --git a/engines/ags/shared/util/compress.cpp b/engines/ags/shared/util/compress.cpp
index 8b93d3e1203..805d021af76 100644
--- a/engines/ags/shared/util/compress.cpp
+++ b/engines/ags/shared/util/compress.cpp
@@ -19,17 +19,12 @@
*
*/
-#ifdef _MANAGED
-// ensure this doesn't get compiled to .NET IL
-#pragma unmanaged
-#endif
-
+#include "ags/shared/util/compress.h"
#include "ags/shared/ac/common.h" // quit, update_polled_stuff
#include "ags/shared/gfx/bitmap.h"
-#include "ags/shared/util/compress.h"
#include "ags/shared/util/file.h"
#include "ags/shared/util/lzw.h"
-#include "ags/shared/util/stream.h"
+#include "ags/shared/util/memory_stream.h"
#include "ags/globals.h"
#if AGS_PLATFORM_ENDIAN_BIG
#include "ags/shared/util/bbop.h"
@@ -324,52 +319,61 @@ Shared::Bitmap *load_rle_bitmap8(Stream *in, RGB(*pal)[256]) {
// LZW
//-----------------------------------------------------------------------------
-const char *lztempfnm = "~aclzw.tmp";
-
-void save_lzw(Stream *out, const Bitmap *bmpp, const RGB *pall) {
- // First write original bitmap into temporary file
- Stream *lz_temp_s = File::OpenFileCI(lztempfnm, kFile_CreateAlways, kFile_Write);
- lz_temp_s->WriteInt32(bmpp->GetWidth() * bmpp->GetBPP());
- lz_temp_s->WriteInt32(bmpp->GetHeight());
- lz_temp_s->WriteArray(bmpp->GetData(), bmpp->GetLineLength(), bmpp->GetHeight());
- delete lz_temp_s;
+void save_lzw(Stream *out, const Bitmap *bmpp, const RGB(*pal)[256]) {
+ // First write original bitmap's info and data into the memory buffer
+ // NOTE: we must do this purely for backward compatibility with old room formats:
+ // because they also included bmp width and height into compressed data!
+ std::vector<uint8_t> membuf;
+ {
+ MemoryStream memws(membuf, kStream_Write);
+ int w = bmpp->GetWidth(), h = bmpp->GetHeight(), bpp = bmpp->GetBPP();
+ memws.WriteInt32(w * bpp); // stride
+ memws.WriteInt32(h);
+ switch (bpp) {
+ case 1: memws.Write(bmpp->GetData(), w * h * bpp); break;
+ case 2: memws.WriteArrayOfInt16(reinterpret_cast<const int16_t *>(bmpp->GetData()), w *h); break;
+ case 4: memws.WriteArrayOfInt32(reinterpret_cast<const int32_t *>(bmpp->GetData()), w *h); break;
+ default: assert(0); break;
+ }
+ }
- // Now open same file for reading, and begin writing compressed data into required output stream
- lz_temp_s = File::OpenFileCI(lztempfnm);
- soff_t temp_sz = lz_temp_s->GetLength();
- out->WriteArray(&pall[0], sizeof(RGB), 256);
- out->WriteInt32(temp_sz);
- soff_t gobacto = out->GetPosition();
+ // Open same buffer for reading, and begin writing compressed data into the output
+ MemoryStream mem_in(membuf);
+ // NOTE: old format saves full RGB struct here (4 bytes, including the filler)
+ if (pal)
+ out->WriteArray(*pal, sizeof(RGB), 256);
+ else
+ out->WriteByteCount(0, sizeof(RGB) * 256);
+ out->WriteInt32((uint32_t)mem_in.GetLength());
// reserve space for compressed size
- out->WriteInt32(temp_sz);
- lzwcompress(lz_temp_s, out);
+ soff_t cmpsz_at = out->GetPosition();
+ out->WriteInt32(0);
+ lzwcompress(&mem_in, out);
soff_t toret = out->GetPosition();
- out->Seek(gobacto, kSeekBegin);
- soff_t compressed_sz = (toret - gobacto) - 4;
- out->WriteInt32(compressed_sz); // write compressed size
-
- // Delete temp file
- delete lz_temp_s;
- File::DeleteFile(lztempfnm);
-
- // Seek back to the end of the output stream
+ out->Seek(cmpsz_at, kSeekBegin);
+ soff_t compressed_sz = (toret - cmpsz_at) - sizeof(uint32_t);
+ out->WriteInt32(compressed_sz); // write compressed size
+ // seek back to the end of the output stream
out->Seek(toret, kSeekBegin);
}
-void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, RGB *pall) {
+void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, RGB(*pal)[256]) {
soff_t uncompsiz;
int *loptr;
unsigned char *membuffer;
int arin;
- in->Read(&pall[0], sizeof(RGB) * 256);
+ // NOTE: old format saves full RGB struct here (4 bytes, including the filler)
+ if (pal)
+ in->Read(*pal, sizeof(RGB) * 256);
+ else
+ in->Seek(sizeof(RGB) * 256);
_G(maxsize) = in->ReadInt32();
uncompsiz = in->ReadInt32();
uncompsiz += in->GetPosition();
- _G(outbytes) = 0;
- _G(putbytes) = 0;
+ _G(outbytes) = 0; _G(putbytes) = 0;
update_polled_stuff_if_runtime();
membuffer = lzwexpand_to_mem(in);
@@ -381,20 +385,24 @@ void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, RGB *pall) {
loptr[0] = BBOp::SwapBytesInt32(loptr[0]);
loptr[1] = BBOp::SwapBytesInt32(loptr[1]);
int bitmapNumPixels = loptr[0] * loptr[1] / dst_bpp;
- switch (dst_bpp) { // bytes per pixel!
- case 1: {
+ switch (dst_bpp) // bytes per pixel!
+ {
+ case 1:
+ {
// all done
break;
}
- case 2: {
+ case 2:
+ {
short *sp = (short *)membuffer;
for (int i = 0; i < bitmapNumPixels; ++i) {
sp[i] = BBOp::SwapBytesInt16(sp[i]);
- }
+}
// all done
break;
}
- case 4: {
+ case 4:
+ {
int *ip = (int *)membuffer;
for (int i = 0; i < bitmapNumPixels; ++i) {
ip[i] = BBOp::SwapBytesInt32(ip[i]);
@@ -402,7 +410,7 @@ void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, RGB *pall) {
// all done
break;
}
- }
+ }
#endif // AGS_PLATFORM_ENDIAN_BIG
update_polled_stuff_if_runtime();
diff --git a/engines/ags/shared/util/compress.h b/engines/ags/shared/util/compress.h
index 01f4e039375..cda9bd8708b 100644
--- a/engines/ags/shared/util/compress.h
+++ b/engines/ags/shared/util/compress.h
@@ -44,8 +44,10 @@ void save_rle_bitmap8(Shared::Stream *out, const Shared::Bitmap *bmp, const RGB(
Shared::Bitmap *load_rle_bitmap8(Shared::Stream *in, RGB(*pal)[256] = nullptr);
// LZW compression
-void save_lzw(Shared::Stream *out, const Shared::Bitmap *bmpp, const RGB *pall);
-void load_lzw(Shared::Stream *in, Shared::Bitmap **bmm, int dst_bpp, RGB *pall);
+// Saves bitmap with an optional palette compressed by LZW
+void save_lzw(Shared::Stream *out, const Shared::Bitmap *bmpp, const RGB(*pal)[256] = nullptr);
+// Loads bitmap decompressing
+void load_lzw(Shared::Stream *in, Shared::Bitmap **bmm, int dst_bpp, RGB(*pal)[256] = nullptr);
} // namespace AGS3
diff --git a/engines/ags/shared/util/lzw.cpp b/engines/ags/shared/util/lzw.cpp
index d6efad5a35d..e8cd0ad9140 100644
--- a/engines/ags/shared/util/lzw.cpp
+++ b/engines/ags/shared/util/lzw.cpp
@@ -178,7 +178,7 @@ void lzwcompress(Stream *lzw_in, Stream *out) {
}
if (!((mask += mask) & 0xFF)) {
- out->WriteArray(buf, size, 1);
+ out->Write(buf, size);
_G(outbytes) += size;
size = mask = 1;
buf[0] = 0;
@@ -188,7 +188,7 @@ void lzwcompress(Stream *lzw_in, Stream *out) {
} while (len > 0);
if (size > 1) {
- out->WriteArray(buf, size, 1);
+ out->Write(buf, size);
_G(outbytes) += size;
}
Commit: dc7609a79c663dcfe3ee07298b61cb6c35f55e0f
https://github.com/scummvm/scummvm/commit/dc7609a79c663dcfe3ee07298b61cb6c35f55e0f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:18-07:00
Commit Message:
AGS: Removed lzwexpand_to_mem(), tidied load_lzw()
>From upstream 2b9f130e119f9cfc81d0257a446edc4e31df19c0
Changed paths:
engines/ags/globals.h
engines/ags/shared/util/compress.cpp
engines/ags/shared/util/lzw.cpp
engines/ags/shared/util/lzw.h
engines/ags/shared/util/memory_stream.cpp
engines/ags/shared/util/memory_stream.h
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index d6418494d56..e8ccca6985f 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -1054,7 +1054,7 @@ public:
char *_lzbuffer = nullptr;
int *_node = nullptr;
int _pos = 0;
- long _outbytes = 0, _maxsize = 0, _putbytes = 0;
+ size_t _outbytes = 0, _maxsize = 0, _putbytes = 0;
/**@}*/
diff --git a/engines/ags/shared/util/compress.cpp b/engines/ags/shared/util/compress.cpp
index 805d021af76..5b4ea74cab0 100644
--- a/engines/ags/shared/util/compress.cpp
+++ b/engines/ags/shared/util/compress.cpp
@@ -359,79 +359,42 @@ void save_lzw(Stream *out, const Bitmap *bmpp, const RGB(*pal)[256]) {
}
void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, RGB(*pal)[256]) {
- soff_t uncompsiz;
- int *loptr;
- unsigned char *membuffer;
- int arin;
-
+ *dst_bmp = nullptr;
// NOTE: old format saves full RGB struct here (4 bytes, including the filler)
if (pal)
in->Read(*pal, sizeof(RGB) * 256);
else
in->Seek(sizeof(RGB) * 256);
- _G(maxsize) = in->ReadInt32();
- uncompsiz = in->ReadInt32();
-
- uncompsiz += in->GetPosition();
- _G(outbytes) = 0; _G(putbytes) = 0;
-
- update_polled_stuff_if_runtime();
- membuffer = lzwexpand_to_mem(in);
- update_polled_stuff_if_runtime();
+ const size_t uncomp_sz = in->ReadInt32();
+ const size_t comp_sz = in->ReadInt32();
+ const soff_t end_pos = in->GetPosition() + comp_sz;
- loptr = (int *)&membuffer[0];
- membuffer += 8;
-#if AGS_PLATFORM_ENDIAN_BIG
- loptr[0] = BBOp::SwapBytesInt32(loptr[0]);
- loptr[1] = BBOp::SwapBytesInt32(loptr[1]);
- int bitmapNumPixels = loptr[0] * loptr[1] / dst_bpp;
- switch (dst_bpp) // bytes per pixel!
- {
- case 1:
- {
- // all done
- break;
- }
- case 2:
- {
- short *sp = (short *)membuffer;
- for (int i = 0; i < bitmapNumPixels; ++i) {
- sp[i] = BBOp::SwapBytesInt16(sp[i]);
-}
- // all done
- break;
- }
- case 4:
+ // First decompress data into the memory buffer
+ std::vector<uint8_t> membuf;
{
- int *ip = (int *)membuffer;
- for (int i = 0; i < bitmapNumPixels; ++i) {
- ip[i] = BBOp::SwapBytesInt32(ip[i]);
- }
- // all done
- break;
+ MemoryStream memws(membuf, kStream_Write);
+ lzwexpand(in, &memws, uncomp_sz);
}
- }
-#endif // AGS_PLATFORM_ENDIAN_BIG
-
- update_polled_stuff_if_runtime();
- Bitmap *bmm = BitmapHelper::CreateBitmap((loptr[0] / dst_bpp), loptr[1], dst_bpp * 8);
+ // Open same buffer for reading and get params and pixels
+ MemoryStream mem_in(membuf);
+ int stride = mem_in.ReadInt32(); // width * bpp
+ int height = mem_in.ReadInt32();
+ Bitmap *bmm = BitmapHelper::CreateBitmap((stride / dst_bpp), height, dst_bpp * 8);
if (bmm == nullptr)
- quit("load_room: not enough memory to load room background");
-
- update_polled_stuff_if_runtime();
-
- for (arin = 0; arin < loptr[1]; arin++)
- memcpy(&bmm->GetScanLineForWriting(arin)[0], &membuffer[arin * loptr[0]], loptr[0]);
-
- update_polled_stuff_if_runtime();
-
- free(membuffer - 8);
-
- if (in->GetPosition() != uncompsiz)
- in->Seek(uncompsiz, kSeekBegin);
+ return; // out of mem?
+
+ size_t num_pixels = stride * height / dst_bpp;
+ uint8_t *bmp_data = bmm->GetDataForWriting();
+ switch (dst_bpp) {
+ case 1: mem_in.Read(bmp_data, num_pixels); break;
+ case 2: mem_in.ReadArrayOfInt16(reinterpret_cast<int16_t *>(bmp_data), num_pixels); break;
+ case 4: mem_in.ReadArrayOfInt32(reinterpret_cast<int32_t *>(bmp_data), num_pixels); break;
+ default: assert(0); break;
+ }
- update_polled_stuff_if_runtime();
+ if (in->GetPosition() != end_pos)
+ in->Seek(end_pos, kSeekBegin);
*dst_bmp = bmm;
}
diff --git a/engines/ags/shared/util/lzw.cpp b/engines/ags/shared/util/lzw.cpp
index e8cd0ad9140..f0d4cb7a7d2 100644
--- a/engines/ags/shared/util/lzw.cpp
+++ b/engines/ags/shared/util/lzw.cpp
@@ -195,8 +195,6 @@ void lzwcompress(Stream *lzw_in, Stream *out) {
free(_G(lzbuffer));
}
-int expand_to_mem = 0;
-unsigned char *membfptr = nullptr;
void myputc(int ccc, Stream *out) {
if (_G(maxsize) > 0) {
_G(putbytes)++;
@@ -205,21 +203,17 @@ void myputc(int ccc, Stream *out) {
}
_G(outbytes)++;
- if (expand_to_mem) {
- membfptr[0] = ccc;
- membfptr++;
- } else
- out->WriteInt8(ccc);
+ out->WriteInt8(ccc);
}
-void lzwexpand(Stream *lzw_in, Stream *out) {
+void lzwexpand(Stream *lzw_in, Stream *out, size_t out_size) {
int bits, ch, i, j, len, mask;
- char *buf;
- // printf(" UnShrinking: %s ",filena);
- _G(putbytes) = 0;
+ char *lzbuffer;
+ _G(outbytes) = 0; _G(putbytes) = 0;
+ _G(maxsize) = out_size;
- buf = (char *)malloc(N);
- if (buf == nullptr) {
+ lzbuffer = (char *)malloc(N);
+ if (lzbuffer == nullptr) {
quit("compress.cpp: unable to decompress: insufficient memory");
}
i = N - F;
@@ -237,13 +231,13 @@ void lzwexpand(Stream *lzw_in, Stream *out) {
j = (i - j - 1) & (N - 1);
while (len--) {
- myputc(buf[i] = buf[j], out);
+ myputc(lzbuffer[i] = lzbuffer[j], out);
j = (j + 1) & (N - 1);
i = (i + 1) & (N - 1);
}
} else {
ch = lzw_in->ReadByte();
- myputc(buf[i] = ch, out);
+ myputc(lzbuffer[i] = ch, out);
i = (i + 1) & (N - 1);
}
@@ -258,16 +252,7 @@ void lzwexpand(Stream *lzw_in, Stream *out) {
break;
}
- free(buf);
- expand_to_mem = 0;
-}
-
-unsigned char *lzwexpand_to_mem(Stream *in) {
- unsigned char *membuff = (unsigned char *)malloc(_G(maxsize) + 10);
- expand_to_mem = 1;
- membfptr = membuff;
- lzwexpand(in, nullptr);
- return membuff;
+ free(lzbuffer);
}
} // namespace AGS3
diff --git a/engines/ags/shared/util/lzw.h b/engines/ags/shared/util/lzw.h
index 066af919e4d..feb86829a2b 100644
--- a/engines/ags/shared/util/lzw.h
+++ b/engines/ags/shared/util/lzw.h
@@ -33,7 +33,7 @@ class Stream;
using namespace AGS; // FIXME later
void lzwcompress(Shared::Stream *lzw_in, Shared::Stream *out);
-unsigned char *lzwexpand_to_mem(Shared::Stream *in);
+void lzwexpand(Shared::Stream *lzw_in, Shared::Stream *out, size_t out_size);
} // namespace AGS3
diff --git a/engines/ags/shared/util/memory_stream.cpp b/engines/ags/shared/util/memory_stream.cpp
index b49aa6ebcc9..d9b2c55446c 100644
--- a/engines/ags/shared/util/memory_stream.cpp
+++ b/engines/ags/shared/util/memory_stream.cpp
@@ -35,6 +35,15 @@ MemoryStream::MemoryStream(const std::vector<uint8_t> &cbuf, DataEndianess strea
, _pos(0) {
}
+MemoryStream::MemoryStream(const uint8_t *cbuf, size_t buf_sz, DataEndianess stream_endianess)
+ : DataStream(stream_endianess)
+ , _cbuf(cbuf)
+ , _len(buf_sz)
+ , _buf(nullptr)
+ , _mode(kStream_Read)
+ , _pos(0) {
+}
+
MemoryStream::MemoryStream(const String &cbuf, DataEndianess stream_endianess)
: DataStream(stream_endianess)
, _cbuf(reinterpret_cast<const uint8_t *>(cbuf.GetCStr()))
diff --git a/engines/ags/shared/util/memory_stream.h b/engines/ags/shared/util/memory_stream.h
index 66889ca5ba1..245bbda2b74 100644
--- a/engines/ags/shared/util/memory_stream.h
+++ b/engines/ags/shared/util/memory_stream.h
@@ -47,6 +47,9 @@ public:
// Construct memory stream in the read-only mode over a const std::vector;
// vector must persist in memory until the stream is closed.
MemoryStream(const std::vector<uint8_t> &cbuf, DataEndianess stream_endianess = kLittleEndian);
+ // Construct memory stream in the read-only mode over a const C-buffer;
+ // buffer must persist in memory until the stream is closed.
+ MemoryStream(const uint8_t *cbuf, size_t buf_sz, DataEndianess stream_endianess = kLittleEndian);
// Construct memory stream in the read-only mode over a const String;
// String object must persist in memory until the stream is closed.
MemoryStream(const String &cbuf, DataEndianess stream_endianess = kLittleEndian);
Commit: eecd211bb69accd933e5785d4dc85077cea327be
https://github.com/scummvm/scummvm/commit/eecd211bb69accd933e5785d4dc85077cea327be
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:18-07:00
Commit Message:
AGS: Don't call quit() from lzw functions
>From upstream e5d8375df05b90e1ef51bae054da840071175aa3
Changed paths:
engines/ags/shared/util/lzw.cpp
engines/ags/shared/util/lzw.h
diff --git a/engines/ags/shared/util/lzw.cpp b/engines/ags/shared/util/lzw.cpp
index f0d4cb7a7d2..4bc1cc9143a 100644
--- a/engines/ags/shared/util/lzw.cpp
+++ b/engines/ags/shared/util/lzw.cpp
@@ -25,6 +25,7 @@
//
//=============================================================================
+#include "ags/shared/util/lzw.h"
#include "ags/shared/ac/common.h" // quit
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
@@ -122,13 +123,13 @@ void _delete(int z) {
}
}
-void lzwcompress(Stream *lzw_in, Stream *out) {
+bool lzwcompress(Stream *lzw_in, Stream *out) {
int ch, i, run, len, match, size, mask;
char buf[17];
_G(lzbuffer) = (char *)malloc(N + F + (N + 1 + N + N + 256) * sizeof(int)); // 28.5 k !
if (_G(lzbuffer) == nullptr) {
- quit("unable to compress: out of memory");
+ return false;
}
_G(node) = (int *)(_G(lzbuffer) + N + F);
@@ -193,6 +194,7 @@ void lzwcompress(Stream *lzw_in, Stream *out) {
}
free(_G(lzbuffer));
+ return true;
}
void myputc(int ccc, Stream *out) {
@@ -206,7 +208,7 @@ void myputc(int ccc, Stream *out) {
out->WriteInt8(ccc);
}
-void lzwexpand(Stream *lzw_in, Stream *out, size_t out_size) {
+bool lzwexpand(Stream *lzw_in, Stream *out, size_t out_size) {
int bits, ch, i, j, len, mask;
char *lzbuffer;
_G(outbytes) = 0; _G(putbytes) = 0;
@@ -214,7 +216,7 @@ void lzwexpand(Stream *lzw_in, Stream *out, size_t out_size) {
lzbuffer = (char *)malloc(N);
if (lzbuffer == nullptr) {
- quit("compress.cpp: unable to decompress: insufficient memory");
+ return false;
}
i = N - F;
@@ -244,8 +246,10 @@ void lzwexpand(Stream *lzw_in, Stream *out, size_t out_size) {
if ((_G(putbytes) >= _G(maxsize)) && (_G(maxsize) > 0))
break;
- if ((lzw_in->EOS()) && (_G(maxsize) > 0))
- quit("Read error decompressing image - file is corrupt");
+ if ((lzw_in->EOS()) && (_G(maxsize) > 0)) {
+ free(lzbuffer);
+ return false;
+ }
} // end for mask
if ((_G(putbytes) >= _G(maxsize)) && (_G(maxsize) > 0))
@@ -253,6 +257,7 @@ void lzwexpand(Stream *lzw_in, Stream *out, size_t out_size) {
}
free(lzbuffer);
+ return true;
}
} // namespace AGS3
diff --git a/engines/ags/shared/util/lzw.h b/engines/ags/shared/util/lzw.h
index feb86829a2b..15a5f290c36 100644
--- a/engines/ags/shared/util/lzw.h
+++ b/engines/ags/shared/util/lzw.h
@@ -32,8 +32,8 @@ class Stream;
using namespace AGS; // FIXME later
-void lzwcompress(Shared::Stream *lzw_in, Shared::Stream *out);
-void lzwexpand(Shared::Stream *lzw_in, Shared::Stream *out, size_t out_size);
+bool lzwcompress(Shared::Stream *lzw_in, Shared::Stream *out);
+bool lzwexpand(Shared::Stream *lzw_in, Shared::Stream *out, size_t out_size);
} // namespace AGS3
Commit: 897724f110bbc68fac5d025951ac6fd417fa3835
https://github.com/scummvm/scummvm/commit/897724f110bbc68fac5d025951ac6fd417fa3835
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:18-07:00
Commit Message:
AGS: Split VectorStream from MemoryStream and allow write C-buffer
>From upstream f8d1d99bcbdadfc6f1380d978d2c7261200a73c8
Changed paths:
engines/ags/shared/ac/sprite_file.cpp
engines/ags/shared/util/compress.cpp
engines/ags/shared/util/memory_stream.cpp
engines/ags/shared/util/memory_stream.h
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
index 34bc74af5cf..2b69ae46b42 100644
--- a/engines/ags/shared/ac/sprite_file.cpp
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -587,7 +587,7 @@ void SpriteFileWriter::WriteBitmap(Bitmap *image) {
// (Optional) Compress the image data into the temp buffer
if (_compress) {
compress = 1;
- MemoryStream mems(_membuf, kStream_Write);
+ VectorStream mems(_membuf, kStream_Write);
rle_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems);
// mark to write as a plain byte array
im_data = ImBufferCPtr(&_membuf[0], _membuf.size(), 1);
diff --git a/engines/ags/shared/util/compress.cpp b/engines/ags/shared/util/compress.cpp
index 5b4ea74cab0..4c3a981dd3b 100644
--- a/engines/ags/shared/util/compress.cpp
+++ b/engines/ags/shared/util/compress.cpp
@@ -325,7 +325,7 @@ void save_lzw(Stream *out, const Bitmap *bmpp, const RGB(*pal)[256]) {
// because they also included bmp width and height into compressed data!
std::vector<uint8_t> membuf;
{
- MemoryStream memws(membuf, kStream_Write);
+ VectorStream memws(membuf, kStream_Write);
int w = bmpp->GetWidth(), h = bmpp->GetHeight(), bpp = bmpp->GetBPP();
memws.WriteInt32(w * bpp); // stride
memws.WriteInt32(h);
@@ -338,7 +338,7 @@ void save_lzw(Stream *out, const Bitmap *bmpp, const RGB(*pal)[256]) {
}
// Open same buffer for reading, and begin writing compressed data into the output
- MemoryStream mem_in(membuf);
+ VectorStream mem_in(membuf);
// NOTE: old format saves full RGB struct here (4 bytes, including the filler)
if (pal)
out->WriteArray(*pal, sizeof(RGB), 256);
@@ -372,12 +372,12 @@ void load_lzw(Stream *in, Bitmap **dst_bmp, int dst_bpp, RGB(*pal)[256]) {
// First decompress data into the memory buffer
std::vector<uint8_t> membuf;
{
- MemoryStream memws(membuf, kStream_Write);
+ VectorStream memws(membuf, kStream_Write);
lzwexpand(in, &memws, uncomp_sz);
}
// Open same buffer for reading and get params and pixels
- MemoryStream mem_in(membuf);
+ VectorStream mem_in(membuf);
int stride = mem_in.ReadInt32(); // width * bpp
int height = mem_in.ReadInt32();
Bitmap *bmm = BitmapHelper::CreateBitmap((stride / dst_bpp), height, dst_bpp * 8);
diff --git a/engines/ags/shared/util/memory_stream.cpp b/engines/ags/shared/util/memory_stream.cpp
index d9b2c55446c..30704814f9d 100644
--- a/engines/ags/shared/util/memory_stream.cpp
+++ b/engines/ags/shared/util/memory_stream.cpp
@@ -26,45 +26,26 @@ namespace AGS3 {
namespace AGS {
namespace Shared {
-MemoryStream::MemoryStream(const std::vector<uint8_t> &cbuf, DataEndianess stream_endianess)
- : DataStream(stream_endianess)
- , _cbuf(&cbuf.front())
- , _len(cbuf.size())
- , _buf(nullptr)
- , _mode(kStream_Read)
- , _pos(0) {
-}
-
MemoryStream::MemoryStream(const uint8_t *cbuf, size_t buf_sz, DataEndianess stream_endianess)
: DataStream(stream_endianess)
, _cbuf(cbuf)
+ , _buf_sz(buf_sz)
, _len(buf_sz)
, _buf(nullptr)
, _mode(kStream_Read)
, _pos(0) {
}
-MemoryStream::MemoryStream(const String &cbuf, DataEndianess stream_endianess)
+MemoryStream::MemoryStream(uint8_t *buf, size_t buf_sz, StreamWorkMode mode, DataEndianess stream_endianess)
: DataStream(stream_endianess)
- , _cbuf(reinterpret_cast<const uint8_t *>(cbuf.GetCStr()))
- , _len(cbuf.GetLength())
- , _buf(nullptr)
+ , _buf(buf)
+ , _buf_sz(buf_sz)
+ , _len(0)
+ , _cbuf(nullptr)
, _mode(kStream_Read)
, _pos(0) {
}
-MemoryStream::MemoryStream(std::vector<uint8_t> &buf, StreamWorkMode mode, DataEndianess stream_endianess)
- : DataStream(stream_endianess)
- , _len(buf.size())
- , _buf(&buf)
- , _mode(mode)
- , _pos(buf.size()) {
- _cbuf = (mode == kStream_Read) ? &buf.front() : nullptr;
-}
-
-MemoryStream::~MemoryStream() {
-}
-
void MemoryStream::Close() {
_cbuf = nullptr;
_buf = nullptr;
@@ -122,40 +103,76 @@ int32_t MemoryStream::ReadByte() {
return _cbuf[(size_t)(_pos++)];
}
+bool MemoryStream::Seek(soff_t offset, StreamSeek origin) {
+ if (!CanSeek()) {
+ return false;
+ }
+ switch (origin) {
+ case kSeekBegin: _pos = 0 + offset; break;
+ case kSeekCurrent: _pos = _pos + offset; break;
+ case kSeekEnd: _pos = _len + offset; break;
+ default:
+ return false;
+ }
+ _pos = std::max<soff_t>(0, _pos);
+ _pos = std::min<soff_t>(_len, _pos); // clamp to EOS
+ return true;
+}
+
size_t MemoryStream::Write(const void *buffer, size_t size) {
- if (!_buf) {
+ if (!_buf || _pos >= _buf_sz) {
return 0;
}
- _buf->resize(_buf->size() + size);
- memcpy(_buf->data() + _pos, buffer, size);
+ size = std::min(size, _buf_sz - (size_t)_pos);
+ memcpy(_buf + _pos, buffer, size);
_pos += size;
_len += size;
return size;
}
int32_t MemoryStream::WriteByte(uint8_t val) {
- if (!_buf) {
+ if (!_buf || _pos >= _buf_sz) {
return -1;
}
- _buf->push_back(val);
+ *(_buf + _pos) = val;
_pos++; _len++;
return val;
}
-bool MemoryStream::Seek(soff_t offset, StreamSeek origin) {
- if (!CanSeek()) {
- return false;
+
+VectorStream::VectorStream(const std::vector<uint8_t> &cbuf, DataEndianess stream_endianess)
+ : MemoryStream(&cbuf.front(), cbuf.size(), stream_endianess)
+ , _vec(nullptr) {
+}
+
+VectorStream::VectorStream(std::vector<uint8_t> &buf, StreamWorkMode mode, DataEndianess stream_endianess)
+ : MemoryStream((mode == kStream_Read) ? &buf.front() : nullptr, buf.size(), mode, stream_endianess)
+ , _vec(&buf) {
+}
+
+void VectorStream::Close() {
+ _vec = nullptr;
+ MemoryStream::Close();
+}
+
+size_t VectorStream::Write(const void *buffer, size_t size) {
+ if (!_vec) {
+ return 0;
}
- switch (origin) {
- case kSeekBegin: _pos = 0 + offset; break;
- case kSeekCurrent: _pos = _pos + offset; break;
- case kSeekEnd: _pos = _len + offset; break;
- default:
- return false;
+ _vec->resize(_vec->size() + size);
+ memcpy(_vec->data() + _pos, buffer, size);
+ _pos += size;
+ _len += size;
+ return size;
+}
+
+int32_t VectorStream::WriteByte(uint8_t val) {
+ if (!_vec) {
+ return -1;
}
- _pos = std::max<soff_t>(0, _pos);
- _pos = std::min<soff_t>(_len, _pos); // clamp to EOS
- return true;
+ _vec->push_back(val);
+ _pos++; _len++;
+ return val;
}
} // namespace Shared
diff --git a/engines/ags/shared/util/memory_stream.h b/engines/ags/shared/util/memory_stream.h
index 245bbda2b74..d50526a38fb 100644
--- a/engines/ags/shared/util/memory_stream.h
+++ b/engines/ags/shared/util/memory_stream.h
@@ -19,17 +19,18 @@
*
*/
-//=============================================================================
-//
-// MemoryStream does reading and writing over the buffer of bytes stored in
-// memory. Currently has rather trivial implementation. Does not own a buffer
-// itself, but works with the provided std::vector reference, which means that
-// the buffer *must* persist until stream is closed.
-// TODO: perhaps accept const char* for reading mode, for compatibility with
-// the older code, and maybe to let also read String objects?
-// TODO: separate StringStream for reading & writing String object?
-//
-//=============================================================================
+ //=============================================================================
+ //
+ // MemoryStream does reading and writing over the buffer of bytes stored in
+ // memory. Currently has rather trivial implementation. Does not own a buffer
+ // itself, but works with the provided C-buffer pointer, which means that the
+ // buffer object *must* persist until stream is closed.
+ //
+ // VectorStream is a specialized implementation that works with std::vector.
+ // Unlike base MemoryStream provides continiously resizing buffer for writing.
+ // TODO: separate StringStream for reading & writing String object?
+ //
+ //=============================================================================
#ifndef AGS_SHARED_UTIL_MEMORY_STREAM_H
#define AGS_SHARED_UTIL_MEMORY_STREAM_H
@@ -44,19 +45,15 @@ namespace Shared {
class MemoryStream : public DataStream {
public:
- // Construct memory stream in the read-only mode over a const std::vector;
- // vector must persist in memory until the stream is closed.
- MemoryStream(const std::vector<uint8_t> &cbuf, DataEndianess stream_endianess = kLittleEndian);
// Construct memory stream in the read-only mode over a const C-buffer;
+ // reading will never exceed buf_sz bytes;
// buffer must persist in memory until the stream is closed.
MemoryStream(const uint8_t *cbuf, size_t buf_sz, DataEndianess stream_endianess = kLittleEndian);
- // Construct memory stream in the read-only mode over a const String;
- // String object must persist in memory until the stream is closed.
- MemoryStream(const String &cbuf, DataEndianess stream_endianess = kLittleEndian);
- // Construct memory stream in the chosen mode over a given std::vector;
- // vector must persist in memory until the stream is closed.
- MemoryStream(std::vector<uint8_t> &buf, StreamWorkMode mode, DataEndianess stream_endianess = kLittleEndian);
- ~MemoryStream() override;
+ // Construct memory stream in the chosen mode over a given C-buffer;
+ // neither reading nor writing will ever exceed buf_sz bytes;
+ // buffer must persist in memory until the stream is closed.
+ MemoryStream(uint8_t *buf, size_t buf_sz, StreamWorkMode mode, DataEndianess stream_endianess = kLittleEndian);
+ ~MemoryStream() override {}
void Close() override;
bool Flush() override;
@@ -80,12 +77,35 @@ public:
bool Seek(soff_t offset, StreamSeek origin) override;
-private:
+protected:
const uint8_t *_cbuf;
- size_t _len;
- std::vector<uint8_t> *_buf;
- const StreamWorkMode _mode;
- soff_t _pos;
+ size_t _buf_sz; // hard buffer limit
+ size_t _len; // calculated length of stream
+ const StreamWorkMode _mode;
+ soff_t _pos; // current stream pos
+
+private:
+ uint8_t *_buf;
+};
+
+
+class VectorStream : public MemoryStream {
+public:
+ // Construct memory stream in the read-only mode over a const std::vector;
+ // vector must persist in memory until the stream is closed.
+ VectorStream(const std::vector<uint8_t> &cbuf, DataEndianess stream_endianess = kLittleEndian);
+ // Construct memory stream in the chosen mode over a given std::vector;
+ // vector must persist in memory until the stream is closed.
+ VectorStream(std::vector<uint8_t> &buf, StreamWorkMode mode, DataEndianess stream_endianess = kLittleEndian);
+ ~VectorStream() override {}
+
+ void Close() override;
+
+ size_t Write(const void *buffer, size_t size) override;
+ int32_t WriteByte(uint8_t b) override;
+
+private:
+ std::vector<uint8_t> *_vec; // writeable vector (may be null)
};
} // namespace Shared
Commit: 6d4a816fb4b42b81e70745a608763a8936429575
https://github.com/scummvm/scummvm/commit/6d4a816fb4b42b81e70745a608763a8936429575
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:18-07:00
Commit Message:
AGS: SpriteFile supports compression type selection (RLE/LZW)
>From upstream 1526a939223974a88abf4c9601f1d4e421296a65
Changed paths:
engines/ags/shared/ac/sprite_cache.cpp
engines/ags/shared/ac/sprite_cache.h
engines/ags/shared/ac/sprite_file.cpp
engines/ags/shared/ac/sprite_file.h
engines/ags/shared/util/compress.cpp
engines/ags/shared/util/compress.h
diff --git a/engines/ags/shared/ac/sprite_cache.cpp b/engines/ags/shared/ac/sprite_cache.cpp
index b03fcb26773..9236fb7f8c0 100644
--- a/engines/ags/shared/ac/sprite_cache.cpp
+++ b/engines/ags/shared/ac/sprite_cache.cpp
@@ -423,7 +423,7 @@ void SpriteCache::RemapSpriteToSprite0(sprkey_t index) {
#endif
}
-int SpriteCache::SaveToFile(const String &filename, int store_flags, bool compress, SpriteFileIndex &index) {
+int SpriteCache::SaveToFile(const String &filename, int store_flags, SpriteCompression compress, SpriteFileIndex &index) {
std::vector<Bitmap *> sprites;
for (const auto &data : _spriteData) {
// NOTE: this is a horrible hack:
diff --git a/engines/ags/shared/ac/sprite_cache.h b/engines/ags/shared/ac/sprite_cache.h
index d759639c5d8..411948b2aab 100644
--- a/engines/ags/shared/ac/sprite_cache.h
+++ b/engines/ags/shared/ac/sprite_cache.h
@@ -92,17 +92,17 @@ public:
~SpriteCache();
// Loads sprite reference information and inits sprite stream
- HError InitFile(const Shared::String &filename, const Shared::String &sprindex_filename);
+ HError InitFile(const Shared::String &filename, const Shared::String &sprindex_filename);
// Saves current cache contents to the file
- int SaveToFile(const Shared::String &filename, int store_flags, bool compress, SpriteFileIndex &index);
+ int SaveToFile(const Shared::String &filename, int store_flags, SpriteCompression compress, SpriteFileIndex &index);
// Closes an active sprite file stream
void DetachFile();
inline int GetStoreFlags() const {
return _file.GetStoreFlags();
}
- inline bool IsFileCompressed() const {
- return _file.IsFileCompressed();
+ inline SpriteCompression GetSpriteCompression() const {
+ return _file.GetSpriteCompression();
}
// Tells if there is a sprite registered for the given index;
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
index 2b69ae46b42..dcbee02cab5 100644
--- a/engines/ags/shared/ac/sprite_file.cpp
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -141,7 +141,6 @@ static inline uint8_t GetPaletteBPP(SpriteFormat fmt) {
SpriteFile::SpriteFile() {
- _compressed = false;
_curPos = -2;
}
@@ -176,13 +175,13 @@ HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filen
_storeFlags = 0;
if (_version < kSprfVersion_Compressed) {
- _compressed = false;
+ _compress = kSprCompress_None;
// skip the palette
_stream->Seek(256 * 3); // sizeof(RGB) * 256
} else if (_version == kSprfVersion_Compressed) {
- _compressed = true;
+ _compress = kSprCompress_RLE;
} else if (_version >= kSprfVersion_Last32bit) {
- _compressed = (_stream->ReadInt8() == 1);
+ _compress = (SpriteCompression)_stream->ReadInt8();
spriteFileID = _stream->ReadInt32();
}
@@ -225,8 +224,8 @@ int SpriteFile::GetStoreFlags() const {
return _storeFlags;
}
-bool SpriteFile::IsFileCompressed() const {
- return _compressed;
+SpriteCompression SpriteFile::GetSpriteCompression() const {
+ return _compress;
}
sprkey_t SpriteFile::GetTopmostSprite() const {
@@ -300,17 +299,18 @@ bool SpriteFile::LoadSpriteIndexFile(const String &filename, int expectedFileID,
}
static inline void ReadSprHeader(SpriteDatHeader &hdr, Stream *in,
- const SpriteFileVersion ver, int gl_compress) {
+ const SpriteFileVersion ver, SpriteCompression gl_compress) {
int bpp = in->ReadInt8();
SpriteFormat sformat = (SpriteFormat)in->ReadInt8();
// note we MUST read first 2 * int8 before skipping rest
if (bpp == 0) {
hdr = SpriteDatHeader(); return;
} // empty slot
- int compress = gl_compress, pal_count = 0;
+ int pal_count = 0;
+ SpriteCompression compress = gl_compress;
if (ver >= kSprfVersion_StorageFormats) {
pal_count = (uint8_t)in->ReadInt8() + 1; // saved as (count - 1)
- compress = in->ReadInt8();
+ compress = (SpriteCompression)in->ReadInt8();
}
int w = in->ReadInt16();
int h = in->ReadInt16();
@@ -323,11 +323,12 @@ HError SpriteFile::RebuildSpriteIndex(Stream *in, sprkey_t topmost,
for (sprkey_t i = 0; !in->EOS() && (i <= topmost); ++i) {
_spriteData[i].Offset = in->GetPosition();
SpriteDatHeader hdr;
- ReadSprHeader(hdr, _stream.get(), _version, _compressed ? 1 : 0);
+ ReadSprHeader(hdr, _stream.get(), _version, _compress);
if (hdr.BPP == 0) return HError::None(); // empty slot, this is normal
int pal_bpp = GetPaletteBPP(hdr.SFormat);
if (pal_bpp > 0) in->Seek(hdr.PalCount * pal_bpp); // skip palette
- size_t data_sz = ((_version >= kSprfVersion_StorageFormats) || _compressed) ?
+ size_t data_sz =
+ ((_version >= kSprfVersion_StorageFormats) || _compress != kSprCompress_None) ?
(uint32_t)in->ReadInt32() : hdr.Width * hdr.Height * hdr.BPP;
in->Seek(data_sz); // skip image data
}
@@ -347,7 +348,7 @@ HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
_curPos = -2; // mark undefined pos
SpriteDatHeader hdr;
- ReadSprHeader(hdr, _stream.get(), _version, _compressed ? 1 : 0);
+ ReadSprHeader(hdr, _stream.get(), _version, _compress);
if (hdr.BPP == 0) return HError::None(); // empty slot, this is normal
int bpp = hdr.BPP, w = hdr.Width, h = hdr.Height;
Bitmap *image = BitmapHelper::CreateBitmap(w, h, bpp * 8);
@@ -370,14 +371,19 @@ HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
im_data = ImBufferPtr(&indexed_buf[0], indexed_buf.size(), 1);
}
// (Optional) Decompress the image data into the temp buffer
- size_t in_data_size = ((_version >= kSprfVersion_StorageFormats) || _compressed) ?
+ size_t in_data_size =
+ ((_version >= kSprfVersion_StorageFormats) || _compress != kSprCompress_None) ?
(uint32_t)_stream->ReadInt32() : (w * h * bpp);
- if (_compressed) {
+ if (hdr.Compress != kSprCompress_None) {
if (in_data_size == 0) {
delete image;
return new Error(String::FromFormat("LoadSprite: bad compressed data for sprite %d.", index));
}
- rle_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get());
+ switch (hdr.Compress) {
+ case kSprCompress_RLE: rle_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get()); break;
+ case kSprCompress_LZW: lzw_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get()); break;
+ default: assert(!"Unsupported compression type!");
+ }
// TODO: test that not more than data_size was read!
}
// Otherwise (no compression) read directly
@@ -414,13 +420,13 @@ HError SpriteFile::LoadRawData(sprkey_t index, SpriteDatHeader &hdr, std::vector
SeekToSprite(index);
_curPos = -2; // mark undefined pos
- ReadSprHeader(hdr, _stream.get(), _version, _compressed ? 1 : 0);
+ ReadSprHeader(hdr, _stream.get(), _version, _compress);
if (hdr.BPP == 0) return HError::None(); // empty slot, this is normal
size_t data_size = 0;
soff_t data_pos = _stream->GetPosition();
// Optional palette
data_size += hdr.PalCount * GetPaletteBPP(hdr.SFormat);
- if ((_version >= kSprfVersion_StorageFormats) || _compressed)
+ if ((_version >= kSprfVersion_StorageFormats) || _compress != kSprCompress_None)
data_size += (uint32_t)_stream->ReadInt32() + sizeof(uint32_t);
else
data_size += hdr.Width * hdr.Height * hdr.BPP;
@@ -451,9 +457,8 @@ static sprkey_t FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
}
int SaveSpriteFile(const String &save_to_file,
- const std::vector<Bitmap *> &sprites,
- SpriteFile *read_from_file,
- int store_flags, bool compress, SpriteFileIndex &index) {
+ const std::vector<Bitmap *> &sprites, SpriteFile *read_from_file,
+ int store_flags, SpriteCompression compress, SpriteFileIndex &index) {
std::unique_ptr<Stream> output(File::CreateFile(save_to_file));
if (output == nullptr)
return -1;
@@ -470,7 +475,7 @@ int SaveSpriteFile(const String &save_to_file,
const bool diff_compress =
read_from_file &&
- (read_from_file->IsFileCompressed() != compress ||
+ (read_from_file->GetSpriteCompression() != compress ||
read_from_file->GetStoreFlags() != store_flags);
for (sprkey_t i = 0; i <= lastslot; ++i) {
@@ -535,7 +540,7 @@ int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index) {
SpriteFileWriter::SpriteFileWriter(std::unique_ptr<Stream> &out) : _out(out) {
}
-void SpriteFileWriter::Begin(int store_flags, bool compress, sprkey_t last_slot) {
+void SpriteFileWriter::Begin(int store_flags, SpriteCompression compress, sprkey_t last_slot) {
if (!_out) return;
_index.SpriteFileIDCheck = g_system->getMillis();
_storeFlags = store_flags;
@@ -577,7 +582,7 @@ void SpriteFileWriter::WriteBitmap(Bitmap *image) {
uint32_t palette[256];
uint32_t pal_count = 0;
SpriteFormat sformat = kSprFmt_Undefined;
- int compress = 0;
+ SpriteCompression compress = kSprCompress_None;
if ((_storeFlags & kSprStore_OptimizeForSize) != 0 && (image->GetBPP() > 1)) { // Try to store this sprite as an indexed bitmap
if (CreateIndexedBitmap(image, indexed_buf, palette, pal_count) && pal_count > 0) {
sformat = PaletteFormatForBPP(image->GetBPP());
@@ -585,10 +590,14 @@ void SpriteFileWriter::WriteBitmap(Bitmap *image) {
}
}
// (Optional) Compress the image data into the temp buffer
- if (_compress) {
- compress = 1;
+ if (_compress != kSprCompress_None) {
+ compress = _compress;
VectorStream mems(_membuf, kStream_Write);
- rle_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems);
+ switch (compress) {
+ case kSprCompress_RLE: rle_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems); break;
+ case kSprCompress_LZW: lzw_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems); break;
+ default: assert(!"Unsupported compression type!");
+ }
// mark to write as a plain byte array
im_data = ImBufferCPtr(&_membuf[0], _membuf.size(), 1);
}
diff --git a/engines/ags/shared/ac/sprite_file.h b/engines/ags/shared/ac/sprite_file.h
index 26e000aa21d..49910040fd7 100644
--- a/engines/ags/shared/ac/sprite_file.h
+++ b/engines/ags/shared/ac/sprite_file.h
@@ -19,14 +19,14 @@
*
*/
-//=============================================================================
-//
-// 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.
-//
-//=============================================================================
+ //=============================================================================
+ //
+ // 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.
+ //
+ //=============================================================================
#ifndef AGS_SHARED_AC_SPRITE_FILE_H
#define AGS_SHARED_AC_SPRITE_FILE_H
@@ -80,6 +80,12 @@ enum SpriteFormat {
kSprFmt_PaletteRgb565 = 34
};
+enum SpriteCompression {
+ kSprCompress_None = 0,
+ kSprCompress_RLE,
+ kSprCompress_LZW
+};
+
typedef int32_t sprkey_t;
// SpriteFileIndex contains sprite file's table of contents
@@ -89,8 +95,12 @@ struct SpriteFileIndex {
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; }
+ inline size_t GetCount() const {
+ return Offsets.size();
+ }
+ inline sprkey_t GetLastSlot() const {
+ return (sprkey_t)GetCount() - 1;
+ }
};
// Invidual sprite data header (as read from the file)
@@ -98,14 +108,14 @@ struct SpriteDatHeader {
int BPP = 0; // color depth (bytes per pixel); or input format
SpriteFormat SFormat = kSprFmt_Undefined; // storage format
uint32_t PalCount = 0; // palette length, if applicable to storage format
- int Compress = 0; // compression type
+ SpriteCompression Compress = kSprCompress_None; // compression type
int Width = 0; // sprite's width
int Height = 0; // sprite's height
SpriteDatHeader() = default;
SpriteDatHeader(int bpp, SpriteFormat sformat = kSprFmt_Undefined,
- uint32_t pal_count = 0, int compress = 0, int w = 0, int h = 0)
- : BPP(bpp), SFormat(sformat), PalCount(pal_count),
+ uint32_t pal_count = 0, SpriteCompression compress = kSprCompress_None,
+ int w = 0, int h = 0) : BPP(bpp), SFormat(sformat), PalCount(pal_count),
Compress(compress), Width(w), Height(h) {
}
};
@@ -127,7 +137,7 @@ public:
int GetStoreFlags() const;
// Tells if bitmaps in the file are compressed
- bool IsFileCompressed() const;
+ SpriteCompression GetSpriteCompression() const;
// Tells the highest known sprite index
sprkey_t GetTopmostSprite() const;
@@ -161,7 +171,7 @@ private:
std::unique_ptr<Stream> _stream; // the sprite stream
SpriteFileVersion _version = kSprfVersion_Current;
int _storeFlags = 0; // storage flags, specify how sprites may be stored
- bool _compressed; // are sprites compressed
+ SpriteCompression _compress = kSprCompress_None; // sprite compression typ
sprkey_t _curPos; // current stream position (sprite slot)
};
@@ -171,17 +181,20 @@ private:
class SpriteFileWriter {
public:
SpriteFileWriter(std::unique_ptr<Stream> &out);
- ~SpriteFileWriter() {}
+ ~SpriteFileWriter() {
+ }
- // Get the sprite index, accumulated after write
- const SpriteFileIndex &GetIndex() const { return _index; }
+ // Get the sprite index, accumulated after write
+ const SpriteFileIndex &GetIndex() const {
+ return _index;
+ }
- // Initializes new sprite file format
- void Begin(int store_flags, 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();
+ // Initializes new sprite file format
+ void Begin(int store_flags, SpriteCompression 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 any additional processing
void WriteRawData(const SpriteDatHeader &hdr, const uint8_t *data, size_t data_sz);
// Finalizes current format; no further writing is possible after this
@@ -195,22 +208,22 @@ private:
std::unique_ptr<Stream> &_out;
int _storeFlags = 0;
- 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
+ SpriteCompression _compress = kSprCompress_None;
+ 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<uint8_t> _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
- int store_flags, bool compress, SpriteFileIndex &index);
+extern 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
+ int store_flags, SpriteCompression compress, SpriteFileIndex &index);
// Saves sprite index table in a separate file
-int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
+extern int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
} // namespace Shared
} // namespace AGS
diff --git a/engines/ags/shared/util/compress.cpp b/engines/ags/shared/util/compress.cpp
index 4c3a981dd3b..2fc6ab3e33d 100644
--- a/engines/ags/shared/util/compress.cpp
+++ b/engines/ags/shared/util/compress.cpp
@@ -319,6 +319,26 @@ Shared::Bitmap *load_rle_bitmap8(Stream *in, RGB(*pal)[256]) {
// LZW
//-----------------------------------------------------------------------------
+void lzw_compress(const uint8_t *data, size_t data_sz, int image_bpp, Shared::Stream *out) {
+ // LZW algorithm that we use fails on sequence less than 16 bytes.
+ if (data_sz < 16) {
+ out->Write(data, data_sz);
+ return;
+ }
+ MemoryStream mem_in(data, data_sz);
+ lzwcompress(&mem_in, out);
+}
+
+void lzw_decompress(uint8_t *data, size_t data_sz, int image_bpp, Shared::Stream *in) {
+ // LZW algorithm that we use fails on sequence less than 16 bytes.
+ if (data_sz < 16) {
+ in->Read(data, data_sz);
+ return;
+ }
+ MemoryStream ms(data, data_sz, kStream_Write);
+ lzwexpand(in, &ms, data_sz);
+}
+
void save_lzw(Stream *out, const Bitmap *bmpp, const RGB(*pal)[256]) {
// First write original bitmap's info and data into the memory buffer
// NOTE: we must do this purely for backward compatibility with old room formats:
diff --git a/engines/ags/shared/util/compress.h b/engines/ags/shared/util/compress.h
index cda9bd8708b..b4824a28104 100644
--- a/engines/ags/shared/util/compress.h
+++ b/engines/ags/shared/util/compress.h
@@ -44,6 +44,8 @@ void save_rle_bitmap8(Shared::Stream *out, const Shared::Bitmap *bmp, const RGB(
Shared::Bitmap *load_rle_bitmap8(Shared::Stream *in, RGB(*pal)[256] = nullptr);
// LZW compression
+void lzw_compress(const uint8_t *data, size_t data_sz, int image_bpp, Shared::Stream *out);
+void lzw_decompress(uint8_t *data, size_t data_sz, int image_bpp, Shared::Stream *in);
// Saves bitmap with an optional palette compressed by LZW
void save_lzw(Shared::Stream *out, const Shared::Bitmap *bmpp, const RGB(*pal)[256] = nullptr);
// Loads bitmap decompressing
Commit: 0f6ca133d223e80ea78199c831d9238a0cfd38aa
https://github.com/scummvm/scummvm/commit/0f6ca133d223e80ea78199c831d9238a0cfd38aa
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:18-07:00
Commit Message:
AGS: fixed gcc compilation of lzw
>From upstream ecca80cfb03463117fdf822fd973bf0939bbd38c
Changed paths:
engines/ags/shared/util/lzw.h
diff --git a/engines/ags/shared/util/lzw.h b/engines/ags/shared/util/lzw.h
index 15a5f290c36..5ca4749e72c 100644
--- a/engines/ags/shared/util/lzw.h
+++ b/engines/ags/shared/util/lzw.h
@@ -22,6 +22,8 @@
#ifndef AGS_SHARED_UTIL_LZW_H
#define AGS_SHARED_UTIL_LZW_H
+#include "ags/shared/core/types.h"
+
namespace AGS3 {
namespace AGS {
Commit: b615ca27b3d8fd77fb6ed01b630906cd6c306582
https://github.com/scummvm/scummvm/commit/b615ca27b3d8fd77fb6ed01b630906cd6c306582
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-30T22:13:18-07:00
Commit Message:
AGS: Fix String::ClipRight causing access violation for empty string
>From upstream b1602096ec06aae8f045c0037298d61846111aa0
Changed paths:
engines/ags/shared/util/string.cpp
diff --git a/engines/ags/shared/util/string.cpp b/engines/ags/shared/util/string.cpp
index 1b9ea89b32c..23770530aff 100644
--- a/engines/ags/shared/util/string.cpp
+++ b/engines/ags/shared/util/string.cpp
@@ -497,7 +497,7 @@ void String::ClipMid(size_t from, size_t count) {
}
void String::ClipRight(size_t count) {
- if (count > 0) {
+ if (_len > 0 && count > 0) {
count = Math::Min(count, _len);
BecomeUnique();
_len -= count;
More information about the Scummvm-git-logs
mailing list