2018-10-20 02:37:03 +00:00
|
|
|
/* 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.
|
|
|
|
*
|
2021-12-26 17:47:58 +00:00
|
|
|
* 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.
|
2018-10-20 02:37:03 +00:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
2021-12-26 17:47:58 +00:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2018-10-20 02:37:03 +00:00
|
|
|
* 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
|
2021-12-26 17:47:58 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2018-10-20 02:37:03 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-11-14 03:47:07 +00:00
|
|
|
#include "glk/picture.h"
|
2018-11-19 05:40:23 +00:00
|
|
|
#include "glk/glk.h"
|
2018-11-23 23:35:13 +00:00
|
|
|
#include "glk/raw_decoder.h"
|
2018-11-19 05:40:23 +00:00
|
|
|
#include "glk/screen.h"
|
2018-11-19 04:29:14 +00:00
|
|
|
#include "common/file.h"
|
|
|
|
#include "image/jpeg.h"
|
|
|
|
#include "image/png.h"
|
2018-10-20 02:37:03 +00:00
|
|
|
|
2018-11-14 04:05:59 +00:00
|
|
|
namespace Glk {
|
2018-10-20 02:37:03 +00:00
|
|
|
|
2019-08-02 05:04:36 +00:00
|
|
|
Pictures::Pictures() : _refCount(0) {
|
|
|
|
Common::File f;
|
|
|
|
if (f.open("apal")) {
|
|
|
|
while (f.pos() < f.size())
|
2020-08-29 20:56:30 +00:00
|
|
|
_adaptivePics.push_back(
|
|
|
|
Common::String::format("%u", f.readUint32BE()));
|
2019-08-02 05:04:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-19 04:29:14 +00:00
|
|
|
void Pictures::clear() {
|
|
|
|
for (uint idx = 0; idx < _store.size(); ++idx) {
|
2019-01-05 01:12:30 +00:00
|
|
|
if (_store[idx]._picture)
|
|
|
|
_store[idx]._picture->decrement();
|
|
|
|
if (_store[idx]._scaled)
|
|
|
|
_store[idx]._scaled->decrement();
|
2018-11-19 04:29:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_store.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pictures::increment() {
|
|
|
|
++_refCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pictures::decrement() {
|
|
|
|
if (_refCount > 0 && --_refCount == 0)
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
2020-08-29 20:56:30 +00:00
|
|
|
PictureEntry *Pictures::search(const Common::String &name) {
|
2018-11-19 04:29:14 +00:00
|
|
|
Picture *pic;
|
|
|
|
|
|
|
|
for (uint idx = 0; idx < _store.size(); ++idx) {
|
|
|
|
pic = _store[idx]._picture;
|
|
|
|
|
2020-08-29 20:56:30 +00:00
|
|
|
if (pic && pic->_name.equalsIgnoreCase(name))
|
2018-11-19 04:29:14 +00:00
|
|
|
return &_store[idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pictures::storeOriginal(Picture *pic) {
|
|
|
|
PictureEntry newPic;
|
|
|
|
newPic._picture = pic;
|
|
|
|
|
|
|
|
_store.push_back(newPic);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pictures::storeScaled(Picture *pic) {
|
2020-08-29 20:56:30 +00:00
|
|
|
PictureEntry *entry = search(pic->_name);
|
2018-11-19 04:29:14 +00:00
|
|
|
if (!entry)
|
|
|
|
return;
|
|
|
|
|
|
|
|
delete entry->_scaled;
|
|
|
|
entry->_scaled = pic;
|
2018-10-24 02:25:00 +00:00
|
|
|
}
|
|
|
|
|
2018-11-19 04:29:14 +00:00
|
|
|
void Pictures::store(Picture *pic) {
|
|
|
|
if (!pic)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!pic->_scaled)
|
|
|
|
storeOriginal(pic);
|
|
|
|
else
|
|
|
|
storeScaled(pic);
|
|
|
|
}
|
|
|
|
|
2020-08-29 20:56:30 +00:00
|
|
|
Picture *Pictures::retrieve(const Common::String &name, bool scaled) {
|
2018-11-19 04:29:14 +00:00
|
|
|
Picture *pic;
|
|
|
|
|
|
|
|
for (uint idx = 0; idx < _store.size(); ++idx) {
|
|
|
|
pic = scaled ? _store[idx]._scaled : _store[idx]._picture;
|
|
|
|
|
2020-08-29 20:56:30 +00:00
|
|
|
if (pic && pic->_name.equalsIgnoreCase(name))
|
2018-11-19 04:29:14 +00:00
|
|
|
return pic;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-08-29 20:56:30 +00:00
|
|
|
Picture *Pictures::load(const Common::String &name) {
|
2018-11-19 04:29:14 +00:00
|
|
|
::Image::PNGDecoder png;
|
|
|
|
::Image::JPEGDecoder jpg;
|
2018-12-06 03:17:48 +00:00
|
|
|
Graphics::Surface rectImg;
|
2018-11-23 23:35:13 +00:00
|
|
|
RawDecoder raw;
|
2018-11-19 04:29:14 +00:00
|
|
|
const Graphics::Surface *img;
|
2018-11-23 23:35:13 +00:00
|
|
|
const byte *palette = nullptr;
|
2018-11-25 05:28:52 +00:00
|
|
|
uint palCount = 0;
|
2023-02-22 18:09:10 +00:00
|
|
|
bool hasTransColor = false;
|
|
|
|
uint32 transColor = 0;
|
2018-11-19 04:29:14 +00:00
|
|
|
Picture *pic;
|
|
|
|
|
|
|
|
// Check if the picture is already in the store
|
2020-08-29 20:56:30 +00:00
|
|
|
pic = retrieve(name, false);
|
2018-11-24 03:47:34 +00:00
|
|
|
if (pic)
|
|
|
|
return pic;
|
2018-11-19 04:29:14 +00:00
|
|
|
|
|
|
|
Common::File f;
|
2023-09-17 16:23:42 +00:00
|
|
|
if ((name.hasSuffixIgnoreCase(".png") && f.open(Common::Path(name)))
|
|
|
|
|| f.open(Common::Path(Common::String::format("pic%s.png", name.c_str())))
|
|
|
|
|| f.open(Common::Path(Common::String::format("%s.png", name.c_str())))
|
2020-08-29 20:56:30 +00:00
|
|
|
) {
|
2019-08-02 05:04:36 +00:00
|
|
|
png.setKeepTransparencyPaletted(true);
|
2018-11-19 04:29:14 +00:00
|
|
|
png.loadStream(f);
|
|
|
|
img = png.getSurface();
|
2018-11-23 23:35:13 +00:00
|
|
|
palette = png.getPalette();
|
2018-11-25 00:53:04 +00:00
|
|
|
palCount = png.getPaletteColorCount();
|
2023-02-22 18:09:10 +00:00
|
|
|
hasTransColor = png.hasTransparentColor();
|
2019-08-02 05:04:36 +00:00
|
|
|
transColor = png.getTransparentColor();
|
2020-08-29 20:56:30 +00:00
|
|
|
} else if (
|
2023-09-17 16:23:42 +00:00
|
|
|
((name.hasSuffixIgnoreCase(".jpg") || name.hasSuffixIgnoreCase(".jpeg")) && f.open(Common::Path(name)))
|
|
|
|
|| f.open(Common::Path(Common::String::format("pic%s.jpg", name.c_str())))
|
|
|
|
|| f.open(Common::Path(Common::String::format("pic%s.jpeg", name.c_str())))
|
|
|
|
|| f.open(Common::Path(Common::String::format("%s.jpg", name.c_str())))
|
2020-08-29 20:56:30 +00:00
|
|
|
) {
|
2019-01-22 14:02:05 +00:00
|
|
|
jpg.setOutputPixelFormat(g_system->getScreenFormat());
|
2018-11-19 04:29:14 +00:00
|
|
|
jpg.loadStream(f);
|
|
|
|
img = jpg.getSurface();
|
2023-09-17 16:23:42 +00:00
|
|
|
} else if ((name.hasSuffixIgnoreCase(".raw") && f.open(Common::Path(name))) ||
|
|
|
|
f.open(Common::Path(Common::String::format("pic%s.raw", name.c_str())))) {
|
2018-11-23 23:35:13 +00:00
|
|
|
raw.loadStream(f);
|
|
|
|
img = raw.getSurface();
|
|
|
|
palette = raw.getPalette();
|
2018-11-25 00:53:04 +00:00
|
|
|
palCount = raw.getPaletteColorCount();
|
2023-02-22 18:09:10 +00:00
|
|
|
hasTransColor = raw.hasTransparentColor();
|
2019-01-06 01:16:42 +00:00
|
|
|
transColor = raw.getTransparentColor();
|
2023-09-17 16:23:42 +00:00
|
|
|
} else if (f.open(Common::Path(Common::String::format("pic%s.rect", name.c_str())))) {
|
2019-07-28 02:57:37 +00:00
|
|
|
rectImg.w = f.readUint32BE();
|
|
|
|
rectImg.h = f.readUint32BE();
|
2018-12-06 03:17:48 +00:00
|
|
|
img = &rectImg;
|
2018-11-24 03:47:34 +00:00
|
|
|
} else {
|
|
|
|
// No such picture
|
|
|
|
return nullptr;
|
2018-11-19 04:29:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-02 05:04:36 +00:00
|
|
|
// Also check if it's going to be an adaptive pic
|
|
|
|
bool isAdaptive = false;
|
|
|
|
for (uint idx = 0; idx < _adaptivePics.size() && !isAdaptive; ++idx)
|
2020-08-29 20:56:30 +00:00
|
|
|
isAdaptive = _adaptivePics[idx].equalsIgnoreCase(name);
|
2019-08-02 05:04:36 +00:00
|
|
|
|
|
|
|
if (isAdaptive) {
|
|
|
|
// It is, so used previously saved palette
|
|
|
|
assert(!_savedPalette.empty());
|
|
|
|
palette = &_savedPalette[0];
|
|
|
|
palCount = _savedPalette.size() / 3;
|
|
|
|
} else if (palette) {
|
|
|
|
// It's a picture with a valid palette, so save a copy of it for later
|
|
|
|
_savedPalette.resize(palCount * 3);
|
|
|
|
Common::copy(palette, palette + palCount * 3, &_savedPalette[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new picture based on the image
|
2018-11-25 00:53:04 +00:00
|
|
|
pic = new Picture(img->w, img->h, g_system->getScreenFormat());
|
2018-11-19 04:29:14 +00:00
|
|
|
pic->_refCount = 1;
|
2021-04-15 19:20:04 +00:00
|
|
|
pic->_name = name;
|
|
|
|
pic->_scaled = false;
|
2023-02-22 18:09:10 +00:00
|
|
|
if (hasTransColor || (!palette && img->format.aBits() > 0))
|
2019-01-06 01:16:42 +00:00
|
|
|
pic->clear(pic->getTransparentColor());
|
2018-11-23 23:35:13 +00:00
|
|
|
|
2018-12-06 03:17:48 +00:00
|
|
|
if (!img->getPixels()) {
|
|
|
|
// Area definition without any content
|
|
|
|
} else if (!palette) {
|
2018-11-25 00:53:04 +00:00
|
|
|
pic->blitFrom(*img);
|
|
|
|
} else {
|
|
|
|
uint pal[256];
|
|
|
|
for (uint idx = 0; idx < palCount; ++idx)
|
|
|
|
pal[idx] = pic->format.RGBToColor(palette[idx * 3],
|
|
|
|
palette[idx * 3 + 1], palette[idx * 3 + 2]);
|
2021-05-04 08:45:03 +00:00
|
|
|
|
2018-12-02 18:53:18 +00:00
|
|
|
const byte *srcP = (const byte *)img->getPixels();
|
|
|
|
byte *destP = (byte *)pic->getPixels();
|
2018-11-25 00:53:04 +00:00
|
|
|
for (int idx = 0; idx < img->w * img->h; ++idx, srcP++, destP += pic->format.bytesPerPixel) {
|
2023-02-22 18:09:10 +00:00
|
|
|
if (!hasTransColor || (uint32)*srcP != transColor) {
|
2019-01-06 01:16:42 +00:00
|
|
|
uint val = (*srcP >= palCount) ? 0 : pal[*srcP];
|
|
|
|
if (pic->format.bytesPerPixel == 2)
|
|
|
|
WRITE_LE_UINT16(destP, val);
|
|
|
|
else
|
|
|
|
WRITE_LE_UINT32(destP, val);
|
|
|
|
}
|
2018-11-25 00:53:04 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-19 04:29:14 +00:00
|
|
|
|
2021-04-15 19:20:04 +00:00
|
|
|
store(pic);
|
|
|
|
return pic;
|
2018-10-24 02:25:00 +00:00
|
|
|
}
|
|
|
|
|
2018-11-19 05:31:58 +00:00
|
|
|
Picture *Pictures::scale(Picture *src, size_t sx, size_t sy) {
|
|
|
|
// Check for the presence of an already scaled version of that size
|
2020-08-29 20:56:30 +00:00
|
|
|
Picture *dst = retrieve(src->_name, true);
|
2021-07-05 00:24:30 +00:00
|
|
|
if (dst && (size_t)dst->w == sx && (size_t)dst->h == sy)
|
2018-11-19 05:31:58 +00:00
|
|
|
return dst;
|
|
|
|
|
|
|
|
// Create a new picture of the destination size and rescale the source picture
|
|
|
|
dst = new Picture(sx, sy, src->format);
|
2020-08-29 20:56:30 +00:00
|
|
|
dst->_name = src->_name;
|
2018-11-19 05:31:58 +00:00
|
|
|
dst->_scaled = true;
|
2020-06-02 19:39:17 +00:00
|
|
|
dst->transBlitFrom(*src, src->getBounds(), dst->getBounds(), (uint)0x8888);
|
2018-11-19 05:31:58 +00:00
|
|
|
|
|
|
|
storeScaled(dst);
|
2018-11-19 05:40:23 +00:00
|
|
|
return dst;
|
2018-11-19 05:31:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-24 02:25:00 +00:00
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
|
2019-01-06 01:16:42 +00:00
|
|
|
Picture::Picture(int width, int height, const Graphics::PixelFormat &fmt) :
|
2020-08-29 20:56:30 +00:00
|
|
|
Graphics::ManagedSurface(width, height, fmt), _refCount(0), _scaled(false) {
|
2019-01-06 01:16:42 +00:00
|
|
|
|
|
|
|
// Default transparent color chosen at random
|
|
|
|
_transColor = format.RGBToColor(0x77, 0x77, 0x77);
|
|
|
|
}
|
|
|
|
|
2018-10-20 02:37:03 +00:00
|
|
|
void Picture::increment() {
|
|
|
|
++_refCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Picture::decrement() {
|
|
|
|
if (_refCount > 0 && --_refCount == 0) {
|
2018-11-19 04:29:14 +00:00
|
|
|
// No longer any references to this picture, so remove it
|
2018-10-20 02:37:03 +00:00
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-19 05:40:23 +00:00
|
|
|
void Picture::drawPicture(const Common::Point &destPos, const Common::Rect &box) {
|
2019-01-17 05:02:20 +00:00
|
|
|
Graphics::ManagedSurface s(*g_vm->_screen, box);
|
|
|
|
Common::Point pt(destPos.x - box.left, destPos.y - box.top);
|
|
|
|
|
2019-02-17 23:20:16 +00:00
|
|
|
s.transBlitFrom(*this, pt, _transColor);
|
2018-10-24 02:25:00 +00:00
|
|
|
}
|
|
|
|
|
2018-11-14 04:05:59 +00:00
|
|
|
} // End of namespace Glk
|