706 lines
22 KiB
C++
706 lines
22 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 "graphics/managed_surface.h"
|
|
#include "image/png.h"
|
|
|
|
#include "qdengine/qdengine.h"
|
|
#include "qdengine/system/graphics/gr_dispatcher.h"
|
|
#include "qdengine/system/graphics/gr_tile_animation.h"
|
|
|
|
|
|
namespace QDEngine {
|
|
|
|
CompressionProgressHandler grTileAnimation::_progressHandler;
|
|
void *grTileAnimation::_progressHandlerContext;
|
|
|
|
grTileAnimation::grTileAnimation() {
|
|
clear();
|
|
}
|
|
|
|
void grTileAnimation::clear() {
|
|
_hasAlpha = false;
|
|
|
|
_compression = TILE_UNCOMPRESSED;
|
|
|
|
_frameCount = 0;
|
|
_frameSize = Vect2i(0, 0);
|
|
_frameTileSize = Vect2i(0, 0);
|
|
|
|
_frameIndex.clear();
|
|
FrameIndex(_frameIndex).swap(_frameIndex);
|
|
|
|
_tileOffsets.clear();
|
|
TileOffsets(_tileOffsets).swap(_tileOffsets);
|
|
|
|
for (auto &i : _decompressedTiles) {
|
|
free((void *)i._value.data());
|
|
}
|
|
_decompressedTiles.clear();
|
|
|
|
_tileData.clear();
|
|
TileData(_tileData).swap(_tileData);
|
|
}
|
|
|
|
void grTileAnimation::init(int frame_count, const Vect2i &frame_size, bool alpha_flag) {
|
|
clear();
|
|
|
|
_hasAlpha = alpha_flag;
|
|
|
|
_frameSize = frame_size;
|
|
|
|
_frameTileSize.x = (frame_size.x + GR_TILE_SPRITE_SIZE_X / 2) / GR_TILE_SPRITE_SIZE_X;
|
|
_frameTileSize.y = (frame_size.y + GR_TILE_SPRITE_SIZE_Y / 2) / GR_TILE_SPRITE_SIZE_Y;
|
|
|
|
_frameIndex.reserve(frame_count * _frameTileSize.x * _frameTileSize.y);
|
|
|
|
_tileOffsets.reserve(frame_count * _frameTileSize.x * _frameTileSize.y + 1);
|
|
_tileOffsets.push_back(0);
|
|
|
|
_tileData.reserve(frame_count * _frameTileSize.x * _frameTileSize.y * GR_TILE_SPRITE_SIZE);
|
|
|
|
_frameCount = frame_count;
|
|
}
|
|
|
|
void grTileAnimation::compact() {
|
|
TileOffsets(_tileOffsets).swap(_tileOffsets);
|
|
TileData(_tileData).swap(_tileData);
|
|
debugC(3, kDebugLog, "Tile animation: %u Kbytes", (_frameIndex.size() + _tileData.size() + _tileOffsets.size()) * 4 / 1024);
|
|
}
|
|
|
|
bool grTileAnimation::compress(grTileCompressionMethod method) {
|
|
if (_compression != TILE_UNCOMPRESSED)
|
|
return false;
|
|
|
|
_compression = method;
|
|
|
|
TileData tile_data;
|
|
tile_data.reserve(_tileData.size());
|
|
|
|
TileOffsets tile_offsets;
|
|
tile_offsets.reserve(_tileOffsets.size());
|
|
tile_offsets.push_back(0);
|
|
|
|
TileData tile_vector = TileData(GR_TILE_SPRITE_SIZE * 4, 0);
|
|
|
|
int count = tileCount();
|
|
for (int i = 0; i < count; i++) {
|
|
if (_progressHandler) {
|
|
int percent_done = 100 * (i + 1) / count;
|
|
(*_progressHandler)(percent_done, _progressHandlerContext);
|
|
}
|
|
|
|
uint32 *data = &*_tileData.begin() + i * GR_TILE_SPRITE_SIZE;
|
|
|
|
uint32 offs = tile_offsets.back();
|
|
uint32 sz = grTileSprite::compress(data, &*tile_vector.begin(), method);
|
|
|
|
for (uint32 j = 0; j < sz; j++)
|
|
tile_data.push_back(tile_vector[j]);
|
|
|
|
tile_offsets.push_back(offs + sz);
|
|
}
|
|
|
|
_tileData.swap(tile_data);
|
|
_tileOffsets.swap(tile_offsets);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
grTileSprite grTileAnimation::getTile(int tile_index) const {
|
|
debugC(3, kDebugTemp, "The tile index is given by %d", tile_index);
|
|
static uint32 tile_buf[GR_TILE_SPRITE_SIZE];
|
|
|
|
if (_decompressedTiles.contains(tile_index)) {
|
|
return _decompressedTiles[tile_index];
|
|
}
|
|
|
|
switch (_compression) {
|
|
case TILE_UNCOMPRESSED:
|
|
return grTileSprite(&*_tileData.begin() + _tileOffsets[tile_index]);
|
|
default:
|
|
if (tile_index >= (int)_tileOffsets.size()) {
|
|
warning("grTileAnimation::getTile(): Too big tile index %d >= %d", tile_index, _tileOffsets.size());
|
|
break;
|
|
}
|
|
if (_tileOffsets[tile_index] >= _tileData.size()) {
|
|
warning("grTileAnimation::getTile(): Too big tile offset %d (%d >= %d)", tile_index, _tileOffsets[tile_index], _tileData.size());
|
|
break;
|
|
}
|
|
if (!grTileSprite::uncompress(&*_tileData.begin() + _tileOffsets[tile_index], GR_TILE_SPRITE_SIZE, tile_buf, _compression)) {
|
|
warning("Unknown compression algorithm");
|
|
}
|
|
}
|
|
|
|
uint32 *tempBuf = new uint32[GR_TILE_SPRITE_SIZE];
|
|
memcpy(tempBuf, tile_buf, GR_TILE_SPRITE_SIZE * sizeof(uint32));
|
|
|
|
_decompressedTiles[tile_index] = grTileSprite(tempBuf);
|
|
return _decompressedTiles[tile_index];
|
|
}
|
|
|
|
void grTileAnimation::addFrame(const uint32 *frame_data) {
|
|
TileData tile_vector = TileData(GR_TILE_SPRITE_SIZE, 0);
|
|
TileData tile_vector2 = TileData(GR_TILE_SPRITE_SIZE * 4, 0);
|
|
|
|
if (_progressHandler) {
|
|
int percent_done = 100 * (_frameIndex.size() / (_frameTileSize.x * _frameTileSize.y) + 1) / (_frameCount ? _frameCount : 1);
|
|
(*_progressHandler)(percent_done, _progressHandlerContext);
|
|
}
|
|
|
|
for (int i = 0; i < _frameTileSize.y; i++) {
|
|
for (int j = 0; j < _frameTileSize.x; j++) {
|
|
Common::fill(tile_vector.begin(), tile_vector.end(), 0);
|
|
|
|
const uint32 *data_ptr = frame_data + j * GR_TILE_SPRITE_SIZE_X
|
|
+ i * GR_TILE_SPRITE_SIZE_Y * _frameSize.x;
|
|
|
|
uint32 *tile_ptr = &tile_vector[0];
|
|
for (int y = 0; y < GR_TILE_SPRITE_SIZE_Y; y++) {
|
|
if (y + i * GR_TILE_SPRITE_SIZE_Y >= _frameSize.y) break;
|
|
|
|
for (int x = 0; x < GR_TILE_SPRITE_SIZE_X; x++) {
|
|
if (x + j * GR_TILE_SPRITE_SIZE_X >= _frameSize.x) break;
|
|
tile_ptr[x] = data_ptr[x];
|
|
}
|
|
|
|
data_ptr += _frameSize.x;
|
|
tile_ptr += GR_TILE_SPRITE_SIZE_X;
|
|
}
|
|
|
|
int tile_id = -1;
|
|
int tile_count = tileCount();
|
|
for (int tile_idx = 0; tile_idx < tile_count; tile_idx++) {
|
|
grTileSprite tile = getTile(tile_idx);
|
|
if (tile == grTileSprite(&tile_vector[0])) {
|
|
tile_id = tile_idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tile_id == -1) {
|
|
uint32 sz = GR_TILE_SPRITE_SIZE;
|
|
uint32 offs = _tileOffsets.back();
|
|
|
|
for (auto &it : tile_vector)
|
|
_tileData.push_back(it);
|
|
|
|
_tileOffsets.push_back(offs + sz);
|
|
_frameIndex.push_back(tile_count);
|
|
} else
|
|
_frameIndex.push_back(tile_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool grTileAnimation::load(Common::SeekableReadStream *fh, int version) {
|
|
int dL = (version >= 105) ? 2 : 7;
|
|
|
|
debugC(dL, kDebugLoad, "grTileAnimation::load(): pos start: %d", (int)fh->pos());
|
|
|
|
_frameCount = fh->readSint32LE();
|
|
_frameSize.x = fh->readSint32LE();
|
|
_frameSize.y = fh->readSint32LE();
|
|
_frameTileSize.x = fh->readSint32LE();
|
|
_frameTileSize.y = fh->readSint32LE();
|
|
uint32 size = fh->readUint32LE();
|
|
|
|
debugC(dL, kDebugLoad, "grTileAnimation::load(): frameCount: %d, frame: %d x %d, tile: %d x %d, comp: %d", _frameCount, _frameSize.x, _frameSize.y,
|
|
_frameTileSize.x, _frameTileSize.y, size);
|
|
|
|
_compression = grTileCompressionMethod(size);
|
|
|
|
if (version >= 105) {
|
|
size = fh->readUint32LE();
|
|
_scaleArray.resize(size);
|
|
|
|
debugC(dL, kDebugLoad, "grTileAnimation::load(): pos: %d _scaleArray size: %u", (int)fh->pos() - 4, size);
|
|
|
|
debugCN(dL + 1, kDebugLoad, " ");
|
|
|
|
for (uint i = 0; i < size; i++) {
|
|
float scale = fh->readFloatLE();
|
|
|
|
addScale(i, scale);
|
|
|
|
debugCN(dL + 1, kDebugLoad, " %f, { %d x %d, [%d x %d], tiles: %d } ", _scaleArray[i]._scale,
|
|
_scaleArray[i]._frameSize.x, _scaleArray[i]._frameSize.y, _scaleArray[i]._frameTileSize.x,
|
|
_scaleArray[i]._frameTileSize.y, _scaleArray[i]._frameStart);
|
|
}
|
|
debugCN(dL + 1, kDebugLoad, "\n");
|
|
}
|
|
|
|
_frameSizeArray.resize(_frameCount);
|
|
|
|
if (version < 106) {
|
|
for (int i = 0; i < _frameCount; i++)
|
|
_frameSizeArray[i] = _frameSize;
|
|
} else {
|
|
debugC(dL, kDebugLoad, "grTileAnimation::load(): pos: %d _frameSizeArray size: %u", (int)fh->pos() - 4, _frameCount);
|
|
|
|
debugCN(dL + 1, kDebugLoad, " ");
|
|
|
|
for (int i = 0; i < _frameCount; i++) {
|
|
_frameSizeArray[i].x = fh->readUint32LE();
|
|
_frameSizeArray[i].y = fh->readUint32LE();
|
|
|
|
debugCN(dL + 1, kDebugLoad, " %d x %d, ", _frameSizeArray[i].x, _frameSizeArray[i].y);
|
|
}
|
|
debugCN(dL + 1, kDebugLoad, "\n");
|
|
}
|
|
|
|
size = fh->readUint32LE();
|
|
_frameIndex.resize(size);
|
|
debugC(dL, kDebugLoad, "grTileAnimation::load(): pos: %d _frameIndex size: %u", (int)fh->pos() - 4, size);
|
|
|
|
debugCN(dL + 2, kDebugLoad, " ");
|
|
for (uint i = 0; i < size; i++) {
|
|
_frameIndex[i] = fh->readUint32LE();
|
|
debugCN(dL + 2, kDebugLoad, " %5d ", _frameIndex[i]);
|
|
|
|
if ((i + 1) % 20 == 0)
|
|
debugCN(dL + 2, kDebugLoad, "\n ");
|
|
}
|
|
debugCN(dL + 2, kDebugLoad, "\n");
|
|
|
|
size = fh->readUint32LE();
|
|
|
|
debugC(dL, kDebugLoad, "grTileAnimation::load(): pos: %d _tileOffsets size: %u", (int)fh->pos() - 4, size);
|
|
|
|
_tileOffsets.resize(size);
|
|
|
|
debugCN(dL + 2, kDebugLoad, " ");
|
|
for (uint i = 0; i < size; i++) {
|
|
_tileOffsets[i] = fh->readUint32LE();
|
|
debugCN(dL + 2, kDebugLoad, " %6d ", _tileOffsets[i]);
|
|
|
|
if ((i + 1) % 20 == 0)
|
|
debugCN(dL + 2, kDebugLoad, "\n ");
|
|
}
|
|
debugCN(dL + 2, kDebugLoad, "\n");
|
|
|
|
size = fh->readUint32LE();
|
|
|
|
debugC(dL, kDebugLoad, "grTileAnimation::load(): pos: %d _tileData size: %u", (int)fh->pos() - 4, size);
|
|
|
|
_tileData.resize(size);
|
|
|
|
for (uint i = 0; i < size; i++)
|
|
_tileData[i] = fh->readUint32LE();
|
|
|
|
debugC(dL + 1, kDebugLoad, " --> grTileAnimation::load(): pos: %d remaining: %d", (int)fh->pos(), (int)(fh->size() - fh->pos()));
|
|
|
|
return true;
|
|
}
|
|
|
|
void grTileAnimation::drawFrame(const Vect2i &position, int32 frame_index, int32 mode, int closest_scale) const {
|
|
debugC(3, kDebugGraphics, "grTileAnimation::drawFrame([%d, %d], frame: %d, mode: %d, scale_idx: %d)", position.x, position.y, frame_index, mode, closest_scale);
|
|
|
|
Vect2i frameSize = _frameSize;
|
|
Vect2i frameTileSize = _frameTileSize;
|
|
int frameStart = 0;
|
|
|
|
if (closest_scale != -1) {
|
|
frameSize = _scaleArray[closest_scale]._frameSize;
|
|
frameTileSize = _scaleArray[closest_scale]._frameTileSize;
|
|
frameStart = _scaleArray[closest_scale]._frameStart;
|
|
}
|
|
|
|
int xx = position.x - frameSize.x / 2;
|
|
int yy = position.y - frameSize.y / 2;
|
|
|
|
int32 dx = GR_TILE_SPRITE_SIZE_X;
|
|
int32 dy = GR_TILE_SPRITE_SIZE_Y;
|
|
|
|
if (mode & GR_FLIP_HORIZONTAL) {
|
|
xx += frameSize.x - GR_TILE_SPRITE_SIZE_X;
|
|
dx = -dx;
|
|
}
|
|
if (mode & GR_FLIP_VERTICAL) {
|
|
yy += frameSize.y - GR_TILE_SPRITE_SIZE_Y;
|
|
dy = -dy;
|
|
}
|
|
|
|
// grDispatcher::instance()->Rectangle(position.x - _frameSize.x/2, position.y - _frameSize.y/2, _frameSize.x, _frameSize.y, 0xFFFFF, 0, GR_OUTLINED);
|
|
|
|
const uint32 *index_ptr = &_frameIndex[frameStart] + frameTileSize.x * frameTileSize.y * frame_index;
|
|
|
|
int x = xx, y = yy;
|
|
for (int32 i = 0; i < frameTileSize.y; i++) {
|
|
x = xx;
|
|
|
|
for (int32 j = 0; j < frameTileSize.x; j++) {
|
|
grDispatcher::instance()->putTileSpr(x, y, getTile(*index_ptr++), _hasAlpha, mode);
|
|
x += dx;
|
|
}
|
|
|
|
y += dy;
|
|
}
|
|
}
|
|
|
|
void grTileAnimation::drawFrame(const Vect2i &position, int frame_index, float angle, int mode) const {
|
|
debugC(3, kDebugGraphics, "grTileAnimation::drawFrame([%d, %d], frame: %d, angle: %f, scale: %d)", position.x, position.y, frame_index, angle, mode);
|
|
|
|
byte *buf = decode_frame_data(frame_index, -1);
|
|
Vect2i pos = position - _frameSize / 2;
|
|
|
|
grDispatcher::instance()->putSpr_rot(pos, _frameSize, buf, _hasAlpha, mode, angle);
|
|
}
|
|
|
|
void grTileAnimation::drawFrame(const Vect2i &position, int frame_index, float angle, const Vect2f &scale, int mode) const {
|
|
debugC(3, kDebugGraphics, "grTileAnimation::drawFrame([%d, %d], frame: %d, angle: %f, scale: [%f, %f], mode: %d)", position.x, position.y, frame_index, angle, scale.x, scale.y, mode);
|
|
|
|
byte *buf = decode_frame_data(frame_index, -1);
|
|
Vect2i pos = position - _frameSize / 2;
|
|
|
|
grDispatcher::instance()->putSpr_rot(pos, _frameSize, buf, _hasAlpha, mode, angle, scale);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//// New version 105 & 106 code
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
grTileSprite grTileAnimation::getFrameTile(int frame_number, int tile_index) const {
|
|
return getTile(_frameIndex[tile_index + frame_number * _frameTileSize.x * _frameTileSize.y]);
|
|
}
|
|
|
|
bool grTileAnimation::hit(int frame_number, Vect2i &pos) const {
|
|
int x = _frameSize.x / 2 + pos.x;
|
|
int y = _frameSize.y / 2 + pos.y;
|
|
|
|
if (x < 0 || x >= _frameSize.x || y < 0 || y >= _frameSize.y)
|
|
return false;
|
|
|
|
const byte *tile = (const byte *)getFrameTile(frame_number, x / 16 + y / 16 * _frameTileSize.x).data();
|
|
|
|
return tile[64 * (y % 16) + 4 * (x % 16) + 3] < 0xC8u;
|
|
}
|
|
|
|
void grTileAnimation::drawFrame_scale(const Vect2i &position, int frame_index, float scale, int mode) const {
|
|
debugC(3, kDebugGraphics, "grTileAnimation::drawFrame_scale([%d, %d], frame: %d, scale: %f, mode: %d)", position.x, position.y, frame_index, scale, mode);
|
|
|
|
int closest_scale = find_closest_scale(&scale);
|
|
|
|
if (wasFrameSizeChanged(frame_index, closest_scale, scale)) {
|
|
byte *data = decode_frame_data(frame_index, closest_scale);
|
|
|
|
Vect2i frameSize;
|
|
|
|
if (closest_scale == -1)
|
|
frameSize = _frameSize;
|
|
else
|
|
frameSize =_scaleArray[closest_scale]._frameSize;
|
|
|
|
int x = position.x - round(float(frameSize.x) * scale) / 2;
|
|
int y = position.y - round(float(frameSize.y) * scale) / 2;
|
|
|
|
grDispatcher::instance()->putSpr_a(x, y, frameSize.x, frameSize.y, data, mode, scale);
|
|
} else {
|
|
drawFrame(position, frame_index, mode, closest_scale);
|
|
}
|
|
}
|
|
|
|
void grTileAnimation::drawMask(const Vect2i &pos, int frame_index, uint32 mask_colour, int mask_alpha, int mode, int closest_scale) const {
|
|
Vect2i frameSize;
|
|
|
|
if (closest_scale == -1)
|
|
frameSize = _frameSize;
|
|
else
|
|
frameSize =_scaleArray[closest_scale]._frameSize;
|
|
|
|
byte *buf = decode_frame_data(frame_index, closest_scale);
|
|
|
|
grDispatcher::instance()->putSprMask_a(pos.x - frameSize.x / 2, pos.y - frameSize.y / 2, frameSize.x, frameSize.y, buf, mask_colour, mask_alpha, mode);
|
|
}
|
|
|
|
void grTileAnimation::drawMask_scale(const Vect2i &pos, int frame_index, uint32 mask_colour, int mask_alpha, float scale, int mode) const {
|
|
int closest_scale = find_closest_scale(&scale);
|
|
|
|
if (wasFrameSizeChanged(frame_index, closest_scale, scale)) {
|
|
byte *buf = decode_frame_data(frame_index, closest_scale);
|
|
|
|
Vect2i frameSize;
|
|
|
|
if (closest_scale == -1)
|
|
frameSize = _frameSize;
|
|
else
|
|
frameSize =_scaleArray[closest_scale]._frameSize;
|
|
|
|
int x = pos.x - (int)((float)(frameSize.x / 2) * scale);
|
|
int y = pos.y - (int)((float)(frameSize.y / 2) * scale);
|
|
|
|
grDispatcher::instance()->putSprMask_a(x, y, frameSize.x, frameSize.y, buf, mask_colour, mask_alpha, mode, scale);
|
|
} else {
|
|
drawMask(pos, frame_index, mask_colour, mask_alpha, mode, closest_scale);
|
|
}
|
|
}
|
|
|
|
void grTileAnimation::drawMask_rot(const Vect2i &pos, int frame_index, uint32 mask_colour, int mask_alpha, float angle, int mode) const {
|
|
byte *buf = decode_frame_data(frame_index, -1);
|
|
|
|
grDispatcher::instance()->putSprMask_rot(Vect2i(pos.x - _frameSize.x / 2, pos.y - _frameSize.y / 2), _frameSize, buf, _hasAlpha, mask_colour, mask_alpha, mode, angle);
|
|
}
|
|
|
|
void grTileAnimation::drawMask_rot(const Vect2i &pos, int frame_index, uint32 mask_colour, int mask_alpha, float angle, Vect2f scale, int mode) const {
|
|
byte *buf = decode_frame_data(frame_index, -1);
|
|
|
|
int x = pos.x - (int)((float)(_frameSize.x / 2) * scale.x);
|
|
int y = pos.y - (int)((float)(_frameSize.y / 2) * scale.y);
|
|
|
|
grDispatcher::instance()->putSprMask_rot(Vect2i(x, y), _frameSize, buf, _hasAlpha, mask_colour, mask_alpha, mode, angle, scale);
|
|
}
|
|
|
|
void grTileAnimation::drawContour(const Vect2i &pos, int frame_index, uint32 color, int mode, int closest_scale) const {
|
|
Vect2i frameSize;
|
|
|
|
if (closest_scale == -1)
|
|
frameSize = _frameSize;
|
|
else
|
|
frameSize =_scaleArray[closest_scale]._frameSize;
|
|
|
|
byte *buf = decode_frame_data(frame_index, closest_scale);
|
|
|
|
grDispatcher::instance()->drawSprContour_a(pos.x - frameSize.x / 2, pos.y - frameSize.y / 2, frameSize.x, frameSize.y, buf, color, mode);
|
|
}
|
|
|
|
void grTileAnimation::drawContour(const Vect2i &pos, int frame_index, uint32 color, float scale, int mode) const {
|
|
int closest_scale = find_closest_scale(&scale);
|
|
|
|
if (wasFrameSizeChanged(frame_index, closest_scale, scale)) {
|
|
byte *data = decode_frame_data(frame_index, closest_scale);
|
|
|
|
Vect2i frameSize;
|
|
|
|
if (closest_scale == -1)
|
|
frameSize = _frameSize;
|
|
else
|
|
frameSize =_scaleArray[closest_scale]._frameSize;
|
|
|
|
int x = pos.x - (int)((float)(frameSize.x / 2) * scale);
|
|
int y = pos.y - (int)((float)(frameSize.y / 2) * scale);
|
|
|
|
grDispatcher::instance()->drawSprContour_a(x, y, frameSize.x, frameSize.y, data, color, mode, scale);
|
|
} else {
|
|
drawContour(pos, frame_index, color, mode, closest_scale);
|
|
}
|
|
}
|
|
|
|
void grTileAnimation::addScale(int i, float scale) {
|
|
_scaleArray[i]._scale = scale;
|
|
_scaleArray[i]._frameSize.x = round((double)_frameSize.x * scale);
|
|
_scaleArray[i]._frameSize.y = round((double)_frameSize.y * scale);
|
|
_scaleArray[i]._frameTileSize.x = (_scaleArray[i]._frameSize.x + 15) / 16;
|
|
_scaleArray[i]._frameTileSize.y = (_scaleArray[i]._frameSize.y + 15) / 16;
|
|
|
|
if (i == 0)
|
|
_scaleArray[i]._frameStart = _frameTileSize.x * _frameTileSize.y * _frameCount;
|
|
else
|
|
_scaleArray[i]._frameStart = _scaleArray[i - 1]._frameStart
|
|
+ _frameCount * _scaleArray[i - 1]._frameTileSize.y * _scaleArray[i - 1]._frameTileSize.x;
|
|
}
|
|
|
|
byte *grTileAnimation::decode_frame_data(int frame_index, int closest_scale) const {
|
|
Vect2i frameSize;
|
|
|
|
if (closest_scale == -1)
|
|
frameSize = _frameSize;
|
|
else
|
|
frameSize = _scaleArray[closest_scale]._frameSize;
|
|
|
|
Vect2i frameTileSize;
|
|
if (closest_scale == -1)
|
|
frameTileSize = _frameTileSize;
|
|
else
|
|
frameTileSize = _scaleArray[closest_scale]._frameTileSize;
|
|
|
|
int frameStart;
|
|
if (closest_scale == -1)
|
|
frameStart = 0;
|
|
else
|
|
frameStart = _scaleArray[closest_scale]._frameStart;
|
|
|
|
byte *buf = (byte *)grDispatcher::instance()->temp_buffer(frameSize.x * frameSize.y * 4);
|
|
|
|
const uint32 *index_ptr = &_frameIndex[frameStart] + frameTileSize.x * frameTileSize.y * frame_index;
|
|
|
|
for (int i = 0; i < frameTileSize.y; i++) {
|
|
for (int j = 0; j < frameTileSize.x; j++) {
|
|
byte *buf_ptr = buf + (i * frameSize.x * GR_TILE_SPRITE_SIZE_Y + j * GR_TILE_SPRITE_SIZE_X) * 4;
|
|
|
|
const byte *data_ptr = (const byte *)getTile(*index_ptr++).data();
|
|
int dx = MIN(frameSize.x - j * GR_TILE_SPRITE_SIZE_X, GR_TILE_SPRITE_SIZE_X) * 4;
|
|
int dy = MIN(frameSize.y - i * GR_TILE_SPRITE_SIZE_Y, GR_TILE_SPRITE_SIZE_Y);
|
|
for (int k = 0; k < dy; k++) {
|
|
memcpy(buf_ptr, data_ptr, dx);
|
|
data_ptr += GR_TILE_SPRITE_SIZE_X * 4;
|
|
buf_ptr += frameSize.x * 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
int grTileAnimation::find_closest_scale(float *scale) const {
|
|
int idx = -1;
|
|
float temp = 1.0;
|
|
|
|
for (uint i = 0; i < _scaleArray.size(); i++) {
|
|
if (fabs(*scale - _scaleArray[i]._scale) < fabs(*scale - temp)) {
|
|
idx = i;
|
|
temp = _scaleArray[i]._scale;
|
|
}
|
|
}
|
|
|
|
if (idx != -1)
|
|
*scale = *scale / temp;
|
|
|
|
return idx;
|
|
}
|
|
|
|
bool grTileAnimation::wasFrameSizeChanged(int frame_index, int scaleIdx, float scale) const {
|
|
int sx = _frameSizeArray[frame_index].x;
|
|
int sy = _frameSizeArray[frame_index].y;
|
|
|
|
float newScale;
|
|
|
|
if (scaleIdx == -1)
|
|
newScale = 1.0;
|
|
else
|
|
newScale = _scaleArray[scaleIdx]._scale;
|
|
|
|
if ((int)((float)sx * newScale * scale) == sx &&
|
|
(int)((float)sy * newScale * scale) == sy)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Graphics::ManagedSurface *grTileAnimation::dumpFrameTiles(int frame_index, float scale) const {
|
|
int closest_scale = find_closest_scale(&scale);
|
|
|
|
Vect2i frameSize;
|
|
|
|
if (closest_scale == -1)
|
|
frameSize = _frameSize;
|
|
else
|
|
frameSize = _scaleArray[closest_scale]._frameSize;
|
|
|
|
Vect2i frameTileSize;
|
|
if (closest_scale == -1)
|
|
frameTileSize = _frameTileSize;
|
|
else
|
|
frameTileSize = _scaleArray[closest_scale]._frameTileSize;
|
|
|
|
int frameStart;
|
|
if (closest_scale == -1)
|
|
frameStart = 0;
|
|
else
|
|
frameStart = _scaleArray[closest_scale]._frameStart;
|
|
|
|
int w = frameTileSize.x * (GR_TILE_SPRITE_SIZE_X + 1);
|
|
int h = frameTileSize.y * (GR_TILE_SPRITE_SIZE_Y + 1);
|
|
|
|
Graphics::ManagedSurface *dstSurf = new Graphics::ManagedSurface(w, h, g_engine->_pixelformat);
|
|
|
|
int idx = frameStart + frameTileSize.x * frameTileSize.y * frame_index;
|
|
int dx = grDispatcher::instance()->pixel_format() == GR_RGB565 ? 2 : 4;
|
|
|
|
for (int i = 0; i < frameTileSize.y; i++) {
|
|
for (int j = 0; j < frameTileSize.x; j++) {
|
|
if (idx >= (int)_frameIndex.size()) {
|
|
warning("grTileAnimation::dumpFrameTiles(): overflow of frame index (%d > %d)", idx, _frameIndex.size());
|
|
break;
|
|
}
|
|
|
|
const byte *src = (const byte *)getTile(_frameIndex[idx++]).data();
|
|
|
|
for (int yy = 0; yy < GR_TILE_SPRITE_SIZE_Y; yy++) {
|
|
byte *dst = (byte *)dstSurf->getBasePtr(j * (GR_TILE_SPRITE_SIZE_X + 1), i * (GR_TILE_SPRITE_SIZE_Y + 1) + yy);
|
|
|
|
for (int xx = 0; xx < GR_TILE_SPRITE_SIZE_X; xx++) {
|
|
grDispatcher::instance()->setPixelFast(dst, grDispatcher::instance()->make_rgb(src[2], src[1], src[0]));
|
|
dst += dx;
|
|
src += 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return dstSurf;
|
|
}
|
|
|
|
|
|
Graphics::ManagedSurface *grTileAnimation::dumpTiles(int tilesPerRow) const {
|
|
int w = tilesPerRow;
|
|
int h = (_tileOffsets.size() + tilesPerRow - 1) / tilesPerRow;
|
|
|
|
Graphics::ManagedSurface *dstSurf = new Graphics::ManagedSurface(w * (GR_TILE_SPRITE_SIZE_X + 1), h * (GR_TILE_SPRITE_SIZE_Y + 1), g_engine->_pixelformat);
|
|
|
|
int index = 0;
|
|
|
|
int x = 0, y = 0;
|
|
for (int32 i = 0; i < h; i++) {
|
|
x = 0;
|
|
|
|
for (int32 j = 0; j < w; j++) {
|
|
grDispatcher::instance()->putTileSpr(x, y, getTile(index++), _hasAlpha, 0, dstSurf, false);
|
|
x += GR_TILE_SPRITE_SIZE_X + 1;
|
|
|
|
if (index >= (int)_tileOffsets.size())
|
|
break;
|
|
}
|
|
|
|
if (index >= (int)_tileOffsets.size())
|
|
break;
|
|
|
|
y += GR_TILE_SPRITE_SIZE_X + 1;
|
|
}
|
|
|
|
return dstSurf;
|
|
}
|
|
|
|
void grTileAnimation::dumpTiles(Common::Path basename, int tilesPerRow) const {
|
|
Common::Path path = Common::Path(Common::String::format("dumps/%s.tiles.png", transCyrillic(basename.baseName())));
|
|
|
|
Common::DumpFile bitmapFile;
|
|
if (!bitmapFile.open(path, true)) {
|
|
warning("Cannot dump tile into file '%s'", path.toString().c_str());
|
|
return;
|
|
}
|
|
|
|
Graphics::ManagedSurface *dstSurf = dumpTiles(tilesPerRow);
|
|
|
|
|
|
Image::writePNG(bitmapFile, *(dstSurf->surfacePtr()));
|
|
bitmapFile.close();
|
|
|
|
warning("Dumped tile %s of %d x %d", path.toString().c_str(), dstSurf->w, dstSurf->h);
|
|
|
|
delete dstSurf;
|
|
}
|
|
|
|
} // namespace QDEngine
|