Initial commit
This commit is contained in:
362
engines/qdengine/system/graphics/rle_compress.cpp
Normal file
362
engines/qdengine/system/graphics/rle_compress.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/std/vector.h"
|
||||
|
||||
#include "common/file.h"
|
||||
|
||||
#include "qdengine/system/graphics/rle_compress.h"
|
||||
#include "qdengine/system/graphics/gr_dispatcher.h"
|
||||
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
byte *g_buffer0 = nullptr;
|
||||
byte *g_buffer1 = nullptr;
|
||||
uint32 g_buffersLen = 0;
|
||||
|
||||
static void ensureBuffers() {
|
||||
if (g_buffer0 == nullptr) {
|
||||
g_buffer0 = (byte *)calloc(4096, 1);
|
||||
g_buffer1 = (byte *)calloc(4096, 1);
|
||||
|
||||
g_buffersLen = 4096U;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator == (const RLEBuffer &buf1, const RLEBuffer &buf2) {
|
||||
if (!(buf1._header_offset == buf2._header_offset)) return false;
|
||||
if (!(buf1._data_offset == buf2._data_offset)) return false;
|
||||
if (!(buf1._header == buf2._header)) return false;
|
||||
if (!(buf1._data == buf2._data)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RLEBuffer::RLEBuffer() : _bits_per_pixel(32) {
|
||||
}
|
||||
|
||||
RLEBuffer::RLEBuffer(const RLEBuffer &buf) : _header_offset(buf._header_offset),
|
||||
_data_offset(buf._data_offset),
|
||||
_header(buf._header),
|
||||
_data(buf._data),
|
||||
_bits_per_pixel(buf._bits_per_pixel) {
|
||||
|
||||
ensureBuffers();
|
||||
}
|
||||
|
||||
RLEBuffer::~RLEBuffer() {
|
||||
_header_offset.clear();
|
||||
_data_offset.clear();
|
||||
_header.clear();
|
||||
|
||||
_data.clear();
|
||||
}
|
||||
|
||||
bool RLEBuffer::decode_line(int y, int buffer_id) const {
|
||||
ensureBuffers();
|
||||
|
||||
if (buffer_id)
|
||||
return decode_line(y, g_buffer1);
|
||||
else
|
||||
return decode_line(y, g_buffer0);
|
||||
}
|
||||
|
||||
const byte *RLEBuffer::get_buffer(int buffer_id) {
|
||||
ensureBuffers();
|
||||
|
||||
if (buffer_id)
|
||||
return g_buffer1;
|
||||
else
|
||||
return g_buffer0;
|
||||
}
|
||||
|
||||
void RLEBuffer::releaseBuffers() {
|
||||
free(g_buffer0);
|
||||
g_buffer0 = nullptr;
|
||||
free(g_buffer1);
|
||||
g_buffer1 = nullptr;
|
||||
}
|
||||
|
||||
RLEBuffer &RLEBuffer::operator = (const RLEBuffer &buf) {
|
||||
if (this == &buf) return *this;
|
||||
|
||||
_header_offset = buf._header_offset;
|
||||
_data_offset = buf._data_offset;
|
||||
|
||||
_header = buf._header;
|
||||
_data = buf._data;
|
||||
|
||||
_bits_per_pixel = buf._bits_per_pixel;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool RLEBuffer::encode(int sx, int sy, const byte *buf) {
|
||||
_header_offset.resize(sy);
|
||||
_data_offset.resize(sy);
|
||||
|
||||
_header.clear();
|
||||
_data.clear();
|
||||
|
||||
_data.reserve(sx * sy);
|
||||
|
||||
const uint32 *buffer = reinterpret_cast<const uint32 *>(buf);
|
||||
|
||||
for (int y = 0; y < sy; y++) {
|
||||
int count = 0;
|
||||
|
||||
_header_offset[y] = _header.size();
|
||||
_data_offset[y] = _data.size();
|
||||
|
||||
while (count < sx) {
|
||||
int index = count;
|
||||
uint32 pixel = buffer[index++];
|
||||
|
||||
while (index < sx && index - count < 127 && buffer[index] == pixel)
|
||||
index++;
|
||||
|
||||
if (index - count == 1) {
|
||||
while (index < sx && index - count < 127 && (buffer[index] != buffer[index - 1] || (index > 1 && buffer[index] != buffer[index - 2])))
|
||||
index++;
|
||||
|
||||
while (index < sx && buffer[index] == buffer[index - 1])
|
||||
index--;
|
||||
|
||||
_header.push_back(static_cast<int8>(count - index));
|
||||
for (int i = count; i < index; i++)
|
||||
_data.push_back(buffer[i]);
|
||||
} else {
|
||||
_header.push_back(static_cast<int8>(index - count));
|
||||
_data.push_back(pixel);
|
||||
}
|
||||
|
||||
count = index;
|
||||
}
|
||||
buffer += sx;
|
||||
}
|
||||
Std::vector<uint32>(_data).swap(_data);
|
||||
|
||||
resize_buffers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RLEBuffer::decode_line(int y, byte *out_buf) const {
|
||||
const int8 *header_ptr = &*(_header.begin() + _header_offset[y]);
|
||||
const uint32 *data_ptr = &*(_data.begin() + _data_offset[y]);
|
||||
|
||||
uint32 *out_ptr = reinterpret_cast<uint32 *>(out_buf);
|
||||
|
||||
int size = line_header_length(y);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
int8 count = *header_ptr++;
|
||||
if (count > 0) {
|
||||
for (int j = 0; j < count; j++)
|
||||
*out_ptr++ = *data_ptr;
|
||||
data_ptr++;
|
||||
} else {
|
||||
count = -count;
|
||||
memcpy(out_ptr, data_ptr, count * sizeof(uint32));
|
||||
|
||||
out_ptr += count;
|
||||
data_ptr += count;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RLEBuffer::decode_pixel(int x, int y, uint32 &pixel) {
|
||||
const int8 *header_ptr = &*(_header.begin() + _header_offset[y]);
|
||||
const uint32 *data_ptr = &*(_data.begin() + _data_offset[y]);
|
||||
|
||||
int xx = 0;
|
||||
int8 count = *header_ptr++;
|
||||
|
||||
while (xx + abs(count) < x) {
|
||||
if (count > 0) {
|
||||
data_ptr++;
|
||||
} else {
|
||||
count = -count;
|
||||
data_ptr += count;
|
||||
}
|
||||
xx += count;
|
||||
count = *header_ptr++;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
pixel = *data_ptr;
|
||||
} else {
|
||||
data_ptr += x - xx;
|
||||
pixel = *data_ptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 RLEBuffer::size() {
|
||||
return _data.size() * sizeof(uint32) + _data_offset.size() + _header_offset.size() * sizeof(uint32) + _header.size();
|
||||
}
|
||||
|
||||
bool RLEBuffer::convert_data(int bits_per_pixel) {
|
||||
if (_bits_per_pixel == bits_per_pixel)
|
||||
return true;
|
||||
|
||||
int sz = _data.size();
|
||||
|
||||
switch (_bits_per_pixel) {
|
||||
case 15:
|
||||
case 16:
|
||||
if (bits_per_pixel == 24 || bits_per_pixel == 32) {
|
||||
uint16 *short_ptr = reinterpret_cast<uint16 *>(&*_data.begin());
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
short_ptr++;
|
||||
*short_ptr++ <<= 8;
|
||||
}
|
||||
|
||||
short_ptr = reinterpret_cast<uint16 *>(&*_data.begin());
|
||||
byte *char_ptr = reinterpret_cast<byte *>(&*_data.begin());
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
byte r, g, b;
|
||||
|
||||
if (_bits_per_pixel == 15)
|
||||
grDispatcher::split_rgb555u(*short_ptr++, r, g, b);
|
||||
else
|
||||
grDispatcher::split_rgb565u(*short_ptr++, r, g, b);
|
||||
|
||||
short_ptr++;
|
||||
|
||||
char_ptr[0] = b;
|
||||
char_ptr[1] = g;
|
||||
char_ptr[2] = r;
|
||||
|
||||
char_ptr += 4;
|
||||
}
|
||||
} else {
|
||||
uint16 *short_ptr = reinterpret_cast<uint16 *>(&*_data.begin());
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
byte r, g, b;
|
||||
|
||||
if (_bits_per_pixel == 15) {
|
||||
grDispatcher::split_rgb555u(*short_ptr, r, g, b);
|
||||
*short_ptr++ = grDispatcher::make_rgb565u(r, g, b);
|
||||
} else {
|
||||
grDispatcher::split_rgb565u(*short_ptr, r, g, b);
|
||||
*short_ptr++ = grDispatcher::make_rgb555u(r, g, b);
|
||||
}
|
||||
|
||||
short_ptr++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
case 32:
|
||||
if (bits_per_pixel == 15 || bits_per_pixel == 16) {
|
||||
byte *src_ptr = reinterpret_cast<byte *>(&*_data.begin());
|
||||
uint16 *dest_ptr = reinterpret_cast<uint16 *>(&*_data.begin());
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
*dest_ptr++ = (bits_per_pixel == 15) ? grDispatcher::make_rgb555u(src_ptr[2], src_ptr[1], src_ptr[0]) : grDispatcher::make_rgb565u(src_ptr[2], src_ptr[1], src_ptr[0]);
|
||||
*dest_ptr++ >>= 8;
|
||||
src_ptr += 4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
_bits_per_pixel = bits_per_pixel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RLEBuffer::resize_buffers() {
|
||||
uint32 len = line_length() * sizeof(uint32);
|
||||
|
||||
if (g_buffersLen < len) {
|
||||
if (!(g_buffer0 = (byte *)realloc(g_buffer0, len)) || !(g_buffer1 = (byte *)realloc(g_buffer1, len)))
|
||||
error("RLEBuffer::resize_buffers(): Cannot realloc buffers");
|
||||
|
||||
g_buffersLen = len;
|
||||
}
|
||||
}
|
||||
|
||||
int RLEBuffer::line_length() {
|
||||
if (_header_offset.empty()) return 0;
|
||||
|
||||
uint sz = (_header_offset.size() > 1) ? _header_offset[1] : _header.size();
|
||||
|
||||
uint len = 0;
|
||||
for (uint i = 0; i < sz; i++) {
|
||||
len += abs(_header[i]);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int RLEBuffer::line_header_length(int line_num) const {
|
||||
if (line_num < (int)_header_offset.size() - 1)
|
||||
return _header_offset[line_num + 1] - _header_offset[line_num];
|
||||
else
|
||||
return _header.size() - _header_offset[line_num];
|
||||
}
|
||||
|
||||
|
||||
bool RLEBuffer::load(Common::SeekableReadStream *fh) {
|
||||
uint32 sz = fh->readUint32LE();
|
||||
_header_offset.resize(sz);
|
||||
|
||||
sz = fh->readSint32LE();
|
||||
_data_offset.resize(sz);
|
||||
|
||||
sz = fh->readSint32LE();
|
||||
_header.resize(sz + 1);
|
||||
_header[sz] = 0;
|
||||
|
||||
sz = fh->readSint32LE();
|
||||
_data.resize(sz);
|
||||
|
||||
for (uint i = 0; i < _header_offset.size(); i++) {
|
||||
_header_offset[i] = fh->readUint32LE();
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _data_offset.size(); i++) {
|
||||
_data_offset[i] = fh->readUint32LE();
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _header.size() - 1; i++) {
|
||||
_header[i] = fh->readByte();
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _data.size(); i++) {
|
||||
_data[i] = fh->readUint32LE();
|
||||
}
|
||||
|
||||
resize_buffers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace QDEngine
|
||||
Reference in New Issue
Block a user