Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,923 @@
/* 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 "ags/engine/gfx/gfxfilter_scummvm_renderer.h"
#include "ags/engine/gfx/ali_3d_scummvm.h"
#include "common/std/algorithm.h"
#include "ags/engine/ac/sys_events.h"
#include "ags/engine/gfx/gfxfilter_scummvm_renderer.h"
#include "ags/engine/gfx/gfx_util.h"
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/engine/platform/base/sys_main.h"
#include "ags/engine/ac/timer.h"
#include "ags/ags.h"
#include "ags/globals.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
namespace ALSW {
using namespace Shared;
static RGB faded_out_palette[256];
// ----------------------------------------------------------------------------
// ScummVMRendererGraphicsDriver
// ----------------------------------------------------------------------------
ScummVMRendererGraphicsDriver::ScummVMRendererGraphicsDriver() {
_tint_red = 0;
_tint_green = 0;
_tint_blue = 0;
virtualScreen = nullptr;
_stageVirtualScreen = nullptr;
}
ScummVMRendererGraphicsDriver::~ScummVMRendererGraphicsDriver() {
delete _screen;
ScummVMRendererGraphicsDriver::UnInit();
}
bool ScummVMRendererGraphicsDriver::IsModeSupported(const DisplayMode &mode) {
if (mode.Width <= 0 || mode.Height <= 0 || mode.ColorDepth <= 0) {
warning("Invalid resolution parameters: %d x %d x %d",
mode.Width, mode.Height, mode.ColorDepth);
return false;
}
Graphics::PixelFormat format;
return ::AGS::g_vm->getPixelFormat(mode.ColorDepth, format);
}
int ScummVMRendererGraphicsDriver::GetDisplayDepthForNativeDepth(int native_color_depth) const {
// TODO: check for device caps to know which depth is supported?
if (native_color_depth > 8)
return 32;
return native_color_depth;
}
IGfxModeList *ScummVMRendererGraphicsDriver::GetSupportedModeList(int color_depth) {
std::vector<DisplayMode> modes;
sys_get_desktop_modes(modes, color_depth);
if ((modes.size() == 0) && color_depth == 32) {
// Pretend that 24-bit are 32-bit
sys_get_desktop_modes(modes, 24);
for (auto &m : modes)
m.ColorDepth = 32;
}
return new ScummVMRendererGfxModeList(modes);
}
PGfxFilter ScummVMRendererGraphicsDriver::GetGraphicsFilter() const {
return _filter;
}
void ScummVMRendererGraphicsDriver::SetGraphicsFilter(PSDLRenderFilter filter) {
_filter = filter;
OnSetFilter();
// TODO: support separate nearest and linear filters, initialize hint by calls to filter object
// e.g like D3D and OGL filters act
// SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
// SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // make the scaled rendering look smoother.
}
void ScummVMRendererGraphicsDriver::SetTintMethod(TintMethod /*method*/) {
// TODO: support new D3D-style tint method
}
bool ScummVMRendererGraphicsDriver::SetDisplayMode(const DisplayMode &mode) {
ReleaseDisplayMode();
set_color_depth(mode.ColorDepth);
if (_initGfxCallback != nullptr)
_initGfxCallback(nullptr);
if (!IsModeSupported(mode))
return false;
_capsVsync = true; // reset vsync flag, allow to try setting again
const int driver = GFX_SCUMMVM;
if (set_gfx_mode(driver, mode.Width, mode.Height, mode.ColorDepth) != 0)
return false;
if (g_system->hasFeature(OSystem::kFeatureVSync)) {
g_system->beginGFXTransaction();
g_system->setFeatureState(OSystem::kFeatureVSync, mode.Vsync);
g_system->endGFXTransaction();
}
else {
_capsVsync = false;
Debug::Printf(kDbgMsg_Warn, "WARNING: Vertical sync is not supported. Setting will be kept at driver default.");
}
OnInit();
OnModeSet(mode);
return true;
}
void ScummVMRendererGraphicsDriver::UpdateDeviceScreen(const Size &screen_sz) {
_mode.Width = screen_sz.Width;
_mode.Height = screen_sz.Height;
#if AGS_PLATFORM_OS_ANDROID
SDL_RenderSetLogicalSize(_renderer, _mode.Width, _mode.Height);
#endif
}
void ScummVMRendererGraphicsDriver::CreateVirtualScreen() {
if (!IsNativeSizeValid())
return;
DestroyVirtualScreen();
// Initialize virtual screen; size is equal to native resolution
const int vscreen_w = _srcRect.GetWidth();
const int vscreen_h = _srcRect.GetHeight();
_origVirtualScreen.reset(new Bitmap(vscreen_w, vscreen_h, _srcColorDepth));
virtualScreen = _origVirtualScreen.get();
_stageVirtualScreen = virtualScreen;
_lastTexPixels = nullptr;
_lastTexPitch = -1;
}
void ScummVMRendererGraphicsDriver::DestroyVirtualScreen() {
delete[] _fakeTexBitmap; // don't use destroy_bitmap(), because it's a fake structure
_fakeTexBitmap = nullptr;
_origVirtualScreen.reset();
virtualScreen = nullptr;
_stageVirtualScreen = nullptr;
}
void ScummVMRendererGraphicsDriver::ReleaseDisplayMode() {
OnModeReleased();
ClearDrawLists();
}
bool ScummVMRendererGraphicsDriver::SetNativeResolution(const GraphicResolution &native_res) {
OnSetNativeRes(native_res);
CreateVirtualScreen();
return !_srcRect.IsEmpty();
}
bool ScummVMRendererGraphicsDriver::SetRenderFrame(const Rect &dst_rect) {
OnSetRenderFrame(dst_rect);
return !_dstRect.IsEmpty();
}
void ScummVMRendererGraphicsDriver::ClearRectangle(int /*x1*/, int /*y1*/, int /*x2*/, int /*y2*/, RGB * /*colorToUse*/) {
// TODO: but maybe is not necessary, as we use SDL_Renderer with accelerated gfx here?
// See SDL_RenderDrawRect
}
void ScummVMRendererGraphicsDriver::UnInit() {
OnUnInit();
ReleaseDisplayMode();
DestroyVirtualScreen();
sys_window_destroy();
}
bool ScummVMRendererGraphicsDriver::SupportsGammaControl() {
return _hasGamma;
}
void ScummVMRendererGraphicsDriver::SetGamma(int newGamma) {
if (!_hasGamma) {
return;
}
#ifdef TODO
uint16 gamma_red[256];
uint16 gamma_green[256];
uint16 gamma_blue[256];
for (int i = 0; i < 256; i++) {
gamma_red[i] = MIN(((int)_defaultGammaRed[i] * newGamma) / 100, 0xffff);
gamma_green[i] = MIN(((int)_defaultGammaGreen[i] * newGamma) / 100, 0xffff);
gamma_blue[i] = MIN(((int)_defaultGammaBlue[i] * newGamma) / 100, 0xffff);
}
SDL_SetWindowGammaRamp(sys_get_window(), gamma_red, gamma_green, gamma_blue);
_gamma = newGamma;
#endif
}
bool ScummVMRendererGraphicsDriver::DoesSupportVsyncToggle() {
return g_system->hasFeature(OSystem::kFeatureVSync);
}
bool ScummVMRendererGraphicsDriver::SetVsyncImpl(bool enabled, bool &vsync_res) {
if (g_system->hasFeature(OSystem::kFeatureVSync)) {
g_system->beginGFXTransaction();
g_system->setFeatureState(OSystem::kFeatureVSync, enabled);
g_system->endGFXTransaction();
vsync_res = g_system->getFeatureState(OSystem::kFeatureVSync);
if (!vsync_res)
Debug::Printf(kDbgMsg_Warn, "Renderer: SetVsync (%d) failed", enabled);
return vsync_res;
}
return false;
}
int ScummVMRendererGraphicsDriver::GetCompatibleBitmapFormat(int color_depth) {
return color_depth;
}
IDriverDependantBitmap *ScummVMRendererGraphicsDriver::CreateDDB(int width, int height, int color_depth, bool opaque) {
return new ALSoftwareBitmap(width, height, color_depth, opaque);
}
IDriverDependantBitmap *ScummVMRendererGraphicsDriver::CreateDDBFromBitmap(Bitmap *bitmap, bool has_alpha, bool opaque) {
return new ALSoftwareBitmap(bitmap, has_alpha, opaque);
}
IDriverDependantBitmap *ScummVMRendererGraphicsDriver::CreateRenderTargetDDB(int width, int height, int color_depth, bool opaque) {
return new ALSoftwareBitmap(width, height, color_depth, opaque);
}
void ScummVMRendererGraphicsDriver::UpdateDDBFromBitmap(IDriverDependantBitmap *bitmapToUpdate, Bitmap *bitmap, bool has_alpha) {
ALSoftwareBitmap *alSwBmp = (ALSoftwareBitmap *)bitmapToUpdate;
alSwBmp->_bmp = bitmap;
alSwBmp->_hasAlpha = has_alpha;
}
void ScummVMRendererGraphicsDriver::DestroyDDB(IDriverDependantBitmap *bitmap) {
delete (ALSoftwareBitmap *)bitmap;
}
void ScummVMRendererGraphicsDriver::InitSpriteBatch(size_t index, const SpriteBatchDesc &desc) {
if (_spriteBatches.size() <= index)
_spriteBatches.resize(index + 1);
ALSpriteBatch &batch = _spriteBatches[index];
batch.ID = index;
// Apply parent batch's settings, if preset;
Rect viewport = desc.Viewport;
SpriteTransform transform = desc.Transform;
Bitmap *parent_surf = virtualScreen;
if (desc.Parent != UINT32_MAX) {
const auto &parent = _spriteBatches[desc.Parent];
if (parent.Surface)
parent_surf = parent.Surface.get();
// NOTE: we prioritize parent's surface size as a dest viewport,
// because parent may have a scheduled scaled blit.
if (viewport.IsEmpty())
viewport = parent_surf ? RectWH(parent_surf->GetSize()) : RectWH(parent.Viewport.GetSize());
} else if (viewport.IsEmpty()) {
viewport = _srcRect;
}
// Calculate expected source surf size, based on dest viewport and scaling
const int src_w = viewport.GetWidth() / transform.ScaleX;
const int src_h = viewport.GetHeight() / transform.ScaleY;
// Initialize batch surface, depending on the batch description.
// Surface was prepared externally (common for room cameras)
if (desc.Surface != nullptr) {
batch.Surface = desc.Surface;
batch.Opaque = true;
batch.IsParentRegion = false;
}
// In case something was not initialized
else if (desc.Viewport.IsEmpty() || !virtualScreen) {
batch.Surface.reset();
batch.Opaque = false;
batch.IsParentRegion = false;
}
// Drawing directly on a viewport without transformation (other than offset):
// then make a subbitmap of the parent surface (virtualScreen or else).
else if (transform.ScaleX == 1.f && transform.ScaleY == 1.f) {
// We need this subbitmap for plugins, which use _stageVirtualScreen and are unaware of possible multiple viewports;
// TODO: there could be ways to optimize this further, but best is to update plugin rendering hooks (and upgrade plugins)
if (!batch.Surface || !batch.IsParentRegion ||
(!batch.Surface->IsSameBitmap(parent_surf)) ||
(batch.Surface->GetSize() != Size(src_w, src_h)) ||
(batch.Surface->GetSubOffset() != viewport.GetLT())) {
batch.Surface.reset(BitmapHelper::CreateSubBitmap(parent_surf, viewport));
}
batch.Opaque = true;
batch.IsParentRegion = true;
// Because we sub-bitmap to viewport, render offsets should account for that
transform.X -= viewport.Left;
transform.Y -= viewport.Top;
}
// No surface prepared and has transformation other than offset:
// then create exclusive intermediate bitmap.
else {
if (!batch.Surface || batch.IsParentRegion || (batch.Surface->GetSize() != Size(src_w, src_h))) {
batch.Surface.reset(new Bitmap(src_w, src_h, _srcColorDepth));
}
batch.Opaque = false;
batch.IsParentRegion = false;
}
batch.Viewport = viewport;
batch.Transform = transform;
}
void ScummVMRendererGraphicsDriver::ResetAllBatches() {
// NOTE: we don't release batches themselves here, only sprite lists.
// This is because we cache batch surfaces, for performance reasons.
_spriteList.clear();
}
void ScummVMRendererGraphicsDriver::DrawSprite(int x, int y, IDriverDependantBitmap *bitmap) {
assert(_actSpriteBatch != UINT32_MAX);
_spriteList.push_back(ALDrawListEntry((ALSoftwareBitmap *)bitmap, _actSpriteBatch, x, y));
}
void ScummVMRendererGraphicsDriver::SetScreenFade(int /*red*/, int /*green*/, int /*blue*/) {
// TODO: was not necessary atm
// TODO: checkme later
}
void ScummVMRendererGraphicsDriver::SetScreenTint(int red, int green, int blue) {
assert(_actSpriteBatch != UINT32_MAX);
_tint_red = red;
_tint_green = green;
_tint_blue = blue;
if (((_tint_red > 0) || (_tint_green > 0) || (_tint_blue > 0)) && (_srcColorDepth > 8)) {
_spriteList.push_back(
ALDrawListEntry(reinterpret_cast<ALSoftwareBitmap *>(DRAWENTRY_TINT), _actSpriteBatch, 0, 0));
}
}
void ScummVMRendererGraphicsDriver::SetStageScreen(const Size & /*sz*/, int /*x*/, int /*y*/) {
// unsupported, as using _stageVirtualScreen instead
}
void ScummVMRendererGraphicsDriver::RenderToBackBuffer() {
// Close unended batches, and issue a warning
assert(_actSpriteBatch == UINT32_MAX);
while (_actSpriteBatch != UINT32_MAX)
EndSpriteBatch();
if (_spriteBatchDesc.size() == 0) {
ClearDrawLists();
return; // no batches - no render
}
// Render all the sprite batches with necessary transformations
//
// NOTE: that's not immediately clear whether it would be faster to first draw upon a camera-sized
// surface then stretch final result to the viewport on screen, or stretch-blit each individual
// sprite right onto screen bitmap. We'd need to do proper profiling to know that.
// An important thing is that Allegro does not provide stretching functions for drawing sprites
// with blending and translucency; it seems you'd have to first stretch the original sprite onto a
// temp buffer and then TransBlendBlt / LitBlendBlt it to the final destination. Of course, doing
// that here would slow things down significantly, so if we ever go that way sprite caching will
// be required (similarly to how AGS caches flipped/scaled object sprites now for).
//
const size_t last_batch_to_rend = _spriteBatchDesc.size() - 1;
for (size_t cur_bat = 0u, last_bat = 0u, cur_spr = 0u; last_bat <= last_batch_to_rend;) {
// Test if we are entering this batch (and not continuing after coming back from nested)
if (cur_spr <= _spriteBatchRange[cur_bat].first) {
const auto &batch = _spriteBatches[cur_bat];
// Prepare the transparent surface
if (batch.Surface && !batch.Opaque)
batch.Surface->ClearTransparent();
}
// Render immediate batch sprites, if any, update cur_spr iterator
if ((cur_spr < _spriteList.size()) && (cur_bat == _spriteList[cur_spr].node)) {
const auto &batch = _spriteBatches[cur_bat];
const auto &batch_desc = _spriteBatchDesc[cur_bat];
Bitmap *surface = batch.Surface.get();
Bitmap *parent_surf = ((batch_desc.Parent != UINT32_MAX) && _spriteBatches[batch_desc.Parent].Surface) ? _spriteBatches[batch_desc.Parent].Surface.get() : virtualScreen;
const Rect &viewport = batch.Viewport;
const SpriteTransform &transform = batch.Transform;
_rendSpriteBatch = batch.ID;
parent_surf->SetClip(viewport); // CHECKME: this is not exactly correct?
if (surface && !batch.IsParentRegion) {
_stageVirtualScreen = surface;
cur_spr = RenderSpriteBatch(batch, cur_spr, surface, transform.X, transform.Y);
} else {
_stageVirtualScreen = surface ? surface : parent_surf;
cur_spr = RenderSpriteBatch(batch, cur_spr, _stageVirtualScreen, transform.X, transform.Y);
}
}
// Test if we're exiting current batch (and not going into nested ones):
// if there's no sprites belonging to this batch (direct, or nested),
// and if there's no nested batches (even if empty ones)
const uint32_t was_bat = cur_bat;
while ((cur_bat != UINT32_MAX) && (cur_spr >= _spriteBatchRange[cur_bat].second) &&
((last_bat == last_batch_to_rend) || (_spriteBatchDesc[last_bat + 1].Parent != cur_bat))) {
const auto &batch = _spriteBatches[cur_bat];
const auto &batch_desc = _spriteBatchDesc[cur_bat];
Bitmap *surface = batch.Surface.get();
Bitmap *parent_surf = ((batch_desc.Parent != UINT32_MAX) && _spriteBatches[batch_desc.Parent].Surface) ? _spriteBatches[batch_desc.Parent].Surface.get() : virtualScreen;
const Rect &viewport = batch.Viewport;
// If we're not drawing directly to the subregion of a parent surface,
// then blit our own surface to the parent's
if (surface && !batch.IsParentRegion) {
parent_surf->StretchBlt(surface, viewport, batch.Opaque ? kBitmap_Copy : kBitmap_Transparency);
}
// Back to the parent batch
cur_bat = batch_desc.Parent;
}
// If we stayed at the same batch, this means that there are still nested batches;
// if there's no batches in the stack left, this means we got to move forward anyway.
if ((was_bat == cur_bat) || (cur_bat == UINT32_MAX)) {
cur_bat = ++last_bat;
}
}
_stageVirtualScreen = virtualScreen;
_rendSpriteBatch = UINT32_MAX;
ClearDrawLists();
}
size_t ScummVMRendererGraphicsDriver::RenderSpriteBatch(const ALSpriteBatch &batch, size_t from, Bitmap *surface, int surf_offx, int surf_offy) {
for (; (from < _spriteList.size()) && (_spriteList[from].node == batch.ID); ++from) {
const auto &sprite = _spriteList[from];
if (sprite.ddb == nullptr) {
if (_spriteEvtCallback)
_spriteEvtCallback(sprite.x, sprite.y);
else
error("Unhandled attempt to draw null sprite");
// Stage surface could have been replaced by plugin
surface = _stageVirtualScreen;
continue;
} else if (sprite.ddb == reinterpret_cast<ALSoftwareBitmap *>(DRAWENTRY_TINT)) {
// draw screen tint fx
set_trans_blender(_tint_red, _tint_green, _tint_blue, 0);
surface->LitBlendBlt(surface, 0, 0, 128);
continue;
}
ALSoftwareBitmap *bitmap = sprite.ddb;
int drawAtX = sprite.x + surf_offx;
int drawAtY = sprite.y + surf_offy;
if (bitmap->_alpha == 0) {
} // fully transparent, do nothing
else if ((bitmap->_opaque) && (bitmap->_bmp == surface) && (bitmap->_alpha == 255)) {
} else if (bitmap->_opaque) {
surface->Blit(bitmap->_bmp, 0, 0, drawAtX, drawAtY, bitmap->_bmp->GetWidth(), bitmap->_bmp->GetHeight());
// TODO: we need to also support non-masked translucent blend, but...
// Allegro 4 **does not have such function ready** :( (only masked blends, where it skips magenta pixels);
// I am leaving this problem for the future, as coincidentally software mode does not need this atm.
} else if (bitmap->_hasAlpha) {
if (bitmap->_alpha == 255) // no global transparency, simple alpha blend
set_alpha_blender();
else
set_blender_mode(kArgbToRgbBlender, 0, 0, 0, bitmap->_alpha);
surface->TransBlendBlt(bitmap->_bmp, drawAtX, drawAtY);
} else {
// here _transparency is used as alpha (between 1 and 254), but 0 means opaque!
GfxUtil::DrawSpriteWithTransparency(surface, bitmap->_bmp, drawAtX, drawAtY,
bitmap->_alpha);
}
}
return from;
}
void ScummVMRendererGraphicsDriver::copySurface(const Graphics::Surface &src, bool mode) {
assert(src.w == _screen->w && src.h == _screen->h && src.pitch == _screen->pitch);
const uint32 *srcP = (const uint32 *)src.getPixels();
uint32 *destP = (uint32 *)_screen->getPixels();
uint32 pixel;
int x1 = 9999, y1 = 9999, x2 = -1, y2 = -1;
for (int y = 0; y < src.h; ++y) {
for (int x = 0; x < src.w; ++x, ++srcP, ++destP) {
if (!mode) {
pixel = (*srcP & 0xff00ff00) |
((*srcP & 0xff) << 16) |
((*srcP >> 16) & 0xff);
} else {
pixel = ((*srcP & 0xffffff) << 8) |
((*srcP >> 24) & 0xff);
}
if (*destP != pixel) {
*destP = pixel;
x1 = MIN(x1, x);
y1 = MIN(y1, y);
x2 = MAX(x2, x);
y2 = MAX(y2, y);
}
}
}
if (x2 != -1)
_screen->addDirtyRect(Common::Rect(x1, y1, x2 + 1, y2 + 1));
}
void ScummVMRendererGraphicsDriver::Present(int xoff, int yoff, Shared::GraphicFlip flip) {
Graphics::Surface *srcTransformed = nullptr;
if (xoff != 0 || yoff != 0 || flip != Shared::kFlip_None) {
srcTransformed = new Graphics::Surface();
srcTransformed->copyFrom(virtualScreen->GetAllegroBitmap()->getSurface());
switch(flip) {
case kFlip_Horizontal:
srcTransformed->flipHorizontal(Common::Rect(srcTransformed->w, srcTransformed->h));
break;
case kFlip_Vertical:
srcTransformed->flipVertical(Common::Rect(srcTransformed->w, srcTransformed->h));
break;
case kFlip_Both:
srcTransformed->flipHorizontal(Common::Rect(srcTransformed->w, srcTransformed->h));
srcTransformed->flipVertical(Common::Rect(srcTransformed->w, srcTransformed->h));
break;
default:
break;
}
srcTransformed->move(xoff, yoff, srcTransformed->h);
}
const Graphics::Surface &src = srcTransformed ?
*srcTransformed :
virtualScreen->GetAllegroBitmap()->getSurface();
enum {
kRenderInitial, kRenderDirect, kRenderToABGR, kRenderToRGBA,
kRenderOther
} renderMode;
// Check for rendering to use. The virtual screen can change, so I'm
// playing it safe and checking the render mode for each frame
const Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
if (src.format == screenFormat) {
// The virtual surface can be directly blitted to the screen
renderMode = kRenderDirect;
} else if (src.format != Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)) {
// Not a 32-bit surface, so will have to use an intermediate
// surface to correct the virtual screen to the correct format
renderMode = kRenderOther;
} else if (screenFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) {
renderMode = kRenderToRGBA;
} else if (screenFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)) {
renderMode = kRenderToABGR;
} else {
renderMode = kRenderOther;
}
if (renderMode != kRenderDirect && !_screen)
_screen = new Graphics::Screen();
switch (renderMode) {
case kRenderToABGR:
// ARGB to ABGR
copySurface(src, false);
break;
case kRenderToRGBA:
// ARGB to RGBA
copySurface(src, true);
break;
case kRenderOther: {
// Blit the surface to the temporary screen, ignoring the alphas.
// This takes care of converting to the screen format
Graphics::Surface srcCopy = src;
srcCopy.format.aLoss = 8;
_screen->blitFrom(srcCopy);
break;
}
case kRenderDirect:
// Blit the virtual surface directly to the screen
g_system->copyRectToScreen(src.getPixels(), src.pitch,
0, 0, src.w, src.h);
g_system->updateScreen();
if (srcTransformed) {
srcTransformed->free();
delete srcTransformed;
}
return;
default:
break;
}
if (srcTransformed) {
srcTransformed->free();
delete srcTransformed;
}
if (_screen)
_screen->update();
}
void ScummVMRendererGraphicsDriver::Render(int xoff, int yoff, GraphicFlip flip) {
RenderToBackBuffer();
Present(xoff, yoff, flip);
}
void ScummVMRendererGraphicsDriver::Render() {
Render(0, 0, kFlip_None);
}
Bitmap *ScummVMRendererGraphicsDriver::GetMemoryBackBuffer() {
return virtualScreen;
}
void ScummVMRendererGraphicsDriver::SetMemoryBackBuffer(Bitmap *backBuffer) {
// We need to also test internal AL BITMAP pointer, because we may receive it raw from plugin,
// in which case the Bitmap object may be a different wrapper over our own virtual screen.
if (backBuffer && (backBuffer->GetAllegroBitmap() != _origVirtualScreen->GetAllegroBitmap())) {
virtualScreen = backBuffer;
} else {
virtualScreen = _origVirtualScreen.get();
}
_stageVirtualScreen = virtualScreen;
// Reset old virtual screen's subbitmaps;
// NOTE: this MUST NOT be called in the midst of the RenderSpriteBatches!
assert(_rendSpriteBatch == UINT32_MAX);
if (_rendSpriteBatch != UINT32_MAX)
return;
for (auto &batch : _spriteBatches) {
if (batch.IsParentRegion)
batch.Surface.reset();
}
}
Bitmap *ScummVMRendererGraphicsDriver::GetStageBackBuffer(bool /*mark_dirty*/) {
return _stageVirtualScreen;
}
void ScummVMRendererGraphicsDriver::SetStageBackBuffer(Bitmap *backBuffer) {
Bitmap *cur_stage = (_rendSpriteBatch == UINT32_MAX) ? virtualScreen : _spriteBatches[_rendSpriteBatch].Surface.get();
// We need to also test internal AL BITMAP pointer, because we may receive it raw from plugin,
// in which case the Bitmap object may be a different wrapper over our own virtual screen.
if (backBuffer && (backBuffer->GetAllegroBitmap() != cur_stage->GetAllegroBitmap()))
_stageVirtualScreen = backBuffer;
else
_stageVirtualScreen = cur_stage;
}
bool ScummVMRendererGraphicsDriver::GetCopyOfScreenIntoBitmap(Bitmap *destination, const Rect *src_rect, bool at_native_res,
GraphicResolution *want_fmt, uint32_t /*batch_skip_filter*/) {
(void)at_native_res; // software driver always renders at native resolution at the moment
// software filter is taught to copy to any size, so only check color depth
if (destination->GetColorDepth() != _srcColorDepth) {
if (want_fmt)
*want_fmt = GraphicResolution(destination->GetWidth(), destination->GetHeight(), _srcColorDepth);
return false;
}
Rect copy_from = src_rect ? *src_rect : _srcRect;
if (destination->GetSize() == copy_from.GetSize()) {
destination->Blit(virtualScreen, copy_from.Left, copy_from.Top, 0, 0, copy_from.GetWidth(), copy_from.GetHeight());
} else {
destination->StretchBlt(virtualScreen, copy_from, RectWH(destination->GetSize()));
}
return true;
}
/**
fade.c - High Color Fading Routines
Last Revision: 21 June, 2002
Author: Matthew Leverton
**/
void ScummVMRendererGraphicsDriver::highcolor_fade_in(Bitmap *vs, void(*draw_callback)(),
int speed, int targetColourRed, int targetColourGreen, int targetColourBlue) {
Bitmap *bmp_orig = vs;
const int col_depth = bmp_orig->GetColorDepth();
const int clearColor = makecol_depth(col_depth, targetColourRed, targetColourGreen, targetColourBlue);
if (speed <= 0) speed = 16;
Bitmap *bmp_buff = new Bitmap(bmp_orig->GetWidth(), bmp_orig->GetHeight(), col_depth);
SetMemoryBackBuffer(bmp_buff);
for (int a = 0; a < 256; a += speed) {
bmp_buff->Fill(clearColor);
set_trans_blender(0, 0, 0, a);
bmp_buff->TransBlendBlt(bmp_orig, 0, 0);
if (draw_callback)
draw_callback();
RenderToBackBuffer();
Present();
sys_evt_process_pending();
if (_pollingCallback)
_pollingCallback();
WaitForNextFrame();
}
delete bmp_buff;
SetMemoryBackBuffer(vs);
if (draw_callback)
draw_callback();
RenderToBackBuffer();
Present();
}
void ScummVMRendererGraphicsDriver::highcolor_fade_out(Bitmap *vs, void(*draw_callback)(),
int speed, int targetColourRed, int targetColourGreen, int targetColourBlue) {
Bitmap *bmp_orig = vs;
const int col_depth = vs->GetColorDepth();
const int clearColor = makecol_depth(col_depth, targetColourRed, targetColourGreen, targetColourBlue);
if (speed <= 0) speed = 16;
Bitmap *bmp_buff = new Bitmap(bmp_orig->GetWidth(), bmp_orig->GetHeight(), col_depth);
SetMemoryBackBuffer(bmp_buff);
for (int a = 255 - speed; a > 0; a -= speed) {
bmp_buff->Fill(clearColor);
set_trans_blender(0, 0, 0, a);
bmp_buff->TransBlendBlt(bmp_orig, 0, 0);
if (draw_callback)
draw_callback();
RenderToBackBuffer();
Present();
sys_evt_process_pending();
if (_pollingCallback)
_pollingCallback();
WaitForNextFrame();
}
delete bmp_buff;
SetMemoryBackBuffer(vs);
vs->Clear(clearColor);
if (draw_callback)
draw_callback();
RenderToBackBuffer();
Present();
}
/** END FADE.C **/
// palette fading routiens
// from allegro, modified for mp3
void initialize_fade_256(int r, int g, int b) {
int a;
for (a = 0; a < 256; a++) {
faded_out_palette[a].r = r / 4;
faded_out_palette[a].g = g / 4;
faded_out_palette[a].b = b / 4;
}
}
void ScummVMRendererGraphicsDriver::__fade_from_range(PALETTE source, PALETTE dest, int speed, int from, int to) {
PALETTE temp;
int c;
for (c = 0; c < PAL_SIZE; c++)
temp[c] = source[c];
for (c = 0; c < 64; c += speed) {
fade_interpolate(source, dest, temp, c, from, to);
set_palette_range(temp, from, to, TRUE);
RenderToBackBuffer();
Present();
g_system->delayMillis(5);
sys_evt_process_pending();
if (_pollingCallback)
_pollingCallback();
WaitForNextFrame();
}
set_palette_range(dest, from, to, TRUE);
}
void ScummVMRendererGraphicsDriver::__fade_out_range(int speed, int from, int to, int targetColourRed, int targetColourGreen, int targetColourBlue) {
PALETTE temp;
initialize_fade_256(targetColourRed, targetColourGreen, targetColourBlue);
get_palette(temp);
__fade_from_range(temp, faded_out_palette, speed, from, to);
}
void ScummVMRendererGraphicsDriver::FadeOut(int speed, int targetColourRed, int targetColourGreen, int targetColourBlue, uint32_t /*batch_skip_filter*/) {
if (_srcColorDepth > 8) {
highcolor_fade_out(virtualScreen, _drawPostScreenCallback, speed * 4, targetColourRed, targetColourGreen, targetColourBlue);
} else {
__fade_out_range(speed, 0, 255, targetColourRed, targetColourGreen, targetColourBlue);
}
}
void ScummVMRendererGraphicsDriver::FadeIn(int speed, PALETTE p, int targetColourRed, int targetColourGreen, int targetColourBlue, uint32_t /*batch_skip_filter*/) {
if (_drawScreenCallback) {
_drawScreenCallback();
RenderToBackBuffer();
}
if (_srcColorDepth > 8) {
highcolor_fade_in(virtualScreen, _drawPostScreenCallback, speed * 4, targetColourRed, targetColourGreen, targetColourBlue);
} else {
initialize_fade_256(targetColourRed, targetColourGreen, targetColourBlue);
__fade_from_range(faded_out_palette, p, speed, 0, 255);
}
}
void ScummVMRendererGraphicsDriver::BoxOutEffect(bool blackingOut, int speed, int delay, uint32_t /*batch_skip_filter*/) {
if (blackingOut) {
int yspeed = _srcRect.GetHeight() / (_srcRect.GetWidth() / speed);
int boxwid = speed, boxhit = yspeed;
Bitmap *bmp_orig = virtualScreen;
Bitmap *bmp_buff = new Bitmap(bmp_orig->GetWidth(), bmp_orig->GetHeight(), bmp_orig->GetColorDepth());
SetMemoryBackBuffer(bmp_buff);
while (boxwid < _srcRect.GetWidth()) {
boxwid += speed;
boxhit += yspeed;
int vcentre = _srcRect.GetHeight() / 2;
bmp_orig->FillRect(Rect(_srcRect.GetWidth() / 2 - boxwid / 2, vcentre - boxhit / 2,
_srcRect.GetWidth() / 2 + boxwid / 2, vcentre + boxhit / 2), 0);
bmp_buff->Fill(0);
bmp_buff->Blit(bmp_orig);
if (_drawPostScreenCallback)
_drawPostScreenCallback();
RenderToBackBuffer();
Present();
sys_evt_process_pending();
if (_pollingCallback)
_pollingCallback();
_G(platform)->Delay(delay);
}
delete bmp_buff;
SetMemoryBackBuffer(bmp_orig);
} else {
error("BoxOut fade-in not implemented in sw gfx driver");
}
}
// end fading routines
ScummVMRendererGraphicsFactory *ScummVMRendererGraphicsFactory::_factory = nullptr;
ScummVMRendererGraphicsFactory::~ScummVMRendererGraphicsFactory() {
_factory = nullptr;
}
size_t ScummVMRendererGraphicsFactory::GetFilterCount() const {
return 1;
}
const GfxFilterInfo *ScummVMRendererGraphicsFactory::GetFilterInfo(size_t index) const {
switch (index) {
case 0:
return _G(scummvmGfxFilter);
default:
return nullptr;
}
}
String ScummVMRendererGraphicsFactory::GetDefaultFilterID() const {
return _GP(scummvmGfxFilter).Id;
}
/* static */ ScummVMRendererGraphicsFactory *ScummVMRendererGraphicsFactory::GetFactory() {
if (!_factory)
_factory = new ScummVMRendererGraphicsFactory();
return _factory;
}
ScummVMRendererGraphicsDriver *ScummVMRendererGraphicsFactory::EnsureDriverCreated() {
if (!_driver)
_driver = new ScummVMRendererGraphicsDriver();
return _driver;
}
ScummVMRendererGfxFilter *ScummVMRendererGraphicsFactory::CreateFilter(const String &id) {
if (_GP(scummvmGfxFilter).Id.CompareNoCase(id) == 0)
return new ScummVMRendererGfxFilter();
return nullptr;
}
} // namespace ALSW
} // namespace Engine
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,330 @@
/* 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/>.
*
*/
//=============================================================================
//
// Software graphics factory, draws raw bitmaps onto a virtual screen,
// converts to SDL_Texture and finally presents with SDL_Renderer.
//
// TODO: replace nearest-neighbour software filter with SDL's own accelerated
// scaling, maybe add more filter types if SDL renderer supports them.
// Only keep Hqx filter as a software option (might need to change how the
// filter code works).
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_ALI_3D_SCUMMVM_H
#define AGS_ENGINE_GFX_ALI_3D_SCUMMVM_H
#include "common/std/memory.h"
#include "common/std/vector.h"
#include "ags/shared/core/platform.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/engine/gfx/ddb.h"
#include "ags/engine/gfx/gfx_driver_factory_base.h"
#include "ags/engine/gfx/gfx_driver_base.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
namespace ALSW {
class ScummVMRendererGraphicsDriver;
class ScummVMRendererGfxFilter;
using AGS::Shared::Bitmap;
enum RendererFlip {
FLIP_NONE = 0x00000000, /**< Do not flip */
FLIP_HORIZONTAL = 0x00000001, /**< flip horizontally */
FLIP_VERTICAL = 0x00000002 /**< flip vertically */
};
class ALSoftwareBitmap : public BaseDDB {
public:
uint32_t GetRefID() const override { return UINT32_MAX /* not supported */; }
int GetAlpha() const override {
return _alpha;
}
void SetAlpha(int alpha) override {
_alpha = alpha;
}
void SetFlippedLeftRight(bool isFlipped) override {
_flipped = isFlipped;
}
void SetStretch(int width, int height, bool /*useResampler*/) override {
_stretchToWidth = width;
_stretchToHeight = height;
}
void SetLightLevel(int /*lightLevel*/) override {}
void SetTint(int /*red*/, int /*green*/, int /*blue*/, int /*tintSaturation*/) override {}
Bitmap *_bmp = nullptr;
bool _flipped = false;
int _stretchToWidth = 0, _stretchToHeight = 0;
int _alpha = 255;
ALSoftwareBitmap(int width, int height, int color_depth, bool opaque) {
_width = width;
_height = height;
_colDepth = color_depth;
_opaque = opaque;
_stretchToWidth = _width;
_stretchToHeight = _height;
}
ALSoftwareBitmap(Bitmap *bmp, bool has_alpha, bool opaque) {
_bmp = bmp;
_width = bmp->GetWidth();
_height = bmp->GetHeight();
_colDepth = bmp->GetColorDepth();
_opaque = opaque;
_hasAlpha = has_alpha;
_stretchToWidth = _width;
_stretchToHeight = _height;
}
int GetWidthToRender() {
return _stretchToWidth;
}
int GetHeightToRender() {
return _stretchToHeight;
}
~ALSoftwareBitmap() override = default;
};
class ScummVMRendererGfxModeList : public IGfxModeList {
public:
ScummVMRendererGfxModeList(const std::vector<DisplayMode> &modes)
: _modes(modes) {
}
int GetModeCount() const override {
return _modes.size();
}
bool GetMode(int index, DisplayMode &mode) const override {
if (index >= 0 && (size_t)index < _modes.size()) {
mode = _modes[index];
return true;
}
return false;
}
private:
std::vector<DisplayMode> _modes;
};
typedef SpriteDrawListEntry<ALSoftwareBitmap> ALDrawListEntry;
// Software renderer's sprite batch
struct ALSpriteBatch {
uint32_t ID = 0u;
// Clipping viewport, also used as a destination for blitting optional Surface;
// in *relative* coordinates to parent surface.
Rect Viewport;
// Optional model transformation, to be applied to each sprite
SpriteTransform Transform;
// Intermediate surface which will be drawn upon and transformed if necessary
std::shared_ptr<Bitmap> Surface;
// Whether surface is a parent surface's region (e.g. virtual screen)
bool IsParentRegion = false;
// Tells whether the surface is treated as opaque or transparent
bool Opaque = false;
};
typedef std::vector<ALSpriteBatch> ALSpriteBatches;
class ScummVMRendererGraphicsDriver : public GraphicsDriverBase {
public:
ScummVMRendererGraphicsDriver();
~ScummVMRendererGraphicsDriver() override;
const char *GetDriverID() override {
return "Software";
}
bool RequiresFullRedrawEachFrame() override { return false; }
bool HasAcceleratedTransform() override { return false; }
bool UsesMemoryBackBuffer() override { return true; }
bool ShouldReleaseRenderTargets() override { return false; }
const char *GetDriverName() override {
return "ScummVM 2D renderer";
}
void SetTintMethod(TintMethod /*method*/) override;
bool SetDisplayMode(const DisplayMode &mode) override;
void UpdateDeviceScreen(const Size &screen_sz) override;
bool SetNativeResolution(const GraphicResolution &native_res) override;
bool SetRenderFrame(const Rect &dst_rect) override;
bool IsModeSupported(const DisplayMode &mode) override;
int GetDisplayDepthForNativeDepth(int native_color_depth) const override;
IGfxModeList *GetSupportedModeList(int color_depth) override;
PGfxFilter GetGraphicsFilter() const override;
void UnInit();
// Clears the screen rectangle. The coordinates are expected in the **native game resolution**.
void ClearRectangle(int x1, int y1, int x2, int y2, RGB *colorToUse) override;
int GetCompatibleBitmapFormat(int color_depth) override;
size_t GetAvailableTextureMemory() override {
// not using textures for sprites anyway
return 0;
}
IDriverDependantBitmap *CreateDDB(int width, int height, int color_depth, bool opaque) override;
IDriverDependantBitmap *CreateDDBFromBitmap(Bitmap *bitmap, bool has_alpha, bool opaque) override;
IDriverDependantBitmap *CreateRenderTargetDDB(int width, int height, int color_depth, bool opaque) override;
void UpdateDDBFromBitmap(IDriverDependantBitmap *ddb, Bitmap *bitmap, bool has_alpha) override;
void DestroyDDB(IDriverDependantBitmap *ddb) override;
IDriverDependantBitmap *GetSharedDDB(uint32_t /*sprite_id*/,
Bitmap *bitmap, bool has_alpha, bool opaque) override {
// Software renderer does not require a texture cache, because it uses bitmaps directly
return CreateDDBFromBitmap(bitmap, has_alpha, opaque);
}
void UpdateSharedDDB(uint32_t /*sprite_id*/, Bitmap */*bitmap*/, bool /*has_alpha*/, bool /*opaque*/) override {
/* do nothing */
}
void ClearSharedDDB(uint32_t /*sprite_id*/) override {
/* do nothing */
}
void DrawSprite(int x, int y, IDriverDependantBitmap *ddb) override;
void SetScreenFade(int red, int green, int blue) override;
void SetScreenTint(int red, int green, int blue) override;
void SetStageScreen(const Size &sz, int x = 0, int y = 0) override;
void RenderToBackBuffer() override;
void Render() override;
void Render(int xoff, int yoff, Shared::GraphicFlip flip) override;
bool GetCopyOfScreenIntoBitmap(Bitmap *destination, const Rect *src_rect, bool at_native_res, GraphicResolution *want_fmt,
uint32_t batch_skip_filter = 0u) override;
void FadeOut(int speed, int targetColourRed, int targetColourGreen, int targetColourBlue,
uint32_t batch_skip_filter = 0u) override;
void FadeIn(int speed, PALETTE pal, int targetColourRed, int targetColourGreen, int targetColourBlue,
uint32_t batch_skip_filter = 0u) override;
void BoxOutEffect(bool blackingOut, int speed, int delay, uint32_t batch_skip_filter = 0u) override;
bool SupportsGammaControl() override;
void SetGamma(int newGamma) override;
void UseSmoothScaling(bool /*enabled*/) override {}
bool DoesSupportVsyncToggle() override;
void RenderSpritesAtScreenResolution(bool /*enabled*/) override {}
Bitmap *GetMemoryBackBuffer() override;
void SetMemoryBackBuffer(Bitmap *backBuffer) override;
Bitmap *GetStageBackBuffer(bool mark_dirty) override;
void SetStageBackBuffer(Bitmap *backBuffer) override;
bool GetStageMatrixes(RenderMatrixes & /*rm*/) override {
return false; /* not supported */
}
typedef std::shared_ptr<ScummVMRendererGfxFilter> PSDLRenderFilter;
void SetGraphicsFilter(PSDLRenderFilter filter);
protected:
bool SetVsyncImpl(bool vsync, bool &vsync_res) override;
size_t GetLastDrawEntryIndex() override {
return _spriteList.size();
}
private:
Graphics::Screen *_screen = nullptr;
PSDLRenderFilter _filter;
bool _hasGamma = false;
#ifdef TODO
uint16 _defaultGammaRed[256] {};
uint16 _defaultGammaGreen[256] {};
uint16 _defaultGammaBlue[256] {};
int _gamma = 100;
#endif
/* SDL_Renderer *_renderer = nullptr;
SDL_Texture *_screenTex = nullptr; */
// BITMAP struct for wrapping screen texture locked pixels, so that we may use blit()
BITMAP *_fakeTexBitmap = nullptr;
unsigned char *_lastTexPixels = nullptr;
int _lastTexPitch = -1;
// Original virtual screen created and managed by the renderer.
std::unique_ptr<Bitmap> _origVirtualScreen;
// Current virtual screen bitmap; may be either pointing to _origVirtualScreen,
// or provided by external user (for example - plugin).
// Its pixels are copied to the video texture to be presented by SDL_Renderer.
Bitmap *virtualScreen;
// Stage screen meant for particular rendering stages, may be referencing
// actual virtual screen or separate bitmap of different size that is
// blitted to virtual screen at the stage finalization.
Bitmap *_stageVirtualScreen;
int _tint_red, _tint_green, _tint_blue;
// Sprite batches (parent scene nodes)
ALSpriteBatches _spriteBatches;
// List of sprites to render
std::vector<ALDrawListEntry> _spriteList;
void InitSpriteBatch(size_t index, const SpriteBatchDesc &desc) override;
void ResetAllBatches() override;
// Use gfx filter to create a new virtual screen
void CreateVirtualScreen();
void DestroyVirtualScreen();
// Unset parameters and release resources related to the display mode
void ReleaseDisplayMode();
// Renders single sprite batch on the precreated surface
size_t RenderSpriteBatch(const ALSpriteBatch &batch, size_t from, Shared::Bitmap *surface, int surf_offx, int surf_offy);
void highcolor_fade_in(Bitmap *vs, void(*draw_callback)(), int speed, int targetColourRed, int targetColourGreen, int targetColourBlue);
void highcolor_fade_out(Bitmap *vs, void(*draw_callback)(), int speed, int targetColourRed, int targetColourGreen, int targetColourBlue);
void __fade_from_range(PALETTE source, PALETTE dest, int speed, int from, int to);
void __fade_out_range(int speed, int from, int to, int targetColourRed, int targetColourGreen, int targetColourBlue);
// Copy raw screen bitmap pixels to the screen
void copySurface(const Graphics::Surface &src, bool mode);
// Render bitmap on screen
void Present(int xoff = 0, int yoff = 0, Shared::GraphicFlip flip = Shared::kFlip_None);
};
class ScummVMRendererGraphicsFactory : public GfxDriverFactoryBase<ScummVMRendererGraphicsDriver, ScummVMRendererGfxFilter> {
public:
~ScummVMRendererGraphicsFactory() override;
size_t GetFilterCount() const override;
const GfxFilterInfo *GetFilterInfo(size_t index) const override;
String GetDefaultFilterID() const override;
static ScummVMRendererGraphicsFactory *GetFactory();
private:
ScummVMRendererGraphicsDriver *EnsureDriverCreated() override;
ScummVMRendererGfxFilter *CreateFilter(const String &id) override;
static ScummVMRendererGraphicsFactory *_factory;
};
} // namespace ALSW
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,51 @@
/* 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 "ags/engine/gfx/blender.h"
#include "ags/lib/allegro.h"
#include "ags/shared/core/types.h"
namespace AGS3 {
void set_my_trans_blender(int r, int g, int b, int a) {
// use standard allegro 15 and 16 bit blenders, but customize
// the 32-bit one to preserve the alpha channel
set_blender_mode(kAlphaPreservedBlenderMode, r, g, b, a);
}
void set_additive_alpha_blender() {
set_blender_mode(kAdditiveBlenderMode, 0, 0, 0, 0);
}
void set_argb2argb_blender(int alpha) {
set_blender_mode(kArgbToArgbBlender, 0, 0, 0, alpha);
}
void set_opaque_alpha_blender() {
set_blender_mode(kOpaqueBlenderMode, 0, 0, 0, 0);
}
void set_argb2any_blender() {
// TODO: Properly implement this new mode
set_blender_mode(kArgbToArgbBlender, 0, 0, 0, 0xff);
}
} // namespace AGS3

View File

@@ -0,0 +1,58 @@
/* 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/>.
*
*/
//=============================================================================
//
// AGS specific color blending routines for transparency and tinting effects
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_BLENDER_H
#define AGS_ENGINE_GFX_BLENDER_H
namespace AGS3 {
//
// Allegro's standard alpha blenders result in:
// - src and dst RGB are combined proportionally to src alpha
// (src.rgb * src.alpha + dst.rgb * (1 - dst.alpha));
// - final alpha is zero.
// This blender is suggested for use with opaque destinations
// (ones without alpha channel).
//
/* Declared in Allegro's color.h:
void set_alpha_blender();
*/
// Customizable alpha blender that uses the supplied alpha value as src alpha,
// and preserves destination's alpha channel (if there was one);
void set_my_trans_blender(int r, int g, int b, int a);
// Additive alpha blender plain copies src over, applying a summ of src and
// dst alpha values.
void set_additive_alpha_blender();
// Opaque alpha blender plain copies src over, applying opaque alpha value.
void set_opaque_alpha_blender();
// Sets argb2argb for 32-bit mode, and provides appropriate funcs for blending 32-bit onto 15/16/24-bit destination
void set_argb2any_blender();
} // namespace AGS3
#endif

