EMI: Implement screenshot thumbnails for savegames

This commit is contained in:
Christian Krause 2014-06-30 22:46:50 +02:00
parent 81745bfe62
commit 1e82136592
18 changed files with 280 additions and 253 deletions

View file

@ -29,6 +29,7 @@
#include "engines/grim/set.h"
#include "engines/grim/gfx_base.h"
#include "engines/grim/actor.h"
#include "graphics/pixelbuffer.h"
namespace Grim {
@ -184,6 +185,42 @@ void EMIEngine::drawNormalMode() {
}
void EMIEngine::storeSaveGameImage(SaveGame *state) {
unsigned int width = 160, height = 120;
Bitmap *screenshot = g_driver->getScreenshot(width, height, true);
assert(screenshot);
// screenshots are not using the whole size of the texture
// copy the actual screenshot to the correct position
unsigned int texWidth = 256, texHeight = 128;
Graphics::PixelBuffer buffer = Graphics::PixelBuffer::createBuffer<565>(texWidth * texHeight, DisposeAfterUse::YES);
buffer.clear(texWidth * texHeight);
for (unsigned int j = 0; j < 120; j++) {
buffer.copyBuffer(j * texWidth, j * width, width, screenshot->getData(0));
}
Bitmap *newscreenshot = new Bitmap(buffer, texWidth, texHeight, "screenshot");
state->beginSection('SIMG');
if (newscreenshot) {
int size = newscreenshot->getWidth() * newscreenshot->getHeight();
uint16 *data = (uint16 *)newscreenshot->getData(0).getRawBuffer();
for (int l = 0; l < size; l++) {
state->writeLEUint16(data[l]);
}
} else {
error("Unable to store screenshot");
}
state->endSection();
delete newscreenshot;
delete screenshot;
}
void EMIEngine::temporaryStoreSaveGameImage() {
// store current rendered screen in g_driver
g_grim->updateDisplayScene();
g_driver->storeDisplay();
}
void EMIEngine::updateDrawMode() {
// For EMI, draw mode is just like normal mode with frozen frame time.
updateNormalMode();

View file

@ -45,6 +45,8 @@ public:
void invalidateTextObjectsSortOrder() override;
void invalidateSortOrder();
void sortActiveActorsList();
void temporaryStoreSaveGameImage();
void storeSaveGameImage(SaveGame *state) override;
private:
LuaBase *createLua() override;

View file

@ -27,6 +27,7 @@
#include "engines/grim/emi/lua_v2.h"
#include "engines/grim/emi/emi_registry.h"
#include "engines/grim/lua/lauxlib.h"
#include "graphics/pixelbuffer.h"
#include "engines/grim/resource.h"
#include "engines/grim/set.h"
@ -116,7 +117,7 @@ void Lua_V2::MakeScreenTextures() {
/*int index = (int)lua_getnumber(indexObj);*/
// The index does not seem to matter
g_driver->createSpecialtyTextures();
g_driver->makeScreenTextures();
lua_pushnumber(1.0);
} else {
lua_pushnil();
@ -285,7 +286,7 @@ void Lua_V2::ClearOverworld() {
}
void Lua_V2::ScreenshotForSavegame() {
warning("Lua_V2::ScreenshotForSavegame: implement opcode");
g_emi->temporaryStoreSaveGameImage();
}
void Lua_V2::EngineDisplay() {
@ -385,10 +386,50 @@ void Lua_V2::ThumbnailFromFile() {
lua_Object texIdObj = lua_getparam(1);
lua_Object filenameObj = lua_getparam(2);
if (!lua_isnumber(texIdObj) || !lua_isstring(filenameObj))
if (!lua_isnumber(texIdObj) || !lua_isstring(filenameObj)) {
warning("Lua_V2::ThumbnailFromFile: wrong parameters");
return;
}
int index = (int)lua_getnumber(texIdObj);
const char *filename = lua_getstring(filenameObj);
int width = 256, height = 128;
Bitmap *screenshot;
SaveGame *savedState = SaveGame::openForLoading(filename);
if (!savedState || !savedState->isCompatible()) {
delete savedState;
warning("Lua_V2::ThumbnailFromFile: savegame %s not compatible", filename);
lua_pushnil();
return;
}
int dataSize = savedState->beginSection('SIMG');
if (dataSize != width * height * 2) {
warning("Lua_V2::ThumbnailFromFile: savegame uses unexpected thumbnail size, ignore it");
lua_pushnil();
delete savedState;
return;
}
uint16 *data = new uint16[dataSize / 2];
for (int l = 0; l < dataSize / 2; l++) {
data[l] = savedState->readLEUint16();
}
Graphics::PixelBuffer buf(Graphics::createPixelFormat<565>(), (byte *)data);
screenshot = new Bitmap(buf, width, height, "screenshot");
if (!screenshot) {
lua_pushnil();
warning("Lua_V2::ThumbnailFromFile: Could not restore screenshot from file %s", filename);
delete[] data;
delete savedState;
return;
}
screenshot->_data->convertToColorFormat(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
g_driver->createSpecialtyTexture(index, (char*)screenshot->getData(0).getRawBuffer(), width, height);
delete[] data;
savedState->endSection();
delete savedState;
warning("Lua_V2::ThumbnailFromFile: implement opcode, pushing true");
pushbool(true);
}
@ -577,7 +618,6 @@ static void stubError(const char *funcName) {
// STUB_FUNC2(Lua_V2::SetLightPosition)
// STUB_FUNC2(Lua_V2::GetAngleBetweenVectors)
// STUB_FUNC2(Lua_V2::IsPointInSector)
// STUB_FUNC2(Lua_V2::ThumbnailFromFile)
// Stubbed functions with semi-known arguments:
// TODO: Verify and implement these: (And add type-checking), also rename params

View file

@ -263,11 +263,8 @@ void EMIModel::prepareForRender() {
void EMIModel::prepareTextures() {
_mats = new Material*[_numTextures];
for (uint32 i = 0; i < _numTextures; i++) {
// HACK: As we dont know what specialty-textures are yet, we skip loading them
if (!_texNames[i].contains("specialty"))
// FIXME: should specialty textures be clamped?
_mats[i] = _costume->loadMaterial(_texNames[i], false);
else
_mats[i] = g_driver->getSpecialtyTexture(_texNames[i][9] - '0');
}
}

View file

@ -38,6 +38,8 @@
#include "engines/grim/gfx_base.h"
#include "engines/grim/savegame.h"
#include "engines/grim/bitmap.h"
#include "engines/grim/grim.h"
#include "engines/grim/model.h"
@ -49,7 +51,9 @@ GfxBase::GfxBase() :
_screenWidth(0), _screenHeight(0), _isFullscreen(false),
_scaleW(1.0f), _scaleH(1.0f), _currentShadowArray(nullptr),
_shadowColorR(255), _shadowColorG(255), _shadowColorB(255) {
for (int i = 0; i < _numSpecialtyTextures; i++) {
_specialtyTextures[i]._isShared = true;
}
}
void GfxBase::setShadowMode() {
@ -112,22 +116,6 @@ GfxBase *CreateGfxOpenGL() {
}
#endif // USE_OPENGL
void SpecialtyMaterial::select() const {
if (_texture) {
g_driver->selectTexture(_texture);
}
}
void SpecialtyMaterial::create(const char *data, int width, int height) {
delete _texture;
_texture = new Texture();
_texture->_width = width;
_texture->_height = height;
_texture->_bpp = 4;
_texture->_colorFormat = BM_RGBA;
g_driver->createTexture(_texture, data, nullptr, false);
}
Math::Matrix4 GfxBase::makeLookMatrix(const Math::Vector3d& pos, const Math::Vector3d& interest, const Math::Vector3d& up) {
Math::Vector3d f = (interest - pos).getNormalized();
Math::Vector3d u = up.getNormalized();
@ -172,4 +160,84 @@ Math::Matrix4 GfxBase::makeProjMatrix(float fov, float nclip, float fclip) {
return proj;
}
void GfxBase::createSpecialtyTexture(unsigned int id, const char *data, int width, int height) {
if (id >= _numSpecialtyTextures) return;
if (_specialtyTextures[id]._texture) {
destroyTexture(&_specialtyTextures[id]);
}
delete[] _specialtyTextures[id]._data;
_specialtyTextures[id]._width = width;
_specialtyTextures[id]._height = height;
_specialtyTextures[id]._bpp = 4;
_specialtyTextures[id]._colorFormat = BM_RGBA;
g_driver->createTexture(&_specialtyTextures[id], data, nullptr, false);
}
Bitmap *GfxBase::createScreenshotBitmap(const Graphics::PixelBuffer src, int w, int h, bool flipOrientation) {
Graphics::PixelBuffer buffer = Graphics::PixelBuffer::createBuffer<565>(w * h, DisposeAfterUse::YES);
int i1 = (_screenWidth * w - 1) / _screenWidth + 1;
int j1 = (_screenHeight * h - 1) / _screenHeight + 1;
for (int j = 0; j < j1; j++) {
for (int i = 0; i < i1; i++) {
int x0 = i * _screenWidth / w;
int x1 = ((i + 1) * _screenWidth - 1) / w + 1;
int y0 = j * _screenHeight / h;
int y1 = ((j + 1) * _screenHeight - 1) / h + 1;
uint16 sr = 0, sg = 0, sb = 0;
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
uint8 r, g, b;
src.getRGBAt(y * _screenWidth + x, r, g, b);
sr += r;
sg += g;
sb += b;
}
}
sr /= (x1 - x0) * (y1 - y0);
sg /= (x1 - x0) * (y1 - y0);
sb /= (x1 - x0) * (y1 - y0);
if (g_grim->getGameType() == GType_MONKEY4) {
buffer.setPixelAt( (flipOrientation ? j : (h - j - 1) ) * w + i, sr, sg, sb);
} else {
uint32 color = (sr + sg + sb) / 3;
buffer.setPixelAt( (flipOrientation ? j : (h - j - 1) ) * w + i, color, color, color);
}
}
}
Bitmap *screenshot = new Bitmap(buffer, w, h, "screenshot");
return screenshot;
}
void GfxBase::makeScreenTextures() {
//make a buffer big enough to hold any of the textures
char *buffer = new char[256 * 256 * 4];
// TODO: Handle screen resolutions other than 640 x 480
createSpecialtyTextureFromScreen(0, buffer, 0, 0, 256, 256);
createSpecialtyTextureFromScreen(1, buffer, 256, 0, 256, 256);
createSpecialtyTextureFromScreen(2, buffer, 512, 0, 128, 128);
createSpecialtyTextureFromScreen(3, buffer, 512, 128, 128, 128);
createSpecialtyTextureFromScreen(4, buffer, 0, 256, 256, 256);
createSpecialtyTextureFromScreen(5, buffer, 256, 256, 256, 256);
createSpecialtyTextureFromScreen(6, buffer, 512, 256, 128, 128);
createSpecialtyTextureFromScreen(7, buffer, 512, 384, 128, 128);
delete[] buffer;
}
Texture *GfxBase::getSpecialtyTexturePtr(Common::String name) {
assert(name.hasPrefix("specialty"));
name.erase(0, 9);
unsigned int id;
sscanf(name.c_str(), "%u", &id);
if (id >= _numSpecialtyTextures) {
return nullptr;
}
return &_specialtyTextures[id];
}
}

View file

@ -27,6 +27,8 @@
#include "math/quat.h"
#include "graphics/pixelformat.h"
#include "graphics/pixelbuffer.h"
#include "common/str.h"
#include "engines/grim/material.h"
@ -46,7 +48,6 @@ class Color;
class PrimitiveObject;
class Font;
class TextObject;
class Material;
class EMIModel;
class EMIMeshFace;
class ModelNode;
@ -56,15 +57,6 @@ class Sprite;
class Light;
class Texture;
class SpecialtyMaterial : public Material {
public:
SpecialtyMaterial() { _texture = NULL; }
~SpecialtyMaterial() { delete _texture; }
void create(const char *data, int width, int height);
virtual void select() const override;
Texture *_texture;
};
/**
* The Color-formats used for bitmaps in Grim Fandango/Escape From Monkey Island
*/
@ -205,7 +197,7 @@ public:
virtual void drawTextObject(const TextObject *text) = 0;
virtual void destroyTextObject(TextObject *text) = 0;
virtual Bitmap *getScreenshot(int w, int h) = 0;
virtual Bitmap *getScreenshot(int w, int h, bool useStored) = 0;
virtual void storeDisplay() = 0;
virtual void copyStoredToDisplay() = 0;
@ -264,8 +256,7 @@ public:
virtual void renderBitmaps(bool render);
virtual void renderZBitmaps(bool render);
virtual void createSpecialtyTextures() = 0;
virtual Material *getSpecialtyTexture(int n) { return &_specialty[n]; }
virtual void makeScreenTextures();
virtual void createMesh(Mesh *mesh) {}
virtual void destroyMesh(const Mesh *mesh) {}
@ -279,10 +270,17 @@ public:
virtual void drawBuffers() {}
virtual void refreshBuffers() {}
virtual void createSpecialtyTexture(unsigned int id, const char *data, int width, int height);
virtual void createSpecialtyTextureFromScreen(unsigned int id, char *data, int x, int y, int width, int height) = 0;
static Math::Matrix4 makeLookMatrix(const Math::Vector3d& pos, const Math::Vector3d& interest, const Math::Vector3d& up);
static Math::Matrix4 makeProjMatrix(float fov, float nclip, float fclip);
Texture *getSpecialtyTexturePtr(unsigned int id) { if (id >= _numSpecialtyTextures) return nullptr; return &_specialtyTextures[id]; };
Texture *getSpecialtyTexturePtr(Common::String name);
protected:
Bitmap *createScreenshotBitmap(const Graphics::PixelBuffer src, int w, int h, bool flipOrientation);
static const int _numSpecialtyTextures = 22;
Texture _specialtyTextures[_numSpecialtyTextures];
static const int _gameHeight = 480;
static const int _gameWidth = 640;
float _scaleW, _scaleH;
@ -296,7 +294,6 @@ protected:
bool _renderZBitmaps;
bool _shadowModeActive;
Graphics::PixelFormat _pixelFormat;
SpecialtyMaterial _specialty[8];
Math::Vector3d _currentPos;
Math::Quaternion _currentQuat;
float _dimLevel;

View file

@ -1616,35 +1616,14 @@ void GfxOpenGL::drawEmergString(int x, int y, const char *text, const Color &fgC
glPopMatrix();
}
Bitmap *GfxOpenGL::getScreenshot(int w, int h) {
Graphics::PixelBuffer buffer = Graphics::PixelBuffer::createBuffer<565>(w * h, DisposeAfterUse::YES);
Bitmap *GfxOpenGL::getScreenshot(int w, int h, bool useStored) {
Graphics::PixelBuffer src(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), _screenWidth * _screenHeight, DisposeAfterUse::YES);
if (useStored) {
memcpy(src.getRawBuffer(), _storedDisplay, _screenWidth * _screenHeight * 4);
} else {
glReadPixels(0, 0, _screenWidth, _screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, src.getRawBuffer());
int i1 = (_screenWidth * w - 1) / _screenWidth + 1;
int j1 = (_screenHeight * h - 1) / _screenHeight + 1;
for (int j = 0; j < j1; j++) {
for (int i = 0; i < i1; i++) {
int x0 = i * _screenWidth / w;
int x1 = ((i + 1) * _screenWidth - 1) / w + 1;
int y0 = j * _screenHeight / h;
int y1 = ((j + 1) * _screenHeight - 1) / h + 1;
uint32 color = 0;
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
uint8 lr, lg, lb;
src.getRGBAt(y * _screenWidth + x, lr, lg, lb);
color += (lr + lg + lb) / 3;
}
}
color /= (x1 - x0) * (y1 - y0);
buffer.setPixelAt((h - j - 1) * w + i, color, color, color);
}
}
Bitmap *screenshot = new Bitmap(buffer, w, h, "screenshot");
return screenshot;
return createScreenshotBitmap(src, w, h, false);
}
void GfxOpenGL::storeDisplay() {
@ -1976,36 +1955,9 @@ static void readPixels(int x, int y, int width, int height, char *buffer) {
}
}
void GfxOpenGL::createSpecialtyTextures() {
//make a buffer big enough to hold any of the textures
char *buffer = new char[256 * 256 * 4];
// TODO: Handle screen resolutions other than 640 x 480
readPixels(0, 0, 256, 256, buffer);
_specialty[0].create(buffer, 256, 256);
readPixels(256, 0, 256, 256, buffer);
_specialty[1].create(buffer, 256, 256);
readPixels(512, 0, 128, 128, buffer);
_specialty[2].create(buffer, 128, 128);
readPixels(512, 128, 128, 128, buffer);
_specialty[3].create(buffer, 128, 128);
readPixels(0, 256, 256, 256, buffer);
_specialty[4].create(buffer, 256, 256);
readPixels(256, 256, 256, 256, buffer);
_specialty[5].create(buffer, 256, 256);
readPixels(512, 256, 128, 128, buffer);
_specialty[6].create(buffer, 128, 128);
readPixels(512, 384, 128, 128, buffer);
_specialty[7].create(buffer, 128, 128);
delete[] buffer;
void GfxOpenGL::createSpecialtyTextureFromScreen(unsigned int id, char *data, int x, int y, int width, int height) {
readPixels(x, y, width, height, data);
createSpecialtyTexture(id, data, width, height);
}
} // end of namespace Grim

View file

@ -107,7 +107,7 @@ public:
void drawTextObject(const TextObject *text) override;
void destroyTextObject(TextObject *text) override;
Bitmap *getScreenshot(int w, int h) override;
Bitmap *getScreenshot(int w, int h, bool useStored) override;
void storeDisplay() override;
void copyStoredToDisplay() override;
void dimScreen() override;
@ -125,9 +125,8 @@ public:
void drawMovieFrame(int offsetX, int offsetY) override;
void releaseMovieFrame() override;
void createSpecialtyTextures() override;
protected:
void createSpecialtyTextureFromScreen(unsigned int id, char *data, int x, int y, int width, int height) override;
void drawDepthBitmap(int x, int y, int w, int h, char *data);
void initExtensions();
private:

View file

@ -1492,38 +1492,6 @@ void GfxOpenGLS::destroyTextObject(TextObject *text) {
delete td;
}
Bitmap *GfxOpenGLS::getScreenshot(int w, int h) {
Graphics::PixelBuffer buffer = Graphics::PixelBuffer::createBuffer<565>(w * h, DisposeAfterUse::YES);
Graphics::PixelBuffer src(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), _screenWidth * _screenHeight, DisposeAfterUse::YES);
glReadPixels(0, 0, _screenWidth, _screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, src.getRawBuffer());
int i1 = (_screenWidth * w - 1) / _screenWidth + 1;
int j1 = (_screenHeight * h - 1) / _screenHeight + 1;
for (int j = 0; j < j1; j++) {
for (int i = 0; i < i1; i++) {
int x0 = i * _screenWidth / w;
int x1 = ((i + 1) * _screenWidth - 1) / w + 1;
int y0 = j * _screenHeight / h;
int y1 = ((j + 1) * _screenHeight - 1) / h + 1;
uint32 color = 0;
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
uint8 lr, lg, lb;
src.getRGBAt(y * _screenWidth + x, lr, lg, lb);
color += (lr + lg + lb) / 3;
}
}
color /= (x1 - x0) * (y1 - y0);
buffer.setPixelAt((h - j - 1) * w + i, color, color, color);
}
}
Bitmap *screenshot = new Bitmap(buffer, w, h, "screenshot");
return screenshot;
}
void GfxOpenGLS::storeDisplay() {
glBindTexture(GL_TEXTURE_2D, _storedDisplay);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _screenWidth, _screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
@ -1857,37 +1825,6 @@ void GfxOpenGLS::renderZBitmaps(bool render) {
}
void GfxOpenGLS::createSpecialtyTextures() {
//make a buffer big enough to hold any of the textures
char *buffer = new char[256 * 256 * 4];
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_specialty[0].create(buffer, 256, 256);
glReadPixels(256, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_specialty[1].create(buffer, 256, 256);
glReadPixels(512, 0, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_specialty[2].create(buffer, 128, 128);
glReadPixels(512, 128, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_specialty[3].create(buffer, 128, 128);
glReadPixels(0, 256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_specialty[4].create(buffer, 256, 256);
glReadPixels(256, 256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_specialty[5].create(buffer, 256, 256);
glReadPixels(512, 256, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_specialty[6].create(buffer, 128, 128);
glReadPixels(512, 384, 128, 128, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
_specialty[7].create(buffer, 128, 128);
delete[] buffer;
}
void GfxOpenGLS::createEMIModel(EMIModel *model) {
EMIModelUserData *mud = new EMIModelUserData;
model->_userData = mud;
@ -1977,6 +1914,34 @@ void GfxOpenGLS::destroyMesh(const Mesh *mesh) {
delete mud;
}
static void readPixels(int x, int y, int width, int height, char *buffer) {
char *p = buffer;
for (int i = y; i < y + height; i++) {
glReadPixels(x, 479 - i, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, p);
p += width * 4;
}
}
Bitmap *GfxOpenGLS::getScreenshot(int w, int h, bool useStored) {
Graphics::PixelBuffer src(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), _screenWidth * _screenHeight, DisposeAfterUse::YES);
if (useStored) {
glBindTexture(GL_TEXTURE_2D, _storedDisplay);
char *buffer = new char[_screenWidth * _screenHeight * 4];
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
memcpy(src.getRawBuffer(), buffer, _screenWidth * _screenHeight * 4);
delete[] buffer;
} else {
readPixels(0, 0, _screenWidth, _screenHeight, reinterpret_cast<char *>(src.getRawBuffer()));
}
return createScreenshotBitmap(src, w, h, false);
}
void GfxOpenGLS::createSpecialtyTextureFromScreen(unsigned int id, char *data, int x, int y, int width, int height) {
readPixels(x, y, width, height, data);
createSpecialtyTexture(id, data, width, height);
}
}

View file

@ -143,7 +143,7 @@ public:
virtual void drawTextObject(const TextObject *text) override;
virtual void destroyTextObject(TextObject *text) override;
virtual Bitmap *getScreenshot(int w, int h) override;
virtual Bitmap *getScreenshot(int w, int h, bool useStored) override;
virtual void storeDisplay() override;
virtual void copyStoredToDisplay() override;
@ -198,8 +198,6 @@ public:
virtual void renderBitmaps(bool render) override;
virtual void renderZBitmaps(bool render) override;
virtual void createSpecialtyTextures() override;
virtual void createMesh(Mesh *mesh) override;
virtual void destroyMesh(const Mesh *mesh) override;
virtual void createEMIModel(EMIModel *model) override;
@ -209,6 +207,7 @@ protected:
void setupShaders();
GLuint compileShader(const char *vertex, const char *fragment);
GLuint compileShader(const char *shader) { return compileShader(shader, shader); }
void createSpecialtyTextureFromScreen(unsigned int id, char *data, int x, int y, int width, int height) override;
private:
const Actor *_currentActor;

View file

@ -1437,33 +1437,19 @@ void GfxTinyGL::drawEmergString(int x, int y, const char *text, const Color &fgC
}
}
Bitmap *GfxTinyGL::getScreenshot(int w, int h) {
Graphics::PixelBuffer buffer = Graphics::PixelBuffer::createBuffer<565>(w * h, DisposeAfterUse::YES);
int i1 = (_gameWidth * w - 1) / _gameWidth + 1;
int j1 = (_gameHeight * h - 1) / _gameHeight + 1;
for (int j = 0; j < j1; j++) {
for (int i = 0; i < i1; i++) {
int x0 = i * _gameWidth / w;
int x1 = ((i + 1) * _gameWidth - 1) / w + 1;
int y0 = j * _gameHeight / h;
int y1 = ((j + 1) * _gameHeight - 1) / h + 1;
uint32 color = 0;
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
uint8 lr, lg, lb;
_zb->readPixelRGB(y * _gameWidth + x, lr, lg, lb);
color += (lr + lg + lb) / 3;
}
}
color /= (x1 - x0) * (y1 - y0);
buffer.setPixelAt(j * w + i, color, color, color);
Bitmap *GfxTinyGL::getScreenshot(int w, int h, bool useStored) {
if (useStored) {
return createScreenshotBitmap(_storedDisplay, w, h, true);
} else {
Graphics::PixelBuffer src(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), _screenWidth * _screenHeight, DisposeAfterUse::YES);
_zb->copyToBuffer(src);
return createScreenshotBitmap(src, w, h, true);
}
}
Bitmap *screenshot = new Bitmap(buffer, w, h, "screenshot");
return screenshot;
void GfxTinyGL::createSpecialtyTextureFromScreen(unsigned int id, char *data, int x, int y, int width, int height) {
readPixels(x, y, width, height, (uint8*)data);
createSpecialtyTexture(id, data, width, height);
}
void GfxTinyGL::storeDisplay() {
@ -1612,35 +1598,4 @@ void GfxTinyGL::readPixels(int x, int y, int width, int height, uint8 *buffer) {
}
}
void GfxTinyGL::createSpecialtyTextures() {
//make a buffer big enough to hold any of the textures
uint8 *buffer = new uint8[256 * 256 * 4];
readPixels(0, 0, 256, 256, buffer);
_specialty[0].create((const char *)buffer, 256, 256);
readPixels(256, 0, 256, 256, buffer);
_specialty[1].create((const char *)buffer, 256, 256);
readPixels(512, 0, 128, 128, buffer);
_specialty[2].create((const char *)buffer, 128, 128);
readPixels(512, 128, 128, 128, buffer);
_specialty[3].create((const char *)buffer, 128, 128);
readPixels(0, 256, 256, 224, buffer);
_specialty[4].create((const char *)buffer, 256, 256);
readPixels(256, 256, 256, 224, buffer);
_specialty[5].create((const char *)buffer, 256, 256);
readPixels(512, 256, 128, 128, buffer);
_specialty[6].create((const char *)buffer, 128, 128);
readPixels(512, 384, 128, 96, buffer);
_specialty[7].create((const char *)buffer, 128, 128);
delete[] buffer;
}
} // end of namespace Grim

View file

@ -108,7 +108,7 @@ public:
void dimRegion(int x, int y, int w, int h, float level) override;
void irisAroundRegion(int x1, int y1, int x2, int y2) override;
Bitmap *getScreenshot(int w, int h) override;
Bitmap *getScreenshot(int w, int h, bool useStored) override;
void storeDisplay() override;
void copyStoredToDisplay() override;
@ -123,8 +123,6 @@ public:
void drawMovieFrame(int offsetX, int offsetY) override;
void releaseMovieFrame() override;
void createSpecialtyTextures() override;
int genBuffer() override;
void delBuffer(int buffer) override;
void selectBuffer(int buffer) override;
@ -133,6 +131,7 @@ public:
void refreshBuffers() override;
protected:
void createSpecialtyTextureFromScreen(unsigned int id, char *data, int x, int y, int width, int height);
private:
TinyGL::FrameBuffer *_zb;

View file

@ -946,7 +946,7 @@ void GrimEngine::storeSaveGameImage(SaveGame *state) {
g_grim->setMode(_previousMode);
g_grim->updateDisplayScene();
g_driver->storeDisplay();
screenshot = g_driver->getScreenshot(width, height);
screenshot = g_driver->getScreenshot(width, height, false);
g_grim->setMode(mode);
state->beginSection('SIMG');
if (screenshot) {

View file

@ -212,7 +212,7 @@ protected:
void savegameRestore();
void restoreGRIM();
void storeSaveGameImage(SaveGame *savedState);
virtual void storeSaveGameImage(SaveGame *savedState);
bool _savegameLoadRequest;
bool _savegameSaveRequest;

View file

@ -456,7 +456,7 @@ void Lua_V1::ScreenShot() {
GrimEngine::EngineMode mode = g_grim->getMode();
g_grim->setMode(GrimEngine::NormalMode);
g_grim->updateDisplayScene();
Bitmap *screenshot = g_driver->getScreenshot(width, height);
Bitmap *screenshot = g_driver->getScreenshot(width, height, false);
g_grim->setMode(mode);
if (screenshot) {
lua_pushusertag(screenshot->getId(), MKTAG('V','B','U','F'));

View file

@ -54,7 +54,7 @@ void MaterialData::initGrim(Common::SeekableReadStream *data) {
data->seek(12, SEEK_SET);
_numImages = data->readUint32LE();
_textures = new Texture[_numImages];
_textures = new Texture*[_numImages];
/* Discovered by diffing orange.mat with pink.mat and blue.mat .
* Actual meaning unknown, so I prefer to use it as an enum-ish
* at the moment, to detect unexpected values.
@ -68,7 +68,7 @@ void MaterialData::initGrim(Common::SeekableReadStream *data) {
data->seek(60 + _numImages * 40 + offset, SEEK_SET);
for (int i = 0; i < _numImages; ++i) {
Texture *t = _textures + i;
Texture *t = _textures[i] = new Texture();
t->_width = data->readUint32LE();
t->_height = data->readUint32LE();
t->_hasAlpha = data->readUint32LE();
@ -118,9 +118,9 @@ void loadTGA(Common::SeekableReadStream *data, Texture *t) {
}
void MaterialData::initEMI(Common::SeekableReadStream *data) {
Common::Array<Common::String> texFileNames;
if (_fname.hasSuffix(".sur")) { // This expects that we want all the materials in the sur-file
Common::Array<Common::String> texFileNames;
char readFileName[64];
TextSplitter *ts = new TextSplitter(_fname, data);
ts->setLineNumber(2); // Skip copyright-line
@ -133,30 +133,39 @@ void MaterialData::initEMI(Common::SeekableReadStream *data) {
Common::String mFileName(readFileName);
texFileNames.push_back(ResourceLoader::fixFilename(mFileName, false));
}
_textures = new Texture[texFileNames.size()];
_textures = new Texture*[texFileNames.size()];
for (uint i = 0; i < texFileNames.size(); i++) {
Common::String name = texFileNames[i];
if (name.hasPrefix("specialty")) {
_textures[i] = g_driver->getSpecialtyTexturePtr(name);
} else {
_textures[i] = new Texture();
Common::SeekableReadStream *texData = g_resourceloader->openNewStreamFile(texFileNames[i].c_str(), true);
if (!texData) {
warning("Couldn't find tex-file: %s", texFileNames[i].c_str());
_textures[i]._width = 0;
_textures[i]._height = 0;
_textures[i]._texture = new int(1); // HACK to avoid initializing.
_textures[i]._data = nullptr;
_textures[i]->_width = 0;
_textures[i]->_height = 0;
_textures[i]->_texture = new int(1); // HACK to avoid initializing.
_textures[i]->_data = nullptr;
continue;
}
loadTGA(texData, _textures + i);
loadTGA(texData, _textures[i]);
delete texData;
}
}
_numImages = texFileNames.size();
delete ts;
return;
} else if (_fname.hasSuffix(".tga")) {
_numImages = 1;
_textures = new Texture();
loadTGA(data, _textures);
// texFileNames.push_back(filename);
_textures = new Texture*[1];
_textures[0] = new Texture();
loadTGA(data, _textures[0]);
return;
} else if (_fname.hasPrefix("specialty")) {
_numImages = 1;
_textures = new Texture*[1];
_textures[0] = g_driver->getSpecialtyTexturePtr(_fname);
} else {
warning("Unknown material-format: %s", _fname.c_str());
}
@ -170,10 +179,13 @@ MaterialData::~MaterialData() {
}
for (int i = 0; i < _numImages; ++i) {
Texture *t = _textures + i;
Texture *t = _textures[i];
if (!t) continue;
if (t->_isShared) continue; // don't delete specialty textures
if (t->_width && t->_height && t->_texture)
g_driver->destroyTexture(t);
delete[] t->_data;
delete t;
}
delete[] _textures;
}
@ -225,14 +237,16 @@ void Material::reload(CMap *cmap) {
}
void Material::select() const {
Texture *t = _data->_textures + _currImage;
if (t->_width && t->_height) {
Texture *t = _data->_textures[_currImage];
if (t && t->_width && t->_height) {
if (!t->_texture) {
g_driver->createTexture(t, t->_data, _data->_cmap, _clampTexture);
delete[] t->_data;
t->_data = nullptr;
}
g_driver->selectTexture(t);
} else {
warning("Can't select material: %s", getFilename().c_str());
}
}

View file

@ -31,6 +31,8 @@ class CMap;
class Texture {
public:
Texture() :
_width(0), _height(0), _colorFormat(0), _bpp(0), _hasAlpha(false), _texture(nullptr), _data(nullptr), _isShared(false) {};
int _width;
int _height;
int _colorFormat;
@ -38,6 +40,7 @@ public:
bool _hasAlpha;
void *_texture;
char *_data;
bool _isShared;
};
class MaterialData {
@ -51,7 +54,7 @@ public:
Common::String _fname;
const ObjectPtr<CMap> _cmap;
int _numImages;
Texture *_textures;
Texture **_textures;
int _refCount;
private:

View file

@ -407,7 +407,7 @@ Material *ResourceLoader::loadMaterial(const Common::String &filename, CMap *c,
Common::SeekableReadStream *stream;
stream = openNewStreamFile(fname.c_str(), true);
if (!stream) {
if (!stream && !filename.hasPrefix("specialty")) {
// FIXME: EMI demo references files that aren't included. Return a known material.
// This should be fixed in the data files instead.
if (g_grim->getGameType() == GType_MONKEY4 && g_grim->getGameFlags() & ADGF_DEMO) {