mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
1404 lines
37 KiB
C++
1404 lines
37 KiB
C++
/* 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/debug.h"
|
|
#include "common/file.h"
|
|
#include "common/savefile.h"
|
|
|
|
#include "engines/metaengine.h"
|
|
#include "graphics/surface.h"
|
|
#include "image/tga.h"
|
|
|
|
#include "qdengine/qdengine.h"
|
|
#include "qdengine/qd_fwd.h"
|
|
#include "qdengine/system/graphics/gr_dispatcher.h"
|
|
#include "qdengine/system/graphics/rle_compress.h"
|
|
#include "qdengine/qdcore/qd_setup.h"
|
|
#include "qdengine/qdcore/qd_sprite.h"
|
|
#include "qdengine/qdcore/qd_file_manager.h"
|
|
#include "qdengine/qdcore/util/2PassScale.h"
|
|
#include "qdengine/qdcore/util/Filters.h"
|
|
|
|
|
|
namespace QDEngine {
|
|
|
|
bool operator == (const qdSprite &sp1, const qdSprite &sp2) {
|
|
if (sp1._size == sp2._size && sp1._picture_offset == sp2._picture_offset && sp1._picture_size == sp2._picture_size) {
|
|
if (sp1.is_compressed()) {
|
|
if (!sp2.is_compressed()) return false;
|
|
|
|
if (sp1._rle_data && sp2._rle_data) {
|
|
if (*sp1._rle_data == *sp2._rle_data)
|
|
return true;
|
|
} else
|
|
return false;
|
|
} else {
|
|
if (sp1._data && sp2._data) {
|
|
const byte *d1 = sp1._data;
|
|
const byte *d2 = sp2._data;
|
|
int sz = sp1._picture_size.x * sp1._picture_size.y;
|
|
|
|
if (sp1.check_flag(qdSprite::ALPHA_FLAG) && !sp2.check_flag(qdSprite::ALPHA_FLAG)) return false;
|
|
if (sp2.check_flag(qdSprite::ALPHA_FLAG) && !sp1.check_flag(qdSprite::ALPHA_FLAG)) return false;
|
|
|
|
switch (sp1._format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
sz *= 2;
|
|
if (sp1.check_flag(qdSprite::ALPHA_FLAG)) sz *= 2;
|
|
break;
|
|
case GR_RGB888:
|
|
sz *= 3;
|
|
break;
|
|
case GR_ARGB8888:
|
|
sz *= 4;
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < sz; i++)
|
|
if (*d1++ != *d2++) return false;
|
|
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
qdSprite::qdSprite() : _data(0),
|
|
_rle_data(0),
|
|
_flags(0) {
|
|
_size = _picture_size = _picture_offset = Vect2i(0, 0);
|
|
|
|
_format = 0;
|
|
}
|
|
|
|
qdSprite::qdSprite(int wid, int hei, int format):
|
|
_rle_data(0),
|
|
_flags(0) {
|
|
_size = _picture_size = Vect2i(wid, hei);
|
|
_picture_offset = Vect2i(0, 0);
|
|
|
|
_format = format;
|
|
|
|
int bytes_per_pix;
|
|
switch (format) {
|
|
case GR_RGB565:
|
|
bytes_per_pix = 2;
|
|
break;
|
|
case GR_ARGB1555:
|
|
bytes_per_pix = 2;
|
|
set_flag(ALPHA_FLAG);
|
|
break;
|
|
case GR_RGB888:
|
|
bytes_per_pix = 3;
|
|
break;
|
|
case GR_ARGB8888:
|
|
bytes_per_pix = 4;
|
|
set_flag(ALPHA_FLAG);
|
|
break;
|
|
default:
|
|
bytes_per_pix = 1;
|
|
break;
|
|
};
|
|
|
|
_data = new byte[wid * hei * bytes_per_pix];
|
|
}
|
|
|
|
qdSprite::qdSprite(const qdSprite &spr) : qdResource(spr), _data(0),
|
|
_rle_data(0),
|
|
_flags(0) {
|
|
*this = spr;
|
|
}
|
|
|
|
qdSprite::~qdSprite() {
|
|
free();
|
|
}
|
|
|
|
qdSprite &qdSprite::operator = (const qdSprite &spr) {
|
|
if (this == &spr) return *this;
|
|
|
|
_format = spr._format;
|
|
_flags = spr._flags;
|
|
_size = spr._size;
|
|
_picture_size = spr._picture_size;
|
|
_picture_offset = spr._picture_offset;
|
|
|
|
delete [] _data;
|
|
if (spr._data) {
|
|
int ssx;
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
if (check_flag(ALPHA_FLAG))
|
|
ssx = 4;
|
|
else
|
|
ssx = 2;
|
|
break;
|
|
case GR_RGB888:
|
|
ssx = 3;
|
|
break;
|
|
case GR_ARGB8888:
|
|
ssx = 4;
|
|
break;
|
|
default:
|
|
ssx = 2;
|
|
break;
|
|
}
|
|
|
|
_data = new byte[_picture_size.x * _picture_size.y * ssx];
|
|
memcpy(_data, spr._data, _picture_size.x * _picture_size.y * ssx);
|
|
} else
|
|
_data = NULL;
|
|
|
|
delete _rle_data;
|
|
if (spr._rle_data)
|
|
_rle_data = new RLEBuffer(*spr._rle_data);
|
|
else
|
|
_rle_data = NULL;
|
|
|
|
_file = spr._file;
|
|
|
|
return *this;
|
|
}
|
|
|
|
void qdSprite::free() {
|
|
delete [] _data;
|
|
delete _rle_data;
|
|
|
|
_size = _picture_size = _picture_offset = Vect2i(0, 0);
|
|
|
|
_format = 0;
|
|
_data = 0;
|
|
|
|
_rle_data = 0;
|
|
|
|
drop_flag(ALPHA_FLAG);
|
|
}
|
|
|
|
bool qdSprite::load(const Common::Path fpath) {
|
|
set_file(fpath);
|
|
|
|
return load();
|
|
}
|
|
|
|
bool qdSprite::load() {
|
|
free();
|
|
|
|
debugC(3, kDebugLoad, "qdSprite::load(%s)", transCyrillic(_file.toString()));
|
|
|
|
Common::SeekableReadStream *fh;
|
|
|
|
if (_file.isRelativeTo("scummvm")) {
|
|
Common::InSaveFile *saveFile = g_engine->getSaveFileManager()->openForLoading(_file.baseName());
|
|
|
|
ExtendedSavegameHeader saveHeader;
|
|
if (MetaEngine::readSavegameHeader(saveFile, &saveHeader, false)) {
|
|
_size = _picture_size = Vect2i(g_engine->_thumbSizeX, g_engine->_thumbSizeY);
|
|
_picture_offset = Vect2i(0, 0);
|
|
|
|
_format = GR_RGB565;
|
|
|
|
_data = new byte[_size.x * _size.y * 2];
|
|
|
|
// Scale image down
|
|
float rx = static_cast<float>(g_engine->_screenW) / g_engine->_thumbSizeX;
|
|
float ry = static_cast<float>(g_engine->_screenH) / g_engine->_thumbSizeY;
|
|
|
|
for (int i = 0; i < _size.y; i++) {
|
|
uint16 *dst = (uint16 *)&_data[2 * _size.x * i];
|
|
for (int j = 0; j < _size.x; j++) {
|
|
*dst++ = *(uint16 *)saveHeader.thumbnail->getBasePtr(rx * j, ry * i);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete saveFile;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!qdFileManager::instance().open_file(&fh, _file)) {
|
|
return false;
|
|
}
|
|
|
|
Image::TGADecoder tgaDecoder;
|
|
tgaDecoder.loadStream(*fh);
|
|
const Graphics::Surface *tgaSurface = tgaDecoder.getSurface();
|
|
|
|
int width = _picture_size.x = tgaSurface->w; ///< width of the sprite. number of pixels in width
|
|
int height = _picture_size.y = tgaSurface->h; ///< height of the sprite. number of pixels in height
|
|
|
|
_size = _picture_size;
|
|
|
|
int bytesPerPixel = tgaSurface->format.bytesPerPixel;
|
|
int widthNB = width * bytesPerPixel; ///< width in bytes
|
|
|
|
switch (bytesPerPixel) {
|
|
//! 16 bit mode is not implemented
|
|
//case 2:
|
|
// format_ = GR_ARGB1555;
|
|
// break;
|
|
case 3:
|
|
drop_flag(ALPHA_FLAG);
|
|
_format = GR_RGB888;
|
|
break;
|
|
case 4:
|
|
set_flag(ALPHA_FLAG);
|
|
_format = GR_ARGB8888;
|
|
break;
|
|
// otherwise the file format is incorrect
|
|
default:
|
|
warning("qdSprite::load(): Bad file format3: '%s'", transCyrillic(_file.toString()));
|
|
return false;
|
|
}
|
|
|
|
_data = new byte[widthNB * height];
|
|
|
|
byte *dataPtr = _data;
|
|
|
|
for (int i = 0; i < height; i++) {
|
|
const byte *ptr = (const byte *)tgaSurface->getBasePtr(0, i);
|
|
|
|
memcpy(dataPtr, ptr, widthNB);
|
|
dataPtr += widthNB;
|
|
}
|
|
|
|
delete fh;
|
|
|
|
const byte min_color = 8;
|
|
if (_format == GR_ARGB8888) {
|
|
for (int i = 0; i < _picture_size.x * _picture_size.y; i++) {
|
|
byte b = _data[i * 4 + 0];
|
|
byte g = _data[i * 4 + 1];
|
|
byte r = _data[i * 4 + 2];
|
|
byte a = _data[i * 4 + 3];
|
|
|
|
if (a >= 250 && r < min_color && g < min_color && b < min_color) {
|
|
r = g = b = min_color;
|
|
}
|
|
|
|
_data[i * 4 + 0] = b * a >> 8;
|
|
_data[i * 4 + 1] = g * a >> 8;
|
|
_data[i * 4 + 2] = r * a >> 8;
|
|
_data[i * 4 + 3] = 255 - a;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < _picture_size.x * _picture_size.y; i++) {
|
|
byte b = _data[i * 3 + 0];
|
|
byte g = _data[i * 3 + 1];
|
|
byte r = _data[i * 3 + 2];
|
|
|
|
if ((r || g || b) && (r < min_color && g < min_color && b < min_color))
|
|
_data[i * 3 + 0] = _data[i * 3 + 1] = _data[i * 3 + 2] = min_color;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void qdSprite::save(const Common::Path fname) {
|
|
if (_format != GR_RGB888 && _format != GR_ARGB8888) return;
|
|
|
|
const Common::Path out_file = !fname.empty() ? fname : _file;
|
|
|
|
static byte header[18];
|
|
|
|
Common::DumpFile fh;
|
|
|
|
fh.open(out_file);
|
|
|
|
memset(header, 0, 18);
|
|
header[2] = 2;
|
|
|
|
header[13] = (_picture_size.x >> 8) & 0xFF;
|
|
header[12] = _picture_size.x & 0xFF;
|
|
|
|
header[15] = (_picture_size.y >> 8) & 0xFF;
|
|
header[14] = _picture_size.y & 0xFF;
|
|
|
|
header[16] = (_format == GR_ARGB8888) ? 32 : 24;
|
|
header[17] = 0x20;
|
|
|
|
fh.write(header, 18);
|
|
|
|
if (_format == GR_ARGB8888) {
|
|
byte *buf = new byte[_picture_size.x * _picture_size.y * 4];
|
|
byte *p = buf;
|
|
byte *dp = _data;
|
|
|
|
for (int i = 0; i < _picture_size.x * _picture_size.y; i++) {
|
|
uint16 r, g, b, a;
|
|
|
|
r = dp[0];
|
|
g = dp[1];
|
|
b = dp[2];
|
|
a = 255 - dp[3];
|
|
|
|
if (a) {
|
|
p[0] = (r << 8) / a;
|
|
p[1] = (g << 8) / a;
|
|
p[2] = (b << 8) / a;
|
|
} else
|
|
p[0] = p[1] = p[2] = 0;
|
|
|
|
p[3] = a;
|
|
|
|
p += 4;
|
|
dp += 4;
|
|
}
|
|
|
|
fh.write(buf, _picture_size.x * _picture_size.y * 4);
|
|
delete [] buf;
|
|
} else
|
|
fh.write(_data, _picture_size.x * _picture_size.y * 3);
|
|
|
|
fh.close();
|
|
}
|
|
|
|
bool qdSprite::compress() {
|
|
if (is_compressed()) return false;
|
|
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
if (_data) {
|
|
_rle_data = new RLEBuffer;
|
|
|
|
if (!check_flag(ALPHA_FLAG)) {
|
|
byte *p = new byte[_picture_size.x * _picture_size.y * 4];
|
|
uint16 *dp = reinterpret_cast<uint16 *>(p);
|
|
uint16 *sp = reinterpret_cast<uint16 *>(_data);
|
|
for (int i = 0; i < _picture_size.x * _picture_size.y; i++) {
|
|
*dp++ = *sp++;
|
|
*dp++ = 0;
|
|
}
|
|
_rle_data->encode(_picture_size.x, _picture_size.y, p);
|
|
delete [] p;
|
|
} else
|
|
_rle_data->encode(_picture_size.x, _picture_size.y, _data);
|
|
|
|
delete [] _data;
|
|
_data = 0;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
case GR_RGB888:
|
|
if (_data) {
|
|
byte *p = new byte[_picture_size.x * _picture_size.y * 4];
|
|
byte *ptr = p;
|
|
byte *data_ptr = _data;
|
|
for (int i = 0; i < _picture_size.x * _picture_size.y; i++) {
|
|
ptr[0] = data_ptr[0];
|
|
ptr[1] = data_ptr[1];
|
|
ptr[2] = data_ptr[2];
|
|
ptr[3] = 0;
|
|
|
|
ptr += 4;
|
|
data_ptr += 3;
|
|
}
|
|
|
|
_rle_data = new RLEBuffer;
|
|
_rle_data->encode(_picture_size.x, _picture_size.y, p);
|
|
|
|
delete [] p;
|
|
delete [] _data;
|
|
_data = 0;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
case GR_ARGB8888:
|
|
if (_data) {
|
|
_rle_data = new RLEBuffer;
|
|
_rle_data->encode(_picture_size.x, _picture_size.y, _data);
|
|
set_flag(ALPHA_FLAG);
|
|
|
|
delete [] _data;
|
|
_data = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool qdSprite::uncompress() {
|
|
if (!is_compressed()) return false;
|
|
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
if (check_flag(ALPHA_FLAG)) {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 4];
|
|
byte *p = _data;
|
|
for (int i = 0; i < _picture_size.y; i++) {
|
|
_rle_data->decode_line(i, p);
|
|
p += _picture_size.x * sizeof(uint32);
|
|
}
|
|
} else {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 2];
|
|
uint16 *p = reinterpret_cast<uint16 *>(_data);
|
|
for (int i = 0; i < _picture_size.y; i++) {
|
|
const uint16 *rle_p = reinterpret_cast<const uint16 *>(RLEBuffer::get_buffer(0));
|
|
_rle_data->decode_line(i);
|
|
|
|
for (int j = 0; j < _picture_size.x; j++) {
|
|
*p++ = *rle_p++;
|
|
rle_p++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case GR_RGB888:
|
|
if (!check_flag(ALPHA_FLAG)) {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 3];
|
|
byte *p = _data;
|
|
for (int i = 0; i < _picture_size.y; i++) {
|
|
const byte *rle_p = reinterpret_cast<const byte *>(RLEBuffer::get_buffer(0));
|
|
_rle_data->decode_line(i);
|
|
|
|
for (int j = 0; j < _picture_size.x; j++) {
|
|
p[0] = rle_p[0];
|
|
p[1] = rle_p[1];
|
|
p[2] = rle_p[2];
|
|
p += 3;
|
|
rle_p += 4;
|
|
}
|
|
}
|
|
} else {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 4];
|
|
byte *p = _data;
|
|
|
|
for (int i = 0; i < _picture_size.y; i++) {
|
|
_rle_data->decode_line(i, p);
|
|
p += _picture_size.x * 4;
|
|
}
|
|
|
|
_format = GR_ARGB8888;
|
|
}
|
|
break;
|
|
case GR_ARGB8888:
|
|
if (!check_flag(ALPHA_FLAG)) {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 3];
|
|
byte *p = _data;
|
|
for (int i = 0; i < _picture_size.y; i++) {
|
|
const byte *rle_p = reinterpret_cast<const byte *>(RLEBuffer::get_buffer(0));
|
|
_rle_data->decode_line(i);
|
|
|
|
for (int j = 0; j < _picture_size.x; j++) {
|
|
p[0] = rle_p[0];
|
|
p[1] = rle_p[1];
|
|
p[2] = rle_p[2];
|
|
p += 3;
|
|
rle_p += 4;
|
|
}
|
|
}
|
|
_format = GR_RGB888;
|
|
} else {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 4];
|
|
byte *p = _data;
|
|
|
|
for (int i = 0; i < _picture_size.y; i++) {
|
|
_rle_data->decode_line(i, p);
|
|
p += _picture_size.x * 4;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
delete _rle_data;
|
|
_rle_data = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void qdSprite::redraw(int x, int y, int z, int mode) const {
|
|
debugC(3, kDebugGraphics, "qdSprite::redraw([%d, %d, %d], mode: %d)", x, y, z, mode);
|
|
|
|
int xx = x - size_x() / 2;
|
|
int yy = y - size_y() / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
xx += _size.x - _picture_offset.x - _picture_size.x;
|
|
else
|
|
xx += _picture_offset.x;
|
|
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
yy += _size.y - _picture_offset.y - _picture_size.y;
|
|
else
|
|
yy += _picture_offset.y;
|
|
|
|
#ifdef _GR_ENABLE_ZBUFFER
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
if (check_flag(ALPHA_FLAG))
|
|
grDispatcher::instance()->putSpr_a_z(xx, yy, z, _picture_size.x, _picture_size.y, _data, mode);
|
|
else
|
|
grDispatcher::instance()->putSpr_z(xx, yy, z, _picture_size.x, _picture_size.y, _data, mode);
|
|
} else
|
|
grDispatcher::instance()->putSpr_rle_z(xx, yy, z, _picture_size.x, _picture_size.y, _rle_data, mode, check_flag(ALPHA_FLAG));
|
|
#else
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
if (check_flag(ALPHA_FLAG))
|
|
grDispatcher::instance()->putSpr_a(xx, yy, _picture_size.x, _picture_size.y, _data, mode);
|
|
else
|
|
grDispatcher::instance()->putSpr(xx, yy, _picture_size.x, _picture_size.y, _data, mode, _format);
|
|
} else
|
|
grDispatcher::instance()->putSpr_rle(xx, yy, _picture_size.x, _picture_size.y, _rle_data, mode, check_flag(ALPHA_FLAG));
|
|
#endif
|
|
|
|
if (debugChannelSet(1, kDebugGraphics))
|
|
grDispatcher::instance()->rectangle(xx, yy, _picture_size.x, _picture_size.y, 0, 0, GR_OUTLINED);
|
|
}
|
|
|
|
void qdSprite::redraw_rot(int x, int y, int z, float angle, int mode) const {
|
|
debugC(3, kDebugGraphics, "qdSprite::redraw_rot([%d, %d, %d], angle: %f, mode: %d)", x, y, z, angle, mode);
|
|
|
|
int xx = x;
|
|
int yy = y;
|
|
|
|
Vect2i delta = _picture_offset + _picture_size / 2 - _size / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
delta.x = -delta.x;
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
delta.y = -delta.y;
|
|
|
|
if (delta.x || delta.y) {
|
|
xx += round(float(delta.x) * cosf(angle) - float(delta.y) * sinf(angle));
|
|
yy += round(float(delta.x) * sinf(angle) + float(delta.y) * cosf(angle));
|
|
}
|
|
|
|
xx -= _picture_size.x / 2;
|
|
yy -= _picture_size.y / 2;
|
|
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
grDispatcher::instance()->putSpr_rot(Vect2i(xx, yy), _picture_size, _data, check_flag(ALPHA_FLAG), mode, angle);
|
|
} else
|
|
grDispatcher::instance()->putSpr_rle_rot(Vect2i(xx, yy), _picture_size, _rle_data, check_flag(ALPHA_FLAG), mode, angle);
|
|
}
|
|
|
|
void qdSprite::redraw_rot(int x, int y, int z, float angle, const Vect2f &scale, int mode) const {
|
|
debugC(3, kDebugGraphics, "qdSprite::redraw_rot([%d, %d, %d], angle: %f, scale: [%f, %f], mode: %d)", x, y, z, angle, scale.x, scale.y, mode);
|
|
|
|
int xx = x;
|
|
int yy = y;
|
|
|
|
Vect2i delta = _picture_offset + _picture_size / 2 - _size / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
delta.x = -delta.x;
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
delta.y = -delta.y;
|
|
|
|
delta.x = round(float(delta.x) * scale.x);
|
|
delta.y = round(float(delta.y) * scale.y);
|
|
|
|
if (delta.x || delta.y) {
|
|
xx += round(float(delta.x) * cosf(angle) - float(delta.y) * sinf(angle));
|
|
yy += round(float(delta.x) * sinf(angle) + float(delta.y) * cosf(angle));
|
|
}
|
|
|
|
xx -= round(float(_picture_size.x / 2) * scale.x);
|
|
yy -= round(float(_picture_size.y / 2) * scale.y);
|
|
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
grDispatcher::instance()->putSpr_rot(Vect2i(xx, yy), _picture_size, _data, check_flag(ALPHA_FLAG), mode, angle, scale);
|
|
} else
|
|
grDispatcher::instance()->putSpr_rle_rot(Vect2i(xx, yy), _picture_size, _rle_data, check_flag(ALPHA_FLAG), mode, angle, scale);
|
|
}
|
|
|
|
void qdSprite::redraw(int x, int y, int z, float scale, int mode) const {
|
|
debugC(3, kDebugGraphics, "qdSprite::redraw([%d, %d, %d], scale: %f, mode: %d)", x, y, z, scale, mode);
|
|
|
|
int xx = x - round(float(size_x()) * scale) / 2;
|
|
int yy = y - round(float(size_y()) * scale) / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
xx += round(float(_size.x - _picture_offset.x - _picture_size.x) * scale);
|
|
else
|
|
xx += round(float(_picture_offset.x) * scale);
|
|
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
yy += round(float(_size.y - _picture_offset.y - _picture_size.y) * scale);
|
|
else
|
|
yy += round(float(_picture_offset.y) * scale);
|
|
|
|
#ifdef _GR_ENABLE_ZBUFFER
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
if (check_flag(ALPHA_FLAG))
|
|
grDispatcher::instance()->putSpr_a_z(xx, yy, z, _picture_size.x, _picture_size.y, _data, mode, scale);
|
|
else
|
|
grDispatcher::instance()->putSpr_z(xx, yy, z, _picture_size.x, _picture_size.y, _data, mode, scale);
|
|
} else
|
|
grDispatcher::instance()->putSpr_rle_z(xx, yy, z, _picture_size.x, _picture_size.y, _rle_data, mode, scale, check_flag(ALPHA_FLAG));
|
|
#else
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
if (check_flag(ALPHA_FLAG))
|
|
grDispatcher::instance()->putSpr_a(xx, yy, _picture_size.x, _picture_size.y, _data, mode, scale);
|
|
else
|
|
grDispatcher::instance()->putSpr(xx, yy, _picture_size.x, _picture_size.y, _data, mode, scale);
|
|
} else
|
|
grDispatcher::instance()->putSpr_rle(xx, yy, _picture_size.x, _picture_size.y, _rle_data, mode, scale, check_flag(ALPHA_FLAG));
|
|
#endif
|
|
}
|
|
|
|
void qdSprite::draw_mask(int x, int y, int z, uint32 mask_color, int mask_alpha, int mode) const {
|
|
int xx = x - size_x() / 2;
|
|
int yy = y - size_y() / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
xx += _size.x - _picture_offset.x - _picture_size.x;
|
|
else
|
|
xx += _picture_offset.x;
|
|
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
yy += _size.y - _picture_offset.y - _picture_size.y;
|
|
else
|
|
yy += _picture_offset.y;
|
|
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
if (check_flag(ALPHA_FLAG))
|
|
grDispatcher::instance()->putSprMask_a(xx, yy, _picture_size.x, _picture_size.y, _data, mask_color, mask_alpha, mode);
|
|
else
|
|
grDispatcher::instance()->putSprMask(xx, yy, _picture_size.x, _picture_size.y, _data, mask_color, mask_alpha, mode);
|
|
} else
|
|
grDispatcher::instance()->putSprMask_rle(xx, yy, _picture_size.x, _picture_size.y, _rle_data, mask_color, mask_alpha, mode, check_flag(ALPHA_FLAG));
|
|
}
|
|
|
|
void qdSprite::draw_mask(int x, int y, int z, uint32 mask_color, int mask_alpha, float scale, int mode) const {
|
|
int xx = x - round(float(size_x()) * scale) / 2;
|
|
int yy = y - round(float(size_y()) * scale) / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
xx += round(float(_size.x - _picture_offset.x - _picture_size.x) * scale);
|
|
else
|
|
xx += round(float(_picture_offset.x) * scale);
|
|
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
yy += round(float(_size.y - _picture_offset.y - _picture_size.y) * scale);
|
|
else
|
|
yy += round(float(_picture_offset.y) * scale);
|
|
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
if (check_flag(ALPHA_FLAG))
|
|
grDispatcher::instance()->putSprMask_a(xx, yy, _picture_size.x, _picture_size.y, _data, mask_color, mask_alpha, mode, scale);
|
|
else
|
|
grDispatcher::instance()->putSprMask(xx, yy, _picture_size.x, _picture_size.y, _data, mask_color, mask_alpha, mode, scale);
|
|
} else
|
|
grDispatcher::instance()->putSprMask_rle(xx, yy, _picture_size.x, _picture_size.y, _rle_data, mask_color, mask_alpha, mode, scale, check_flag(ALPHA_FLAG));
|
|
}
|
|
|
|
void qdSprite::draw_mask_rot(int x, int y, int z, float angle, uint32 mask_color, int mask_alpha, int mode) const {
|
|
int xx = x;
|
|
int yy = y;
|
|
|
|
Vect2i delta = _picture_offset + _picture_size / 2 - _size / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
delta.x = -delta.x;
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
delta.y = -delta.y;
|
|
|
|
if (delta.x || delta.y) {
|
|
xx += round(float(delta.x) * cosf(angle) - float(delta.y) * sinf(angle));
|
|
yy += round(float(delta.x) * sinf(angle) + float(delta.y) * cosf(angle));
|
|
}
|
|
|
|
xx -= _picture_size.x / 2;
|
|
yy -= _picture_size.y / 2;
|
|
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
grDispatcher::instance()->putSprMask_rot(Vect2i(xx, yy), _picture_size, _data, check_flag(ALPHA_FLAG), mask_color, mask_alpha, mode, angle);
|
|
} else
|
|
grDispatcher::instance()->putSprMask_rle_rot(Vect2i(xx, yy), _picture_size, _rle_data, check_flag(ALPHA_FLAG), mask_color, mask_alpha, mode, angle);
|
|
}
|
|
|
|
void qdSprite::draw_mask_rot(int x, int y, int z, float angle, uint32 mask_color, int mask_alpha, const Vect2f &scale, int mode) const {
|
|
int xx = x;
|
|
int yy = y;
|
|
|
|
Vect2i delta = _picture_offset + _picture_size / 2 - _size / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
delta.x = -delta.x;
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
delta.y = -delta.y;
|
|
|
|
delta.x = round(float(delta.x) * scale.x);
|
|
delta.y = round(float(delta.y) * scale.y);
|
|
|
|
if (delta.x || delta.y) {
|
|
xx += round(float(delta.x) * cosf(angle) - float(delta.y) * sinf(angle));
|
|
yy += round(float(delta.x) * sinf(angle) + float(delta.y) * cosf(angle));
|
|
}
|
|
|
|
xx -= round(float(_picture_size.x / 2) * scale.x);
|
|
yy -= round(float(_picture_size.y / 2) * scale.y);
|
|
|
|
if (!is_compressed()) {
|
|
if (!_data) return;
|
|
grDispatcher::instance()->putSprMask_rot(Vect2i(xx, yy), _picture_size, _data, check_flag(ALPHA_FLAG), mask_color, mask_alpha, mode, angle, scale);
|
|
} else
|
|
grDispatcher::instance()->putSprMask_rle_rot(Vect2i(xx, yy), _picture_size, _rle_data, check_flag(ALPHA_FLAG), mask_color, mask_alpha, mode, angle, scale);
|
|
}
|
|
|
|
void qdSprite::draw_contour(int x, int y, uint32 color, int mode) const {
|
|
int xx = x - size_x() / 2;
|
|
int yy = y - size_y() / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
xx += _size.x - _picture_offset.x - _picture_size.x;
|
|
else
|
|
xx += _picture_offset.x;
|
|
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
yy += _size.y - _picture_offset.y - _picture_size.y;
|
|
else
|
|
yy += _picture_offset.y;
|
|
|
|
if (is_compressed()) {
|
|
grDispatcher::instance()->drawSprContour(xx, yy, _picture_size.x, _picture_size.y, _rle_data, color, mode, check_flag(ALPHA_FLAG));
|
|
} else {
|
|
if (!_data) return;
|
|
if (check_flag(ALPHA_FLAG))
|
|
grDispatcher::instance()->drawSprContour_a(xx, yy, _picture_size.x, _picture_size.y, _data, color, mode);
|
|
else
|
|
grDispatcher::instance()->drawSprContour(xx, yy, _picture_size.x, _picture_size.y, _data, color, mode);
|
|
}
|
|
}
|
|
|
|
void qdSprite::draw_contour(int x, int y, uint32 color, float scale, int mode) const {
|
|
int xx = x - round(float(size_x()) * scale) / 2;
|
|
int yy = y - round(float(size_y()) * scale) / 2;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
xx += round(float(_size.x - _picture_offset.x - _picture_size.x) * scale);
|
|
else
|
|
xx += round(float(_picture_offset.x) * scale);
|
|
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
yy += round(float(_size.y - _picture_offset.y - _picture_size.y) * scale);
|
|
else
|
|
yy += round(float(_picture_offset.y) * scale);
|
|
|
|
if (!is_compressed()) {
|
|
if (check_flag(ALPHA_FLAG))
|
|
grDispatcher::instance()->drawSprContour_a(xx, yy, _picture_size.x, _picture_size.y, _data, color, mode, scale);
|
|
else
|
|
grDispatcher::instance()->drawSprContour(xx, yy, _picture_size.x, _picture_size.y, _data, color, mode, scale);
|
|
} else
|
|
grDispatcher::instance()->drawSprContour(xx, yy, _picture_size.x, _picture_size.y, _rle_data, color, mode, scale, check_flag(ALPHA_FLAG));
|
|
}
|
|
|
|
bool qdSprite::hit(int x, int y) const {
|
|
x += _size.x / 2;
|
|
y += _size.y / 2;
|
|
|
|
if (x < 0 || y < 0 || x >= _size.x || y >= _size.y) return false;
|
|
|
|
if (x >= _picture_offset.x && x < _picture_offset.x + _picture_size.x && y >= _picture_offset.y && y < _picture_offset.y + _picture_size.y) {
|
|
x -= _picture_offset.x;
|
|
y -= _picture_offset.y;
|
|
|
|
if (!is_compressed()) {
|
|
if (!_data) return false;
|
|
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
if (check_flag(ALPHA_FLAG)) {
|
|
if (reinterpret_cast<uint16 * >(_data)[(x + y * _picture_size.x) * 2 + 1] < 240) return true;
|
|
} else {
|
|
if (reinterpret_cast<uint16 * >(_data)[x + y * _picture_size.x]) return true;
|
|
}
|
|
break;
|
|
case GR_RGB888:
|
|
if (_data[(x + y * _picture_size.x) * 3] || _data[(x + y * _picture_size.x) * 3 + 1] || _data[(x + y * _picture_size.x) * 3 + 2]) return true;
|
|
break;
|
|
case GR_ARGB8888:
|
|
if (_data[(x + y * _picture_size.x) * 4 + 3] < 240) return true;
|
|
break;
|
|
}
|
|
} else {
|
|
uint32 pixel;
|
|
_rle_data->decode_pixel(x, y, pixel);
|
|
if (check_flag(ALPHA_FLAG)) {
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
if (reinterpret_cast<uint16 * >(&pixel)[1] < 240) return true;
|
|
return false;
|
|
case GR_RGB888:
|
|
case GR_ARGB8888:
|
|
if (reinterpret_cast<byte * >(&pixel)[3] < 240) return true;
|
|
return false;
|
|
}
|
|
} else {
|
|
if (pixel)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool qdSprite::hit(int x, int y, float scale) const {
|
|
x = round(float(x) / scale);
|
|
y = round(float(y) / scale);
|
|
|
|
return hit(x, y);
|
|
}
|
|
|
|
bool qdSprite::put_pixel(int x, int y, byte r, byte g, byte b) {
|
|
x -= _picture_offset.x;
|
|
y -= _picture_offset.y;
|
|
|
|
if ((x < 0) || (x >= _size.x) || (y < 0) || (y >= _size.y))
|
|
return false;
|
|
|
|
int bytes_per_pix;
|
|
uint16 word;
|
|
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
bytes_per_pix = 2;
|
|
word = grDispatcher::make_rgb565u(r, g, b);
|
|
_data[bytes_per_pix * (y * _size.x + x) + 0] = static_cast<byte>(word & 0x00FF);
|
|
_data[bytes_per_pix * (y * _size.x + x) + 1] = static_cast<byte>(word >> 8 & 0x00FF);
|
|
break;
|
|
case GR_RGB888:
|
|
bytes_per_pix = 3;
|
|
_data[bytes_per_pix * (y * _size.x + x) + 0] = b;
|
|
_data[bytes_per_pix * (y * _size.x + x) + 1] = g;
|
|
_data[bytes_per_pix * (y * _size.x + x) + 2] = r;
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void qdSprite::qda_load(Common::SeekableReadStream *fh, int version) {
|
|
free();
|
|
|
|
static char str[256];
|
|
|
|
_size.x = fh->readSint32LE();
|
|
_size.y = fh->readSint32LE();
|
|
_picture_size.x = fh->readSint32LE();
|
|
_picture_size.y = fh->readSint32LE();
|
|
_picture_offset.x = fh->readSint32LE();
|
|
_picture_offset.y = fh->readSint32LE();
|
|
_format = fh->readSint32LE();
|
|
int32 len = fh->readSint32LE();
|
|
|
|
int32 al_flag, compress_flag;
|
|
|
|
str[len] = 0;
|
|
fh->read(str, len);
|
|
set_file(str);
|
|
|
|
if (version >= 101) {
|
|
_flags = fh->readSint32LE();
|
|
al_flag = fh->readSint32LE();
|
|
compress_flag = fh->readSint32LE();
|
|
} else {
|
|
_flags = 0;
|
|
compress_flag = 0;
|
|
al_flag = fh->readSint32LE();
|
|
}
|
|
|
|
if (!compress_flag) {
|
|
if (version < 102) {
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
if (!al_flag) {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 2];
|
|
} else {
|
|
warning("qdSprite::qda_load(): al_flag is set, check the sprite picture"); // TODO)
|
|
_data = new byte[_picture_size.x * _picture_size.y * 4];
|
|
}
|
|
|
|
fh->read(_data, _picture_size.x * _picture_size.y * 2);
|
|
break;
|
|
case GR_RGB888:
|
|
if (!al_flag) {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 3];
|
|
} else {
|
|
warning("qdSprite::qda_load(): al_flag is set, check the sprite picture"); // TODO
|
|
_data = new byte[_picture_size.x * _picture_size.y * 4];
|
|
}
|
|
|
|
fh->read(_data, _picture_size.x * _picture_size.y * 3);
|
|
break;
|
|
case GR_ARGB8888:
|
|
_data = new byte[_picture_size.x * _picture_size.y * 4];
|
|
fh->read(_data, _picture_size.x * _picture_size.y * 4);
|
|
break;
|
|
}
|
|
if (al_flag) {
|
|
byte *alpha_data = new byte[_picture_size.x * _picture_size.y];
|
|
fh->read(alpha_data, _picture_size.x * _picture_size.y);
|
|
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555: {
|
|
byte *dp = _data + _picture_size.x * _picture_size.y * 4 - 4;
|
|
byte *sp = _data + _picture_size.x * _picture_size.y * 2 - 2;
|
|
byte *ap = alpha_data + _picture_size.x * _picture_size.y - 1;
|
|
|
|
for (int i = 0; i < _picture_size.x * _picture_size.y; i++) {
|
|
dp[0] = sp[0];
|
|
dp[1] = sp[1];
|
|
dp[2] = 0;
|
|
dp[3] = *ap--;
|
|
|
|
dp -= 4;
|
|
sp -= 2;
|
|
}
|
|
}
|
|
break;
|
|
case GR_RGB888: {
|
|
byte *dp = _data + _picture_size.x * _picture_size.y * 4 - 4;
|
|
byte *sp = _data + _picture_size.x * _picture_size.y * 3 - 3;
|
|
byte *ap = alpha_data + _picture_size.x * _picture_size.y - 1;
|
|
|
|
for (int i = 0; i < _picture_size.x * _picture_size.y; i++) {
|
|
dp[0] = sp[0];
|
|
dp[1] = sp[1];
|
|
dp[2] = sp[2];
|
|
dp[3] = *ap--;
|
|
|
|
dp -= 4;
|
|
sp -= 3;
|
|
}
|
|
|
|
_format = GR_ARGB8888;
|
|
}
|
|
break;
|
|
}
|
|
|
|
set_flag(ALPHA_FLAG);
|
|
delete [] alpha_data;
|
|
}
|
|
} else {
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
if (check_flag(ALPHA_FLAG)) {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 4];
|
|
fh->read(_data, _picture_size.x * _picture_size.y * 4);
|
|
} else {
|
|
_data = new byte[_picture_size.x * _picture_size.y * 2];
|
|
fh->read(_data, _picture_size.x * _picture_size.y * 2);
|
|
}
|
|
break;
|
|
case GR_RGB888:
|
|
_data = new byte[_picture_size.x * _picture_size.y * 3];
|
|
fh->read(_data, _picture_size.x * _picture_size.y * 3);
|
|
break;
|
|
case GR_ARGB8888:
|
|
_data = new byte[_picture_size.x * _picture_size.y * 4];
|
|
fh->read(_data, _picture_size.x * _picture_size.y * 4);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
_rle_data = new RLEBuffer;
|
|
_rle_data->load(fh);
|
|
}
|
|
}
|
|
|
|
bool qdSprite::crop() {
|
|
int left, top, right, bottom;
|
|
if (!get_edges_width(left, top, right, bottom)) return false;
|
|
return crop(left, top, right, bottom);
|
|
}
|
|
|
|
bool qdSprite::crop(int left, int top, int right, int bottom, bool store_offsets) {
|
|
int sx = _picture_size.x - left - right;
|
|
int sy = _picture_size.y - top - bottom;
|
|
|
|
if (sx == _picture_size.x && sy == _picture_size.y) return true;
|
|
|
|
int psz = 1;
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
psz = (check_flag(ALPHA_FLAG)) ? 4 : 2;
|
|
break;
|
|
case GR_RGB888:
|
|
psz = 3;
|
|
break;
|
|
case GR_ARGB8888:
|
|
psz = 4;
|
|
break;
|
|
}
|
|
|
|
int idx1 = 0;
|
|
int idx = left * psz + top * _picture_size.x * psz;
|
|
|
|
byte *data_new = new byte[sx * sy * psz];
|
|
for (int y = 0; y < sy; y++) {
|
|
memcpy(data_new + idx1, _data + idx, sx * psz);
|
|
idx += _picture_size.x * psz;
|
|
idx1 += sx * psz;
|
|
}
|
|
delete [] _data;
|
|
_data = data_new;
|
|
|
|
if (store_offsets) {
|
|
_picture_offset.x += left;
|
|
_picture_offset.y += top;
|
|
} else {
|
|
_size.x = _picture_offset.x + sx;
|
|
_size.y = _picture_offset.x + sy;
|
|
}
|
|
|
|
_picture_size.x = sx;
|
|
_picture_size.y = sy;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool qdSprite::undo_crop() {
|
|
if (!_data) return false;
|
|
|
|
if (_picture_size == _size) return false;
|
|
|
|
int psx = 1;
|
|
if (_format == GR_RGB565 || _format == GR_ARGB1555)
|
|
psx = (check_flag(ALPHA_FLAG)) ? 4 : 2;
|
|
if (_format == GR_RGB888) {
|
|
psx = 3;
|
|
drop_flag(ALPHA_FLAG);
|
|
}
|
|
if (_format == GR_ARGB8888)
|
|
psx = 4;
|
|
|
|
byte *new_data = new byte[_size.x * _size.y * psx];
|
|
memset(new_data, 0, _size.x * _size.y * psx);
|
|
|
|
if (check_flag(ALPHA_FLAG)) {
|
|
byte *p = (_format == GR_ARGB8888) ? new_data + 3 : new_data + 2;
|
|
for (int i = 0; i < _size.x * _size.y; i++) {
|
|
*p = 255;
|
|
p += 4;
|
|
}
|
|
}
|
|
|
|
byte *dp = _data;
|
|
byte *p = new_data + (_picture_offset.x + _picture_offset.y * _size.x) * psx;
|
|
|
|
for (int i = 0; i < _picture_size.y; i++) {
|
|
memcpy(p, dp, _picture_size.x * psx);
|
|
|
|
p += _size.x * psx;
|
|
dp += _picture_size.x * psx;
|
|
}
|
|
|
|
delete [] _data;
|
|
_data = new_data;
|
|
|
|
_picture_size = _size;
|
|
_picture_offset = Vect2i(0, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool qdSprite::get_edges_width(int &left, int &top, int &right, int &bottom) {
|
|
if (!_data) return false;
|
|
|
|
left = _picture_size.x - 1;
|
|
top = _picture_size.y - 1;
|
|
|
|
right = left;
|
|
bottom = top;
|
|
|
|
if (_format == GR_ARGB1555 || _format == GR_RGB565) {
|
|
if (check_flag(ALPHA_FLAG)) {
|
|
int idx = 0;
|
|
uint16 *data_ptr = reinterpret_cast<uint16 *>(_data);
|
|
for (int y = 0; y < _picture_size.y; y++) {
|
|
int x = 0;
|
|
while (x < _picture_size.x && data_ptr[(idx + x) * 2 + 1] == 255) x++;
|
|
if (x < left) left = x;
|
|
idx += _picture_size.x - 1;
|
|
|
|
x = 0;
|
|
while (x < _picture_size.x && data_ptr[(idx - x) * 2 + 1] == 255) x++;
|
|
if (x < right) right = x;
|
|
idx++;
|
|
}
|
|
|
|
idx = 0;
|
|
for (int x = 0; x < _picture_size.x; x++) {
|
|
int y = 0;
|
|
while (y < _picture_size.y && data_ptr[(idx + y * _picture_size.x) * 2 + 1] == 255) y++;
|
|
if (y < top) top = y;
|
|
|
|
y = 0;
|
|
while (y < _picture_size.y && data_ptr[(idx - y * _picture_size.x + (_picture_size.y - 1) * _picture_size.x) * 2 + 1] == 255) y++;
|
|
if (y < bottom) bottom = y;
|
|
idx++;
|
|
}
|
|
} else {
|
|
int idx = 0;
|
|
uint16 *data_ptr = reinterpret_cast<uint16 *>(_data);
|
|
for (int y = 0; y < _picture_size.y; y++) {
|
|
int x = 0;
|
|
while (x < _picture_size.x && !data_ptr[idx + x]) x++;
|
|
if (x < left) left = x;
|
|
idx += _picture_size.x - 1;
|
|
|
|
x = 0;
|
|
while (x < _picture_size.x && !data_ptr[idx - x]) x++;
|
|
if (x < right) right = x;
|
|
idx++;
|
|
}
|
|
|
|
idx = 0;
|
|
for (int x = 0; x < _picture_size.x; x++) {
|
|
int y = 0;
|
|
while (y < _picture_size.y && !data_ptr[idx + y * _picture_size.x]) y++;
|
|
if (y < top) top = y;
|
|
|
|
y = 0;
|
|
while (y < _picture_size.y && !data_ptr[idx - y * _picture_size.x + (_picture_size.y - 1) * _picture_size.x]) y++;
|
|
if (y < bottom) bottom = y;
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
if (_format == GR_RGB888) {
|
|
int idx = 0;
|
|
for (int y = 0; y < _picture_size.y; y++) {
|
|
int x = 0;
|
|
while (x < _picture_size.x && !(_data[idx + x * 3 + 0] + _data[idx + x * 3 + 1] + _data[idx + x * 3 + 2])) x++;
|
|
if (x < left) left = x;
|
|
idx += (_picture_size.x - 1) * 3;
|
|
|
|
x = 0;
|
|
while (x < _picture_size.x && !(_data[idx - x * 3 + 0] + _data[idx - x * 3 + 1] + _data[idx - x * 3 + 2])) x++;
|
|
if (x < right) right = x;
|
|
idx += 3;
|
|
}
|
|
|
|
idx = 0;
|
|
for (int x = 0; x < _picture_size.x; x++) {
|
|
int y = 0;
|
|
while (y < _picture_size.y && !(_data[idx + y * _picture_size.x * 3 + 0] + _data[idx + y * _picture_size.x * 3 + 1] + _data[idx + y * _picture_size.x * 3 + 2])) y++;
|
|
if (y < top) top = y;
|
|
|
|
y = 0;
|
|
while (y < _picture_size.y && !(_data[idx - y * _picture_size.x * 3 + (_picture_size.y - 1) * _picture_size.x * 3 + 0] + _data[idx - y * _picture_size.x * 3 + (_picture_size.y - 1) * _picture_size.x * 3 + 1] + _data[idx - y * _picture_size.x * 3 + (_picture_size.y - 1) * _picture_size.x * 3 + 2])) y++;
|
|
if (y < bottom) bottom = y;
|
|
idx += 3;
|
|
}
|
|
}
|
|
if (_format == GR_ARGB8888) {
|
|
int idx = 0;
|
|
for (int y = 0; y < _picture_size.y; y++) {
|
|
int x = 0;
|
|
while (x < _picture_size.x && _data[idx + x * 4 + 3] == 255) x++;
|
|
if (x < left) left = x;
|
|
idx += (_picture_size.x - 1) * 4;
|
|
|
|
x = 0;
|
|
while (x < _picture_size.x && _data[idx - x * 4 + 3] == 255) x++;
|
|
if (x < right) right = x;
|
|
idx += 4;
|
|
}
|
|
|
|
idx = 0;
|
|
for (int x = 0; x < _picture_size.x; x++) {
|
|
int y = 0;
|
|
while (y < _picture_size.y && _data[idx + y * _picture_size.x * 4 + 3] == 255) y++;
|
|
if (y < top) top = y;
|
|
|
|
y = 0;
|
|
while (y < _picture_size.y && _data[idx - y * _picture_size.x * 4 + (_picture_size.y - 1) * _picture_size.x * 4 + 3] == 255) y++;
|
|
if (y < bottom) bottom = y;
|
|
idx += 4;
|
|
}
|
|
}
|
|
|
|
if (left + right >= _size.x) {
|
|
left = 0;
|
|
right = _size.x - 1;
|
|
}
|
|
if (top + bottom >= _size.y) {
|
|
top = 0;
|
|
bottom = _size.y - 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32 qdSprite::data_size() const {
|
|
if (!is_compressed()) {
|
|
uint32 sz = _picture_size.x * _picture_size.y;
|
|
|
|
switch (_format) {
|
|
case GR_RGB565:
|
|
case GR_ARGB1555:
|
|
sz *= 2;
|
|
if (check_flag(ALPHA_FLAG)) sz *= 2;
|
|
break;
|
|
case GR_RGB888:
|
|
sz *= 3;
|
|
break;
|
|
case GR_ARGB8888:
|
|
sz *= 4;
|
|
break;
|
|
}
|
|
return sz;
|
|
} else
|
|
return _rle_data->size();
|
|
}
|
|
|
|
bool qdSprite::scale(float coeff_x, float coeff_y) {
|
|
static scl::C2PassScale<scl::CBilinearFilter> scale_engine;
|
|
static Std::vector<byte> temp_buffer(300 * 400 * 4, 0);
|
|
|
|
bool compress_flag = false;
|
|
|
|
if (is_compressed()) {
|
|
uncompress();
|
|
compress_flag = true;
|
|
}
|
|
|
|
undo_crop();
|
|
|
|
int sx = round(float(_picture_size.x) * coeff_x);
|
|
int sy = round(float(_picture_size.y) * coeff_y);
|
|
|
|
byte *src_data = _data;
|
|
|
|
if (_format == GR_RGB888) {
|
|
if ((int)temp_buffer.size() < _picture_size.x * _picture_size.y * 4)
|
|
temp_buffer.resize(_picture_size.x * _picture_size.y * 4);
|
|
|
|
src_data = &*temp_buffer.begin();
|
|
memset(src_data, 0, _picture_size.x * _picture_size.y * 4);
|
|
|
|
byte *p = src_data;
|
|
byte *dp = _data;
|
|
|
|
for (int i = 0; i < _picture_size.x * _picture_size.y; i++) {
|
|
p[0] = dp[0];
|
|
p[1] = dp[1];
|
|
p[2] = dp[2];
|
|
p[3] = 0;
|
|
|
|
p += 4;
|
|
dp += 3;
|
|
}
|
|
}
|
|
|
|
byte *dest_data = new byte[sx * sy * 4];
|
|
|
|
scale_engine.scale(reinterpret_cast<uint32 *>(src_data), _picture_size.x, _picture_size.y, reinterpret_cast<uint32 *>(dest_data), sx, sy);
|
|
|
|
delete [] _data;
|
|
|
|
if (_format == GR_RGB888) {
|
|
_data = new byte[sx * sy * 3];
|
|
|
|
byte *p = dest_data;
|
|
byte *dp = _data;
|
|
|
|
for (int i = 0; i < sx * sy; i++) {
|
|
dp[0] = p[0];
|
|
dp[1] = p[1];
|
|
dp[2] = p[2];
|
|
|
|
p += 4;
|
|
dp += 3;
|
|
}
|
|
|
|
delete [] dest_data;
|
|
} else {
|
|
_data = dest_data;
|
|
}
|
|
|
|
_picture_size.x = sx;
|
|
_picture_size.y = sy;
|
|
|
|
_size.x = round(float(_size.x) * coeff_x);
|
|
_size.y = round(float(_size.y) * coeff_y);
|
|
|
|
_picture_offset.x = round(float(_picture_offset.x) * coeff_x);
|
|
_picture_offset.y = round(float(_picture_offset.y) * coeff_y);
|
|
|
|
crop();
|
|
|
|
if (compress_flag)
|
|
if (!compress()) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Vect2i qdSprite::remove_edges() {
|
|
int left, top, right, bottom;
|
|
get_edges_width(left, top, right, bottom);
|
|
crop(left, top, right, bottom, false);
|
|
|
|
return Vect2i(left, top);
|
|
}
|
|
|
|
grScreenRegion qdSprite::screen_region(int mode, float scale) const {
|
|
int x, y;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL)
|
|
x = round(float(_size.x / 2 - _picture_offset.x - _picture_size.x / 2) * scale);
|
|
else
|
|
x = round(float(_picture_offset.x + _picture_size.x / 2 - _size.x / 2) * scale);
|
|
|
|
if (mode & GR_FLIP_VERTICAL)
|
|
y = round(float(_size.y / 2 - _picture_offset.y - _picture_size.y / 2) * scale);
|
|
else
|
|
y = round(float(_picture_offset.y + _picture_size.y / 2 - _size.y / 2) * scale);
|
|
|
|
int sx = round(float(_picture_size.x) * scale) + 4;
|
|
int sy = round(float(_picture_size.y) * scale) + 4;
|
|
|
|
return grScreenRegion(x, y, sx, sy);
|
|
}
|
|
} // namespace QDEngine
|