View File

@@ -0,0 +1,66 @@
/* 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/>.
*
*/
//=============================================================================
//
// Implementation from wgt2allg.cpp specific to Engine runtime
//
//=============================================================================
#include "ags/lib/allegro.h"
#include "ags/shared/util/wgt2_allg.h"
namespace AGS3 {
void __my_setcolor(int *ctset, int newcol, int wantColDep) {
if (wantColDep == 8)
ctset[0] = newcol;
else if (newcol & 0x40000000) // already calculated it
ctset[0] = newcol;
else if ((newcol >= 32) && (wantColDep > 16)) {
// true-color
#ifdef SWAP_RB_HICOL_FOR_32to24_32
ctset[0] = makeacol32(getb16(newcol), getg16(newcol), getr16(newcol), 255);
#else
ctset[0] = makeacol32(getr16(newcol), getg16(newcol), getb16(newcol), 255);
#endif
} else if (newcol >= 32) {
// If it's 15-bit, convert the color
if (wantColDep == 15)
ctset[0] = (newcol & 0x001f) | ((newcol >> 1) & 0x7fe0);
else
ctset[0] = newcol;
} else {
ctset[0] = makecol_depth(wantColDep, col_lookups[newcol] >> 16,
(col_lookups[newcol] >> 8) & 0x000ff, col_lookups[newcol] & 0x000ff);
// in case it's used on an alpha-channel sprite, make sure it's visible
if (wantColDep > 16)
ctset[0] |= 0xff000000;
}
// if it's 32-bit color, signify that the colour has been calculated
//if (wantColDep >= 24)
// ctset[0] |= 0x40000000;
}
} // namespace AGS3

View File

@@ -0,0 +1,65 @@
/* 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/>.
*
*/
//=============================================================================
//
// Driver-dependant bitmap interface.
//
// This interface describes an individual sprite object. The actual texture
// data (pixel data) may be shared among multiple DDBs, while DDB define
// additional settings telling how to present the texture: transform, colorize,
// and so on.
//=============================================================================
#ifndef AGS_ENGINE_GFX_DDB_H
#define AGS_ENGINE_GFX_DDB_H
namespace AGS3 {
namespace AGS {
namespace Engine {
class IDriverDependantBitmap {
public:
// Get an arbitrary sprite ID, returns UINT32_MAX if does not have one
virtual uint32_t GetRefID() const = 0;
virtual int GetAlpha() const = 0;
virtual void SetAlpha(int alpha) = 0; // 0-255
virtual void SetFlippedLeftRight(bool isFlipped) = 0;
virtual void SetStretch(int width, int height, bool useResampler = true) = 0;
virtual void SetLightLevel(int light_level) = 0; // 0-255
virtual void SetTint(int red, int green, int blue, int tintSaturation) = 0; // 0-255
virtual int GetWidth() const = 0;
virtual int GetHeight() const = 0;
virtual int GetColorDepth() const = 0;
virtual bool MatchesFormat(AGS::Shared::Bitmap *other) const = 0;
protected:
IDriverDependantBitmap() {}
virtual ~IDriverDependantBitmap() {}
};
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,87 @@
/* 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/>.
*
*/
#ifndef AGS_ENGINE_GFX_GFX_DEFINES_H
#define AGS_ENGINE_GFX_GFX_DEFINES_H
#include "ags/shared/core/types.h"
#include "ags/shared/util/geometry.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
// GraphicResolution struct determines image size and color depth
struct GraphicResolution : Size {
int32_t ColorDepth; // color depth in bits per pixel
GraphicResolution()
: ColorDepth(0) {
}
GraphicResolution(int32_t width, int32_t height, int32_t color_depth)
: Size(width, height), ColorDepth(color_depth) {
}
GraphicResolution(Size size, int32_t color_depth)
: Size(size), ColorDepth(color_depth) {
}
inline bool IsValid() const {
return Width > 0 && Height > 0 && ColorDepth > 0;
}
};
enum WindowMode {
kWnd_Windowed, // regular resizable window with a border and a caption
kWnd_Fullscreen, // real (aka exclusive) fullscreen mode
kWnd_FullDesktop // borderless window filling whole desktop
};
// DisplayMode struct provides extended description of display mode
struct DisplayMode : public GraphicResolution {
int32_t RefreshRate = 0;
bool Vsync = false;
WindowMode Mode = kWnd_Windowed;
// Tells if this is logically a normal windowed mode
inline bool IsWindowed() const {
return Mode == kWnd_Windowed;
}
// Tells if this mode defines a real fullscreen, which would require gfx driver to support it
inline bool IsRealFullscreen() const {
return Mode == kWnd_Fullscreen;
}
DisplayMode() = default;
DisplayMode(const GraphicResolution & res, WindowMode mode = kWnd_Windowed, int32_t refresh = 0, bool vsync = false)
: GraphicResolution(res)
, RefreshRate(refresh)
, Vsync(vsync)
, Mode(mode) {
}
};
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,567 @@
/* 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 "ags/shared/gfx/bitmap.h"
#include "ags/engine/gfx/gfxfilter.h"
#include "ags/engine/gfx/gfx_driver_base.h"
#include "ags/engine/gfx/gfx_util.h"
#include "ags/shared/debugging/out.h"
namespace AGS3 {
using namespace AGS::Shared;
namespace AGS {
namespace Engine {
GraphicsDriverBase::GraphicsDriverBase()
: _pollingCallback(nullptr)
, _drawScreenCallback(nullptr)
, _spriteEvtCallback(nullptr)
, _initGfxCallback(nullptr) {
_actSpriteBatch = UINT32_MAX;
_rendSpriteBatch = UINT32_MAX;
}
bool GraphicsDriverBase::IsModeSet() const {
return _mode.Width != 0 && _mode.Height != 0 && _mode.ColorDepth != 0;
}
bool GraphicsDriverBase::IsNativeSizeValid() const {
return !_srcRect.IsEmpty();
}
bool GraphicsDriverBase::IsRenderFrameValid() const {
return !_srcRect.IsEmpty() && !_dstRect.IsEmpty();
}
DisplayMode GraphicsDriverBase::GetDisplayMode() const {
return _mode;
}
Size GraphicsDriverBase::GetNativeSize() const {
return _srcRect.GetSize();
}
Rect GraphicsDriverBase::GetRenderDestination() const {
return _dstRect;
}
bool GraphicsDriverBase::SetVsync(bool enabled) {
if (!_capsVsync || (_mode.Vsync == enabled)) {
return _mode.Vsync;
}
bool new_value = true;
if (SetVsyncImpl(enabled, new_value) && new_value == enabled) {
Debug::Printf("SetVsync: switched to %d", new_value);
_mode.Vsync = new_value;
}
else {
Debug::Printf("SetVsync: failed, stay at %d", _mode.Vsync);
_capsVsync = false; // mark as non-capable (at least in current mode)
}
return _mode.Vsync;
}
bool GraphicsDriverBase::GetVsync() const {
return _mode.Vsync;
}
void GraphicsDriverBase::BeginSpriteBatch(const Rect &viewport, const SpriteTransform &transform,
GraphicFlip flip, PBitmap surface, uint32_t filter_flags) {
_spriteBatchDesc.push_back(SpriteBatchDesc(_actSpriteBatch, viewport, transform, flip, surface, filter_flags));
_spriteBatchRange.push_back(std::make_pair(GetLastDrawEntryIndex(), (size_t) SIZE_MAX));
_actSpriteBatch = _spriteBatchDesc.size() - 1;
InitSpriteBatch(_actSpriteBatch, _spriteBatchDesc[_actSpriteBatch]);
}
void GraphicsDriverBase::EndSpriteBatch() {
assert(_actSpriteBatch != UINT32_MAX);
if (_actSpriteBatch == UINT32_MAX)
return;
_spriteBatchRange[_actSpriteBatch].second = GetLastDrawEntryIndex();
_actSpriteBatch = _spriteBatchDesc[_actSpriteBatch].Parent;
}
void GraphicsDriverBase::ClearDrawLists() {
ResetAllBatches();
_actSpriteBatch = UINT32_MAX;
_spriteBatchDesc.clear();
_spriteBatchRange.clear();
}
void GraphicsDriverBase::OnInit() {
}
void GraphicsDriverBase::OnUnInit() {
}
void GraphicsDriverBase::OnModeSet(const DisplayMode &mode) {
_mode = mode;
// Adjust some generic parameters as necessary
_mode.Vsync &= _capsVsync;
}
void GraphicsDriverBase::OnModeReleased() {
_mode = DisplayMode();
_dstRect = Rect();
}
void GraphicsDriverBase::OnScalingChanged() {
PGfxFilter filter = GetGraphicsFilter();
if (filter)
_filterRect = filter->SetTranslation(_srcRect.GetSize(), _dstRect);
else
_filterRect = Rect();
_scaling.Init(_srcRect.GetSize(), _dstRect);
}
void GraphicsDriverBase::OnSetNativeRes(const GraphicResolution &native_res) {
_srcRect = RectWH(0, 0, native_res.Width, native_res.Height);
_srcColorDepth = native_res.ColorDepth;
OnScalingChanged();
}
void GraphicsDriverBase::OnSetRenderFrame(const Rect &dst_rect) {
_dstRect = dst_rect;
OnScalingChanged();
}
void GraphicsDriverBase::OnSetFilter() {
_filterRect = GetGraphicsFilter()->SetTranslation(Size(_srcRect.GetSize()), _dstRect);
}
VideoMemoryGraphicsDriver::VideoMemoryGraphicsDriver()
: _stageVirtualScreenDDB(nullptr)
, _stageScreenDirty(false)
, _fxIndex(0) {
// Only to have something meaningful as default
_vmem_a_shift_32 = 24;
_vmem_r_shift_32 = 16;
_vmem_g_shift_32 = 8;
_vmem_b_shift_32 = 0;
}
VideoMemoryGraphicsDriver::~VideoMemoryGraphicsDriver() {
DestroyAllStageScreens();
}
Bitmap *VideoMemoryGraphicsDriver::GetMemoryBackBuffer() {
return nullptr;
}
void VideoMemoryGraphicsDriver::SetMemoryBackBuffer(Bitmap * /*backBuffer*/) {
// do nothing, video-memory drivers don't use main back buffer, only stage bitmaps they pass to plugins
}
Bitmap *VideoMemoryGraphicsDriver::GetStageBackBuffer(bool mark_dirty) {
if (_rendSpriteBatch == UINT32_MAX)
return nullptr;
_stageScreenDirty |= mark_dirty;
return GetStageScreenRaw(_rendSpriteBatch);
}
void VideoMemoryGraphicsDriver::SetStageBackBuffer(Bitmap *backBuffer) {
// do nothing, video-memory drivers don't support this
}
bool VideoMemoryGraphicsDriver::GetStageMatrixes(RenderMatrixes &rm) {
rm = _stageMatrixes;
return true;
}
IDriverDependantBitmap *VideoMemoryGraphicsDriver::CreateDDBFromBitmap(Bitmap *bitmap, bool has_alpha, bool opaque) {
IDriverDependantBitmap * ddb = CreateDDB(bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetColorDepth(), opaque);
if (ddb)
UpdateDDBFromBitmap(ddb, bitmap, has_alpha);
return ddb;
}
IDriverDependantBitmap *VideoMemoryGraphicsDriver::GetSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool has_alpha, bool opaque) {
const auto found = _txRefs.find(sprite_id);
if (found != _txRefs.end()) {
const auto &item = found->_value;
if (!item.Data.expired())
return CreateDDB(item.Data.lock(), item.Res.Width, item.Res.Height, item.Res.ColorDepth, opaque);
}
// Create and add a new element
std::shared_ptr<TextureData> txdata(CreateTextureData(bitmap->GetWidth(), bitmap->GetHeight(), opaque));
txdata->ID = sprite_id;
UpdateTextureData(txdata.get(), bitmap, has_alpha, opaque);
// only add into the map when has valid sprite ID
if (sprite_id != UINT32_MAX) {
_txRefs[sprite_id] = TextureCacheItem(txdata,
GraphicResolution(bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetColorDepth()));
}
return CreateDDB(txdata, bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetColorDepth(), opaque);
}
void VideoMemoryGraphicsDriver::UpdateSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool has_alpha, bool opaque) {
const auto found = _txRefs.find(sprite_id);
if (found == _txRefs.end())
return;
auto txdata = found->_value.Data.lock();
if (!txdata)
return;
// Update texture ONLY if the bitmap's resolution matches;
// otherwise - detach shared texture (don't delete the data yet, as it may be in use)
const auto &res = found->_value.Res;
if (res.Width == bitmap->GetWidth() && res.Height == bitmap->GetHeight() && res.ColorDepth == bitmap->GetColorDepth()) {
UpdateTextureData(txdata.get(), bitmap, has_alpha, opaque);
} else {
txdata->ID = UINT32_MAX;
_txRefs.erase(found);
}
}
void VideoMemoryGraphicsDriver::ClearSharedDDB(uint32_t sprite_id) {
// Reset sprite ID for any remaining shared txdata,
// then remove the reference from the cache;
// NOTE: we do not delete txdata itself, as it may be temporarily in use
const auto found = _txRefs.find(sprite_id);
if (found != _txRefs.end()) {
auto txdata = found->_value.Data.lock();
if (txdata)
txdata->ID = UINT32_MAX;
_txRefs.erase(found);
}
}
void VideoMemoryGraphicsDriver::DestroyDDB(IDriverDependantBitmap* ddb) {
uint32_t sprite_id = ddb->GetRefID();
DestroyDDBImpl(ddb);
// Remove shared object from ref list if no more active refs left
const auto found = _txRefs.find(sprite_id);
if (found != _txRefs.end() && found->_value.Data.expired())
_txRefs.erase(found);
}
void VideoMemoryGraphicsDriver::SetStageScreen(const Size &sz, int x, int y) {
SetStageScreen(_actSpriteBatch, sz, x, y);
}
void VideoMemoryGraphicsDriver::SetStageScreen(size_t index, const Size &sz, int x, int y) {
if (_stageScreens.size() <= index)
_stageScreens.resize(index + 1);
_stageScreens[index].Position = RectWH(x, y, sz.Width, sz.Height);
}
Bitmap *VideoMemoryGraphicsDriver::GetStageScreenRaw(size_t index) {
assert(index < _stageScreens.size());
if (_stageScreens.size() <= index)
return nullptr;
auto &scr = _stageScreens[index];
const Size sz = scr.Position.GetSize();
if (scr.Raw && (scr.Raw->GetSize() != sz)) {
scr.Raw.reset();
if (scr.DDB)
DestroyDDB(scr.DDB);
scr.DDB = nullptr;
}
if (!scr.Raw && !sz.IsNull()) {
scr.Raw.reset(new Bitmap(sz.Width, sz.Height, _mode.ColorDepth));
scr.DDB = CreateDDB(sz.Width, sz.Height, _mode.ColorDepth, false);
}
return scr.Raw.get();
}
IDriverDependantBitmap *VideoMemoryGraphicsDriver::UpdateStageScreenDDB(size_t index, int &x, int &y) {
assert((index < _stageScreens.size()) && _stageScreens[index].DDB);
if ((_stageScreens.size() <= index) || !_stageScreens[index].Raw || !_stageScreens[index].DDB)
return nullptr;
auto &scr = _stageScreens[index];
UpdateDDBFromBitmap(scr.DDB, scr.Raw.get(), true);
scr.Raw->ClearTransparent();
x = scr.Position.Left;
y = scr.Position.Top;
return scr.DDB;
}
void VideoMemoryGraphicsDriver::DestroyAllStageScreens() {
if (_stageVirtualScreenDDB) // FIXME: Not in upstream
this->DestroyDDB(_stageVirtualScreenDDB);
_stageVirtualScreenDDB = nullptr;
for (size_t i = 0; i < _stageScreens.size(); ++i) {
if (_stageScreens[i].DDB)
DestroyDDB(_stageScreens[i].DDB);
}
_stageScreens.clear();
}
IDriverDependantBitmap *VideoMemoryGraphicsDriver::DoSpriteEvtCallback(int evt, int data, int &x, int &y) {
if (!_spriteEvtCallback)
error("Unhandled attempt to draw null sprite");
_stageScreenDirty = false;
// NOTE: this is not clear whether return value of callback may be
// relied on. Existing plugins do not seem to return anything but 0,
// even if they handle this event. This is why we also set
// _stageScreenDirty in certain plugin API function implementations.
_stageScreenDirty |= _spriteEvtCallback(evt, data) != 0;
if (_stageScreenDirty) {
return UpdateStageScreenDDB(_rendSpriteBatch, x, y);
}
return nullptr;
}
IDriverDependantBitmap *VideoMemoryGraphicsDriver::MakeFx(int r, int g, int b) {
if (_fxIndex == _fxPool.size()) _fxPool.push_back(ScreenFx());
ScreenFx &fx = _fxPool[_fxIndex];
if (fx.DDB == nullptr) {
fx.Raw = BitmapHelper::CreateBitmap(16, 16, _mode.ColorDepth);
fx.DDB = CreateDDBFromBitmap(fx.Raw, false, true);
}
if (r != fx.Red || g != fx.Green || b != fx.Blue) {
fx.Raw->Clear(makecol_depth(fx.Raw->GetColorDepth(), r, g, b));
this->UpdateDDBFromBitmap(fx.DDB, fx.Raw, false);
fx.Red = r;
fx.Green = g;
fx.Blue = b;
}
_fxIndex++;
return fx.DDB;
}
void VideoMemoryGraphicsDriver::ResetFxPool() {
_fxIndex = 0;
}
void VideoMemoryGraphicsDriver::DestroyFxPool() {
for (auto &fx : _fxPool) {
if (fx.DDB)
DestroyDDB(fx.DDB);
delete fx.Raw;
}
_fxPool.clear();
_fxIndex = 0;
}
template <typename T> T algetr(const T);
template <typename T> T algetg(const T);
template <typename T> T algetb(const T);
template <typename T> T algeta(const T);
template <> uint8_t algetr(const uint8_t c) { return getr8(c); }
template <> uint8_t algetg(const uint8_t c) { return getg8(c); }
template <> uint8_t algetb(const uint8_t c) { return getb8(c); }
template <> uint8_t algeta(const uint8_t c) { return 0xFF; }
template <> uint16_t algetr(const uint16_t c) { return getr16(c); }
template <> uint16_t algetg(const uint16_t c) { return getg16(c); }
template <> uint16_t algetb(const uint16_t c) { return getb16(c); }
template <> uint16_t algeta(const uint16_t c) { return 0xFF; }
template <> uint32_t algetr(const uint32_t c) { return getr32(c); }
template <> uint32_t algetg(const uint32_t c) { return getg32(c); }
template <> uint32_t algetb(const uint32_t c) { return getb32(c); }
template <> uint32_t algeta(const uint32_t c) { return geta32(c); }
template <typename T> bool is_color_mask(const T);
template <> bool is_color_mask(const uint8_t c) { return c == MASK_COLOR_8;}
template <> bool is_color_mask(const uint16_t c) { return c == MASK_COLOR_16;}
template <> bool is_color_mask(const uint32_t c) { return c == MASK_COLOR_32;}
template <typename T> void get_pixel_if_not_transparent(const T *pixel, T *red, T *green, T *blue, T *divisor) {
const T px_color = pixel[0];
if (!is_color_mask<T>(px_color)) {
*red += algetr<T>(px_color);
*green += algetg<T>(px_color);
*blue += algetb<T>(px_color);
divisor[0]++;
}
}
#define VMEMCOLOR_RGBA(r,g,b,a) \
( (((a) & 0xFF) << _vmem_a_shift_32) | (((r) & 0xFF) << _vmem_r_shift_32) | (((g) & 0xFF) << _vmem_g_shift_32) | (((b) & 0xFF) << _vmem_b_shift_32) )
// Template helper function which converts bitmap to a video memory buffer,
// applies transparency and optionally copies the source alpha channel (if available).
template<typename T, bool HasAlpha>
void VideoMemoryGraphicsDriver::BitmapToVideoMemImpl(const Bitmap *bitmap, const TextureTile *tile, uint8_t *dst_ptr, const int dst_pitch) {
// tell the compiler these won't change mid loop execution
const int t_width = tile->width;
const int t_height = tile->height;
const int t_x = tile->x;
const int t_y = tile->y;
const int idst_pitch = dst_pitch * sizeof(uint8_t) / sizeof(uint32_t); // destination is always 32-bit
auto idst = reinterpret_cast<uint32_t *>(dst_ptr);
for (int y = 0; y < t_height; y++) {
const uint8_t *scanline_at = bitmap->GetScanLine(y + t_y);
for (int x = 0; x < t_width; x++) {
auto srcData = (const T *)&scanline_at[(x + t_x) * sizeof(T)];
const T src_color = srcData[0];
if (HasAlpha) {
idst[x] = VMEMCOLOR_RGBA(algetr<T>(src_color), algetg<T>(src_color), algetb<T>(src_color), algeta<T>(src_color));
} else if (is_color_mask<T>(src_color)) {
idst[x] = 0;
} else {
idst[x] = VMEMCOLOR_RGBA(algetr<T>(src_color), algetg<T>(src_color), algetb<T>(src_color), 0xFF);
}
}
idst += idst_pitch;
}
}
// Template helper function which converts bitmap to a video memory buffer,
// assuming that the destination is always opaque (alpha channel is filled with 0xFF)
template<typename T>
void VideoMemoryGraphicsDriver::BitmapToVideoMemOpaqueImpl(const Bitmap *bitmap, const TextureTile *tile, uint8_t *dst_ptr, const int dst_pitch) {
// tell the compiler these won't change mid loop execution
const int t_width = tile->width;
const int t_height = tile->height;
const int t_x = tile->x;
const int t_y = tile->y;
const int idst_pitch = dst_pitch * sizeof(uint8_t) / sizeof(uint32_t); // destination is always 32-bit
auto idst = reinterpret_cast<uint32_t *>(dst_ptr);
for (int y = 0; y < t_height; y++) {
const uint8_t *scanline_at = bitmap->GetScanLine(y + t_y);
for (int x = 0; x < t_width; x++) {
auto srcData = (const T *)&scanline_at[(x + t_x) * sizeof(T)];
const T src_color = srcData[0];
idst[x] = VMEMCOLOR_RGBA(algetr<T>(src_color), algetg<T>(src_color), algetb<T>(src_color), 0xFF);
}
idst += idst_pitch;
}
}
// Template helper function which converts bitmap to a video memory buffer
// with a semi-transparent pixels fix for "Linear" graphics filter which prevents
// colored outline (usually either of black or "magic pink" color).
template<typename T, bool HasAlpha>
void VideoMemoryGraphicsDriver::BitmapToVideoMemLinearImpl(const Bitmap *bitmap, const TextureTile *tile, uint8_t *dst_ptr, const int dst_pitch) {
// tell the compiler these won't change mid loop execution
const int t_width = tile->width;
const int t_height = tile->height;
const int t_x = tile->x;
const int t_y = tile->y;
const int src_bpp = sizeof(T);
const int idst_pitch = dst_pitch * sizeof(uint8_t) / sizeof(uint32_t); // destination is always 32-bit
auto idst = reinterpret_cast<uint32_t *>(dst_ptr);
bool lastPixelWasTransparent = false;
for (int y = 0; y < t_height; y++) {
lastPixelWasTransparent = false;
const uint8_t *scanline_before = (y > 0) ? bitmap->GetScanLine(y + t_y - 1) : nullptr;
const uint8_t *scanline_at = bitmap->GetScanLine(y + t_y);
const uint8_t *scanline_after = (y < t_height - 1) ? bitmap->GetScanLine(y + t_y + 1) : nullptr;
for (int x = 0; x < t_width; x++) {
auto srcData = (const T *)&scanline_at[(x + t_x) * src_bpp];
const T src_color = srcData[0];
if (is_color_mask<T>(src_color)) {
// set to transparent, but use the colour from the neighbouring
// pixel to stop the linear filter doing colored outlines
T red = 0, green = 0, blue = 0, divisor = 0;
if (x > 0)
get_pixel_if_not_transparent<T>(&srcData[-1], &red, &green, &blue, &divisor);
if (x < t_width - 1)
get_pixel_if_not_transparent<T>(&srcData[1], &red, &green, &blue, &divisor);
if (y > 0)
get_pixel_if_not_transparent<T>((const T *)&scanline_before[(x + t_x) * src_bpp], &red, &green, &blue, &divisor);
if (y < t_height - 1)
get_pixel_if_not_transparent<T>((const T *)&scanline_after[(x + t_x) * src_bpp], &red, &green, &blue, &divisor);
if (divisor > 0)
idst[x] = VMEMCOLOR_RGBA(red / divisor, green / divisor, blue / divisor, 0);
else
idst[x] = 0;
lastPixelWasTransparent = true;
} else if (HasAlpha) {
idst[x] = VMEMCOLOR_RGBA(algetr<T>(src_color), algetg<T>(src_color), algetb<T>(src_color), algeta<T>(src_color));
} else {
idst[x] = VMEMCOLOR_RGBA(algetr<T>(src_color), algetg<T>(src_color), algetb<T>(src_color), 0xFF);
if (lastPixelWasTransparent) {
// update the colour of the previous transparent pixel, to
// stop colored outlines when linear filtering
idst[x - 1] = idst[x] & 0x00FFFFFF;
lastPixelWasTransparent = false;
}
}
}
idst += idst_pitch;
}
}
void VideoMemoryGraphicsDriver::BitmapToVideoMem(const Bitmap *bitmap, const bool has_alpha, const TextureTile *tile,
uint8_t *dst_ptr, const int dst_pitch, const bool usingLinearFiltering) {
switch (bitmap->GetColorDepth()) {
case 8:
if (usingLinearFiltering) {
BitmapToVideoMemLinearImpl<uint8_t, false>(bitmap, tile, dst_ptr, dst_pitch);
} else {
BitmapToVideoMemImpl<uint8_t, false>(bitmap, tile, dst_ptr, dst_pitch);
}
break;
case 16:
if (usingLinearFiltering) {
BitmapToVideoMemLinearImpl<uint16_t, false>(bitmap, tile, dst_ptr, dst_pitch);
} else {
BitmapToVideoMemImpl<uint16_t, false>(bitmap, tile, dst_ptr, dst_pitch);
}
break;
case 32:
if (usingLinearFiltering) {
if (has_alpha) {
BitmapToVideoMemLinearImpl<uint32_t, true>(bitmap, tile, dst_ptr, dst_pitch);
} else {
BitmapToVideoMemLinearImpl<uint32_t, false>(bitmap, tile, dst_ptr, dst_pitch);
}
} else {
if (has_alpha) {
BitmapToVideoMemImpl<uint32_t, true>(bitmap, tile, dst_ptr, dst_pitch);
} else {
BitmapToVideoMemImpl<uint32_t, false>(bitmap, tile, dst_ptr, dst_pitch);
}
}
break;
default:
break;
}
}
void VideoMemoryGraphicsDriver::BitmapToVideoMemOpaque(const Bitmap *bitmap, const TextureTile *tile, uint8_t *dst_ptr, const int dst_pitch) {
switch (bitmap->GetColorDepth()) {
case 8:
BitmapToVideoMemOpaqueImpl<uint8_t>(bitmap, tile, dst_ptr, dst_pitch);
break;
case 16:
BitmapToVideoMemOpaqueImpl<uint16_t>(bitmap, tile, dst_ptr, dst_pitch);
break;
case 32:
BitmapToVideoMemOpaqueImpl<uint32_t>(bitmap, tile, dst_ptr, dst_pitch);
break;
default:
break;
}
}
} // namespace Engine
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,394 @@
/* 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/>.
*
*/
//=============================================================================
//
// Implementation base for graphics driver
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_GFX_DRIVER_BASE_H
#define AGS_ENGINE_GFX_GFX_DRIVER_BASE_H
#include "common/std/memory.h"
#include "common/std/map.h"
#include "common/std/vector.h"
#include "ags/engine/gfx/ddb.h"
#include "ags/shared/gfx/gfx_def.h"
#include "ags/engine/gfx/graphics_driver.h"
#include "ags/shared/util/scaling.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
using Shared::Bitmap;
using Shared::PlaneScaling;
// Sprite batch, defines viewport and an optional model transformation for the list of sprites
struct SpriteBatchDesc {
uint32_t Parent = UINT32_MAX;
// View rectangle for positioning and clipping, in resolution coordinates
// (this may be screen or game frame resolution, depending on circumstances)
Rect Viewport;
// Optional model transformation, to be applied to each sprite
SpriteTransform Transform;
// Optional flip, applied to the whole batch as the last transform
Shared::GraphicFlip Flip = Shared::kFlip_None;
// Optional bitmap to draw sprites upon. Used exclusively by the software rendering mode.
PBitmap Surface;
// Optional filter flags; this lets to filter certain batches out during some operations,
// such as fading effects or making screenshots.
uint32_t FilterFlags = 0u;
SpriteBatchDesc() = default;
SpriteBatchDesc(uint32_t parent, const Rect viewport, const SpriteTransform & transform,
Shared::GraphicFlip flip = Shared::kFlip_None, PBitmap surface = nullptr, uint32_t filter_flags = 0)
: Parent(parent)
, Viewport(viewport)
, Transform(transform)
, Flip(flip)
, Surface(surface)
, FilterFlags(filter_flags) {
}
};
typedef std::vector<SpriteBatchDesc> SpriteBatchDescs;
// The single sprite entry in the render list
template<class T_DDB>
struct SpriteDrawListEntry {
T_DDB *ddb = nullptr; // TODO: use shared pointer?
uint32_t node = 0; // sprite batch / scene node index
int x = 0, y = 0; // sprite position, in local batch / node coordinates
bool skip = false;
SpriteDrawListEntry() = default;
SpriteDrawListEntry(T_DDB * ddb_, uint32_t node_, int x_, int y_)
: ddb(ddb_)
, node(node_)
, x(x_)
, y(y_)
, skip(false) {
}
};
// GraphicsDriverBase - is the parent class for all graphics drivers in AGS,
// that incapsulates the most common functionality.
class GraphicsDriverBase : public IGraphicsDriver {
public:
GraphicsDriverBase();
bool IsModeSet() const override;
bool IsNativeSizeValid() const override;
bool IsRenderFrameValid() const override;
DisplayMode GetDisplayMode() const override;
Size GetNativeSize() const override;
Rect GetRenderDestination() const override;
bool SetVsync(bool enabled) override;
bool GetVsync() const override;
void BeginSpriteBatch(const Rect &viewport, const SpriteTransform &transform,
Shared::GraphicFlip flip = Shared::kFlip_None, PBitmap surface = nullptr,
uint32_t filter_flags = 0) override;
void EndSpriteBatch() override;
void ClearDrawLists() override;
void SetCallbackForPolling(GFXDRV_CLIENTCALLBACK callback) override {
_pollingCallback = callback;
}
void SetCallbackToDrawScreen(GFXDRV_CLIENTCALLBACK callback, GFXDRV_CLIENTCALLBACK post_callback) override {
_drawScreenCallback = callback;
_drawPostScreenCallback = post_callback;
}
void SetCallbackOnInit(GFXDRV_CLIENTCALLBACKINITGFX callback) override {
_initGfxCallback = callback;
}
void SetCallbackOnSpriteEvt(GFXDRV_CLIENTCALLBACKEVT callback) override {
_spriteEvtCallback = callback;
}
protected:
// Special internal values, applied to DrawListEntry
static const uintptr_t DRAWENTRY_STAGECALLBACK = 0x0;
static const uintptr_t DRAWENTRY_FADE = 0x1;
static const uintptr_t DRAWENTRY_TINT = 0x2;
// Called after graphics driver was initialized for use for the first time
virtual void OnInit();
// Called just before graphics mode is going to be uninitialized and its
// resources released
virtual void OnUnInit();
// Called after new mode was successfully initialized
virtual void OnModeSet(const DisplayMode &mode);
// Called when the new native size is set
virtual void OnSetNativeRes(const GraphicResolution &native_res);
// Called before display mode is going to be released
virtual void OnModeReleased();
// Called when new render frame is set
virtual void OnSetRenderFrame(const Rect &dst_rect);
// Called when the new filter is set
virtual void OnSetFilter();
// Try changing vsync setting; fills new current mode in vsync_res,
// returns whether the new setting was set successfully.
virtual bool SetVsyncImpl(bool vsync, bool &vsync_res) { return false; }
// Initialize sprite batch and allocate necessary resources
virtual void InitSpriteBatch(size_t index, const SpriteBatchDesc &desc) = 0;
// Gets the index of a last draw entry (sprite)
virtual size_t GetLastDrawEntryIndex() = 0;
// Clears sprite lists
virtual void ResetAllBatches() = 0;
void OnScalingChanged();
DisplayMode _mode; // display mode settings
Rect _srcRect; // rendering source rect
int _srcColorDepth; // rendering source color depth (in bits per pixel)
Rect _dstRect; // rendering destination rect
Rect _filterRect; // filter scaling destination rect (before final scaling)
PlaneScaling _scaling; // native -> render dest coordinate transformation
// Capability flags
bool _capsVsync = false; // is vsync available
// Callbacks
GFXDRV_CLIENTCALLBACK _pollingCallback;
GFXDRV_CLIENTCALLBACK _drawScreenCallback;
GFXDRV_CLIENTCALLBACK _drawPostScreenCallback;
GFXDRV_CLIENTCALLBACKEVT _spriteEvtCallback;
GFXDRV_CLIENTCALLBACKINITGFX _initGfxCallback;
// Sprite batch parameters
SpriteBatchDescs _spriteBatchDesc;
// The range of sprites in this sprite batch (counting nested sprites):
// the index of a first of the current batch, and the next index past the last one.
std::vector<std::pair<size_t, size_t>> _spriteBatchRange;
// The index of a currently filled sprite batch
size_t _actSpriteBatch;
// The index of a currently rendered sprite batch
// (or -1 / UINT32_MAX if we are outside of the render pass)
uint32_t _rendSpriteBatch;
};
// Parent class for the video memory DDBs
class BaseDDB : public IDriverDependantBitmap {
public:
int GetWidth() const override {
return _width;
}
int GetHeight() const override {
return _height;
}
int GetColorDepth() const override {
return _colDepth;
}
bool MatchesFormat(AGS::Shared::Bitmap *other) const {
return _width == other->GetWidth() && _height == other->GetHeight() && _colDepth == other->GetColorDepth();
}
int _width = 0, _height = 0;
int _colDepth = 0;
bool _hasAlpha = false; // has meaningful alpha channel
bool _opaque = false; // no mask color
protected:
BaseDDB() {}
virtual ~BaseDDB() {}
};
// A base parent for the otherwise opaque texture data object;
// TextureData refers to the pixel data itself, with no additional
// properties. It may be shared between multiple sprites if necessary.
struct TextureData {
uint32_t ID = UINT32_MAX;
bool RenderTarget = false; // replace with flags later
virtual ~TextureData() = default;
protected:
TextureData() = default;
};
// Generic TextureTile base
struct TextureTile {
int x = 0, y = 0;
int width = 0, height = 0;
// allocWidth and allocHeight tell the actual allocated texture size
int allocWidth = 0, allocHeight = 0;
};
// VideoMemoryGraphicsDriver - is the parent class for the graphic drivers
// which drawing method is based on passing the sprite stack into GPU,
// rather than blitting to flat screen bitmap.
class VideoMemoryGraphicsDriver : public GraphicsDriverBase {
public:
VideoMemoryGraphicsDriver();
~VideoMemoryGraphicsDriver() override;
bool RequiresFullRedrawEachFrame() override { return true; }
bool HasAcceleratedTransform() override { return true; }
// NOTE: although we do use ours, we do not let engine draw upon it;
// only plugin handling are allowed to request our mem buffer
// for compatibility reasons.
bool UsesMemoryBackBuffer() override { return false; }
Bitmap *GetMemoryBackBuffer() override;
void SetMemoryBackBuffer(Bitmap *backBuffer) override;
Bitmap *GetStageBackBuffer(bool mark_dirty) override;
void SetStageBackBuffer(Bitmap *backBuffer) override;
bool GetStageMatrixes(RenderMatrixes &rm) override;
// Creates new texture using given parameters
IDriverDependantBitmap *CreateDDB(int width, int height, int color_depth, bool opaque) override = 0;
// Creates new texture and copy bitmap contents over
IDriverDependantBitmap *CreateDDBFromBitmap(Bitmap *bitmap, bool has_alpha, bool opaque = false) override;
// Get shared texture from cache, or create from bitmap and assign ID
IDriverDependantBitmap *GetSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool has_alpha, bool opaque) override;
// Removes the shared texture reference, will force the texture to recreate next time
void ClearSharedDDB(uint32_t sprite_id) override;
// Updates shared texture data, but only if it is present in the cache
void UpdateSharedDDB(uint32_t sprite_id, Bitmap *bitmap, bool has_alpha, bool opaque) override;
void DestroyDDB(IDriverDependantBitmap* ddb) override;
// Sets stage screen parameters for the current batch.
void SetStageScreen(const Size &sz, int x = 0, int y = 0) override;
protected:
// Create texture data with the given parameters
virtual TextureData *CreateTextureData(int width, int height, bool opaque, bool as_render_target = false) = 0;
// Update texture data from the given bitmap
virtual void UpdateTextureData(TextureData *txdata, Bitmap *bmp, bool has_alpha, bool opaque) = 0;
// Create DDB using preexisting texture data
virtual IDriverDependantBitmap *CreateDDB(std::shared_ptr<TextureData> txdata,
int width, int height, int color_depth, bool opaque) = 0;
// Retrieve shared texture data object from the given DDB
virtual std::shared_ptr<TextureData> GetTextureData(IDriverDependantBitmap *ddb) = 0;
virtual void DestroyDDBImpl(IDriverDependantBitmap* ddb) = 0;
// Stage screens are raw bitmap buffers meant to be sent to plugins on demand
// at certain drawing stages. If used at least once these buffers are then
// rendered as additional sprites in their respected order.
// Presets a stage screen with the given position (size is obligatory, offsets not).
void SetStageScreen(size_t index, const Size &sz, int x = 0, int y = 0);
// Returns a raw bitmap for the given stage screen.
Bitmap *GetStageScreenRaw(size_t index);
// Updates and returns a DDB for the given stage screen, and optional x,y position;
// clears the raw bitmap after copying to the texture.
IDriverDependantBitmap *UpdateStageScreenDDB(size_t index, int &x, int &y);
// Disposes all the stage screen raw bitmaps and DDBs.
void DestroyAllStageScreens();
// Use engine callback to pass a render event;
// returns a DDB if anything was drawn onto the current stage screen
// (in which case it also fills optional x,y position),
// or nullptr if this entry should be skipped.
IDriverDependantBitmap *DoSpriteEvtCallback(int evt, int data, int &x, int &y);
// Prepare and get fx item from the pool
IDriverDependantBitmap *MakeFx(int r, int g, int b);
// Resets fx pool counter
void ResetFxPool();
// Disposes all items in the fx pool
void DestroyFxPool();
// Prepares bitmap to be applied to the texture, copies pixels to the provided buffer
void BitmapToVideoMem(const Bitmap *bitmap, const bool has_alpha, const TextureTile *tile,
uint8_t *dst_ptr, const int dst_pitch, const bool usingLinearFiltering);
// Same but optimized for opaque source bitmaps which ignore transparent "mask color"
void BitmapToVideoMemOpaque(const Bitmap *bitmap, const TextureTile *tile,
uint8_t *dst_ptr, const int dst_pitch);
// Stage virtual screen is used to let plugins draw custom graphics
// in between render stages (between room and GUI, after GUI, and so on)
PBitmap _stageVirtualScreen;
IDriverDependantBitmap *_stageVirtualScreenDDB;
// Stage matrixes are used to let plugins with hardware acceleration know model matrix;
// these matrixes are filled compatible with each given renderer
RenderMatrixes _stageMatrixes;
// Color component shifts in video bitmap format (set by implementations)
int _vmem_a_shift_32;
int _vmem_r_shift_32;
int _vmem_g_shift_32;
int _vmem_b_shift_32;
private:
// Stage virtual screens are used to let plugins draw custom graphics
// in between render stages (between room and GUI, after GUI, and so on).
// TODO: possibly may be optimized further by having only 1 bitmap/ddb
// pair, and subbitmaps for raw drawing on separate stages.
struct StageScreen {
Rect Position; // bitmap size and pos preset (bitmap may be created later)
std::unique_ptr<Bitmap> Raw;
IDriverDependantBitmap *DDB = nullptr;
};
std::vector<StageScreen> _stageScreens;
// Flag which indicates whether stage screen was drawn upon during engine
// callback and has to be inserted into sprite stack.
bool _stageScreenDirty;
// Fx quads pool (for screen overlay effects)
struct ScreenFx {
Bitmap *Raw = nullptr;
IDriverDependantBitmap *DDB = nullptr;
int Red = -1;
int Green = -1;
int Blue = -1;
};
std::vector<ScreenFx> _fxPool;
size_t _fxIndex; // next free pool item
// specialized method to convert bitmap to video memory depending on bit depth
template<typename T, bool HasAlpha>
void BitmapToVideoMemImpl(const Bitmap *bitmap, const TextureTile *tile, uint8_t *dst_ptr, const int dst_pitch);
template<typename T>
void BitmapToVideoMemOpaqueImpl(const Bitmap *bitmap, const TextureTile *tile, uint8_t *dst_ptr, const int dst_pitch);
template<typename T, bool HasAlpha>
void BitmapToVideoMemLinearImpl(const Bitmap *bitmap, const TextureTile *tile, uint8_t *dst_ptr, const int dst_pitch);
// Texture short-term cache:
// - caches textures while they are in the immediate use;
// - this lets to share same texture data among multiple sprites on screen.
// TextureCacheItem stores weak references to the existing texture tiles,
// identified by an arbitrary uint32 number.
// TODO: a curious topic to consider: reuse released TextureData for
// textures of the same size (research potential performance impact).
struct TextureCacheItem {
GraphicResolution Res;
std::weak_ptr<TextureData> Data;
TextureCacheItem() = default;
TextureCacheItem(std::shared_ptr<TextureData> data, const GraphicResolution &res)
: Data(data), Res(res) {}
};
std::unordered_map<uint32_t, TextureCacheItem> _txRefs;
};
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,45 @@
/* 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/textconsole.h"
#include "ags/engine/gfx/gfxfilter_scummvm_renderer.h"
#include "ags/engine/gfx/gfx_driver_factory.h"
#include "ags/engine/gfx/ali_3d_scummvm.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
void GetGfxDriverFactoryNames(StringV &ids) {
ids.push_back("ScummVM");
}
IGfxDriverFactory *GetGfxDriverFactory(const String id) {
if (id.CompareNoCase("ScummVM") == 0)
return ALSW::ScummVMRendererGraphicsFactory::GetFactory();
error("No graphics factory with such id: %s", id.GetCStr());
return nullptr;
}
} // namespace Engine
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,86 @@
/* 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/>.
*
*/
//=============================================================================
//
// Graphics driver factory interface
//
// Graphics factory is supposed to be singleton. Factory implementation must
// guarantee that it may be created and destroyed any number of times during
// program execution.
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_GFX_DRIVER_FACTORY_H
#define AGS_ENGINE_GFX_GFX_DRIVER_FACTORY_H
#include "common/std/memory.h"
#include "ags/shared/util/string.h"
#include "ags/shared/util/string_types.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
using Shared::String;
using Shared::StringV;
class IGraphicsDriver;
class IGfxFilter;
struct GfxFilterInfo;
typedef std::shared_ptr<IGfxFilter> PGfxFilter;
class IGfxDriverFactory {
public:
virtual ~IGfxDriverFactory() {
}
// Shutdown graphics factory and deallocate any resources it owns;
// graphics factory will be unusable after calling this function.
virtual void Shutdown() = 0;
// Get graphics driver associated with this factory; creates one if
// it does not exist.
virtual IGraphicsDriver *GetDriver() = 0;
// Destroy graphics driver associated with this factory; does nothing
// if one was not created yet,
virtual void DestroyDriver() = 0;
// Get number of supported filters
virtual size_t GetFilterCount() const = 0;
// Get filter description
virtual const GfxFilterInfo *GetFilterInfo(size_t index) const = 0;
// Get ID of the default filter
virtual String GetDefaultFilterID() const = 0;
// Assign specified filter to graphics driver
virtual PGfxFilter SetFilter(const String &id, String &filter_error) = 0;
};
// Query the available graphics factory names
void GetGfxDriverFactoryNames(StringV &ids);
// Acquire the graphics factory singleton object by its id
IGfxDriverFactory *GetGfxDriverFactory(const String id);
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,108 @@
/* 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/>.
*
*/
//=============================================================================
//
// Base implementation of IGfxDriverFactory
//
// GfxDriverFactoryBase is a template implementation of basic driver factory
// functionality, such as creating and destruction of graphics driver, and
// managing graphic filters.
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_GFX_DRIVER_FACTORY_BASE_H
#define AGS_ENGINE_GFX_GFX_DRIVER_FACTORY_BASE_H
#include "common/std/vector.h"
#include "ags/engine/gfx/gfx_driver_factory.h"
#include "ags/engine/gfx/gfxfilter.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
template <class TGfxDriverClass, class TGfxFilterClass>
class GfxDriverFactoryBase : public IGfxDriverFactory {
protected:
~GfxDriverFactoryBase() override {
delete _driver;
}
public:
void Shutdown() override {
delete this;
}
IGraphicsDriver *GetDriver() override {
if (!_driver)
_driver = EnsureDriverCreated();
return _driver;
}
void DestroyDriver() override {
delete _driver;
_driver = nullptr;
}
PGfxFilter SetFilter(const String &id, String &filter_error) override {
TGfxDriverClass *driver = EnsureDriverCreated();
if (!driver) {
filter_error = "Graphics driver was not created";
return PGfxFilter();
}
const int color_depth = driver->GetDisplayMode().ColorDepth;
if (color_depth == 0) {
filter_error = "Graphics mode is not set";
return PGfxFilter();
}
std::shared_ptr<TGfxFilterClass> filter(CreateFilter(id));
if (!filter) {
filter_error = "Filter does not exist";
return PGfxFilter();
}
if (!filter->Initialize(color_depth, filter_error)) {
return PGfxFilter();
}
driver->SetGraphicsFilter(filter);
return filter;
}
protected:
GfxDriverFactoryBase()
: _driver(nullptr) {
}
virtual TGfxDriverClass *EnsureDriverCreated() = 0;
virtual TGfxFilterClass *CreateFilter(const String &id) = 0;
TGfxDriverClass *_driver;
};
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,49 @@
/* 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/>.
*
*/
//=============================================================================
//
// Supported graphics mode interface
//
//=============================================================================
#ifndef AGS_ENGINE_GFX__GFXMODELIST_H
#define AGS_ENGINE_GFX__GFXMODELIST_H
#include "ags/shared/core/types.h"
#include "ags/engine/gfx/gfx_defines.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
class IGfxModeList {
public:
virtual ~IGfxModeList() {}
virtual int GetModeCount() const = 0;
virtual bool GetMode(int index, DisplayMode &mode) const = 0;
};
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,128 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/std/memory.h"
#include "ags/shared/core/platform.h"
#include "ags/engine/gfx/gfx_util.h"
#include "ags/engine/gfx/blender.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
using namespace Shared;
namespace GfxUtil {
Bitmap *ConvertBitmap(Bitmap *src, int dst_color_depth) {
int src_col_depth = src->GetColorDepth();
if (src_col_depth != dst_color_depth) {
int old_conv = get_color_conversion();
// TODO: find out what is this, and why do we need to call this every time (do we?)
set_color_conversion(COLORCONV_KEEP_TRANS | COLORCONV_TOTAL);
Bitmap *dst = BitmapHelper::CreateBitmapCopy(src, dst_color_depth);
set_color_conversion(old_conv);
return dst;
}
return src;
}
struct BlendModeSetter {
// Blender setter for destination with and without alpha channel;
// assign kRgbToRgbBlender if not supported
BlenderMode AllAlpha; // src w alpha -> dst w alpha
BlenderMode AlphaToOpaque; // src w alpha -> dst w/o alpha
BlenderMode OpaqueToAlpha; // src w/o alpha -> dst w alpha
BlenderMode OpaqueToAlphaNoTrans; // src w/o alpha -> dst w alpha (opt-ed for no transparency)
BlenderMode AllOpaque; // src w/o alpha -> dst w/o alpha
};
// Array of blender descriptions
// NOTE: set kRgbToRgbBlender to fallback to common image blitting
static const BlendModeSetter BlendModeSets[kNumBlendModes] = {
{ kRgbToRgbBlender, kRgbToRgbBlender, kRgbToRgbBlender, kRgbToRgbBlender, kRgbToRgbBlender }, // kBlendMode_NoAlpha
{ kArgbToArgbBlender, kArgbToRgbBlender, kRgbToArgbBlender, kOpaqueBlenderMode, kRgbToRgbBlender }, // kBlendMode_Alpha
// NOTE: add new modes here
};
bool SetBlender(BlendMode blend_mode, bool dst_has_alpha, bool src_has_alpha, int blend_alpha) {
if (blend_mode < 0 || blend_mode >= kNumBlendModes)
return false;
const BlendModeSetter &set = BlendModeSets[blend_mode];
BlenderMode blender;
if (dst_has_alpha)
blender = src_has_alpha ? set.AllAlpha :
(blend_alpha == 0xFF ? set.OpaqueToAlphaNoTrans : set.OpaqueToAlpha);
else
blender = src_has_alpha ? set.AlphaToOpaque : set.AllOpaque;
set_blender_mode(blender, 0, 0, 0, blend_alpha);
return true;
}
void DrawSpriteBlend(Bitmap *ds, const Point &ds_at, Bitmap *sprite,
BlendMode blend_mode, bool dst_has_alpha, bool src_has_alpha, int blend_alpha) {
if (blend_alpha <= 0)
return; // do not draw 100% transparent image
if (// support only 32-bit blending at the moment
ds->GetColorDepth() == 32 && sprite->GetColorDepth() == 32 &&
// set blenders if applicable and tell if succeeded
SetBlender(blend_mode, dst_has_alpha, src_has_alpha, blend_alpha)) {
ds->TransBlendBlt(sprite, ds_at.X, ds_at.Y);
} else {
GfxUtil::DrawSpriteWithTransparency(ds, sprite, ds_at.X, ds_at.Y, blend_alpha);
}
}
void DrawSpriteWithTransparency(Bitmap *ds, Bitmap *sprite, int x, int y, int alpha) {
if (alpha <= 0) {
// fully transparent, don't draw it at all
return;
}
const int surface_depth = ds->GetColorDepth();
const int sprite_depth = sprite->GetColorDepth();
// Allegro does not support masked blit or blend between different formats
// *except* when drawing 8-bit sprites onto a higher dest.
std::unique_ptr<Bitmap> conv_bm;
if ((surface_depth != sprite_depth) && (sprite_depth > 8)) {
// use ConvertBitmap in order to keep mask pixels
conv_bm.reset(ConvertBitmap(sprite, surface_depth));
sprite = conv_bm.get();
}
if ((alpha < 0xFF) && (surface_depth > 8) && (sprite_depth > 8)) {
set_trans_blender(0, 0, 0, alpha);
ds->TransBlendBlt(sprite, x, y);
} else {
ds->Blit(sprite, x, y, kBitmap_Transparency);
}
}
} // namespace GfxUtil
} // namespace Engine
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,69 @@
/* 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/>.
*
*/
//=============================================================================
//
// Intermediate level drawing utility functions.
//
// GfxUtil namespace is meant for intermediate-to-lower level functions, that
// implement specific conversions, tricks and hacks for drawing bitmaps and
// geometry.
// The suggested convention is to add only those functions, that do not require
// any knowledge of higher-level engine types and objects.
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_GFX_UTIL_H
#define AGS_ENGINE_GFX_GFX_UTIL_H
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/gfx/gfx_def.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
using Shared::Bitmap;
namespace GfxUtil {
// Creates a COPY of the source bitmap, converted to the given format.
// Keeps mask pixels intact, only converting mask color value if necessary.
Bitmap *ConvertBitmap(Bitmap *src, int dst_color_depth);
// Considers the given information about source and destination surfaces,
// then draws a bimtap over another either using requested blending mode,
// or fallbacks to common "magic pink" transparency mode;
// optionally uses blending alpha (overall image transparency).
void DrawSpriteBlend(Bitmap *ds, const Point &ds_at, Bitmap *sprite,
Shared::BlendMode blend_mode, bool dst_has_alpha = true, bool src_has_alpha = true, int blend_alpha = 0xFF);
// Draws a bitmap over another one with given alpha level (0 - 255),
// takes account of the bitmap's mask color,
// ignores image's alpha channel, even if there's one;
// does a conversion if sprite and destination color depths do not match.
void DrawSpriteWithTransparency(Bitmap *ds, Bitmap *sprite, int x, int y, int alpha = 0xFF);
} // namespace GfxUtil
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,78 @@
/* 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/>.
*
*/
//=============================================================================
//
// Graphics filter interface
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_GFXFILTER_H
#define AGS_ENGINE_GFX_GFXFILTER_H
#include "common/std/memory.h"
#include "ags/shared/util/geometry.h"
#include "ags/shared/util/string.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
using Shared::String;
struct GfxFilterInfo {
String Id;
String Name;
int MinScale;
int MaxScale;
GfxFilterInfo() {
}
GfxFilterInfo(String id, String name, int min_scale = 0, int max_scale = 0)
: Id(id)
, Name(name)
, MinScale(min_scale)
, MaxScale(max_scale) {
}
};
class IGfxFilter {
public:
virtual ~IGfxFilter() {}
virtual const GfxFilterInfo &GetInfo() const = 0;
// Init filter for the specified color depth
virtual bool Initialize(const int color_depth, String &err_str) = 0;
virtual void UnInitialize() = 0;
// Try to set rendering translation; returns actual supported destination rect
virtual Rect SetTranslation(const Size src_size, const Rect dst_rect) = 0;
// Get defined destination rect for this filter
virtual Rect GetDestination() const = 0;
};
typedef std::shared_ptr<IGfxFilter> PGfxFilter;
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,50 @@
/* 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 "ags/engine/gfx/gfxfilter_scaling.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
bool ScalingGfxFilter::Initialize(const int /*color_depth*/, String & /*err_str*/) {
// succeed by default
return true;
}
void ScalingGfxFilter::UnInitialize() {
// do nothing by default
}
Rect ScalingGfxFilter::SetTranslation(const Size src_size, const Rect dst_rect) {
// do not restrict scaling by default
_dstRect = dst_rect;
_scaling.Init(src_size, dst_rect);
return _dstRect;
}
Rect ScalingGfxFilter::GetDestination() const {
return _dstRect;
}
} // namespace Engine
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,56 @@
/* 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/>.
*
*/
//=============================================================================
//
// Base class for graphic filter which provides virtual screen scaling
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_SCALING_GFX_FILTER_H
#define AGS_ENGINE_GFX_SCALING_GFX_FILTER_H
#include "ags/engine/gfx/gfxfilter.h"
#include "ags/shared/util/scaling.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
using AGS::Shared::PlaneScaling;
class ScalingGfxFilter : public IGfxFilter {
public:
bool Initialize(const int color_depth, String &err_str) override;
void UnInitialize() override;
Rect SetTranslation(const Size src_size, const Rect dst_rect) override;
Rect GetDestination() const override;
protected:
Rect _dstRect;
PlaneScaling _scaling;
};
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,37 @@
/* 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 "ags/engine/gfx/gfxfilter_scummvm_renderer.h"
#include "ags/globals.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
namespace ALSW {
const GfxFilterInfo &ScummVMRendererGfxFilter::GetInfo() const {
return _GP(scummvmGfxFilter);
}
} // namespace ALSW
} // namespace Engine
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,51 @@
/* 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/>.
*
*/
//=============================================================================
//
// SDL software renderer filter. Technically a non-op, as SDL_Renderer
// does the job.
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_SCUMMVM_RENDERER_FILTER_H
#define AGS_ENGINE_GFX_SCUMMVM_RENDERER_FILTER_H
#include "ags/engine/gfx/gfxfilter_scaling.h"
namespace AGS3 {
namespace AGS {
namespace Engine {
namespace ALSW {
class ScummVMRendererGfxFilter : public ScalingGfxFilter {
public:
~ScummVMRendererGfxFilter() override {}
const GfxFilterInfo &GetInfo() const override;
};
} // namespace ALSW
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,265 @@
/* 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/>.
*
*/
//=============================================================================
//
// Graphics driver interface
//
//=============================================================================
#ifndef AGS_ENGINE_GFX_GRAPHICS_DRIVER_H
#define AGS_ENGINE_GFX_GRAPHICS_DRIVER_H
//#include "math/matrix.h"
#include "common/std/memory.h"
#include "ags/lib/allegro.h" // RGB, PALETTE
#include "ags/shared/gfx/gfx_def.h"
#include "ags/engine/gfx/gfx_defines.h"
#include "ags/engine/gfx/gfx_mode_list.h"
#include "ags/shared/util/geometry.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
class Bitmap;
typedef std::shared_ptr<Shared::Bitmap> PBitmap;
} // namespace Shared
namespace Engine {
// Forward declaration
class IDriverDependantBitmap;
class IGfxFilter;
typedef std::shared_ptr<IGfxFilter> PGfxFilter;
using Shared::PBitmap;
enum TintMethod {
TintReColourise = 0,
TintSpecifyMaximum = 1
};
struct SpriteColorTransform {
int Alpha = 255; // alpha color value (0 - 255)
SpriteColorTransform() = default;
SpriteColorTransform(int alpha) : Alpha(alpha) {
}
};
// Sprite transformation
// TODO: combine with stretch parameters in the IDriverDependantBitmap?
struct SpriteTransform {
// Translate
int X = 0, Y = 0;
float ScaleX = 1.f, ScaleY = 1.f;
float Rotate = 0.f; // angle, in radians
SpriteColorTransform Color;
SpriteTransform() = default;
SpriteTransform(int x, int y, float scalex = 1.0f, float scaley = 1.0f, float rotate = 0.0f,
SpriteColorTransform color = SpriteColorTransform())
: X(x), Y(y), ScaleX(scalex), ScaleY(scaley), Rotate(rotate), Color(color) {
}
};
// Describes 3 render matrixes: world, view and projection
struct RenderMatrixes {
/*
glm::mat4 World;
glm::mat4 View;
glm::mat4 Projection;
*/
};
typedef void (*GFXDRV_CLIENTCALLBACK)();
typedef bool (*GFXDRV_CLIENTCALLBACKEVT)(int evt, int data);
typedef void (*GFXDRV_CLIENTCALLBACKINITGFX)(void *data);
class IGraphicsDriver {
public:
// Gets graphic driver's identifier
virtual const char *GetDriverID() = 0;
// Gets graphic driver's "friendly name"
virtual const char *GetDriverName() = 0;
// Tells if this gfx driver has to redraw whole scene each time
virtual bool RequiresFullRedrawEachFrame() = 0;
// Tells if this gfx driver uses GPU to transform sprites
virtual bool HasAcceleratedTransform() = 0;
// Tells if this gfx driver draws on a virtual screen before rendering on real screen.
virtual bool UsesMemoryBackBuffer() = 0;
// Tells if this gfx driver requires releasing render targets
// in case of display mode change or reset.
virtual bool ShouldReleaseRenderTargets() = 0;
virtual void SetTintMethod(TintMethod method) = 0;
// Initialize given display mode
virtual bool SetDisplayMode(const DisplayMode &mode) = 0;
// Updates previously set display mode, accommodating to the new screen size
virtual void UpdateDeviceScreen(const Size &screen_size) = 0;
// Gets if a graphics mode was initialized
virtual bool IsModeSet() const = 0;
// Set the size of the native image size
virtual bool SetNativeResolution(const GraphicResolution &native_res) = 0;
virtual bool IsNativeSizeValid() const = 0;
// Set game render frame and translation
virtual bool SetRenderFrame(const Rect &dst_rect) = 0;
virtual bool IsRenderFrameValid() const = 0;
// Report which color depth options are best suited for the given native color depth
virtual int GetDisplayDepthForNativeDepth(int native_color_depth) const = 0;
virtual IGfxModeList *GetSupportedModeList(int color_depth) = 0;
virtual bool IsModeSupported(const DisplayMode &mode) = 0;
virtual DisplayMode GetDisplayMode() const = 0;
virtual PGfxFilter GetGraphicsFilter() const = 0;
virtual Size GetNativeSize() const = 0;
virtual Rect GetRenderDestination() const = 0;
virtual void SetCallbackForPolling(GFXDRV_CLIENTCALLBACK callback) = 0;
// TODO: get rid of draw screen callback at some point when all fade functions are more or less grouped in one
virtual void SetCallbackToDrawScreen(GFXDRV_CLIENTCALLBACK callback, GFXDRV_CLIENTCALLBACK post_callback) = 0;
virtual void SetCallbackOnInit(GFXDRV_CLIENTCALLBACKINITGFX callback) = 0;
// The event callback is called in the main render loop when a
// event entry is encountered inside a sprite list.
// You can use this to hook into the rendering process.
virtual void SetCallbackOnSpriteEvt(GFXDRV_CLIENTCALLBACKEVT callback) = 0;
// Clears the screen rectangle. The coordinates are expected in the **native game resolution**.
virtual void ClearRectangle(int x1, int y1, int x2, int y2, RGB *colorToUse) = 0;
// Gets closest recommended bitmap format (currently - only color depth) for the given original format.
// Engine needs to have game bitmaps brought to the certain range of formats, easing conversion into the video bitmaps.
virtual int GetCompatibleBitmapFormat(int color_depth) = 0;
// Returns available texture memory, or 0 if this query is not supported
virtual size_t GetAvailableTextureMemory() = 0;
// Creates a "raw" DDB, without pixel initialization
virtual IDriverDependantBitmap *CreateDDB(int width, int height, int color_depth, bool opaque = false) = 0;
// Creates DDB, initializes from the given bitmap.
virtual IDriverDependantBitmap *CreateDDBFromBitmap(Shared::Bitmap *bitmap, bool has_alpha, bool opaque = false) = 0;
// Creates DDB intended to be used as a render target (allow render other DDBs on it).
virtual IDriverDependantBitmap *CreateRenderTargetDDB(int width, int height, int color_depth, bool opaque = false) = 0;
// Updates DBB using the given bitmap; bitmap must have same size and format
// as the one that this DDB was initialized with.
virtual void UpdateDDBFromBitmap(IDriverDependantBitmap *bitmapToUpdate, Shared::Bitmap *bitmap, bool has_alpha) = 0;
// Destroy the DDB.
virtual void DestroyDDB(IDriverDependantBitmap *bitmap) = 0;
// Get shared texture from cache, or create from bitmap and assign ID
// FIXME: opaque should be either texture data's flag, - in which case same sprite_id
// will be either opaque or not opaque, - or DDB's flag, but in that case it cannot
// be applied to the shared texture data. Currently it's possible to share same
// texture data, but update it with different "opaque" values, which breaks logic.
virtual IDriverDependantBitmap *GetSharedDDB(uint32_t sprite_id,
Shared::Bitmap *bitmap = nullptr, bool has_alpha = true, bool opaque = false) = 0;
virtual void UpdateSharedDDB(uint32_t sprite_id, Shared::Bitmap *bitmap = nullptr, bool has_alpha = true, bool opaque = false) = 0;
// Removes the shared texture reference, will force the texture to recreate next time
virtual void ClearSharedDDB(uint32_t sprite_id) = 0;
// Prepares next sprite batch, a list of sprites with defined viewport and optional
// global model transformation; all subsequent calls to DrawSprite will be adding
// sprites to this batch's list.
// Beginning a batch while the previous was not ended will create a sub-batch
// (think of it as of a child scene node).
// Optionally you can assign "filter flags" to this batch; this lets to filter certain
// batches out during some operations, such as fading effects or making screenshots.
virtual void BeginSpriteBatch(const Rect &viewport, const SpriteTransform &transform = SpriteTransform(),
Shared::GraphicFlip flip = Shared::kFlip_None, PBitmap surface = nullptr, uint32_t filter_flags = 0) = 0;
// Ends current sprite batch
virtual void EndSpriteBatch() = 0;
// Adds sprite to the active batch
virtual void DrawSprite(int x, int y, IDriverDependantBitmap *bitmap) = 0;
// Adds fade overlay fx to the active batch
virtual void SetScreenFade(int red, int green, int blue) = 0;
// Adds tint overlay fx to the active batch
// TODO: redesign this to allow various post-fx per sprite batch?
virtual void SetScreenTint(int red, int green, int blue) = 0;
// Sets stage screen parameters for the current batch.
// Currently includes size and optional position offset;
// the position is relative, as stage screens are using sprite batch transforms.
// Stage screens are used to let plugins do raw drawing during render callbacks.
// TODO: find a better term? note, it's used in several places around renderers.
virtual void SetStageScreen(const Size &sz, int x = 0, int y = 0) = 0;
// Clears all sprite batches, resets batch counter
virtual void ClearDrawLists() = 0;
virtual void RenderToBackBuffer() = 0;
virtual void Render() = 0;
// Renders with additional final offset and flip
// TODO: leftover from old code, solely for software renderer; remove when
// software mode either discarded or scene node graph properly implemented.
virtual void Render(int xoff, int yoff, Shared::GraphicFlip flip) = 0;
// Copies contents of the game screen into bitmap using simple blit or pixel copy.
// Bitmap must be of supported size and pixel format. If it's not the method will
// fail and optionally write wanted destination format into 'want_fmt' pointer.
virtual bool GetCopyOfScreenIntoBitmap(Shared::Bitmap *destination, const Rect *src_rect, bool at_native_res,
GraphicResolution *want_fmt = nullptr, uint32_t batch_skip_filter = 0u) = 0;
// Tells if the renderer supports toggling vsync after initializing the mode.
virtual bool DoesSupportVsyncToggle() = 0;
// Toggles vertical sync mode, if renderer supports one; returns the *new state*.
virtual bool SetVsync(bool enabled) = 0;
// Tells if the renderer currently has vsync enabled.
virtual bool GetVsync() const = 0;
// Enables or disables rendering mode that draws sprite list directly into
// the final resolution, as opposed to drawing to native-resolution buffer
// and scaling to final frame. The effect may be that sprites that are
// drawn with additional fractional scaling will appear more detailed than
// the rest of the game. The effect is stronger for the low-res games being
// rendered in the high-res mode.
virtual void RenderSpritesAtScreenResolution(bool enabled) = 0;
// TODO: move fade-in/out/boxout functions out of the graphics driver!! make everything render through
// main drawing procedure. Since currently it does not - we need to init our own sprite batch
// internally to let it set up correct viewport settings instead of relying on a chance.
// Runs fade-out animation in a blocking manner.
virtual void FadeOut(int speed, int targetColourRed, int targetColourGreen, int targetColourBlue,
uint32_t batch_skip_filter = 0u) = 0;
// Runs fade-in animation in a blocking manner.
virtual void FadeIn(int speed, PALETTE p, int targetColourRed, int targetColourGreen, int targetColourBlue,
uint32_t batch_skip_filter = 0u) = 0;
// Runs box-out animation in a blocking manner.
virtual void BoxOutEffect(bool blackingOut, int speed, int delay, uint32_t batch_skip_filter = 0u) = 0;
virtual void UseSmoothScaling(bool enabled) = 0;
virtual bool SupportsGammaControl() = 0;
virtual void SetGamma(int newGamma) = 0;
// Returns the virtual screen. Will return NULL if renderer does not support memory backbuffer.
// In normal case you should use GetStageBackBuffer() instead.
virtual Shared::Bitmap *GetMemoryBackBuffer() = 0;
// Sets custom backbuffer bitmap to render to.
// Passing NULL pointer will tell renderer to switch back to its original virtual screen.
// Note that only software renderer supports this.
virtual void SetMemoryBackBuffer(Shared::Bitmap *backBuffer) = 0;
// Returns memory backbuffer for the current rendering stage (or base virtual screen if called outside of render pass).
// All renderers should support this.
virtual Shared::Bitmap *GetStageBackBuffer(bool mark_dirty = false) = 0;
// Sets custom backbuffer bitmap to render current render stage to.
// Passing NULL pointer will tell renderer to switch back to its original stage buffer.
// Note that only software renderer supports this.
virtual void SetStageBackBuffer(Shared::Bitmap *backBuffer) = 0;
// Retrieves 3 transform matrixes for the current rendering stage: world (model), view and projection.
// These matrixes will be filled in accordance to the renderer's compatible format;
// returns false if renderer does not use matrixes (not a 3D renderer).
virtual bool GetStageMatrixes(RenderMatrixes &rm) = 0;
virtual ~IGraphicsDriver() {}
};
} // namespace Engine
} // namespace AGS
} // namespace AGS3
#endif