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,316 @@
/* 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/>.
*
*/
// Allow use of stuff in <time.h>
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
// Disable printf override in common/forbidden.h to avoid
// clashes with log.h from the Android SDK.
// That header file uses
// __attribute__ ((format(printf, 3, 4)))
// which gets messed up by our override mechanism; this could
// be avoided by either changing the Android SDK to use the equally
// legal and valid
// __attribute__ ((format(printf, 3, 4)))
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the Android port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#include "backends/platform/android/android.h"
#include "backends/platform/android/jni-android.h"
#include "backends/graphics/android/android-graphics.h"
#include "backends/graphics/opengl/pipelines/pipeline.h"
#include "backends/graphics/opengl/texture.h"
#include "graphics/blit.h"
#include "graphics/managed_surface.h"
//
// AndroidGraphicsManager
//
AndroidGraphicsManager::AndroidGraphicsManager() :
_touchcontrols(nullptr),
_old_touch_mode(OSystem_Android::TOUCH_MODE_TOUCHPAD) {
ENTER();
// Initialize our OpenGL ES context.
initSurface();
_rendering3d = (_renderer3d != nullptr);
// maybe in 3D, not in GUI
dynamic_cast<OSystem_Android *>(g_system)->applyTouchSettings(_rendering3d, false);
dynamic_cast<OSystem_Android *>(g_system)->applyOrientationSettings();
}
AndroidGraphicsManager::~AndroidGraphicsManager() {
ENTER();
deinitSurface();
delete _touchcontrols;
}
void AndroidGraphicsManager::initSurface() {
LOGD("initializing 2D surface");
assert(!JNI::haveSurface());
if (!JNI::initSurface()) {
error("JNI::initSurface failed");
}
if (JNI::egl_bits_per_pixel == 16) {
// We default to RGB565 and RGBA5551 which is closest to what we setup in Java side
notifyContextCreate(OpenGL::kContextGLES2,
new OpenGL::Backbuffer(),
Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0),
Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0));
} else {
// If not 16, this must be 24 or 32 bpp so make use of them
notifyContextCreate(OpenGL::kContextGLES2,
new OpenGL::Backbuffer(),
OpenGL::Texture::getRGBPixelFormat(),
OpenGL::Texture::getRGBAPixelFormat()
);
}
if (_touchcontrols) {
_touchcontrols->recreate();
_touchcontrols->updateGLTexture();
} else {
_touchcontrols = createSurface(_defaultFormatAlpha);
}
dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
this, JNI::egl_surface_width, JNI::egl_surface_height);
handleResize(JNI::egl_surface_width, JNI::egl_surface_height);
}
void AndroidGraphicsManager::deinitSurface() {
if (!JNI::haveSurface())
return;
LOGD("deinitializing 2D surface");
// Deregister us from touch control
dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
nullptr, 0, 0);
if (_touchcontrols) {
_touchcontrols->destroy();
}
notifyContextDestroy();
JNI::deinitSurface();
}
void AndroidGraphicsManager::resizeSurface() {
// If we had lost surface just init it again
if (!JNI::haveSurface()) {
initSurface();
return;
}
// Recreate the EGL surface, context is preserved
JNI::deinitSurface();
if (!JNI::initSurface()) {
error("JNI::initSurface failed");
}
dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
this, JNI::egl_surface_width, JNI::egl_surface_height);
handleResize(JNI::egl_surface_width, JNI::egl_surface_height);
}
void AndroidGraphicsManager::updateScreen() {
//ENTER();
if (!JNI::haveSurface())
return;
// Sets _forceRedraw if needed
dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().beforeDraw();
OpenGLGraphicsManager::updateScreen();
}
void AndroidGraphicsManager::displayMessageOnOSD(const Common::U32String &msg) {
ENTER("%s", msg.encode().c_str());
JNI::displayMessageOnOSD(msg);
}
void AndroidGraphicsManager::recalculateDisplayAreas() {
Common::Rect oldDrawRect = _activeArea.drawRect;
OpenGLGraphicsManager::recalculateDisplayAreas();
int offsetX = _activeArea.drawRect.left - oldDrawRect.left;
int offsetY = _activeArea.drawRect.top - oldDrawRect.top;
int newX = _cursorX + offsetX;
int newY = _cursorY + offsetY;
newX = CLIP<int16>(newX, _activeArea.drawRect.left, _activeArea.drawRect.right);
newY = CLIP<int16>(newY, _activeArea.drawRect.top, _activeArea.drawRect.bottom);
setMousePosition(newX, newY);
}
void AndroidGraphicsManager::showOverlay(bool inGUI) {
if (_overlayVisible && inGUI == _overlayInGUI)
return;
// Don't change touch mode when not changing mouse coordinates
if (inGUI) {
_old_touch_mode = JNI::getTouchMode();
// maybe in 3D, in overlay
dynamic_cast<OSystem_Android *>(g_system)->applyTouchSettings(_renderer3d != nullptr, true);
dynamic_cast<OSystem_Android *>(g_system)->applyOrientationSettings();
} else if (_overlayInGUI) {
// Restore touch mode active before overlay was shown
JNI::setTouchMode(_old_touch_mode);
}
OpenGL::OpenGLGraphicsManager::showOverlay(inGUI);
}
void AndroidGraphicsManager::hideOverlay() {
if (!_overlayVisible)
return;
if (_overlayInGUI) {
// Restore touch mode active before overlay was shown
JNI::setTouchMode(_old_touch_mode);
dynamic_cast<OSystem_Android *>(g_system)->applyOrientationSettings();
}
OpenGL::OpenGLGraphicsManager::hideOverlay();
}
float AndroidGraphicsManager::getHiDPIScreenFactor() const {
JNI::DPIValues dpi;
JNI::getDPI(dpi);
// Scale down the Android factor else the GUI is too big and
// there is not much options to go smaller
return dpi[2] / 1.2f;
}
bool AndroidGraphicsManager::loadVideoMode(uint requestedWidth, uint requestedHeight, bool resizable, int antialiasing) {
ENTER("%d, %d, %s", requestedWidth, requestedHeight, format.toString().c_str());
// As GLES2 provides FBO, OpenGL graphics manager must ask us for a resizable surface
assert(resizable);
if (antialiasing != 0) {
warning("Requesting antialiased video mode while not available");
}
const bool render3d = (_renderer3d != nullptr);
if (_rendering3d != render3d) {
_rendering3d = render3d;
// 3D status changed: refresh the touch mode
applyTouchSettings();
}
// We get this whenever a new resolution is requested. Since Android is
// using a fixed output size we do nothing like that here.
return true;
}
void AndroidGraphicsManager::refreshScreen() {
//ENTER();
// Last minute draw of touch controls
dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().draw();
JNI::swapBuffers();
}
void AndroidGraphicsManager::applyTouchSettings() const {
// maybe in 3D, maybe in GUI
dynamic_cast<OSystem_Android *>(g_system)->applyTouchSettings(_renderer3d != nullptr, _overlayVisible && _overlayInGUI);
}
void AndroidGraphicsManager::syncVirtkeyboardState(bool virtkeybd_on) {
_screenAlign = SCREEN_ALIGN_CENTER;
if (virtkeybd_on) {
_screenAlign |= SCREEN_ALIGN_TOP;
} else {
_screenAlign |= SCREEN_ALIGN_MIDDLE;
}
recalculateDisplayAreas();
_forceRedraw = true;
}
void AndroidGraphicsManager::touchControlInitSurface(const Graphics::ManagedSurface &surf) {
if (_touchcontrols->getWidth() == surf.w && _touchcontrols->getHeight() == surf.h) {
return;
}
_touchcontrols->allocate(surf.w, surf.h);
Graphics::Surface *dst = _touchcontrols->getSurface();
Graphics::crossBlit(
(byte *)dst->getPixels(), (const byte *)surf.getPixels(),
dst->pitch, surf.pitch,
surf.w, surf.h,
dst->format, surf.format);
_touchcontrols->updateGLTexture();
}
void AndroidGraphicsManager::touchControlDraw(uint8 alpha, int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) {
_targetBuffer->enableBlend(OpenGL::Framebuffer::kBlendModeTraditionalTransparency);
OpenGL::Pipeline *pipeline = getPipeline();
pipeline->activate();
if (alpha != 255) {
pipeline->setColor(1.0f, 1.0f, 1.0f, alpha / 255.0f);
}
pipeline->drawTexture(_touchcontrols->getGLTexture(),
x, y, w, h, clip);
if (alpha != 255) {
pipeline->setColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
void AndroidGraphicsManager::touchControlNotifyChanged() {
// Make sure we redraw the screen
_forceRedraw = true;
}
bool AndroidGraphicsManager::notifyMousePosition(Common::Point &mouse) {
mouse.x = CLIP<int16>(mouse.x, _activeArea.drawRect.left, _activeArea.drawRect.right);
mouse.y = CLIP<int16>(mouse.y, _activeArea.drawRect.top, _activeArea.drawRect.bottom);
setMousePosition(mouse.x, mouse.y);
mouse = convertWindowToVirtual(mouse.x, mouse.y);
return true;
}
WindowedGraphicsManager::Insets AndroidGraphicsManager::getSafeAreaInsets() const {
return WindowedGraphicsManager::Insets{
(int16)JNI::cutout_insets[0], (int16)JNI::cutout_insets[1],
(int16)JNI::cutout_insets[2], (int16)JNI::cutout_insets[3]};
}

View File

@@ -0,0 +1,76 @@
/* 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 BACKENDS_GRAPHICS_ANDROID_ANDROID_GRAPHICS_H
#define BACKENDS_GRAPHICS_ANDROID_ANDROID_GRAPHICS_H
#include "common/scummsys.h"
#include "backends/graphics/opengl/opengl-graphics.h"
#include "backends/platform/android/touchcontrols.h"
class AndroidGraphicsManager :
public OpenGL::OpenGLGraphicsManager, public TouchControlsDrawer {
public:
AndroidGraphicsManager();
virtual ~AndroidGraphicsManager();
void initSurface();
void deinitSurface();
void resizeSurface();
WindowedGraphicsManager::Insets getSafeAreaInsets() const override;
void updateScreen() override;
void displayMessageOnOSD(const Common::U32String &msg) override;
bool notifyMousePosition(Common::Point &mouse);
Common::Point getMousePosition() { return Common::Point(_cursorX, _cursorY); }
float getHiDPIScreenFactor() const override;
void touchControlInitSurface(const Graphics::ManagedSurface &surf) override;
void touchControlNotifyChanged() override;
void touchControlDraw(uint8 alpha, int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) override;
void syncVirtkeyboardState(bool virtkeybd_on);
void applyTouchSettings() const;
protected:
void recalculateDisplayAreas() override;
void setSystemMousePosition(const int x, const int y) override {}
void showOverlay(bool inGUI) override;
void hideOverlay() override;
bool loadVideoMode(uint requestedWidth, uint requestedHeight, bool resizable, int antialiasing) override;
void refreshScreen() override;
private:
OpenGL::Surface *_touchcontrols;
int _old_touch_mode;
bool _rendering3d;
};
#endif

View File

@@ -0,0 +1,959 @@
/* 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 "../../platform/atari/symbols.h"
| C2P by Mikael Kalms (public domain)
| See https://github.com/Kalmalyzer/kalms-c2p
.globl SYM(asm_c2p1x1_8)
.globl SYM(asm_c2p1x1_8_tt)
.globl SYM(asm_c2p1x1_8_rect)
.globl SYM(asm_c2p1x1_4)
.globl SYM(asm_c2p1x1_4_rect)
.text
| void asm_c2p1x1_8(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen);
SYM(asm_c2p1x1_8):
#ifdef __FASTCALL__
| a0: chunky
move.l a1,d0 | chunky end
move.l 4(sp),a1 | screen
#else
move.l (4,sp),a0 | chunky
move.l (8,sp),d0 | chunky end
move.l (12,sp),a1 | screen
#endif
movem.l d2-d7/a2-a6,-(sp)
move.l d0,a2
move.l #0x0f0f0f0f,d4
move.l #0x00ff00ff,d5
move.l #0x55555555,d6
move.l (a0)+,d0
move.l (a0)+,d1
move.l (a0)+,d2
move.l (a0)+,d3
| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
move.l d1,d7
lsr.l #4,d7
eor.l d0,d7
and.l d4,d7
eor.l d7,d0
lsl.l #4,d7
eor.l d7,d1
move.l d3,d7
lsr.l #4,d7
eor.l d2,d7
and.l d4,d7
eor.l d7,d2
lsl.l #4,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
move.l d2,d7
lsr.l #8,d7
eor.l d0,d7
and.l d5,d7
eor.l d7,d0
lsl.l #8,d7
eor.l d7,d2
move.l d3,d7
lsr.l #8,d7
eor.l d1,d7
and.l d5,d7
eor.l d7,d1
lsl.l #8,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
bra.s c2p1x1_8_start
c2p1x1_8_pix16:
move.l (a0)+,d0
move.l (a0)+,d1
move.l (a0)+,d2
move.l (a0)+,d3
| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
move.l d1,d7
lsr.l #4,d7
move.l a3,(a1)+
eor.l d0,d7
and.l d4,d7
eor.l d7,d0
lsl.l #4,d7
eor.l d7,d1
move.l d3,d7
lsr.l #4,d7
eor.l d2,d7
and.l d4,d7
eor.l d7,d2
move.l a4,(a1)+
lsl.l #4,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
move.l d2,d7
lsr.l #8,d7
eor.l d0,d7
and.l d5,d7
eor.l d7,d0
move.l a5,(a1)+
lsl.l #8,d7
eor.l d7,d2
move.l d3,d7
lsr.l #8,d7
eor.l d1,d7
and.l d5,d7
eor.l d7,d1
move.l a6,(a1)+
lsl.l #8,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
c2p1x1_8_start:
move.l d2,d7
lsr.l #1,d7
eor.l d0,d7
and.l d6,d7
eor.l d7,d0
add.l d7,d7
eor.l d7,d2
move.l d3,d7
lsr.l #1,d7
eor.l d1,d7
and.l d6,d7
eor.l d7,d1
add.l d7,d7
eor.l d7,d3
| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1
| a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
| a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.w d2,d7
move.w d0,d2
swap d2
move.w d2,d0
move.w d7,d2
move.w d3,d7
move.w d1,d3
swap d3
move.w d3,d1
move.w d7,d3
| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0
| c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
| c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.l d2,d7
lsr.l #2,d7
eor.l d0,d7
and.l #0x33333333,d7
eor.l d7,d0
lsl.l #2,d7
eor.l d7,d2
move.l d3,d7
lsr.l #2,d7
eor.l d1,d7
and.l #0x33333333,d7
eor.l d7,d1
lsl.l #2,d7
eor.l d7,d3
| a7b7c7d7e7f7g7h7 i7j7k7l7m7n7o7p7 a6b6c6d6e6f6g6h6 i6j6k6l6m6n6o6p6
| a3b3c3d3e3f3g3h3 i3j3k3l3m3n3o3p3 a2b2c2d2e2f2g2h2 i2j2k2l2m2n2o2p2
| a5b5c5d5e5f5g5h5 i5j5k5l5m5n5o5p5 a4b4c4d4e4f4g4h4 i4j4k4l4m4n4o4p4
| a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0
swap d0
swap d1
swap d2
swap d3
move.l d0,a6
move.l d2,a5
move.l d1,a4
move.l d3,a3
cmp.l a0,a2
bne c2p1x1_8_pix16
move.l a3,(a1)+
move.l a4,(a1)+
move.l a5,(a1)+
move.l a6,(a1)+
movem.l (sp)+,d2-d7/a2-a6
rts
| void asm_c2p1x1_8_tt(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen, uint32 screenPitch);
SYM(asm_c2p1x1_8_tt):
movem.l d2-d7/a2-a6,-(sp) | 6 + 5 = 11 longs
#ifdef __FASTCALL__
| a0: chunky
move.l a1,a2 | a2: chunky end
move.l (11*4+4,sp),a1 | a1: screen
| d0.l: screen pitch (double width)
#else
move.l (11*4+4,sp),a0 | a0: chunky
move.l (11*4+8,sp),a2 | a2: chunky end
move.l (11*4+12,sp),a1 | a1: screen
move.l (11*4+16,sp),d0 | d0.l: screen pitch (double width)
#endif
move.l sp,old_sp
move.l d0,screen_pitch
lsr.l #1,d0
lea (a1,d0.l),a7 | a7: end of first dst line
move.l d0,screen_offset
move.l #0x0f0f0f0f,d4
move.l #0x00ff00ff,d5
move.l #0x55555555,d6
move.l (a0)+,d0
move.l (a0)+,d1
move.l (a0)+,d2
move.l (a0)+,d3
| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
move.l d1,d7
lsr.l #4,d7
eor.l d0,d7
and.l d4,d7
eor.l d7,d0
lsl.l #4,d7
eor.l d7,d1
move.l d3,d7
lsr.l #4,d7
eor.l d2,d7
and.l d4,d7
eor.l d7,d2
lsl.l #4,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
move.l d2,d7
lsr.l #8,d7
eor.l d0,d7
and.l d5,d7
eor.l d7,d0
lsl.l #8,d7
eor.l d7,d2
move.l d3,d7
lsr.l #8,d7
eor.l d1,d7
and.l d5,d7
eor.l d7,d1
lsl.l #8,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
bra.s c2p1x1_8_tt_start
c2p1x1_8_tt_pix16:
move.l (a0)+,d0
move.l (a0)+,d1
move.l (a0)+,d2
move.l (a0)+,d3
| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
move.l d1,d7
lsr.l #4,d7
move.l a3,(a1)+
eor.l d0,d7
and.l d4,d7
eor.l d7,d0
lsl.l #4,d7
eor.l d7,d1
move.l d3,d7
lsr.l #4,d7
eor.l d2,d7
and.l d4,d7
eor.l d7,d2
move.l a4,(a1)+
lsl.l #4,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
move.l d2,d7
lsr.l #8,d7
eor.l d0,d7
and.l d5,d7
eor.l d7,d0
move.l a5,(a1)+
lsl.l #8,d7
eor.l d7,d2
move.l d3,d7
lsr.l #8,d7
eor.l d1,d7
and.l d5,d7
eor.l d7,d1
move.l a6,(a1)+
lsl.l #8,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
cmp.l a1,a7 | end of dst line?
bne.s c2p1x1_8_tt_start
add.l (screen_offset,pc),a1
add.l (screen_pitch,pc),a7
c2p1x1_8_tt_start:
move.l d2,d7
lsr.l #1,d7
eor.l d0,d7
and.l d6,d7
eor.l d7,d0
add.l d7,d7
eor.l d7,d2
move.l d3,d7
lsr.l #1,d7
eor.l d1,d7
and.l d6,d7
eor.l d7,d1
add.l d7,d7
eor.l d7,d3
| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1
| a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
| a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.w d2,d7
move.w d0,d2
swap d2
move.w d2,d0
move.w d7,d2
move.w d3,d7
move.w d1,d3
swap d3
move.w d3,d1
move.w d7,d3
| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0
| c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
| c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.l d2,d7
lsr.l #2,d7
eor.l d0,d7
and.l #0x33333333,d7
eor.l d7,d0
lsl.l #2,d7
eor.l d7,d2
move.l d3,d7
lsr.l #2,d7
eor.l d1,d7
and.l #0x33333333,d7
eor.l d7,d1
lsl.l #2,d7
eor.l d7,d3
| a7b7c7d7e7f7g7h7 i7j7k7l7m7n7o7p7 a6b6c6d6e6f6g6h6 i6j6k6l6m6n6o6p6
| a3b3c3d3e3f3g3h3 i3j3k3l3m3n3o3p3 a2b2c2d2e2f2g2h2 i2j2k2l2m2n2o2p2
| a5b5c5d5e5f5g5h5 i5j5k5l5m5n5o5p5 a4b4c4d4e4f4g4h4 i4j4k4l4m4n4o4p4
| a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0
swap d0
swap d1
swap d2
swap d3
move.l d0,a6
move.l d2,a5
move.l d1,a4
move.l d3,a3
cmp.l a0,a2
bne c2p1x1_8_tt_pix16
move.l a3,(a1)+
move.l a4,(a1)+
move.l a5,(a1)+
move.l a6,(a1)+
move.l old_sp,sp
movem.l (sp)+,d2-d7/a2-a6
rts
| void asm_c2p1x1_8_rect(const byte *pChunky, const byte *pChunkyEnd, uint32 chunkyWidth, uint32 chunkyPitch, byte *pScreen, uint32 screenPitch);
SYM(asm_c2p1x1_8_rect):
movem.l d2-d7/a2-a6,-(sp) | 6 + 5 = 11 longs
#ifdef __FASTCALL__
| a0: chunky
move.l a1,chunky_end
| d0.l: chunky width
move.l (11*4+4,sp),a1 | a1: screen
exg d1,d2 | d2.l: chunky pitch
| d1.l: screen pitch
#else
move.l (11*4+4,sp),a0 | a0: chunky
move.l (11*4+8,sp),chunky_end
move.l (11*4+12,sp),d0 | d0.l: chunky width
move.l (11*4+16,sp),d2 | d2.l: chunky pitch
move.l (11*4+20,sp),a1 | a1: screen
move.l (11*4+24,sp),d1 | d1.l: screen pitch
#endif
move.l sp,old_sp
lea (a0,d0.l),a2 | a2: end of first src line
lea (a1,d0.l),a7 | a7: end of first dst line
move.l d1,screen_pitch
sub.l d0,d1
move.l d1,screen_offset
move.l d2,chunky_pitch
sub.l d0,d2
move.l d2,chunky_offset
move.l #0x0f0f0f0f,d4
move.l #0x00ff00ff,d5
move.l #0x55555555,d6
move.l (a0)+,d0
move.l (a0)+,d1
move.l (a0)+,d2
move.l (a0)+,d3
| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
move.l d1,d7
lsr.l #4,d7
eor.l d0,d7
and.l d4,d7
eor.l d7,d0
lsl.l #4,d7
eor.l d7,d1
move.l d3,d7
lsr.l #4,d7
eor.l d2,d7
and.l d4,d7
eor.l d7,d2
lsl.l #4,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
move.l d2,d7
lsr.l #8,d7
eor.l d0,d7
and.l d5,d7
eor.l d7,d0
lsl.l #8,d7
eor.l d7,d2
move.l d3,d7
lsr.l #8,d7
eor.l d1,d7
and.l d5,d7
eor.l d7,d1
lsl.l #8,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
bra.s c2p1x1_8_rect_start
c2p1x1_8_rect_pix16:
move.l (a0)+,d0
move.l (a0)+,d1
move.l (a0)+,d2
move.l (a0)+,d3
| a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0 d7d6d5d4d3d2d1d0
| e7e6e5e4e3e2e1e0 f7f6f5f4f3f2f1f0 g7g6g5g4g3g2g1g0 h7h6h5h4h3h2h1h0
| i7i6i5i4i3i2i1i0 j7j6j5j4j3j2j1j0 k7k6k5k4k3k2k1k0 l7l6l5l4l3l2l1l0
| m7m6m5m4m3m2m1m0 n7n6n5n4n3n2n1n0 o7o6o5o4o3o2o1o0 p7p6p5p4p3p2p1p0
move.l d1,d7
lsr.l #4,d7
move.l a3,(a1)+
eor.l d0,d7
and.l d4,d7
eor.l d7,d0
lsl.l #4,d7
eor.l d7,d1
move.l d3,d7
lsr.l #4,d7
eor.l d2,d7
and.l d4,d7
eor.l d7,d2
move.l a4,(a1)+
lsl.l #4,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 b7b6b5b4f7f6f5f4 c7c6c5c4g7g6g5g4 d7d6d5d4h7h6h5h4
| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
| i7i6i5i4m7m6m5m4 j7j6j5j4n7n6n5n4 k7k6k5k4o7o6o5o4 l7l6l5l4p7p6p5p4
| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
move.l d2,d7
lsr.l #8,d7
eor.l d0,d7
and.l d5,d7
eor.l d7,d0
move.l a5,(a1)+
lsl.l #8,d7
eor.l d7,d2
move.l d3,d7
lsr.l #8,d7
eor.l d1,d7
and.l d5,d7
eor.l d7,d1
move.l a6,(a1)+
lsl.l #8,d7
eor.l d7,d3
| a7a6a5a4e7e6e5e4 i7i6i5i4m7m6m5m4 c7c6c5c4g7g6g5g4 k7k6k5k4o7o6o5o4
| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
| b7b6b5b4f7f6f5f4 j7j6j5j4n7n6n5n4 d7d6d5d4h7h6h5h4 l7l6l5l4p7p6p5p4
| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
cmp.l a1,a7 | end of dst line?
bne.s c2p1x1_8_rect_start
add.l (screen_offset,pc),a1
add.l (screen_pitch,pc),a7
c2p1x1_8_rect_start:
move.l d2,d7
lsr.l #1,d7
eor.l d0,d7
and.l d6,d7
eor.l d7,d0
add.l d7,d7
eor.l d7,d2
move.l d3,d7
lsr.l #1,d7
eor.l d1,d7
and.l d6,d7
eor.l d7,d1
add.l d7,d7
eor.l d7,d3
| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1
| a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
| a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.w d2,d7
move.w d0,d2
swap d2
move.w d2,d0
move.w d7,d2
move.w d3,d7
move.w d1,d3
swap d3
move.w d3,d1
move.w d7,d3
| a7b7a5b5e7f7e5f5 i7j7i5j5m7n7m5n5 a6b6a4b4e6f6e4f4 i6j6i4j4m6n6m4n4
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0
| c7d7c5d5g7h7g5h5 k7l7k5l5o7p7o5p5 c6d6c4d4g6h6g4h4 k6l6k4l4o6p6o4p4
| c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.l d2,d7
lsr.l #2,d7
eor.l d0,d7
and.l #0x33333333,d7
eor.l d7,d0
lsl.l #2,d7
eor.l d7,d2
move.l d3,d7
lsr.l #2,d7
eor.l d1,d7
and.l #0x33333333,d7
eor.l d7,d1
lsl.l #2,d7
eor.l d7,d3
| a7b7c7d7e7f7g7h7 i7j7k7l7m7n7o7p7 a6b6c6d6e6f6g6h6 i6j6k6l6m6n6o6p6
| a3b3c3d3e3f3g3h3 i3j3k3l3m3n3o3p3 a2b2c2d2e2f2g2h2 i2j2k2l2m2n2o2p2
| a5b5c5d5e5f5g5h5 i5j5k5l5m5n5o5p5 a4b4c4d4e4f4g4h4 i4j4k4l4m4n4o4p4
| a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0
swap d0
swap d1
swap d2
swap d3
move.l d0,a6
move.l d2,a5
move.l d1,a4
move.l d3,a3
cmp.l a0,a2 | end of src line?
bne c2p1x1_8_rect_pix16
cmp.l (chunky_end,pc),a2
beq.s c2p1x1_8_rect_done
add.l (chunky_offset,pc),a0
add.l (chunky_pitch,pc),a2
bra c2p1x1_8_rect_pix16
c2p1x1_8_rect_done:
move.l a3,(a1)+
move.l a4,(a1)+
move.l a5,(a1)+
move.l a6,(a1)+
move.l old_sp,sp
movem.l (sp)+,d2-d7/a2-a6
rts
| void asm_c2p1x1_4(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen);
SYM(asm_c2p1x1_4):
#ifdef __FASTCALL__
| a0: chunky
move.l a1,d0 | chunky end
move.l 4(sp),a1 | screen
#else
move.l (4,sp),a0 | chunky
move.l (8,sp),d0 | chunky end
move.l (12,sp),a1 | screen
#endif
movem.l d2-d7/a2-a6,-(sp)
move.l d0,a2
move.l #0x33333333,d4
move.l #0x00ff00ff,d5
move.l #0x55555555,d6
move.l (a0)+,d0
move.l (a0)+,d2
move.l (a0)+,d1
move.l (a0)+,d3
lsl.l #4,d0
lsl.l #4,d1
or.l d2,d0
or.l d3,d1
bra.s c2p1x1_4_start
c2p1x1_4_pix16:
move.l (a0)+,d0
move.l (a0)+,d2
move.l (a0)+,d1
move.l (a0)+,d3
lsl.l #4,d0
lsl.l #4,d1
move.l a5,(a1)+
or.l d2,d0
or.l d3,d1
move.l a6,(a1)+
c2p1x1_4_start:
| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
move.l d1,d7
lsr.l #8,d7
eor.l d0,d7
and.l d5,d7
eor.l d7,d0
lsl.l #8,d7
eor.l d7,d1
| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
move.l d1,d7
lsr.l #1,d7
eor.l d0,d7
and.l d6,d7
eor.l d7,d0
add.l d7,d7
eor.l d7,d1
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1
| a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.w d1,d7
move.w d0,d1
swap d1
move.w d1,d0
move.w d7,d1
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0
| c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.l d1,d7
lsr.l #2,d7
eor.l d0,d7
and.l d4,d7
eor.l d7,d0
lsl.l #2,d7
eor.l d7,d1
| a3b3c3d3e3f3g3h3 i3j3k3l3m3n3o3p3 a2b2c2d2e2f2g2h2 i2j2k2l2m2n2o2p2
| a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0
swap d0
swap d1
move.l d1,a5
move.l d0,a6
cmp.l a0,a2
bne.s c2p1x1_4_pix16
move.l a5,(a1)+
move.l a6,(a1)+
movem.l (sp)+,d2-d7/a2-a6
rts
| void asm_c2p1x1_4_rect(const byte *pChunky, const byte *pChunkyEnd, uint32 chunkyWidth, uint32 chunkyPitch, byte *pScreen, uint32 screenPitch);
SYM(asm_c2p1x1_4_rect):
movem.l d2-d7/a2-a6,-(sp) | 6 + 5 = 11 longs
#ifdef __FASTCALL__
| a0: chunky
move.l a1,chunky_end
| d0.l: chunky width
move.l (11*4+4,sp),a1 | a1: screen
exg d1,d2 | d2.l: chunky pitch
| d1.l: screen pitch
#else
move.l (11*4+4,sp),a0 | a0: chunky
move.l (11*4+8,sp),chunky_end
move.l (11*4+12,sp),d0 | d0.l: chunky width
move.l (11*4+16,sp),d2 | d2.l: chunky pitch
move.l (11*4+20,sp),a1 | a1: screen
move.l (11*4+24,sp),d1 | d1.l: screen pitch
#endif
move.l sp,old_sp
move.l d0,d3 | d3.l: screen width
lsr.l #1,d3 |
lea (a0,d0.l),a2 | a2: end of first src line
lea (a1,d3.l),a7 | a7: end of first dst line
move.l d1,screen_pitch
sub.l d3,d1
move.l d1,screen_offset
move.l d2,chunky_pitch
sub.l d0,d2
move.l d2,chunky_offset
move.l #0x33333333,d4
move.l #0x00ff00ff,d5
move.l #0x55555555,d6
move.l (a0)+,d0
move.l (a0)+,d2
move.l (a0)+,d1
move.l (a0)+,d3
lsl.l #4,d0
lsl.l #4,d1
or.l d2,d0
or.l d3,d1
bra.s c2p1x1_4_rect_start
c2p1x1_4_rect_pix16:
move.l (a0)+,d0
move.l (a0)+,d2
move.l (a0)+,d1
move.l (a0)+,d3
lsl.l #4,d0
lsl.l #4,d1
move.l a5,(a1)+
or.l d2,d0
or.l d3,d1
move.l a6,(a1)+
cmp.l a1,a7 | end of dst line?
bne.s c2p1x1_4_rect_start
add.l (screen_offset,pc),a1
add.l (screen_pitch,pc),a7
c2p1x1_4_rect_start:
| a3a2a1a0e3e2e1e0 b3b2b1b0f3f2f1f0 c3c2c1c0g3g2g1g0 d3d2d1d0h3h2h1h0
| i3i2i1i0m3m2m1m0 j3j2j1j0n3n2n1n0 k3k2k1k0o3o2o1o0 l3l2l1l0p3p2p1p0
move.l d1,d7
lsr.l #8,d7
eor.l d0,d7
and.l d5,d7
eor.l d7,d0
lsl.l #8,d7
eor.l d7,d1
| a3a2a1a0e3e2e1e0 i3i2i1i0m3m2m1m0 c3c2c1c0g3g2g1g0 k3k2k1k0o3o2o1o0
| b3b2b1b0f3f2f1f0 j3j2j1j0n3n2n1n0 d3d2d1d0h3h2h1h0 l3l2l1l0p3p2p1p0
move.l d1,d7
lsr.l #1,d7
eor.l d0,d7
and.l d6,d7
eor.l d7,d0
add.l d7,d7
eor.l d7,d1
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1
| a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.w d1,d7
move.w d0,d1
swap d1
move.w d1,d0
move.w d7,d1
| a3b3a1b1e3f3e1f1 i3j3i1j1m3n3m1n1 a2b2a0b0e2f2e0f0 i2j2i0j0m2n2m0n0
| c3d3c1d1g3h3g1h1 k3l3k1l1o3p3o1p1 c2d2c0d0g2h2g0h0 k2l2k0l0o2p2o0p0
move.l d1,d7
lsr.l #2,d7
eor.l d0,d7
and.l d4,d7
eor.l d7,d0
lsl.l #2,d7
eor.l d7,d1
| a3b3c3d3e3f3g3h3 i3j3k3l3m3n3o3p3 a2b2c2d2e2f2g2h2 i2j2k2l2m2n2o2p2
| a1b1c1d1e1f1g1h1 i1j1k1l1m1n1o1p1 a0b0c0d0e0f0g0h0 i0j0k0l0m0n0o0p0
swap d0
swap d1
move.l d1,a5
move.l d0,a6
cmp.l a0,a2 | end of src line?
bne.s c2p1x1_4_rect_pix16
cmp.l (chunky_end,pc),a2
beq.s c2p1x1_4_rect_done
add.l (chunky_offset,pc),a0
add.l (chunky_pitch,pc),a2
bra.s c2p1x1_4_rect_pix16
c2p1x1_4_rect_done:
move.l a5,(a1)+
move.l a6,(a1)+
move.l old_sp,sp
movem.l (sp)+,d2-d7/a2-a6
rts
| place it within reach of 32K (PC relative)
screen_pitch:
ds.l 1
screen_offset:
ds.l 1
chunky_pitch:
ds.l 1
chunky_offset:
ds.l 1
chunky_end:
ds.l 1
.bss
.even
old_sp: ds.l 1

View File

@@ -0,0 +1,88 @@
/* 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 BACKENDS_GRAPHICS_ATARI_C2P_ASM_H
#define BACKENDS_GRAPHICS_ATARI_C2P_ASM_H
#include "common/scummsys.h"
extern "C" {
/**
* Chunky to planar conversion routine. Converts a chunky (byte) buffer into eight bitplanes.
* Optimized for surface-to-surface copy with the same pitch.
*
* @param pChunky chunky buffer start
* @param pChunkyEnd chunky buffer end (past-the-end iterator)
* @param pScreen bitplane screen start
*/
void asm_c2p1x1_8(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen);
/**
* Chunky to planar conversion routine. Converts a chunky (byte) buffer into eight bitplanes.
* Optimized for surface-to-surface copy with the double screen pitch (typically 320x480).
*
* @param pChunky chunky buffer start
* @param pChunkyEnd chunky buffer end (past-the-end iterator)
* @param pScreen bitplane screen start
* @param screenPitch bitplane screen width (in bytes)
*/
void asm_c2p1x1_8_tt(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen, uint32 screenPitch);
/**
* Chunky to planar conversion routine. Converts a chunky (byte) buffer into eight bitplanes.
* Optimized for arbitrary rectangle position and dimension (16px aligned).
*
* @param pChunky chunky buffer at rectangle's [X1, Y1] position
* @param pChunkyEnd chunky buffer at rectangle's [X2, Y2] position (included)
* @param chunkyWidth rectangle width
* @param chunkyPitch chunky buffer width (in bytes)
* @param pScreen bitplane screen at rectangle's [X1, Y1] position
* @param screenPitch bitplane screen width (in bytes)
*/
void asm_c2p1x1_8_rect(const byte *pChunky, const byte *pChunkyEnd, uint32 chunkyWidth, uint32 chunkyPitch, byte *pScreen, uint32 screenPitch);
/**
* Chunky to planar conversion routine. Converts a chunky (byte) buffer into four bitplanes.
* Optimized for surface-to-surface copy with the same pitch.
*
* @param pChunky chunky buffer start
* @param pChunkyEnd chunky buffer end (past-the-end iterator)
* @param pScreen bitplane screen start
*/
void asm_c2p1x1_4(const byte *pChunky, const byte *pChunkyEnd, byte *pScreen);
/**
* Chunky to planar conversion routine. Converts a chunky (byte) buffer into four bitplanes.
* Optimized for arbitrary rectangle position and dimension (16px aligned).
*
* @param pChunky chunky buffer at rectangle's [X1, Y1] position
* @param pChunkyEnd chunky buffer at rectangle's [X2, Y2] position (included)
* @param chunkyWidth rectangle width
* @param chunkyPitch chunky buffer width (in bytes)
* @param pScreen bitplane screen at rectangle's [X1, Y1] position
* @param screenPitch bitplane screen width (in bytes)
*/
void asm_c2p1x1_4_rect(const byte *pChunky, const byte *pChunkyEnd, uint32 chunkyWidth, uint32 chunkyPitch, byte *pScreen, uint32 screenPitch);
}
#endif

View File

@@ -0,0 +1,320 @@
/* 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 "atari-cursor.h"
#include "atari-supervidel.h"
#include "atari-surface.h"
//#include "backends/platform/atari/atari-debug.h"
bool Cursor::_globalSurfaceChanged;
byte Cursor::_palette[256*3];
const byte *Cursor::_buf;
int Cursor::_width;
int Cursor::_height;
int Cursor::_hotspotX;
int Cursor::_hotspotY;
uint32 Cursor::_keycolor;
Graphics::Surface Cursor::_surface;
Graphics::Surface Cursor::_surfaceMask;
Cursor::~Cursor() {
_savedBackground.free();
// beware, called multiple times (they have to be destroyed before
// AtariSurfaceDeinit() is called)
if (_surface.getPixels())
_surface.free();
if (_surface.getPixels())
_surfaceMask.free();
}
void Cursor::update() {
if (!_buf) {
_outOfScreen = true;
_savedRect = _alignedDstRect = Common::Rect();
return;
}
if (!_visible || (!_positionChanged && !_surfaceChanged))
return;
_srcRect = Common::Rect(_width, _height);
_dstRect = Common::Rect(
_x - _hotspotX, // left
_y - _hotspotY, // top
_x - _hotspotX + _width, // right
_y - _hotspotY + _height); // bottom
_outOfScreen = !_boundingSurf->clip(_srcRect, _dstRect);
if (!_outOfScreen) {
assert(_srcRect.width() == _dstRect.width());
assert(_srcRect.height() == _dstRect.height());
const int dstBitsPerPixel = _screenSurf->getBitsPerPixel();
const int xOffset = (_screenSurf->w - _boundingSurf->w) / 2;
// non-direct rendering never uses 4bpp but maybe in the future ...
_savedRect = AtariSurface::alignRect(
_dstRect.left * dstBitsPerPixel / 8, // fake 4bpp by 8bpp's x/2
_dstRect.top,
_dstRect.right * dstBitsPerPixel / 8, // fake 4bpp by 8bpp's width/2
_dstRect.bottom);
// this is used only in flushBackground() for comparison with rects
// passed by Screen::addDirtyRect (aligned and shifted by the same offset)
_alignedDstRect = AtariSurface::alignRect(
_dstRect.left + xOffset,
_dstRect.top,
_dstRect.right + xOffset,
_dstRect.bottom);
}
}
void Cursor::updatePosition(int deltaX, int deltaY) {
//atari_debug("Cursor::updatePosition: %d, %d", deltaX, deltaX);
if (deltaX == 0 && deltaY == 0)
return;
_x += deltaX;
_y += deltaY;
if (_x < 0)
_x = 0;
else if (_x >= _boundingSurf->w)
_x = _boundingSurf->w - 1;
if (_y < 0)
_y = 0;
else if (_y >= _boundingSurf->h)
_y = _boundingSurf->h - 1;
_positionChanged = true;
}
/* static */ void Cursor::setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor) {
if (w == 0 || h == 0 || buf == nullptr) {
_buf = nullptr;
return;
}
_buf = (const byte *)buf;
_width = w;
_height = h;
_hotspotX = hotspotX;
_hotspotY = hotspotY;
_keycolor = keycolor;
_globalSurfaceChanged = true;
}
/* static */ void Cursor::setPalette(const byte *colors, uint start, uint num) {
memcpy(&_palette[start * 3], colors, num * 3);
_globalSurfaceChanged = true;
}
/* static */ void Cursor::convertSurfaceTo(const Graphics::PixelFormat &format) {
static int rShift, gShift, bShift;
static int rMask, gMask, bMask;
const int cursorWidth = g_hasSuperVidel ? _width : ((_width + 15) & (-16));
const int cursorHeight = _height;
const bool isCLUT8 = format.isCLUT8();
if (_surface.w != cursorWidth || _surface.h != cursorHeight || _surface.format != format) {
if (!isCLUT8 && _surface.format != format) {
rShift = format.rLoss - format.rShift;
gShift = format.gLoss - format.gShift;
bShift = format.bLoss - format.bShift;
rMask = format.rMax() << format.rShift;
gMask = format.gMax() << format.gShift;
bMask = format.bMax() << format.bShift;
}
// always 8-bit as this is both 8-bit src and 4-bit dst for C2P
_surface.create(cursorWidth, cursorHeight, format);
assert(_surface.pitch == _surface.w);
// always 8-bit or 1-bit
_surfaceMask.create(g_hasSuperVidel ? _surface.w : _surface.w / 8, _surface.h, PIXELFORMAT_CLUT8);
_surfaceMask.w = _surface.w;
}
const byte *src = _buf;
byte *dst = (byte *)_surface.getPixels();
byte *dstMask = (byte *)_surfaceMask.getPixels();
uint16 *dstMask16 = (uint16 *)_surfaceMask.getPixels();
const int dstPadding = _surface.w - _width;
uint16 mask16 = 0xffff;
uint16 invertedBit = 0x7fff;
for (int j = 0; j < _height; ++j) {
for (int i = 0; i < _width; ++i) {
const uint32 color = *src++;
if (color != _keycolor) {
if (!isCLUT8) {
// Convert CLUT8 to RGB332/RGB121 palette
*dst++ = ((_palette[color*3 + 0] >> rShift) & rMask)
| ((_palette[color*3 + 1] >> gShift) & gMask)
| ((_palette[color*3 + 2] >> bShift) & bMask);
} else {
*dst++ = color;
}
if (g_hasSuperVidel)
*dstMask++ = 0xff;
else
mask16 &= invertedBit;
} else {
*dst++ = 0x00;
if (g_hasSuperVidel)
*dstMask++ = 0x00;
}
if (!g_hasSuperVidel && invertedBit == 0xfffe) {
*dstMask16++ = mask16;
mask16 = 0xffff;
}
// ror.w #1,invertedBit
invertedBit = (invertedBit >> 1) | (invertedBit << (sizeof (invertedBit) * 8 - 1));
}
if (dstPadding) {
assert(!g_hasSuperVidel);
// this is at most 15 pixels
memset(dst, 0x00, dstPadding);
dst += dstPadding;
*dstMask16++ = mask16;
mask16 = 0xffff;
invertedBit = 0x7fff;
}
}
}
Common::Rect Cursor::flushBackground(const Common::Rect &alignedRect, bool directRendering) {
if (_savedRect.isEmpty())
return _savedRect;
if (!alignedRect.isEmpty() && alignedRect.contains(_alignedDstRect)) {
// better would be _visibilityChanged but update() ignores it
_positionChanged = true;
_savedRect = Common::Rect();
} else if (alignedRect.isEmpty() || alignedRect.intersects(_alignedDstRect)) {
// better would be _visibilityChanged but update() ignores it
_positionChanged = true;
if (directRendering)
restoreBackground();
else
return _alignedDstRect;
}
return Common::Rect();
}
void Cursor::saveBackground() {
if (_savedRect.isEmpty())
return;
// as this is used only for direct rendering, we don't need to worry about offsettedSurf
// having different dimensions than the source surface
const Graphics::Surface &dstSurface = *_screenSurf;
//atari_debug("Cursor::saveBackground: %d %d %d %d", _savedRect.left, _savedRect.top, _savedRect.width(), _savedRect.height());
// save native bitplanes or pixels, so it must be a Graphics::Surface to copy from
if (_savedBackground.w != _savedRect.width()
|| _savedBackground.h != _savedRect.height()
|| _savedBackground.format != dstSurface.format) {
_savedBackground.create(_savedRect.width(), _savedRect.height(), dstSurface.format);
}
_savedBackground.copyRectToSurface(dstSurface, 0, 0, _savedRect);
}
void Cursor::draw() {
AtariSurface &dstSurface = *_screenSurf;
const int dstBitsPerPixel = dstSurface.getBitsPerPixel();
//atari_debug("Cursor::draw: %d %d %d %d", _dstRect.left, _dstRect.top, _dstRect.width(), _dstRect.height());
if (_globalSurfaceChanged) {
convertSurfaceTo(dstSurface.format);
if (!g_hasSuperVidel) {
// C2P in-place
AtariSurface surf;
surf.w = _surface.w;
surf.h = _surface.h;
surf.pitch = _surface.pitch * dstBitsPerPixel / 8; // 4bpp is not byte per pixel anymore
surf.setPixels(_surface.getPixels());
surf.format = _surface.format;
surf.copyRectToSurface(
_surface,
0, 0,
Common::Rect(_surface.w, _surface.h));
}
_globalSurfaceChanged = false;
}
dstSurface.drawMaskedSprite(
_surface, _surfaceMask, *_boundingSurf,
_dstRect.left, _dstRect.top,
_srcRect);
_visibilityChanged = _positionChanged = _surfaceChanged = false;
}
void Cursor::restoreBackground() {
if (_savedRect.isEmpty())
return;
assert(_savedBackground.getPixels());
//atari_debug("Cursor::restoreBackground: %d %d %d %d", _savedRect.left, _savedRect.top, _savedRect.width(), _savedRect.height());
// as this is used only for direct rendering, we don't need to worry about offsettedSurf
// having different dimensions than the source surface
Graphics::Surface &dstSurface = *_screenSurf->surfacePtr();
// restore native bitplanes or pixels, so it must be a Graphics::Surface to copy to
dstSurface.copyRectToSurface(
_savedBackground,
_savedRect.left, _savedRect.top,
Common::Rect(_savedBackground.w, _savedBackground.h));
_savedRect = Common::Rect();
}

View File

@@ -0,0 +1,140 @@
/* 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 BACKENDS_GRAPHICS_ATARI_CURSOR_H
#define BACKENDS_GRAPHICS_ATARI_CURSOR_H
#include "graphics/surface.h"
class AtariSurface;
// Global state consists of:
// - palette (used for the overlay only atm)
// - shape (surface, dimensions, hotspot, keycolor)
// - visibility
// These always get updates by ScummVM, no need to differentiate between engines and the overlay.
struct Cursor {
~Cursor();
void reset(AtariSurface* screenSurf, const Graphics::Surface *boundingSurf) {
_screenSurf = screenSurf;
_boundingSurf = boundingSurf;
_positionChanged = true;
_surfaceChanged = true;
_visibilityChanged = false;
_savedRect = _alignedDstRect = Common::Rect();
}
// updates outOfScreen OR srcRect/dstRect (only if visible/needed)
void update();
// visibility
bool setVisible(bool visible) {
if (_visible == visible) {
return _visible;
}
bool last = _visible;
_visible = visible;
_visibilityChanged = true;
return last;
}
void setSurfaceChanged() {
_surfaceChanged = true;
}
// position
Common::Point getPosition() const {
return Common::Point(_x, _y);
}
void setPosition(int x, int y) {
//atari_debug("Cursor::setPosition: %d, %d", x, y);
if (_x == x && _y == y)
return;
_x = x;
_y = y;
_positionChanged = true;
}
void updatePosition(int deltaX, int deltaY);
// surface
static void setSurface(const void *buf, int w, int h, int hotspotX, int hotspotY, uint32 keycolor);
static void setPalette(const byte *colors, uint start, uint num);
bool isVisible() const {
return !_outOfScreen && _visible;
}
bool isChanged() const {
return _positionChanged || _surfaceChanged || _visibilityChanged;
}
Common::Rect flushBackground(const Common::Rect &alignedRect, bool directRendering);
void saveBackground();
void draw();
private:
static void convertSurfaceTo(const Graphics::PixelFormat &format);
void restoreBackground();
AtariSurface *_screenSurf;
const Graphics::Surface *_boundingSurf = nullptr;
bool _positionChanged = false;
bool _surfaceChanged = false;
bool _visibilityChanged = false;
bool _visible = false;
int _x = 0;
int _y = 0;
bool _outOfScreen = true;
Common::Rect _srcRect;
Common::Rect _dstRect;
Graphics::Surface _savedBackground;
Common::Rect _savedRect;
Common::Rect _alignedDstRect;
// related to 'surface'
static bool _globalSurfaceChanged;
static byte _palette[256*3];
static const byte *_buf;
static int _width;
static int _height;
static int _hotspotX;
static int _hotspotY;
static uint32 _keycolor;
static Graphics::Surface _surface;
static Graphics::Surface _surfaceMask;
};
#endif // BACKENDS_GRAPHICS_ATARI_CURSOR_H

View File

@@ -0,0 +1,510 @@
/* 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 "../../platform/atari/symbols.h"
.global SYM(asm_draw_4bpl_sprite)
.global SYM(asm_draw_8bpl_sprite)
.text
skip_first_pix16:
dc.b 0
skip_last_pix16:
dc.b 0
| extern void asm_draw_4bpl_sprite(uint16 *dstBuffer, const uint16 *srcBuffer, const uint16 *srcMask,
| uint destX, uint destY, uint dstPitch, uint srcPitch, uint w, uint h,
| bool skipFirstPix16, bool skipLastPix16);
SYM(asm_draw_4bpl_sprite):
movem.l d2-d7/a2,-(sp) | 7 longs
#ifdef __FASTCALL__
move.l a0,a2 | a2: dstBuffer
| a1: srcBuffer
move.l (4+7*4,sp),a0 | a0: srcMask
| d0.w: destX
| d1.w: destY
move.l d2,d3 | d3.w: dstPitch
move.l (8+7*4,sp),d4 | d4.w: srcPitch
move.l (12+7*4,sp),d6 | d6.w: w
move.l (16+7*4,sp),d7 | d7.w: h
tst.l (20+7*4,sp) | skipFirstPix16?
sne skip_first_pix16
tst.l (24+7*4,sp) | skipLastPix16?
sne skip_last_pix16
#else
move.l (4+7*4,sp),a2 | a2: dstBuffer
move.l (8+7*4,sp),a1 | a1: srcBuffer
move.l (12+7*4,sp),a0 | a0: srcMask
move.l (16+7*4,sp),d0 | d0.w: destX
move.l (20+7*4,sp),d1 | d1.w: destY
move.l (24+7*4,sp),d3 | d3.w: dstPitch
move.l (28+7*4,sp),d4 | d4.w: srcPitch
move.l (32+7*4,sp),d6 | d6.w: w
move.l (36+7*4,sp),d7 | d7.w: h
tst.l (40+7*4,sp) | skipFirstPix16?
sne skip_first_pix16
tst.l (44+7*4,sp) | skipLastPix16?
sne skip_last_pix16
#endif
| Draws a 4 bitplane sprite at any position on screen.
| (c) 1999 Pieter van der Meer (EarX)
|
| INPUT: d0.w: x position of sprite on screen (left side)
| d1.w: y position of sprite on screen (top side)
| d6.w: number of 16pixel X blocks to do
| d7.w: number of Y lines to to
| a0: address of maskdata
| a1: address of bitmapdata
| a2: screen start address
move.w d0,d2 | / Calculate the
andi.w #0b111111110000,d0 | | number of bits
sub.w d0,d2 | \ to shift right.
lsr.w #1,d0 | / Add x-position to
adda.w d0,a2 | \ screenaddress.
mulu.w d3,d1 | / Add y-position to
adda.l d1,a2 | \ screenaddress.
lsr.w #1,d6
sub.w d6,d3 | / Prepare offset to next
ext.l d3 | \ destination line.
sub.w d6,d4 | / Prepare offset to next
ext.l d4 | \ source line.
subq.w #1,d7 | Adjust for dbra.
lsr.w #3,d6 | d6.w: w/16
subq.w #1,d6 | Adjust for dbra.
move.w d6,d5 | Backup xloopcount in d5.w.
tst.b (skip_first_pix16,pc)
jeq 1f
subq.w #1,d5
1:
tst.b (skip_last_pix16,pc)
jeq 2f
subq.w #1,d5
2:
sprite4_yloop:
move.w d5,d6 | Restore xloop counter.
tst.b (skip_first_pix16,pc)
jeq 1f
moveq #16,d1
sub.w d2,d1
moveq #0xffffffff,d0 | Prepare for maskshifting.
move.w (a0)+,d0 | Get 16pixel mask in d0.w.
rol.l d1,d0 | Shift it!
addq.l #8,a2
and.w d0,(a2)+ | Mask overspill bitplane 0.
and.w d0,(a2)+ | Mask overspill bitplane 1.
and.w d0,(a2)+ | Mask overspill bitplane 2.
and.w d0,(a2)+ | Mask overspill bitplane 3.
subq.l #8,a2 | Return to blockstart.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 0.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 1.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 2.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 3.
subq.l #8,a2
1: tst.w d6
jmi sprite4_xloop_done
sprite4_xloop:
moveq #0xffffffff,d0 | Prepare for maskshifting.
move.w (a0)+,d0 | Get 16pixel mask in d0.w.
ror.l d2,d0 | Shift it!
and.w d0,(a2)+ | Mask bitplane 0.
and.w d0,(a2)+ | Mask bitplane 1.
and.w d0,(a2)+ | Mask bitplane 2.
and.w d0,(a2)+ | Mask bitplane 3.
swap d0 | Get overspill in loword.
and.w d0,(a2)+ | Mask overspill bitplane 0.
and.w d0,(a2)+ | Mask overspill bitplane 1.
and.w d0,(a2)+ | Mask overspill bitplane 2.
and.w d0,(a2)+ | Mask overspill bitplane 3.
lea (-16,a2),a2 | Return to blockstart.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 0.
swap d0 | Get overspill in loword.
or.w d0,6(a2) | Paint overspill bitplane 0.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 1.
swap d0 | Get overspill in loword.
or.w d0,6(a2) | Paint overspill bitplane 1.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 2.
swap d0 | Get overspill in loword.
or.w d0,6(a2) | Paint overspill bitplane 2.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 3.
swap d0 | Get overspill in loword.
or.w d0,6(a2) | Paint overspill bitplane 3.
dbra d6,sprite4_xloop | Loop until blocks done.
sprite4_xloop_done:
tst.b (skip_last_pix16,pc)
jeq 1f
moveq #0xffffffff,d0 | Prepare for maskshifting.
move.w (a0)+,d0 | Get 16pixel mask in d0.w.
ror.l d2,d0 | Shift it!
and.w d0,(a2)+ | Mask bitplane 0.
and.w d0,(a2)+ | Mask bitplane 1.
and.w d0,(a2)+ | Mask bitplane 2.
and.w d0,(a2)+ | Mask bitplane 3.
subq.l #8,a2 | Return to blockstart.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 0.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 1.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 2.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 3.
1: move.l d4,d0
asr.l #2,d0
adda.l d0,a0
adda.l d4,a1
adda.l d3,a2 | Goto next screenline.
dbra d7,sprite4_yloop | Loop until lines done.
movem.l (sp)+,d2-d7/a2
rts
| extern void asm_draw_8bpl_sprite(uint16 *dstBuffer, const uint16 *srcBuffer, const uint16 *srcMask,
| uint destX, uint destY, uint dstPitch, uint srcPitch, uint w, uint h,
| bool skipFirstPix16, bool skipLastPix16);
SYM(asm_draw_8bpl_sprite):
movem.l d2-d7/a2,-(sp) | 7 longs
#ifdef __FASTCALL__
move.l a0,a2 | a2: dstBuffer
| a1: srcBuffer
move.l (4+7*4,sp),a0 | a0: srcMask
| d0.w: destX
| d1.w: destY
move.l d2,d3 | d3.w: dstPitch
move.l (8+7*4,sp),d4 | d4.w: srcPitch
move.l (12+7*4,sp),d6 | d6.w: w
move.l (16+7*4,sp),d7 | d7.w: h
tst.l (20+7*4,sp) | skipFirstPix16?
sne skip_first_pix16
tst.l (24+7*4,sp) | skipLastPix16?
sne skip_last_pix16
#else
move.l (4+7*4,sp),a2 | a2: dstBuffer
move.l (8+7*4,sp),a1 | a1: srcBuffer
move.l (12+7*4,sp),a0 | a0: srcMask
move.l (16+7*4,sp),d0 | d0.w: destX
move.l (20+7*4,sp),d1 | d1.w: destY
move.l (24+7*4,sp),d3 | d3.w: dstPitch
move.l (28+7*4,sp),d4 | d4.w: srcPitch
move.l (32+7*4,sp),d6 | d6.w: w
move.l (36+7*4,sp),d7 | d7.w: h
tst.l (40+7*4,sp) | skipFirstPix16?
sne skip_first_pix16
tst.l (45+7*4,sp) | skipLastPix16?
sne skip_last_pix16
#endif
move.w d0,d2 | / Calculate the
andi.w #0b111111110000,d0 | | number of bits
sub.w d0,d2 | \ to shift right.
adda.w d0,a2 | Add x-position to screenaddress.
mulu.w d3,d1 | / Add y-position to
adda.l d1,a2 | \ screenaddress.
sub.w d6,d3 | / Prepare offset to next
ext.l d3 | \ destination line.
sub.w d6,d4 | / Prepare offset to next
ext.l d4 | \ source line.
subq.w #1,d7 | Adjust for dbra.
lsr.w #4,d6 | d6.w: w/16
subq.w #1,d6 | Adjust for dbra.
move.w d6,d5 | Backup xloopcount in d5.w.
tst.b (skip_first_pix16,pc)
jeq 1f
subq.w #1,d5
1:
tst.b (skip_last_pix16,pc)
jeq 2f
subq.w #1,d5
2:
sprite8_yloop:
move.w d5,d6 | Restore xloop counter.
tst.b (skip_first_pix16,pc)
jeq 1f
moveq #16,d1
sub.w d2,d1
moveq #0xffffffff,d0 | Prepare for maskshifting.
move.w (a0)+,d0 | Get 16pixel mask in d0.w.
rol.l d1,d0 | Shift it!
lea (16,a2),a2
and.w d0,(a2)+ | Mask overspill bitplane 0.
and.w d0,(a2)+ | Mask overspill bitplane 1.
and.w d0,(a2)+ | Mask overspill bitplane 2.
and.w d0,(a2)+ | Mask overspill bitplane 3.
and.w d0,(a2)+ | Mask overspill bitplane 4.
and.w d0,(a2)+ | Mask overspill bitplane 5.
and.w d0,(a2)+ | Mask overspill bitplane 6.
and.w d0,(a2)+ | Mask overspill bitplane 7.
lea (-16,a2),a2 | Return to blockstart.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 0.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 1.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 2.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 3.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 4.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 5.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 6.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
rol.l d1,d0 | Shift it.
or.w d0,(a2)+ | Paint overspill bitplane 7.
lea (-16,a2),a2
1: tst.w d6
jmi sprite8_xloop_done
sprite8_xloop:
moveq #0xffffffff,d0 | Prepare for maskshifting.
move.w (a0)+,d0 | Get 16pixel mask in d0.w.
ror.l d2,d0 | Shift it!
and.w d0,(a2)+ | Mask bitplane 0.
and.w d0,(a2)+ | Mask bitplane 1.
and.w d0,(a2)+ | Mask bitplane 2.
and.w d0,(a2)+ | Mask bitplane 3.
and.w d0,(a2)+ | Mask bitplane 4.
and.w d0,(a2)+ | Mask bitplane 5.
and.w d0,(a2)+ | Mask bitplane 6.
and.w d0,(a2)+ | Mask bitplane 7.
swap d0 | Get overspill in loword.
and.w d0,(a2)+ | Mask overspill bitplane 0.
and.w d0,(a2)+ | Mask overspill bitplane 1.
and.w d0,(a2)+ | Mask overspill bitplane 2.
and.w d0,(a2)+ | Mask overspill bitplane 3.
and.w d0,(a2)+ | Mask overspill bitplane 4.
and.w d0,(a2)+ | Mask overspill bitplane 5.
and.w d0,(a2)+ | Mask overspill bitplane 6.
and.w d0,(a2)+ | Mask overspill bitplane 7.
lea (-32,a2),a2 | Return to blockstart.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 0.
swap d0 | Get overspill in loword.
or.w d0,14(a2) | Paint overspill bitplane 0.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 1.
swap d0 | Get overspill in loword.
or.w d0,14(a2) | Paint overspill bitplane 1.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 2.
swap d0 | Get overspill in loword.
or.w d0,14(a2) | Paint overspill bitplane 2.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 3.
swap d0 | Get overspill in loword.
or.w d0,14(a2) | Paint overspill bitplane 3.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 4.
swap d0 | Get overspill in loword.
or.w d0,14(a2) | Paint overspill bitplane 4.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 5.
swap d0 | Get overspill in loword.
or.w d0,14(a2) | Paint overspill bitplane 5.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 6.
swap d0 | Get overspill in loword.
or.w d0,14(a2) | Paint overspill bitplane 6.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 7.
swap d0 | Get overspill in loword.
or.w d0,14(a2) | Paint overspill bitplane 7.
dbra d6,sprite8_xloop | Loop until blocks done.
sprite8_xloop_done:
tst.b (skip_last_pix16,pc)
jeq 1f
moveq #0xffffffff,d0 | Prepare for maskshifting.
move.w (a0)+,d0 | Get 16pixel mask in d0.w.
ror.l d2,d0 | Shift it!
and.w d0,(a2)+ | Mask bitplane 0.
and.w d0,(a2)+ | Mask bitplane 1.
and.w d0,(a2)+ | Mask bitplane 2.
and.w d0,(a2)+ | Mask bitplane 3.
and.w d0,(a2)+ | Mask bitplane 4.
and.w d0,(a2)+ | Mask bitplane 5.
and.w d0,(a2)+ | Mask bitplane 6.
and.w d0,(a2)+ | Mask bitplane 7.
lea (-16,a2),a2 | Return to blockstart.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 0.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 1.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 2.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 3.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 4.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 5.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 6.
moveq #0,d0 | Prepare for bitmapshifting.
move.w (a1)+,d0 | Get bitplaneword in d0.w.
ror.l d2,d0 | Shift it.
or.w d0,(a2)+ | Paint bitplane 7.
1: move.l d4,d0
asr.l #3,d0
adda.l d0,a0
adda.l d4,a1
adda.l d3,a2 | Goto next screenline.
dbra d7,sprite8_yloop | Loop until lines done.
movem.l (sp)+,d2-d7/a2
rts

View File

@@ -0,0 +1,68 @@
/* 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 BACKENDS_GRAPHICS_ATARI_ASM_H
#define BACKENDS_GRAPHICS_ATARI_ASM_H
#include "common/scummsys.h"
extern "C" {
/**
* Copy 4bpl sprite into 4bpl buffer. Sprite's width must be multiply of 16.
*
* @param dstBuffer destination buffer (four bitplanes)
* @param srcBuffer source buffer (four bitplanes)
* @param srcMask source mask (one bitplane)
* @param destX sprite's X position (in pixels)
* @param destY sprite's Y position (in pixels)
* @param dstPitch destination buffer's pitch (in bytes)
* @param srcPitch source buffer's pitch (in bytes)
* @param w sprite's width (in pixels)
* @param h sprite's height (in pixels)
* @param skipFirstPix16 do not write first 16 pixels
* @param skipLastPix16 do not write last 16 pixels
*/
void asm_draw_4bpl_sprite(uint16 *dstBuffer, const uint16 *srcBuffer, const uint16 *srcMask,
uint destX, uint destY, uint dstPitch, uint srcPitch, uint w, uint h,
bool skipFirstPix16, bool skipLastPix16);
/**
* Copy 8bpl sprite into 8bpl buffer. Sprite's width must be multiply of 16.
*
* @param dstBuffer destination buffer (eight bitplanes)
* @param srcBuffer source buffer (eight bitplanes)
* @param srcMask source mask (one bitplane)
* @param destX sprite's X position (in pixels)
* @param destY sprite's Y position (in pixels)
* @param dstPitch destination buffer's pitch (in bytes)
* @param srcPitch source buffer's pitch (in bytes)
* @param w sprite's width (in pixels)
* @param h sprite's height (in pixels)
* @param skipFirstPix16 do not write first 16 pixels
* @param skipLastPix16 do not write last 16 pixels
*/
void asm_draw_8bpl_sprite(uint16 *dstBuffer, const uint16 *srcBuffer, const uint16 *srcMask,
uint destX, uint destY, uint dstPitch, uint srcPitch, uint w, uint h,
bool skipFirstPix16, bool skipLastPix16);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
/* 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 BACKENDS_GRAPHICS_ATARI_H
#define BACKENDS_GRAPHICS_ATARI_H
#include "backends/graphics/graphics.h"
#include "common/events.h"
#include "graphics/surface.h"
#include "atari-cursor.h"
#include "atari-pendingscreenchanges.h"
#include "atari-screen.h"
#include "atari-supervidel.h"
#define MAX_HZ_SHAKE 16 // Falcon only
#define MAX_V_SHAKE 16
class AtariGraphicsManager final : public GraphicsManager, Common::EventObserver {
friend class PendingScreenChanges;
public:
AtariGraphicsManager();
virtual ~AtariGraphicsManager();
bool hasFeature(OSystem::Feature f) const override;
void setFeatureState(OSystem::Feature f, bool enable) override;
bool getFeatureState(OSystem::Feature f) const override;
const OSystem::GraphicsMode *getSupportedGraphicsModes() const override {
static const OSystem::GraphicsMode graphicsModes[] = {
{ "direct", "Direct rendering", kDirectRendering },
{ "single", "Single buffering", kSingleBuffering },
{ "triple", "Triple buffering", kTripleBuffering },
{ nullptr, nullptr, 0 }
};
return graphicsModes;
}
int getDefaultGraphicsMode() const override { return kTripleBuffering; }
bool setGraphicsMode(int mode, uint flags = OSystem::kGfxModeNoFlags) override;
int getGraphicsMode() const override { return _currentState.mode; }
void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL) override;
int getScreenChangeID() const override { return 0; }
void beginGFXTransaction() override;
OSystem::TransactionError endGFXTransaction() override;
int16 getHeight() const override { return _currentState.height; }
int16 getWidth() const override { return _currentState.width; }
void setPalette(const byte *colors, uint start, uint num) override;
void grabPalette(byte *colors, uint start, uint num) const override;
void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override;
Graphics::Surface *lockScreen() override;
void unlockScreen() override;
void fillScreen(uint32 col) override;
void fillScreen(const Common::Rect &r, uint32 col) override;
void updateScreen() override;
void setShakePos(int shakeXOffset, int shakeYOffset) override;
void setFocusRectangle(const Common::Rect& rect) override {}
void clearFocusRectangle() override {}
void showOverlay(bool inGUI) override;
void hideOverlay() override;
bool isOverlayVisible() const override { return _overlayState == kOverlayVisible; }
Graphics::PixelFormat getOverlayFormat() const override;
void clearOverlay() override;
void grabOverlay(Graphics::Surface &surface) const override;
void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
int16 getOverlayHeight() const override { return 480; }
int16 getOverlayWidth() const override { return _vgaMonitor ? 640 : 640*1.2; }
bool showMouse(bool visible) override;
void warpMouse(int x, int y) override;
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor,
bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override;
void setCursorPalette(const byte *colors, uint start, uint num) override;
Common::Point getMousePosition() const {
if (isOverlayVisible()) {
return _screen[kOverlayBuffer]->cursor.getPosition();
} else {
// kFrontBuffer is always up to date
return _screen[kFrontBuffer]->cursor.getPosition();
}
}
void updateMousePosition(int deltaX, int deltaY);
bool notifyEvent(const Common::Event &event) override;
Common::Keymap *getKeymap() const;
private:
enum {
kUnknownMode = -1,
kDirectRendering = 0,
kSingleBuffering = 1,
kTripleBuffering = 3
};
enum CustomEventAction {
kActionToggleAspectRatioCorrection = 100,
};
void allocateSurfaces();
void freeSurfaces();
#ifndef DISABLE_FANCY_THEMES
int16 getMaximumScreenHeight() const { return 480; }
int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 640 : 640*1.2); }
#else
int16 getMaximumScreenHeight() const { return _tt ? 480 : 240; }
int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 320 : 320*1.2); }
#endif
void addDirtyRectToScreens(const Graphics::Surface &dstSurface,
int x, int y, int w, int h, bool directRendering);
bool updateScreenInternal(Screen *dstScreen, const Graphics::Surface *srcSurface);
void copyRectToAtariSurface(AtariSurface &dstSurface,
const byte *buf, int pitch, int x, int y, int w, int h);
bool isOverlayDirectRendering() const {
#ifndef DISABLE_FANCY_THEMES
// see osystem_atari.cpp
extern bool g_gameEngineActive;
#endif
// overlay is direct rendered if in the launcher or if game is directly rendered
// (on SuperVidel we always want to use _overlaySurface as source for background pixels)
return !g_hasSuperVidel
#ifndef DISABLE_FANCY_THEMES
&& (!g_gameEngineActive || _currentState.mode == kDirectRendering)
#endif
;
}
Graphics::Surface *lockOverlay();
bool _vgaMonitor = true;
bool _tt = false;
struct GraphicsState {
GraphicsState()
: inTransaction(false)
, mode(kUnknownMode)
, width(0)
, height(0)
, format(Graphics::PixelFormat()) {
}
bool isValid() const {
return mode != kUnknownMode && width > 0 && height > 0 && format.bytesPerPixel != 0;
}
bool inTransaction;
int mode;
int width;
int height;
Graphics::PixelFormat format;
};
GraphicsState _pendingState;
GraphicsState _currentState;
// feature flags
bool _aspectRatioCorrection = false;
PendingScreenChanges _pendingScreenChanges;
enum {
kFrontBuffer = 0,
kBackBuffer1 = 1,
kBackBuffer2 = 2,
kOverlayBuffer = 3,
kBufferCount
};
Screen *_screen[kBufferCount] = {};
Graphics::Surface _chunkySurface;
Graphics::Surface _chunkySurfaceOffsetted;
enum {
kOverlayVisible,
kOverlayIgnoredHide,
kOverlayHidden
};
int _overlayState = kOverlayHidden;
bool _ignoreHideOverlay = true;
Graphics::Surface _overlaySurface;
bool _ignoreCursorChanges = false;
Palette _palette;
Palette _overlayPalette;
};
#endif

View File

@@ -0,0 +1,157 @@
/* 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 "atari-pendingscreenchanges.h"
#include <cassert>
#include <mint/falcon.h>
#include "backends/platform/atari/atari-debug.h"
#include "graphics/surface.h"
#include "atari-graphics.h"
#include "atari-supervidel.h"
void PendingScreenChanges::queueAll() {
_changes |= kAll;
if (_manager->_tt)
_changes &= ~kAspectRatioCorrection;
}
/*
* VsetRGB() - stores the palette in a buffer and process it in the nearest non-locked VBL
* EsetPalette() - immediatelly applies the palette
* (V)SetScreen() - immediatelly sets physbase/logbase but explicitly calls Vsync() for resolution changes
* VsetMode() - explicitly calls Vsync()
*/
void PendingScreenChanges::applyBeforeVblLock(const Screen &screen) {
_mode = screen.mode; // avoid modifying 'Screen' content
_resetSuperVidel = false;
_aspectRatioCorrectionYOffset.second = false;
_setScreenOffsets.second = false;
_shrinkVidelVisibleArea.second = false;
if (_changes & kAspectRatioCorrection) {
processAspectRatioCorrection(screen);
_changes &= ~kAspectRatioCorrection;
}
_switchToBlackPalette = (_changes & kVideoMode) || _resetSuperVidel;
if (_changes & kVideoMode) {
processVideoMode(screen);
// don't reset kVideoMode yet
}
}
void PendingScreenChanges::applyAfterVblLock(const Screen &screen) {
// VBL doesn't process new palette nor screen address updates
// if _changes & kVideoMode then the Vsync handler has just returned
if (_changes & kShakeScreen) {
_setScreenOffsets = std::make_pair(true, true);
_changes &= ~kShakeScreen;
}
// restore current (kVideoMode) or set new (kPalette) palette
if (_changes & (kVideoMode | kPalette)) {
if (_switchToBlackPalette || (_changes & kPalette)) {
// Set the palette as fast as possible. Vsync() is a big no-no, updateScreen() and
// therefore this function can be called multiple times per frame. In this case,
// EsetPalette() will set the colours immediately (oops) but VsetRGB() is fine:
// it will be set as soon as VBL is unlocked again.
if (_manager->_tt) {
EsetPalette(0, screen.palette->entries, screen.palette->tt);
} else {
VsetRGB(0, screen.palette->entries, screen.palette->falcon);
}
}
_changes &= ~(kVideoMode | kPalette);
}
assert(_changes == kNone);
}
void PendingScreenChanges::processAspectRatioCorrection(const Screen &screen) {
assert(!_manager->_tt);
assert(_mode != -1);
if (_manager->_aspectRatioCorrection && _manager->_currentState.height == 200 && !_manager->isOverlayVisible()) {
// apply machine-specific aspect ratio correction
if (!_manager->_vgaMonitor) {
_mode &= ~PAL;
// 60 Hz
_mode |= NTSC;
_changes |= kVideoMode;
} else {
_aspectRatioCorrectionYOffset =
std::make_pair((screen.surf->h - 2*MAX_V_SHAKE - screen.offsettedSurf->h) / 2, true);
_shrinkVidelVisibleArea = std::make_pair(true, true);
}
} else {
// reset back to default mode
if (!_manager->_vgaMonitor) {
_mode &= ~NTSC;
// 50 Hz
_mode |= PAL;
_changes |= kVideoMode;
} else {
_aspectRatioCorrectionYOffset = std::make_pair(0, true);
_shrinkVidelVisibleArea = std::make_pair(false, true);
if (g_hasSuperVidel)
_resetSuperVidel = true;
// kPendingVideoMode will reset the shrunken Videl area
_changes |= kVideoMode;
}
}
// for VsetMode() and/or _aspectRatioCorrectionYOffset
_setScreenOffsets = std::make_pair(true, true);
}
void PendingScreenChanges::processVideoMode(const Screen &screen) {
// changing video mode implies an additional Vsync(): there's no way to change resolution
// and set new screen address (and/or shake offsets etc) in one go
if (screen.rez != -1) {
static uint16 black[256];
EsetPalette(0, screen.palette->entries, black);
// unfortunately this reinitializes VDI, too
Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, screen.rez);
} else if (_mode != -1) {
static _RGB black[256];
VsetRGB(0, screen.palette->entries, black);
// VsetMode() must be called before the VBL s_ stuff is set as
// it resets all hz/v, scrolling and line width registers
if (_resetSuperVidel)
VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480
atari_debug("VsetMode: %04x", _mode);
VsetMode(_mode);
}
}

View File

@@ -0,0 +1,111 @@
/* 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 BACKENDS_GRAPHICS_ATARI_PENDINGSCREENCHANGES_H
#define BACKENDS_GRAPHICS_ATARI_PENDINGSCREENCHANGES_H
#include <utility>
class AtariGraphicsManager;
class AtariSurface;
class Screen;
class PendingScreenChanges {
public:
PendingScreenChanges(const AtariGraphicsManager *manager)
: _manager(manager) {
}
void clearTransaction() {
_changes &= ~kTransaction;
}
void setScreenSurface(AtariSurface *surface) {
_surface = surface;
}
void queueVideoMode() {
_changes |= kVideoMode;
}
void queueAspectRatioCorrection() {
_changes |= kAspectRatioCorrection;
}
void queuePalette() {
_changes |= kPalette;
}
void queueShakeScreen() {
_changes |= kShakeScreen;
}
void queueAll();
int get() const {
return _changes;
}
bool empty() const {
return _changes == kNone;
}
AtariSurface *screenSurface() const {
return _surface;
}
const std::pair<int, bool>& aspectRatioCorrectionYOffset() const {
return _aspectRatioCorrectionYOffset;
}
const std::pair<bool, bool>& screenOffsets() const {
return _setScreenOffsets;
}
const std::pair<bool, bool>& shrinkVidelVisibleArea() const {
return _shrinkVidelVisibleArea;
}
void applyBeforeVblLock(const Screen &screen);
void applyAfterVblLock(const Screen &screen);
private:
void processAspectRatioCorrection(const Screen &screen);
void processVideoMode(const Screen &screen);
enum Change {
kNone = 0,
kVideoMode = 1<<0,
kAspectRatioCorrection = 1<<1,
kPalette = 1<<2,
kShakeScreen = 1<<3,
kTransaction = kVideoMode | kAspectRatioCorrection,
kAll = kTransaction | kPalette | kShakeScreen
};
int _changes = kNone;
const AtariGraphicsManager *_manager;
AtariSurface *_surface = nullptr;
int _mode;
bool _resetSuperVidel;
bool _switchToBlackPalette;
// <value, set> ... std::optional would be so much better!
std::pair<int, bool> _aspectRatioCorrectionYOffset;
std::pair<bool, bool> _setScreenOffsets;
std::pair<bool, bool> _shrinkVidelVisibleArea;
};
#endif // ATARI-PENDINGSCREENCHANGES_H

View File

@@ -0,0 +1,170 @@
/* 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 "atari-screen.h"
#include <mint/falcon.h>
#include "atari-graphics.h" // MAX_HZ_SHAKE, MAX_V_SHAKE
#include "atari-supervidel.h" // g_hasSuperVidel
//#include "backends/platform/atari/atari-debug.h"
Screen::Screen(bool tt, int width, int height, const Graphics::PixelFormat &format, const Palette *palette_)
: palette(palette_)
, _tt(tt) {
#ifdef USE_SUPERVIDEL
if (g_hasSuperVidel) {
surf.reset(new SuperVidelSurface(
width + 2 * MAX_HZ_SHAKE,
height + 2 * MAX_V_SHAKE,
format));
_offsettedSurf.reset(new SuperVidelSurface());
} else
#endif
{
surf.reset(new AtariSurface(
width + (_tt ? 0 : 2 * MAX_HZ_SHAKE),
height + 2 * MAX_V_SHAKE,
format));
_offsettedSurf.reset(new AtariSurface());
}
_offsettedSurf->create(
*surf,
Common::Rect(
Common::Point(
(surf->w - width) / 2, // left
(surf->h - height) / 2), // top
width, height));
}
void Screen::reset(int width, int height, const Graphics::Surface &boundingSurf) {
clearDirtyRects();
rez = -1;
mode = -1;
const int bitsPerPixel = surf->getBitsPerPixel();
// erase old screen
_offsettedSurf->fillRect(_offsettedSurf->getBounds(), 0);
if (_tt) {
if (width <= 320 && height <= 240) {
surf->w = 320;
surf->h = 240 + 2 * MAX_V_SHAKE;
surf->pitch = 2 * surf->w * bitsPerPixel / 8;
rez = kRezValueTTLow;
} else {
surf->w = 640;
surf->h = 480 + 2 * MAX_V_SHAKE;
surf->pitch = surf->w * bitsPerPixel / 8;
rez = kRezValueTTMid;
}
} else {
mode = VsetMode(VM_INQUIRE) & PAL;
if (VgetMonitor() == MON_VGA) {
mode |= VGA | (bitsPerPixel == 4 ? BPS4 : (g_hasSuperVidel ? BPS8C : BPS8));
if (width <= 320 && height <= 240) {
surf->w = 320;
surf->h = 240;
mode |= VERTFLAG | COL40;
} else {
surf->w = 640;
surf->h = 480;
mode |= COL80;
}
} else {
mode |= TV | (bitsPerPixel == 4 ? BPS4 : BPS8);
if (width <= 320 && height <= 200) {
surf->w = 320;
surf->h = 200;
mode |= COL40;
} else if (width <= 320*1.2 && height <= 200*1.2) {
surf->w = 320*1.2;
surf->h = 200*1.2;
mode |= OVERSCAN | COL40;
} else if (width <= 640 && height <= 400) {
surf->w = 640;
surf->h = 400;
mode |= VERTFLAG | COL80;
} else {
surf->w = 640*1.2;
surf->h = 400*1.2;
mode |= VERTFLAG | OVERSCAN | COL80;
}
}
surf->w += 2 * MAX_HZ_SHAKE;
surf->h += 2 * MAX_V_SHAKE;
surf->pitch = surf->w * bitsPerPixel / 8;
}
_offsettedSurf->create(
*surf,
Common::Rect(
Common::Point(
(surf->w - width) / 2, // left
(surf->h - height) / 2), // top
width, height));
cursor.reset(_offsettedSurf.get(), &boundingSurf);
cursor.setPosition(boundingSurf.w / 2, boundingSurf.h / 2);
}
void Screen::addDirtyRect(const Graphics::Surface &srcSurface, int x, int y, int w, int h, bool directRendering) {
if (fullRedraw)
return;
if ((w == srcSurface.w && h == srcSurface.h)
|| dirtyRects.size() == 128) { // 320x200 can hold at most 250 16x16 rectangles
//atari_debug("addDirtyRect[%d]: purge %d x %d", (int)dirtyRects.size(), srcSurface.w, srcSurface.h);
dirtyRects.clear();
// even if srcSurface.w != _offsettedSurf.w, alignRect would lead to the same result
dirtyRects.insert(_offsettedSurf->getBounds());
cursor.reset(_offsettedSurf.get(), &srcSurface);
fullRedraw = true;
} else {
// x,y are relative to srcSurface but screen's width is always aligned to 16 bytes
// so both dirty rects and cursor must be drawn in screen coordinates
const int xOffset = (_offsettedSurf->w - srcSurface.w) / 2;
const Common::Rect alignedRect = AtariSurface::alignRect(x + xOffset, y, x + xOffset + w, y + h);
dirtyRects.insert(alignedRect);
// Check whether the cursor background intersects the dirty rect. Has to be done here,
// before the actual drawing (especially in case of direct rendering). There's one more
// check in AtariGraphicsManager::updateScreenInternal for the case when there are no
// dirty rectangles but the cursor itself has changed.
const Common::Rect cursorBackgroundRect = cursor.flushBackground(alignedRect, directRendering);
if (!cursorBackgroundRect.isEmpty()) {
dirtyRects.insert(cursorBackgroundRect);
}
}
}

View File

@@ -0,0 +1,99 @@
/* 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 BACKENDS_GRAPHICS_ATARI_SCREEN_H
#define BACKENDS_GRAPHICS_ATARI_SCREEN_H
#include <unordered_set>
#include <mint/ostruct.h> // _RGB
#include "common/ptr.h"
#include "atari-cursor.h"
#include "atari-surface.h"
template<>
struct std::hash<Common::Rect>
{
std::size_t operator()(Common::Rect const& rect) const noexcept
{
return 31 * (31 * (31 * rect.left + rect.top) + rect.right) + rect.bottom;
}
};
class Palette {
public:
void clear() {
memset(_data, 0, sizeof(_data));
entries = 0;
}
uint16 *const tt = reinterpret_cast<uint16*>(_data);
_RGB *const falcon = reinterpret_cast<_RGB*>(_data);
int entries = 0;
private:
byte _data[256*4] = {};
};
struct Screen {
using DirtyRects = std::unordered_set<Common::Rect>;
Screen(bool tt, int width, int height, const Graphics::PixelFormat &format, const Palette *palette);
void reset(int width, int height, const Graphics::Surface &boundingSurf);
// must be called before any rectangle drawing
void addDirtyRect(const Graphics::Surface &srcSurface, int x, int y, int w, int h, bool directRendering);
void clearDirtyRects() {
dirtyRects.clear();
fullRedraw = false;
}
Common::ScopedPtr<AtariSurface> surf;
const Palette *palette;
DirtyRects dirtyRects;
bool fullRedraw = false;
Cursor cursor;
int rez = -1;
int mode = -1;
const Common::ScopedPtr<AtariSurface> &offsettedSurf = _offsettedSurf;
private:
static constexpr size_t ALIGN = 16; // 16 bytes
enum SteTtRezValue {
kRezValueSTLow = 0, // 320x200@4bpp, ST palette
kRezValueSTMid = 1, // 640x200@2bpp, ST palette
kRezValueSTHigh = 2, // 640x400@1bpp, ST palette
kRezValueTTLow = 7, // 320x480@8bpp, TT palette
kRezValueTTMid = 4, // 640x480@4bpp, TT palette
kRezValueTTHigh = 6 // 1280x960@1bpp, TT palette
};
bool _tt;
Common::ScopedPtr<AtariSurface> _offsettedSurf;
};
#endif // BACKENDS_GRAPHICS_ATARI_SCREEN_H

View File

@@ -0,0 +1,67 @@
/* 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 "atari-supervidel.h"
#include "common/scummsys.h"
bool g_hasSuperVidel = false;
#ifdef USE_SUPERVIDEL
#ifdef USE_SV_BLITTER
int g_superVidelFwVersion = 0;
const byte *g_blitMask = nullptr;
static bool isSuperBlitterLocked;
void SyncSuperBlitter() {
// if externally locked, let the owner decide when to sync (unlock)
if (isSuperBlitterLocked)
return;
// while FIFO not empty...
if (g_superVidelFwVersion >= 9)
while (!(*SV_BLITTER_FIFO & 1));
// while busy blitting...
while (*SV_BLITTER_CONTROL & 1);
}
#endif // USE_SV_BLITTER
void LockSuperBlitter() {
#ifdef USE_SV_BLITTER
assert(!isSuperBlitterLocked);
isSuperBlitterLocked = true;
#endif
}
void UnlockSuperBlitter() {
#ifdef USE_SV_BLITTER
assert(isSuperBlitterLocked);
isSuperBlitterLocked = false;
if (g_hasSuperVidel)
SyncSuperBlitter();
#endif
}
#endif // USE_SUPERVIDEL

View File

@@ -0,0 +1,70 @@
/* 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 BACKENDS_GRAPHICS_ATARI_SUPERVIDEL_H
#define BACKENDS_GRAPHICS_ATARI_SUPERVIDEL_H
#include "common/scummsys.h"
#ifdef USE_SUPERVIDEL
// bits 26:0
#define SV_BLITTER_SRC1 ((volatile long *)0x80010058)
#define SV_BLITTER_SRC2 ((volatile long *)0x8001005C)
#define SV_BLITTER_DST ((volatile long *)0x80010060)
// The amount of bytes that are to be copied in a horizontal line, minus 1
#define SV_BLITTER_COUNT ((volatile long *)0x80010064)
// The amount of bytes that are to be added to the line start address after a line has been copied, in order to reach the next one
#define SV_BLITTER_SRC1_OFFSET ((volatile long *)0x80010068)
#define SV_BLITTER_SRC2_OFFSET ((volatile long *)0x8001006C)
#define SV_BLITTER_DST_OFFSET ((volatile long *)0x80010070)
// bits 11:0 - The amount of horizontal lines to do
#define SV_BLITTER_MASK_AND_LINES ((volatile long *)0x80010074)
// bit 0 - busy / start
// bits 4:1 - blit mode
#define SV_BLITTER_CONTROL ((volatile long *)0x80010078)
// bits 9:0
#define SV_VERSION ((volatile long *)0x8001007C)
// bit 0 - empty (read only)
// bit 1 - full (read only)
// bits 31:0 - data (write only)
#define SV_BLITTER_FIFO ((volatile long *)0x80010080)
#ifdef USE_SV_BLITTER
extern int g_superVidelFwVersion;
extern const byte *g_blitMask;
void SyncSuperBlitter();
#endif // USE_SV_BLITTER
void LockSuperBlitter();
void UnlockSuperBlitter();
#else
static inline void LockSuperBlitter() {}
static inline void UnlockSuperBlitter() {}
#endif // USE_SUPERVIDEL
extern bool g_hasSuperVidel;
#endif // BACKENDS_GRAPHICS_ATARI_SUPERVIDEL_H

View File

@@ -0,0 +1,346 @@
/* 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 "atari-surface.h"
#include "graphics/surface.h"
#include <mint/cookie.h>
#include <mint/falcon.h>
#include <mint/trap14.h>
#define ct60_vm(mode, value) (long)trap_14_wwl((short)0xc60e, (short)(mode), (long)(value))
#define ct60_vmalloc(value) ct60_vm(0, value)
#define ct60_vmfree(value) ct60_vm(1, value)
#include "backends/graphics/atari/atari-c2p-asm.h"
#include "backends/graphics/atari/atari-graphics-asm.h"
#include "backends/graphics/atari/atari-supervidel.h"
#include "backends/platform/atari/atari-debug.h"
#include "backends/platform/atari/dlmalloc.h"
#include "common/textconsole.h" // error()
static struct MemoryPool {
void create() {
if (base)
_mspace = create_mspace_with_base((void *)base, size, 0);
if (_mspace)
atari_debug("Allocated mspace at 0x%08lx (%ld bytes)", base, size);
else
error("mspace allocation failed at 0x%08lx (%ld bytes)", base, size);
}
void destroy() {
if (_mspace) {
destroy_mspace(_mspace);
_mspace = nullptr;
}
}
void *malloc(size_t bytes) {
assert(_mspace);
return mspace_malloc(_mspace, bytes);
}
void *calloc(size_t n_elements, size_t elem_size) {
assert(_mspace);
return mspace_calloc(_mspace, n_elements, elem_size);
}
void free(void *mem) {
assert(_mspace);
mspace_free(_mspace, mem);
}
long base;
long size;
private:
mspace _mspace;
} s_videoRamPool, s_blitterPool;
static MemoryPool *s_currentPool;
namespace Graphics {
void Surface::create(int16 width, int16 height, const PixelFormat &f) {
assert(width >= 0 && height >= 0);
free();
w = width;
h = height;
format = f;
pitch = w * format.bytesPerPixel;
if (width && height) {
if (s_currentPool) {
pixels = s_currentPool->calloc(height * pitch, format.bytesPerPixel);
if (!pixels)
error("Not enough VRAM to allocate a surface");
if (s_currentPool == &s_blitterPool) {
assert(pixels >= (void *)0xA1000000);
} else if (s_currentPool == &s_videoRamPool) {
#ifdef USE_SUPERVIDEL
if (g_hasSuperVidel)
assert(pixels >= (void *)0xA0000000 && pixels < (void *)0xA1000000);
else
#endif
assert(pixels < (void *)0x01000000);
}
} else {
pixels = ::calloc(height * pitch, format.bytesPerPixel);
if (!pixels)
error("Not enough RAM to allocate a surface");
}
assert(((uintptr)pixels & (MALLOC_ALIGNMENT - 1)) == 0);
}
}
void Surface::free() {
if (pixels) {
if (s_currentPool)
s_currentPool->free(pixels);
else
::free(pixels);
pixels = nullptr;
}
w = h = pitch = 0;
format = PixelFormat();
}
} // End of namespace Graphics
///////////////////////////////////////////////////////////////////////////////
AtariSurface::AtariSurface(int16 width, int16 height, const Graphics::PixelFormat &pixelFormat) {
create(width, height, pixelFormat);
}
AtariSurface::~AtariSurface() {
free();
}
void AtariSurface::create(int16 width, int16 height, const Graphics::PixelFormat &pixelFormat) {
MemoryPool *oldPool = s_currentPool;
s_currentPool = &s_videoRamPool;
Graphics::ManagedSurface::create(width * (format == PIXELFORMAT_RGB121 ? 4 : 8) / 8, height, pixelFormat);
w = width;
s_currentPool = oldPool;
}
void AtariSurface::free() {
MemoryPool *oldPool = s_currentPool;
s_currentPool = &s_videoRamPool;
Graphics::ManagedSurface::free();
s_currentPool = oldPool;
}
void AtariSurface::copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height) {
assert(width % 16 == 0);
assert(destX % 16 == 0);
assert(format.bytesPerPixel == 1);
const byte *pChunky = (const byte *)buffer;
const byte *pChunkyEnd = pChunky + (height - 1) * srcPitch + width;
byte *pScreen = (byte *)getBasePtr(0, destY) + destX * getBitsPerPixel()/8;
if (getBitsPerPixel() == 8) {
if (srcPitch == width) {
if (srcPitch == pitch) {
asm_c2p1x1_8(pChunky, pChunkyEnd, pScreen);
return;
} else if (srcPitch == pitch/2) {
asm_c2p1x1_8_tt(pChunky, pChunkyEnd, pScreen, pitch);
return;
}
}
asm_c2p1x1_8_rect(
pChunky, pChunkyEnd,
width,
srcPitch,
pScreen,
pitch);
} else {
if (srcPitch == width && srcPitch/2 == pitch) {
asm_c2p1x1_4(pChunky, pChunkyEnd, pScreen);
return;
}
asm_c2p1x1_4_rect(
pChunky, pChunkyEnd,
width,
srcPitch,
pScreen,
pitch);
}
}
void AtariSurface::drawMaskedSprite(
const Graphics::Surface &srcSurface, const Graphics::Surface &srcMask,
const Graphics::Surface &boundingSurface,
int destX, int destY,
const Common::Rect &subRect) {
assert(srcSurface.w == srcMask.w);
assert(srcSurface.h == srcMask.h);
bool skipFirstPix16 = false;
bool skipLastPix16 = false;
int srcSurfaceLeft = 0;
int srcSurfaceWidth = srcSurface.w;
int dstSurfaceLeft = 0;
if (subRect.left > 0) {
skipFirstPix16 = true;
const int offset = subRect.left & (-16);
srcSurfaceLeft += offset;
srcSurfaceWidth -= offset;
destX = 16 - (subRect.left & (16-1));
dstSurfaceLeft -= 16;
}
if (destX + srcSurfaceWidth > boundingSurface.w) {
skipLastPix16 = true;
const int offset = (destX + srcSurfaceWidth - boundingSurface.w) & (-16);
srcSurfaceWidth -= offset;
}
assert(srcSurfaceLeft % 16 == 0);
assert(srcSurfaceWidth % 16 == 0);
destX += (this->w - boundingSurface.w) / 2;
if (getBitsPerPixel() == 8) {
asm_draw_8bpl_sprite(
(uint16 *)getBasePtr(dstSurfaceLeft, 0),
(const uint16 *)srcSurface.getBasePtr(srcSurfaceLeft, subRect.top),
(const uint16 *)srcMask.getBasePtr(srcSurfaceLeft / 8, subRect.top),
destX, destY,
pitch, srcSurface.w, srcSurfaceWidth, subRect.height(),
skipFirstPix16, skipLastPix16);
} else {
asm_draw_4bpl_sprite(
(uint16 *)getBasePtr(dstSurfaceLeft / 2, 0),
(const uint16 *)srcSurface.getBasePtr(srcSurfaceLeft / 2, subRect.top),
(const uint16 *)srcMask.getBasePtr(srcSurfaceLeft / 8, subRect.top),
destX, destY,
pitch, srcSurface.w / 2, srcSurfaceWidth, subRect.height(),
skipFirstPix16, skipLastPix16);
}
}
///////////////////////////////////////////////////////////////////////////////
#ifdef USE_SUPERVIDEL
static long hasSvRamBoosted() {
register long ret __asm__ ("d0") = 0;
__asm__ volatile(
"\tmovec %%itt0,%%d1\n"
"\tcmp.l #0xA007E060,%%d1\n"
"\tbne.s 1f\n"
"\tmovec %%dtt0,%%d1\n"
"\tcmp.l #0xA007E060,%%d1\n"
"\tbne.s 1f\n"
"\tmoveq #1,%%d0\n"
"1:\n"
: "=g"(ret) /* outputs */
: /* inputs */
: __CLOBBER_RETURN("d0") "d1", "cc"
);
return ret;
}
#endif // USE_SUPERVIDEL
void AtariSurfaceInit() {
#ifdef USE_SUPERVIDEL
g_hasSuperVidel = Getcookie(C_SupV, NULL) == C_FOUND && VgetMonitor() == MON_VGA;
if (g_hasSuperVidel) {
#ifdef USE_SV_BLITTER
g_superVidelFwVersion = *SV_VERSION & 0x01ff;
atari_debug("SuperVidel FW Revision: %d, using %s", g_superVidelFwVersion,
g_superVidelFwVersion >= 9 ? "fast async FIFO" : "slower sync blitting");
#else
atari_debug("SuperVidel FW Revision: %d, SuperBlitter not used", *SV_VERSION & 0x01ff);
#endif
if (Supexec(hasSvRamBoosted))
atari_debug("SV_XBIOS has the pmmu boost enabled");
else
atari_warning("SV_XBIOS has the pmmu boost disabled, set 'pmmu_boost = true' in C:\\SV.INF");
#ifdef USE_SV_BLITTER
s_blitterPool.size = ct60_vmalloc(-1) - (16 * 1024 * 1024); // SV XBIOS seems to forget the initial 16 MB ST RAM mirror
s_blitterPool.base = s_blitterPool.size > 0 ? ct60_vmalloc(s_blitterPool.size) : 0;
s_blitterPool.create();
// default pool is either null or blitter
s_currentPool = &s_blitterPool;
#endif
}
#endif // USE_SUPERVIDEL
s_videoRamPool.size = 2 * 1024 * 1024; // allocate 2 MiB, leave the rest for SDMA / Blitter usage
s_videoRamPool.base = s_videoRamPool.size > 0 ? Mxalloc(s_videoRamPool.size, MX_STRAM) : 0;
#ifdef USE_SUPERVIDEL
if (g_hasSuperVidel && s_videoRamPool.base)
s_videoRamPool.base |= 0xA0000000;
#endif
s_videoRamPool.create();
}
void AtariSurfaceDeinit() {
s_videoRamPool.destroy();
if (s_videoRamPool.base) {
#ifdef USE_SUPERVIDEL
if (g_hasSuperVidel)
s_videoRamPool.base &= 0x00FFFFFF;
#endif
Mfree(s_videoRamPool.base);
s_videoRamPool.base = 0;
s_videoRamPool.size = 0;
}
#ifdef USE_SV_BLITTER
s_blitterPool.destroy();
if (s_blitterPool.base) {
ct60_vmfree(s_blitterPool.base);
s_blitterPool.base = 0;
s_blitterPool.size = 0;
}
#endif
}

View File

@@ -0,0 +1,114 @@
/* 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 BACKENDS_GRAPHICS_ATARI_SURFACE_H
#define BACKENDS_GRAPHICS_ATARI_SURFACE_H
#include "graphics/managed_surface.h"
#include "backends/graphics/atari/atari-supervidel.h"
constexpr Graphics::PixelFormat PIXELFORMAT_CLUT8 = Graphics::PixelFormat::createFormatCLUT8();
constexpr Graphics::PixelFormat PIXELFORMAT_RGB332 = Graphics::PixelFormat(1, 3, 3, 2, 0, 5, 2, 0, 0);
constexpr Graphics::PixelFormat PIXELFORMAT_RGB121 = Graphics::PixelFormat(1, 1, 2, 1, 0, 3, 1, 0, 0);
class AtariSurface : public Graphics::ManagedSurface {
public:
AtariSurface() = default;
AtariSurface(int16 width, int16 height, const Graphics::PixelFormat &pixelFormat);
~AtariSurface() override;
using Graphics::ManagedSurface::create;
void create(int16 width, int16 height, const Graphics::PixelFormat &pixelFormat) final override;
void free() final override;
void addDirtyRect(const Common::Rect &r) final override {};
// no override as ManagedSurface::copyRectToSurface is not a virtual function!
virtual void copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height);
virtual void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect &subRect) {
assert(subRect.left % 16 == 0);
assert(srcSurface.format == format);
copyRectToSurface(
srcSurface.getBasePtr(subRect.left, subRect.top), srcSurface.pitch,
destX, destY,
subRect.width(), subRect.height());
}
virtual void drawMaskedSprite(const Graphics::Surface &srcSurface, const Graphics::Surface &srcMask,
const Graphics::Surface &boundingSurface,
int destX, int destY,
const Common::Rect &subRect);
int getBitsPerPixel() const {
return format == PIXELFORMAT_RGB121 ? 4 : 8;
}
static Common::Rect alignRect(int x1, int y1, int x2, int y2) {
// make non-virtual for performance reasons
return g_hasSuperVidel
? Common::Rect(x1, y1, x2, y2)
: Common::Rect(x1 & (-16), y1, (x2 + 15) & (-16), y2);
}
static Common::Rect alignRect(const Common::Rect &rect) {
// make non-virtual for performance reasons
return g_hasSuperVidel
? rect
: Common::Rect(rect.left & (-16), rect.top, (rect.right + 15) & (-16), rect.bottom);
}
};
#ifdef USE_SUPERVIDEL
class SuperVidelSurface final : public AtariSurface {
public:
SuperVidelSurface() = default;
SuperVidelSurface(int16 width, int16 height, const Graphics::PixelFormat &pixelFormat)
: AtariSurface(width, height, pixelFormat) {
}
//using Graphics::ManagedSurface::copyRectToSurface;
void copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height) override {
Graphics::ManagedSurface::copyRectToSurface(buffer, srcPitch, destX, destY, width, height);
}
void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect &subRect) override {
Graphics::ManagedSurface::copyRectToSurface(srcSurface, destX, destY, subRect);
}
void drawMaskedSprite(const Graphics::Surface &srcSurface, const Graphics::Surface &srcMask,
const Graphics::Surface &boundingSurface,
int destX, int destY,
const Common::Rect &subRect) override {
#ifdef USE_SV_BLITTER
g_blitMask = (const byte *)srcMask.getBasePtr(subRect.left, subRect.top);
#endif
copyRectToSurfaceWithKey(srcSurface, destX, destY, subRect, 0);
#ifdef USE_SV_BLITTER
g_blitMask = nullptr;
#endif
}
};
#endif
void AtariSurfaceInit();
void AtariSurfaceDeinit();
#endif // BACKENDS_GRAPHICS_ATARI_SURFACE_H

View File

@@ -0,0 +1,59 @@
/* 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 BACKENDS_GRAPHICS_DEFAULT_PALETTE_H
#define BACKENDS_GRAPHICS_DEFAULT_PALETTE_H
#include "graphics/paletteman.h"
/**
* This is a default implementation of the PaletteManager interface
* which ensures that grabPalette works as specified. Of course
* it is still necessary to provide code that actually updates
* the (possibly emulated) "hardware" palette of the backend.
* For this purpose, implement the abstract setPaletteIntern
* method.
*/
class DefaultPaletteManager : public PaletteManager {
protected:
byte _palette[3 * 256];
/**
* Subclasses should only implement this method and none of the others.
* Its semantics are like that of setPalette, only that it does not need
* to worry about making it possible to query the palette values once they
* have been set.
*/
virtual void setPaletteIntern(const byte *colors, uint start, uint num) = 0;
public:
void setPalette(const byte *colors, uint start, uint num) {
assert(start + num <= 256);
memcpy(_palette + 3 * start, colors, 3 * num);
setPaletteIntern(colors, start, num);
}
void grabPalette(byte *colors, uint start, uint num) const {
assert(start + num <= 256);
memcpy(colors, _palette + 3 * start, 3 * num);
}
};
#endif

View File

@@ -0,0 +1,134 @@
/* 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 BACKENDS_GRAPHICS_ABSTRACT_H
#define BACKENDS_GRAPHICS_ABSTRACT_H
#include "common/system.h"
#include "common/noncopyable.h"
#include "common/keyboard.h"
#include "common/rect.h"
#include "common/rotationmode.h"
#include "graphics/mode.h"
#include "graphics/paletteman.h"
/**
* Abstract class for graphics manager. Subclasses
* implement the real functionality.
*/
class GraphicsManager : public PaletteManager {
public:
virtual ~GraphicsManager() {}
virtual bool hasFeature(OSystem::Feature f) const = 0;
virtual void setFeatureState(OSystem::Feature f, bool enable) = 0;
virtual bool getFeatureState(OSystem::Feature f) const = 0;
virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const {
static const OSystem::GraphicsMode noGraphicsModes[] = {{"NONE", "Normal", 0}, {nullptr, nullptr, 0 }};
return noGraphicsModes;
}
virtual int getDefaultGraphicsMode() const { return 0; }
virtual bool setGraphicsMode(int mode, uint flags = OSystem::kGfxModeNoFlags) { return (mode == 0); }
virtual int getGraphicsMode() const { return 0; }
#if defined(USE_IMGUI)
virtual void setImGuiCallbacks(const ImGuiCallbacks &callbacks) { }
virtual void *getImGuiTexture(const Graphics::Surface &image, const byte *palette, int palCount) { return nullptr; }
virtual void freeImGuiTexture(void *texture) { }
#endif
virtual bool setShader(const Common::Path &fileName) { return false; }
virtual const OSystem::GraphicsMode *getSupportedStretchModes() const {
static const OSystem::GraphicsMode noStretchModes[] = {{"NONE", "Normal", 0}, {nullptr, nullptr, 0 }};
return noStretchModes;
}
virtual int getDefaultStretchMode() const { return 0; }
virtual bool setStretchMode(int mode) { return false; }
virtual int getStretchMode() const { return 0; }
virtual bool setRotationMode(Common::RotationMode rotation) { return false; }
virtual uint getDefaultScaler() const { return 0; }
virtual uint getDefaultScaleFactor() const { return 1; }
virtual bool setScaler(uint mode, int factor) { return false; }
virtual uint getScaler() const { return 0; }
virtual uint getScaleFactor() const { return 1; }
#ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const = 0;
virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const = 0;
#endif
virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL) = 0;
virtual void initSizeHint(const Graphics::ModeList &modes) {}
virtual int getScreenChangeID() const = 0;
virtual void beginGFXTransaction() = 0;
virtual OSystem::TransactionError endGFXTransaction() = 0;
virtual int16 getHeight() const = 0;
virtual int16 getWidth() const = 0;
virtual void setPalette(const byte *colors, uint start, uint num) = 0;
virtual void grabPalette(byte *colors, uint start, uint num) const = 0;
virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) = 0;
virtual Graphics::Surface *lockScreen() = 0;
virtual void unlockScreen() = 0;
virtual void fillScreen(uint32 col) = 0;
virtual void fillScreen(const Common::Rect &r, uint32 col) = 0;
virtual void updateScreen() = 0;
virtual void presentBuffer() {}
virtual void setShakePos(int shakeXOffset, int shakeYOffset) = 0;
virtual void setFocusRectangle(const Common::Rect& rect) = 0;
virtual void clearFocusRectangle() = 0;
virtual void showOverlay(bool inGUI) = 0;
virtual void hideOverlay() = 0;
virtual bool isOverlayVisible() const = 0;
virtual Graphics::PixelFormat getOverlayFormat() const = 0;
virtual void clearOverlay() = 0;
virtual void grabOverlay(Graphics::Surface &surface) const = 0;
virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) = 0;
virtual int16 getOverlayHeight() const = 0;
virtual int16 getOverlayWidth() const = 0;
virtual Common::Rect getSafeOverlayArea(int16 *width, int16 *height) const {
int16 w = getOverlayWidth(),
h = getOverlayHeight();
if (width) *width = w;
if (height) *height = h;
return Common::Rect(w, h);
}
virtual float getHiDPIScreenFactor() const { return 1.0f; }
virtual bool showMouse(bool visible) = 0;
virtual void warpMouse(int x, int y) = 0;
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = nullptr, const byte *mask = nullptr) = 0;
virtual void setCursorPalette(const byte *colors, uint start, uint num) = 0;
virtual void displayMessageOnOSD(const Common::U32String &msg) {}
virtual void displayActivityIconOnOSD(const Graphics::Surface *icon) {}
// Graphics::PaletteManager interface
//virtual void setPalette(const byte *colors, uint start, uint num) = 0;
//virtual void grabPalette(byte *colors, uint start, uint num) const = 0;
virtual void saveScreenshot() {}
virtual bool lockMouse(bool lock) { return false; }
};
#endif

View File

@@ -0,0 +1,117 @@
/* 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/>.
*
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/graphics/ios/ios-graphics.h"
#include "backends/graphics/ios/renderbuffer.h"
#include "backends/graphics/opengl/pipelines/pipeline.h"
#include "backends/platform/ios7/ios7_osys_main.h"
iOSGraphicsManager::iOSGraphicsManager() :
_insets{0, 0, 0, 0} {
initSurface();
}
iOSGraphicsManager::~iOSGraphicsManager() {
deinitSurface();
}
void iOSGraphicsManager::initSurface() {
OSystem_iOS7 *sys = dynamic_cast<OSystem_iOS7 *>(g_system);
// Create OpenGL context
GLuint rbo = sys->createOpenGLContext();
notifyContextCreate(OpenGL::kContextGLES2,
new OpenGL::RenderbufferTarget(rbo),
OpenGL::Texture::getRGBAPixelFormat(),
OpenGL::Texture::getRGBAPixelFormat());
handleResize(sys->getScreenWidth(), sys->getScreenHeight());
_old_touch_mode = kTouchModeTouchpad;
// not in 3D, not in GUI
sys->applyTouchSettings(false, false);
}
void iOSGraphicsManager::deinitSurface() {
notifyContextDestroy();
dynamic_cast<OSystem_iOS7 *>(g_system)->destroyOpenGLContext();
}
void iOSGraphicsManager::notifyResize(const int width, const int height) {
handleResize(width, height);
}
bool iOSGraphicsManager::loadVideoMode(uint requestedWidth, uint requestedHeight, bool resizable, int antialiasing) {
// As GLES2 provides FBO, OpenGL graphics manager must ask us for a resizable surface
assert(resizable);
if (antialiasing != 0) {
warning("Requesting antialiased video mode while not available");
}
/* The iOS and tvOS video modes are always full screen */
return true;
}
void iOSGraphicsManager::showOverlay(bool inGUI) {
if (_overlayVisible && inGUI == _overlayInGUI)
return;
// Don't change touch mode when not changing mouse coordinates
if (inGUI) {
_old_touch_mode = dynamic_cast<OSystem_iOS7 *>(g_system)->getCurrentTouchMode();
// not in 3D, in overlay
dynamic_cast<OSystem_iOS7 *>(g_system)->applyTouchSettings(false, true);
} else if (_overlayInGUI) {
// Restore touch mode active before overlay was shown
dynamic_cast<OSystem_iOS7 *>(g_system)->setCurrentTouchMode(static_cast<TouchMode>(_old_touch_mode));
}
OpenGL::OpenGLGraphicsManager::showOverlay(inGUI);
}
void iOSGraphicsManager::hideOverlay() {
if (_overlayInGUI) {
// Restore touch mode active before overlay was shown
dynamic_cast<OSystem_iOS7 *>(g_system)->setCurrentTouchMode(static_cast<TouchMode>(_old_touch_mode));
}
OpenGL::OpenGLGraphicsManager::hideOverlay();
}
float iOSGraphicsManager::getHiDPIScreenFactor() const {
return dynamic_cast<OSystem_iOS7 *>(g_system)->getSystemHiDPIScreenFactor();
}
void iOSGraphicsManager::refreshScreen() {
dynamic_cast<OSystem_iOS7 *>(g_system)->refreshScreen();
}
bool iOSGraphicsManager::notifyMousePosition(Common::Point &mouse) {
mouse.x = CLIP<int16>(mouse.x, _activeArea.drawRect.left, _activeArea.drawRect.right);
mouse.y = CLIP<int16>(mouse.y, _activeArea.drawRect.top, _activeArea.drawRect.bottom);
setMousePosition(mouse.x, mouse.y);
mouse = convertWindowToVirtual(mouse.x, mouse.y);
return true;
}

View File

@@ -0,0 +1,62 @@
/* 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 BACKENDS_GRAPHICS_IOS_IOS_GRAPHICS_H
#define BACKENDS_GRAPHICS_IOS_IOS_GRAPHICS_H
#include "common/scummsys.h"
#include <OpenGLES/ES2/gl.h>
#include "backends/graphics/opengl/opengl-graphics.h"
class iOSGraphicsManager :
public OpenGL::OpenGLGraphicsManager {
public:
iOSGraphicsManager();
virtual ~iOSGraphicsManager();
void initSurface();
void deinitSurface();
void notifyResize(const int width, const int height);
bool notifyMousePosition(Common::Point &mouse);
Common::Point getMousePosition() { return Common::Point(_cursorX, _cursorY); }
float getHiDPIScreenFactor() const override;
void setSafeAreaInsets(int l, int r, int t, int b) { _insets.left = l; _insets.top = t; _insets.right = r; _insets.bottom = b; }
WindowedGraphicsManager::Insets getSafeAreaInsets() const override { return _insets; }
protected:
void setSystemMousePosition(const int x, const int y) override {}
bool loadVideoMode(uint requestedWidth, uint requestedHeight, bool resizable, int antialiasing) override;
void showOverlay(bool inGUI) override;
void hideOverlay() override;
void refreshScreen() override;
int _old_touch_mode;
WindowedGraphicsManager::Insets _insets;
};
#endif

View File

@@ -0,0 +1,92 @@
/* 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 "backends/graphics/ios/renderbuffer.h"
#include "graphics/opengl/debug.h"
namespace OpenGL {
//
// Render to backbuffer target implementation
//
RenderbufferTarget::RenderbufferTarget(GLuint renderbufferID)
: _glRBO(renderbufferID), _glFBO(0) {
}
RenderbufferTarget::~RenderbufferTarget() {
GL_CALL_SAFE(glDeleteFramebuffers, (1, &_glFBO));
}
void RenderbufferTarget::activateInternal() {
bool needUpdate = false;
// Allocate framebuffer object if necessary.
if (!_glFBO) {
GL_CALL(glGenFramebuffers(1, &_glFBO));
needUpdate = true;
}
// Attach FBO to rendering context.
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, _glFBO));
// Attach render buffer to newly created FBO.
if (needUpdate) {
GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _glRBO));
}
}
bool RenderbufferTarget::setSize(uint width, uint height) {
// Set viewport dimensions.
_viewport[0] = 0;
_viewport[1] = 0;
_viewport[2] = width;
_viewport[3] = height;
// Setup orthogonal projection matrix.
_projectionMatrix(0, 0) = 2.0f / width;
_projectionMatrix(0, 1) = 0.0f;
_projectionMatrix(0, 2) = 0.0f;
_projectionMatrix(0, 3) = 0.0f;
_projectionMatrix(1, 0) = 0.0f;
_projectionMatrix(1, 1) = -2.0f / height;
_projectionMatrix(1, 2) = 0.0f;
_projectionMatrix(1, 3) = 0.0f;
_projectionMatrix(2, 0) = 0.0f;
_projectionMatrix(2, 1) = 0.0f;
_projectionMatrix(2, 2) = 0.0f;
_projectionMatrix(2, 3) = 0.0f;
_projectionMatrix(3, 0) = -1.0f;
_projectionMatrix(3, 1) = 1.0f;
_projectionMatrix(3, 2) = 0.0f;
_projectionMatrix(3, 3) = 1.0f;
// Directly apply changes when we are active.
if (isActive()) {
applyViewport();
applyProjectionMatrix();
}
return true;
}
} // End of namespace OpenGL

View File

@@ -0,0 +1,55 @@
/* 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 BACKENDS_GRAPHICS_IOS_RENDERBUFFER_H
#define BACKENDS_GRAPHICS_IOS_RENDERBUFFER_H
#include "backends/graphics/opengl/framebuffer.h"
namespace OpenGL {
/**
* Render to renderbuffer framebuffer implementation.
*
* This target allows to render to a renderbuffer, which can then be used as
* a rendering source like expected on iOS.
*/
class RenderbufferTarget : public Framebuffer {
public:
RenderbufferTarget(GLuint renderbufferID);
~RenderbufferTarget() override;
/**
* Set size of the render target.
*/
bool setSize(uint width, uint height) override;
protected:
void activateInternal() override;
private:
GLuint _glRBO;
GLuint _glFBO;
};
} // End of namespace OpenGL
#endif

View File

@@ -0,0 +1,55 @@
/* 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/>.
*
*/
#if defined(MAEMO)
#include "common/scummsys.h"
#include "backends/platform/maemo/maemo.h"
#include "backends/graphics/maemosdl/maemosdl-graphics.h"
MaemoSdlGraphicsManager::MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
}
bool MaemoSdlGraphicsManager::loadGFXMode() {
bool success = SurfaceSdlGraphicsManager::loadGFXMode();
// fix the problematic zoom key capture in Maemo5/N900
SDL_SysWMinfo info;
if (_window->getSDLWMInformation(&info)) {
Display *dpy = info.info.x11.display;
Window win;
unsigned long val = 1;
Atom atom_zoom = XInternAtom(dpy, "_HILDON_ZOOM_KEY_ATOM", 0);
info.info.x11.lock_func();
win = info.info.x11.fswindow;
if (win)
XChangeProperty(dpy, win, atom_zoom, XA_INTEGER, 32, PropModeReplace, (unsigned char *) &val, 1); // grab zoom keys
win = info.info.x11.wmwindow;
if (win)
XChangeProperty(dpy, win, atom_zoom, XA_INTEGER, 32, PropModeReplace, (unsigned char *) &val, 1); // grab zoom keys
info.info.x11.unlock_func();
}
return success;
}
#endif

View File

@@ -0,0 +1,39 @@
/* 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/>.
*
*/
#if defined(MAEMO)
#ifndef BACKENDS_GRAPHICS_MAEMOSDL_GRAPHICS_H
#define BACKENDS_GRAPHICS_MAEMOSDL_GRAPHICS_H
#include "backends/graphics/surfacesdl/surfacesdl-graphics.h"
class MaemoSdlGraphicsManager final : public SurfaceSdlGraphicsManager {
public:
MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
protected:
bool loadGFXMode() override;
};
#endif
#endif

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/>.
*
*/
#include "backends/graphics/miyoo/miyoomini-graphics.h"
void MiyooMiniGraphicsManager::initGraphicsSurface() {
_hwScreen = nullptr;
_realHwScreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 32,
SDL_HWSURFACE);
if (!_realHwScreen)
return;
_hwScreen = SDL_CreateRGBSurface(SDL_HWSURFACE, _videoMode.hardwareWidth, _videoMode.hardwareHeight,
_realHwScreen->format->BitsPerPixel,
_realHwScreen->format->Rmask,
_realHwScreen->format->Gmask,
_realHwScreen->format->Bmask,
_realHwScreen->format->Amask);
_isDoubleBuf = false;
_isHwPalette = false;
}
void MiyooMiniGraphicsManager::unloadGFXMode() {
if (_realHwScreen) {
SDL_FreeSurface(_realHwScreen);
_realHwScreen = nullptr;
}
SurfaceSdlGraphicsManager::unloadGFXMode();
}
void MiyooMiniGraphicsManager::updateScreen(SDL_Rect *dirtyRectList, int actualDirtyRects) {
SDL_BlitSurface(_hwScreen, nullptr, _realHwScreen, nullptr);
SDL_UpdateRects(_realHwScreen, actualDirtyRects, _dirtyRectList);
}
void MiyooMiniGraphicsManager::getDefaultResolution(uint &w, uint &h) {
w = 640;
h = 480;
}

View File

@@ -0,0 +1,40 @@
/* 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 BACKENDS_GRAPHICS_MIYOOMINI_H
#define BACKENDS_GRAPHICS_MIYOOMINI_H
#include "backends/graphics/surfacesdl/surfacesdl-graphics.h"
class MiyooMiniGraphicsManager : public SurfaceSdlGraphicsManager {
public:
MiyooMiniGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window) : SurfaceSdlGraphicsManager(sdlEventSource, window), _realHwScreen(nullptr) {}
void initGraphicsSurface() override;
void unloadGFXMode() override;
void updateScreen(SDL_Rect *dirtyRectList, int actualDirtyRects) override;
void getDefaultResolution(uint &w, uint &h) override;
private:
SDL_Surface *_realHwScreen;
};
#endif /* BACKENDS_GRAPHICS_MIYOOMINI_H */

View File

@@ -0,0 +1,98 @@
/* 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 BACKENDS_GRAPHICS_NULL_H
#define BACKENDS_GRAPHICS_NULL_H
#include "backends/graphics/graphics.h"
class NullGraphicsManager : public GraphicsManager {
public:
virtual ~NullGraphicsManager() {}
bool hasFeature(OSystem::Feature f) const override { return false; }
void setFeatureState(OSystem::Feature f, bool enable) override {}
bool getFeatureState(OSystem::Feature f) const override { return false; }
#ifdef USE_RGB_COLOR
Graphics::PixelFormat getScreenFormat() const override {
return _format;
}
Common::List<Graphics::PixelFormat> getSupportedFormats() const override {
Common::List<Graphics::PixelFormat> list;
list.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); // BBDoU, Frotz, HDB, Hopkins, Nuvie, Petka, Riven, Sherlock (3DO), Titanic, Tony, Ultima 4, Ultima 8, ZVision
list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); // Full Pipe, Gnap (little endian), Griffon, Groovie 2, SCI32 (HQ videos), Sludge, Sword25, Ultima 8, Wintermute
list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)); // Gnap (big endian)
list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); // SCUMM HE99+, Last Express
list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 15)); // Dragons
list.push_back(Graphics::PixelFormat::createFormatCLUT8());
return list;
}
#endif
void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL) override {
_width = width;
_height = height;
_format = format ? *format : Graphics::PixelFormat::createFormatCLUT8();
}
int getScreenChangeID() const override { return 0; }
void beginGFXTransaction() override {}
OSystem::TransactionError endGFXTransaction() override { return OSystem::kTransactionSuccess; }
int16 getHeight() const override { return _height; }
int16 getWidth() const override { return _width; }
void setPalette(const byte *colors, uint start, uint num) override {}
void grabPalette(byte *colors, uint start, uint num) const override {}
void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override {}
Graphics::Surface *lockScreen() override { return NULL; }
void unlockScreen() override {}
void fillScreen(uint32 col) override {}
void fillScreen(const Common::Rect &r, uint32 col) override {}
void updateScreen() override {}
void setShakePos(int shakeXOffset, int shakeYOffset) override {}
void setFocusRectangle(const Common::Rect& rect) override {}
void clearFocusRectangle() override {}
void showOverlay(bool inGUI) override { _overlayVisible = true; }
void hideOverlay() override { _overlayVisible = false; }
bool isOverlayVisible() const override { return _overlayVisible; }
Graphics::PixelFormat getOverlayFormat() const override { return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); }
void clearOverlay() override {}
void grabOverlay(Graphics::Surface &surface) const override {}
void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override {}
int16 getOverlayHeight() const override { return _height; }
int16 getOverlayWidth() const override { return _width; }
bool showMouse(bool visible) override { return !visible; }
void warpMouse(int x, int y) override {}
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override {}
void setCursorPalette(const byte *colors, uint start, uint num) override {}
private:
uint _width, _height;
Graphics::PixelFormat _format;
bool _overlayVisible;
};
#endif

View File

@@ -0,0 +1,63 @@
/* 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 "backends/graphics/opendingux/opendingux-graphics.h"
void OpenDinguxGraphicsManager::initGraphicsSurface() {
#ifdef RS90
Uint32 flags = SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
int bpp = 8;
#else
Uint32 flags = SDL_SWSURFACE | SDL_FULLSCREEN;
int bpp = 16;
#endif
_hwScreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, bpp, flags);
_isDoubleBuf = flags & SDL_DOUBLEBUF;
_isHwPalette = flags & SDL_HWPALETTE;
}
void OpenDinguxGraphicsManager::getDefaultResolution(uint &w, uint &h) {
#ifdef RS90
SDL_PixelFormat p;
p.BitsPerPixel = 16;
p.BytesPerPixel = 2;
p.Rloss = 3;
p.Gloss = 2;
p.Bloss = 3;
p.Rshift = 11;
p.Gshift = 5;
p.Bshift = 0;
p.Rmask = 0xf800;
p.Gmask = 0x07e0;
p.Bmask = 0x001f;
p.colorkey = 0;
p.alpha = 0;
// Only native screen resolution is supported in RGB565 fullscreen hwsurface.
SDL_Rect const* const*availableModes = SDL_ListModes(&p, SDL_FULLSCREEN|SDL_HWSURFACE);
w = availableModes[0]->w;
h = availableModes[0]->h;
// Request 320x200 for the RG99, to let the software aspect correction kick in
if ( w == 320 && h == 480 ) h = 200;
#else
w = 320;
h = 200;
#endif
}

View File

@@ -0,0 +1,35 @@
/* 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 BACKENDS_GRAPHICS_OPENDINGUX_H
#define BACKENDS_GRAPHICS_OPENDINGUX_H
#include "backends/graphics/surfacesdl/surfacesdl-graphics.h"
class OpenDinguxGraphicsManager : public SurfaceSdlGraphicsManager {
public:
OpenDinguxGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window) : SurfaceSdlGraphicsManager(sdlEventSource, window) {}
void initGraphicsSurface() override;
void getDefaultResolution(uint &w, uint &h) override;
};
#endif /* BACKENDS_GRAPHICS_OPENDINGUX_H */

View File

@@ -0,0 +1,316 @@
/* 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 "backends/graphics/opengl/framebuffer.h"
#include "backends/graphics/opengl/pipelines/pipeline.h"
#include "graphics/opengl/debug.h"
#include "graphics/opengl/texture.h"
#include "common/rotationmode.h"
namespace OpenGL {
Framebuffer::Framebuffer()
: _viewport(), _projectionMatrix(), _pipeline(nullptr), _clearColor(),
_blendState(kBlendModeDisabled), _scissorTestState(false), _scissorBox() {
}
void Framebuffer::activate(Pipeline *pipeline) {
assert(pipeline);
_pipeline = pipeline;
applyViewport();
applyProjectionMatrix();
applyClearColor();
applyBlendState();
applyScissorTestState();
applyScissorBox();
activateInternal();
}
void Framebuffer::deactivate() {
deactivateInternal();
_pipeline = nullptr;
}
void Framebuffer::setClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
_clearColor[0] = r;
_clearColor[1] = g;
_clearColor[2] = b;
_clearColor[3] = a;
// Directly apply changes when we are active.
if (isActive()) {
applyClearColor();
}
}
void Framebuffer::enableBlend(BlendMode mode) {
_blendState = mode;
// Directly apply changes when we are active.
if (isActive()) {
applyBlendState();
}
}
void Framebuffer::enableScissorTest(bool enable) {
_scissorTestState = enable;
// Directly apply changes when we are active.
if (isActive()) {
applyScissorTestState();
}
}
void Framebuffer::setScissorBox(GLint x, GLint y, GLsizei w, GLsizei h) {
_scissorBox[0] = x;
_scissorBox[1] = y;
_scissorBox[2] = w;
_scissorBox[3] = h;
// Directly apply changes when we are active.
if (isActive()) {
applyScissorBox();
}
}
void Framebuffer::applyViewport() {
GL_CALL(glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]));
}
void Framebuffer::applyProjectionMatrix() {
assert(_pipeline);
_pipeline->setProjectionMatrix(_projectionMatrix);
}
void Framebuffer::applyClearColor() {
GL_CALL(glClearColor(_clearColor[0], _clearColor[1], _clearColor[2], _clearColor[3]));
}
void Framebuffer::applyBlendState() {
switch (_blendState) {
case kBlendModeDisabled:
GL_CALL(glDisable(GL_BLEND));
break;
case kBlendModeOpaque:
if (!glBlendColor) {
// If glBlendColor is not available (old OpenGL) fallback on disabling blending
GL_CALL(glDisable(GL_BLEND));
break;
}
GL_CALL(glEnable(GL_BLEND));
GL_CALL(glBlendColor(1.f, 1.f, 1.f, 0.f));
GL_CALL(glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR));
break;
case kBlendModeTraditionalTransparency:
GL_CALL(glEnable(GL_BLEND));
GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
break;
case kBlendModePremultipliedTransparency:
GL_CALL(glEnable(GL_BLEND));
GL_CALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
break;
case kBlendModeAdditive:
GL_CALL(glEnable(GL_BLEND));
GL_CALL(glBlendFunc(GL_ONE, GL_ONE));
break;
case kBlendModeMaskAlphaAndInvertByColor:
GL_CALL(glEnable(GL_BLEND));
GL_CALL(glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA));
break;
default:
break;
}
}
void Framebuffer::applyScissorTestState() {
if (_scissorTestState) {
GL_CALL(glEnable(GL_SCISSOR_TEST));
} else {
GL_CALL(glDisable(GL_SCISSOR_TEST));
}
}
void Framebuffer::applyScissorBox() {
GL_CALL(glScissor(_scissorBox[0], _scissorBox[1], _scissorBox[2], _scissorBox[3]));
}
void Framebuffer::copyRenderStateFrom(const Framebuffer &other, uint copyMask) {
if (copyMask & kCopyMaskClearColor) {
memcpy(_clearColor, other._clearColor, sizeof(_clearColor));
}
if (copyMask & kCopyMaskBlendState) {
_blendState = other._blendState;
}
if (copyMask & kCopyMaskScissorState) {
_scissorTestState = other._scissorTestState;
}
if (copyMask & kCopyMaskScissorBox) {
memcpy(_scissorBox, other._scissorBox, sizeof(_scissorBox));
}
if (isActive()) {
applyClearColor();
applyBlendState();
applyScissorTestState();
applyScissorBox();
}
}
//
// Backbuffer implementation
//
void Backbuffer::activateInternal() {
#if !USE_FORCED_GLES
if (OpenGLContext.framebufferObjectSupported) {
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
}
#endif
}
bool Backbuffer::setSize(uint width, uint height) {
// Set viewport dimensions.
_viewport[0] = 0;
_viewport[1] = 0;
_viewport[2] = width;
_viewport[3] = height;
// Setup orthogonal projection matrix.
_projectionMatrix(0, 0) = 2.0f / width;
_projectionMatrix(0, 1) = 0.0f;
_projectionMatrix(0, 2) = 0.0f;
_projectionMatrix(0, 3) = 0.0f;
_projectionMatrix(1, 0) = 0.0f;
_projectionMatrix(1, 1) = -2.0f / height;
_projectionMatrix(1, 2) = 0.0f;
_projectionMatrix(1, 3) = 0.0f;
_projectionMatrix(2, 0) = 0.0f;
_projectionMatrix(2, 1) = 0.0f;
_projectionMatrix(2, 2) = 0.0f;
_projectionMatrix(2, 3) = 0.0f;
_projectionMatrix(3, 0) = -1.0f;
_projectionMatrix(3, 1) = 1.0f;
_projectionMatrix(3, 2) = 0.0f;
_projectionMatrix(3, 3) = 1.0f;
// Directly apply changes when we are active.
if (isActive()) {
applyViewport();
applyProjectionMatrix();
}
return true;
}
//
// Render to texture target implementation
//
#if !USE_FORCED_GLES
TextureTarget::TextureTarget()
: _texture(new Texture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE)), _glFBO(0), _needUpdate(true) {
}
TextureTarget::~TextureTarget() {
delete _texture;
GL_CALL_SAFE(glDeleteFramebuffers, (1, &_glFBO));
}
void TextureTarget::activateInternal() {
// Allocate framebuffer object if necessary.
if (!_glFBO) {
GL_CALL(glGenFramebuffers(1, &_glFBO));
_needUpdate = true;
}
// Attach destination texture to FBO.
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, _glFBO));
// If required attach texture to FBO.
if (_needUpdate) {
GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture->getGLTexture(), 0));
_needUpdate = false;
}
}
void TextureTarget::destroy() {
GL_CALL(glDeleteFramebuffers(1, &_glFBO));
_glFBO = 0;
_texture->destroy();
}
void TextureTarget::create() {
_texture->create();
_needUpdate = true;
}
bool TextureTarget::setSize(uint width, uint height) {
if (!_texture->setSize(width, height)) {
return false;
}
const uint texWidth = _texture->getWidth();
const uint texHeight = _texture->getHeight();
// Set viewport dimensions.
_viewport[0] = 0;
_viewport[1] = 0;
_viewport[2] = texWidth;
_viewport[3] = texHeight;
// Setup orthogonal projection matrix.
_projectionMatrix(0, 0) = 2.0f / texWidth;
_projectionMatrix(0, 1) = 0.0f;
_projectionMatrix(0, 2) = 0.0f;
_projectionMatrix(0, 3) = 0.0f;
_projectionMatrix(1, 0) = 0.0f;
_projectionMatrix(1, 1) = 2.0f / texHeight;
_projectionMatrix(1, 2) = 0.0f;
_projectionMatrix(1, 3) = 0.0f;
_projectionMatrix(2, 0) = 0.0f;
_projectionMatrix(2, 1) = 0.0f;
_projectionMatrix(2, 2) = 0.0f;
_projectionMatrix(2, 3) = 0.0f;
_projectionMatrix(3, 0) = -1.0f;
_projectionMatrix(3, 1) = -1.0f;
_projectionMatrix(3, 2) = 0.0f;
_projectionMatrix(3, 3) = 1.0f;
// Directly apply changes when we are active.
if (isActive()) {
applyViewport();
applyProjectionMatrix();
}
return true;
}
#endif // !USE_FORCED_GLES
} // End of namespace OpenGL

View File

@@ -0,0 +1,240 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_FRAMEBUFFER_H
#define BACKENDS_GRAPHICS_OPENGL_FRAMEBUFFER_H
#include "graphics/opengl/system_headers.h"
#include "math/matrix4.h"
#include "common/rotationmode.h"
namespace OpenGL {
class Pipeline;
/**
* Object describing a framebuffer OpenGL can render to.
*/
class Framebuffer {
public:
Framebuffer();
virtual ~Framebuffer() {};
public:
enum BlendMode {
/**
* Newly drawn pixels overwrite the existing contents of the framebuffer
* without mixing with them.
*/
kBlendModeDisabled,
/**
* Newly drawn pixels overwrite the existing contents of the framebuffer
* without mixing with them. Alpha channel is discarded.
*/
kBlendModeOpaque,
/**
* Newly drawn pixels mix with the framebuffer based on their alpha value
* for transparency.
*/
kBlendModeTraditionalTransparency,
/**
* Newly drawn pixels mix with the framebuffer based on their alpha value
* for transparency.
*
* Requires the image data being drawn to have its color values pre-multiplied
* with the alpha value.
*/
kBlendModePremultipliedTransparency,
/**
* Newly drawn pixels add to the destination value.
*/
kBlendModeAdditive,
/**
* Newly drawn pixels mask out existing pixels based on the alpha value and
* add inversions of the pixels based on the color.
*/
kBlendModeMaskAlphaAndInvertByColor,
};
/**
* Set the clear color of the framebuffer.
*/
void setClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
/**
* Enable/disable GL_BLEND.
*/
void enableBlend(BlendMode mode);
/**
* Enable/disable GL_SCISSOR_TEST.
*/
void enableScissorTest(bool enable);
/**
* Set scissor box dimensions.
*/
void setScissorBox(GLint x, GLint y, GLsizei w, GLsizei h);
/**
* Obtain projection matrix of the framebuffer.
*/
const Math::Matrix4 &getProjectionMatrix() const { return _projectionMatrix; }
enum CopyMask {
kCopyMaskClearColor = (1 << 0),
kCopyMaskBlendState = (1 << 1),
kCopyMaskScissorState = (1 << 2),
kCopyMaskScissorBox = (1 << 4),
kCopyMaskAll = kCopyMaskClearColor | kCopyMaskBlendState |
kCopyMaskScissorState | kCopyMaskScissorBox,
};
/**
* Copy rendering state from another framebuffer
*/
void copyRenderStateFrom(const Framebuffer &other, uint copyMask);
protected:
bool isActive() const { return _pipeline != nullptr; }
GLint _viewport[4];
void applyViewport();
Math::Matrix4 _projectionMatrix;
void applyProjectionMatrix();
/**
* Activate framebuffer.
*
* This is supposed to set all state associated with the framebuffer.
*/
virtual void activateInternal() = 0;
/**
* Deactivate framebuffer.
*
* This is supposed to make any cleanup required when unbinding the
* framebuffer.
*/
virtual void deactivateInternal() {}
public:
/**
* Set the size of the target buffer.
*/
virtual bool setSize(uint width, uint height) = 0;
/**
* Accessor to activate framebuffer for pipeline.
*/
void activate(Pipeline *pipeline);
/**
* Accessor to deactivate framebuffer from pipeline.
*/
void deactivate();
private:
Pipeline *_pipeline;
GLfloat _clearColor[4];
void applyClearColor();
BlendMode _blendState;
void applyBlendState();
bool _scissorTestState;
void applyScissorTestState();
GLint _scissorBox[4];
void applyScissorBox();
};
/**
* Default back buffer implementation.
*/
class Backbuffer : public Framebuffer {
public:
/**
* Set the size of the back buffer.
*/
bool setSize(uint width, uint height) override;
protected:
void activateInternal() override;
};
#if !USE_FORCED_GLES
class Texture;
/**
* Render to texture framebuffer implementation.
*
* This target allows to render to a texture, which can then be used for
* further rendering.
*/
class TextureTarget : public Framebuffer {
public:
TextureTarget();
~TextureTarget() override;
/**
* Notify that the GL context is about to be destroyed.
*/
void destroy();
/**
* Notify that the GL context has been created.
*/
void create();
/**
* Set size of the texture target.
*/
bool setSize(uint width, uint height) override;
/**
* Query pointer to underlying GL texture.
*/
Texture *getTexture() const { return _texture; }
protected:
void activateInternal() override;
private:
Texture *_texture;
GLuint _glFBO;
bool _needUpdate;
};
#endif
} // End of namespace OpenGL
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,538 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
#define BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
#include "backends/graphics/opengl/framebuffer.h"
#include "backends/graphics/windowed.h"
#include "base/plugins.h"
#include "common/frac.h"
#include "common/mutex.h"
#include "common/ustr.h"
#include "graphics/surface.h"
namespace Graphics {
class Font;
} // End of namespace Graphics
namespace OpenGL {
// HACK: We use glColor in the OSD code. This might not be working on GL ES but
// we still enable it because Tizen already shipped with it. Also, the
// SurfaceSDL backend enables it and disabling it can cause issues in sdl.cpp.
#define USE_OSD 1
class Surface;
class Pipeline;
#if !USE_FORCED_GLES
class LibRetroPipeline;
#endif
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
class Renderer3D;
#endif
enum {
GFX_OPENGL = 0
};
class OpenGLGraphicsManager : virtual public WindowedGraphicsManager {
public:
OpenGLGraphicsManager();
virtual ~OpenGLGraphicsManager();
// GraphicsManager API
bool hasFeature(OSystem::Feature f) const override;
void setFeatureState(OSystem::Feature f, bool enable) override;
bool getFeatureState(OSystem::Feature f) const override;
const OSystem::GraphicsMode *getSupportedGraphicsModes() const override;
int getDefaultGraphicsMode() const override;
bool setGraphicsMode(int mode, uint flags = OSystem::kGfxModeNoFlags) override;
int getGraphicsMode() const override;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat getScreenFormat() const override;
Common::List<Graphics::PixelFormat> getSupportedFormats() const override;
#endif
const OSystem::GraphicsMode *getSupportedStretchModes() const override;
int getDefaultStretchMode() const override;
bool setStretchMode(int mode) override;
int getStretchMode() const override;
#ifdef USE_SCALERS
uint getDefaultScaler() const override;
uint getDefaultScaleFactor() const override;
bool setScaler(uint mode, int factor) override;
uint getScaler() const override;
uint getScaleFactor() const override;
#endif
#if !USE_FORCED_GLES
bool setShader(const Common::Path &fileNode) override;
#endif
void beginGFXTransaction() override;
OSystem::TransactionError endGFXTransaction() override;
int getScreenChangeID() const override;
void initSize(uint width, uint height, const Graphics::PixelFormat *format) override;
int16 getWidth() const override;
int16 getHeight() const override;
void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override;
void fillScreen(uint32 col) override;
void fillScreen(const Common::Rect &r, uint32 col) override;
void updateScreen() override;
void presentBuffer() override;
Graphics::Surface *lockScreen() override;
void unlockScreen() override;
void setFocusRectangle(const Common::Rect& rect) override;
void clearFocusRectangle() override;
int16 getOverlayWidth() const override;
int16 getOverlayHeight() const override;
void showOverlay(bool inGUI) override;
void hideOverlay() override;
Graphics::PixelFormat getOverlayFormat() const override;
void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
void clearOverlay() override;
void grabOverlay(Graphics::Surface &surface) const override;
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) override;
void setCursorPalette(const byte *colors, uint start, uint num) override;
void displayMessageOnOSD(const Common::U32String &msg) override;
void displayActivityIconOnOSD(const Graphics::Surface *icon) override;
// PaletteManager interface
void setPalette(const byte *colors, uint start, uint num) override;
void grabPalette(byte *colors, uint start, uint num) const override;
protected:
void renderCursor();
/**
* Whether a GLES or GLES2 context is active.
*/
bool isGLESContext() const { return OpenGLContext.type == kContextGLES || OpenGLContext.type == kContextGLES2; }
/**
* Notify the manager of a OpenGL context change. This should be the first
* thing to call after you created an OpenGL (ES) context!
*
* @param type Type of the OpenGL (ES) contexts created.
* @param defaultFormat The new default format for the game screen
* (this is used for the CLUT8 game screens).
* @param defaultFormatAlpha The new default format with an alpha channel
* (this is used for the overlay and cursor).
*/
void notifyContextCreate(
ContextType type,
Framebuffer *target,
const Graphics::PixelFormat &defaultFormat,
const Graphics::PixelFormat &defaultFormatAlpha);
/**
* Notify the manager that the OpenGL context is about to be destroyed.
* This will free up/reset internal OpenGL related state and *must* be
* called whenever a context might be created again after destroying a
* context.
*/
void notifyContextDestroy();
/**
* Create a surface with the specified pixel format.
*
* @param format The pixel format the Surface object should accept as
* input.
* @param wantAlpha For CLUT8 surfaces this marks whether an alpha
* channel should be used.
* @param wantScaler Whether or not a software scaler should be used.
* @return A pointer to the surface or nullptr on failure.
*/
Surface *createSurface(const Graphics::PixelFormat &format, bool wantAlpha = false, bool wantScaler = false, bool wantMask = false);
//
// Transaction support
//
struct VideoState {
VideoState() : valid(false), gameWidth(0), gameHeight(0),
#ifdef USE_RGB_COLOR
gameFormat(),
#endif
aspectRatioCorrection(false), graphicsMode(GFX_OPENGL), flags(0),
filtering(true), scalerIndex(0), scaleFactor(1), shader() {
}
bool valid;
uint gameWidth, gameHeight;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat gameFormat;
#endif
bool aspectRatioCorrection;
int graphicsMode;
uint flags;
bool filtering;
uint scalerIndex;
int scaleFactor;
Common::Path shader;
bool operator==(const VideoState &right) {
return gameWidth == right.gameWidth && gameHeight == right.gameHeight
#ifdef USE_RGB_COLOR
&& gameFormat == right.gameFormat
#endif
&& aspectRatioCorrection == right.aspectRatioCorrection
&& graphicsMode == right.graphicsMode
&& flags == right.flags
&& filtering == right.filtering
&& shader == right.shader;
}
bool operator!=(const VideoState &right) {
return !(*this == right);
}
};
/**
* The currently set up video state.
*/
VideoState _currentState;
/**
* The old video state used when doing a transaction rollback.
*/
VideoState _oldState;
protected:
enum TransactionMode {
kTransactionNone = 0,
kTransactionActive = 1,
kTransactionRollback = 2
};
TransactionMode getTransactionMode() const { return _transactionMode; }
private:
/**
* The current transaction mode.
*/
TransactionMode _transactionMode;
/**
* The current screen change ID.
*/
int _screenChangeID;
/**
* The current stretch mode.
*/
int _stretchMode;
/**
* Scaled version of _gameScreenShakeXOffset and _gameScreenShakeYOffset (as a Common::Point)
*/
Common::Point _shakeOffsetScaled;
protected:
/**
* Set up the requested video mode. This takes parameters which describe
* what resolution the game screen requests (this is possibly aspect ratio
* corrected!).
*
* A sub-class should take these parameters as hints. It might very well
* set up a mode which it thinks suites the situation best.
*
* @parma requestedWidth This is the requested actual game screen width.
* @param requestedHeight This is the requested actual game screen height.
* @param resizable This indicates that the window should not be resized because we can't handle it.
* @param antialiasing This is the requested antialiasing level.
* @return true on success, false otherwise
*/
virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, bool resizable, int antialiasing) = 0;
bool loadShader(const Common::Path &fileName);
/**
* Refresh the screen contents.
*/
virtual void refreshScreen() = 0;
/**
* Saves a screenshot of the entire window, excluding window decorations.
*
* @param filename The output filename.
* @return true on success, false otherwise
*/
bool saveScreenshot(const Common::Path &filename) const;
// Do not hide the argument-less saveScreenshot from the base class
using WindowedGraphicsManager::saveScreenshot;
private:
//
// OpenGL utilities
//
/**
* Initialize the active context for use.
*/
void initializeGLContext();
/**
* OpenGL pipeline used for rendering.
*/
Pipeline *_pipeline;
#if !USE_FORCED_GLES
/**
* OpenGL pipeline used for post-processing.
*/
LibRetroPipeline *_libretroPipeline;
#endif
protected:
/**
* Try to determine the internal parameters for a given pixel format.
*
* @return true when the format can be used, false otherwise.
*/
bool getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const;
bool gameNeedsAspectRatioCorrection() const override;
int getGameRenderScale() const override;
void recalculateDisplayAreas() override;
void handleResizeImpl(const int width, const int height) override;
void updateTextureSettings();
Pipeline *getPipeline() const { return _pipeline; }
/**
* The default pixel format of the backend.
*/
Graphics::PixelFormat _defaultFormat;
/**
* The default pixel format with an alpha channel.
*/
Graphics::PixelFormat _defaultFormatAlpha;
/**
* Render target.
*/
Framebuffer *_targetBuffer;
/**
* The rendering surface for the virtual game screen.
*/
Surface *_gameScreen;
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
/**
* The rendering helper for 3D games.
*/
Renderer3D *_renderer3d;
#endif
/**
* The game palette if in CLUT8 mode.
*/
byte _gamePalette[3 * 256];
//
// Overlay
//
/**
* The rendering surface for the overlay.
*/
Surface *_overlay;
//
// Cursor
//
/**
* Set up the correct cursor palette.
*/
void updateCursorPalette();
/**
* The rendering surface for the mouse cursor.
*/
Surface *_cursor;
/**
* The rendering surface for the opacity and inversion mask (if any)
*/
Surface *_cursorMask;
/**
* The X offset for the cursor hotspot in unscaled game coordinates.
*/
int _cursorHotspotX;
/**
* The Y offset for the cursor hotspot in unscaled game coordinates.
*/
int _cursorHotspotY;
/**
* Recalculate the cursor scaling. Scaling is always done according to
* the game screen.
*/
void recalculateCursorScaling();
/**
* The X offset for the cursor hotspot in scaled game display area
* coordinates.
*/
int _cursorHotspotXScaled;
/**
* The Y offset for the cursor hotspot in scaled game display area
* coordinates.
*/
int _cursorHotspotYScaled;
/**
* The width of the cursor in scaled game display area coordinates.
*/
float _cursorWidthScaled;
/**
* The height of the cursor in scaled game display area coordinates.
*/
float _cursorHeightScaled;
/**
* The key color.
*/
uint32 _cursorKeyColor;
/**
* If true, use key color.
*/
bool _cursorUseKey;
/**
* Whether no cursor scaling should be applied.
*/
bool _cursorDontScale;
/**
* Whether the special cursor palette is enabled.
*/
bool _cursorPaletteEnabled;
/**
* The special cursor palette in case enabled.
*/
byte _cursorPalette[3 * 256];
#ifdef USE_SCALERS
/**
* The list of scaler plugins
*/
const PluginList &_scalerPlugins;
#endif
#ifdef USE_OSD
//
// OSD
//
protected:
/**
* Returns the font used for on screen display
*/
virtual const Graphics::Font *getFontOSD() const;
private:
/**
* Request for the OSD icon surface to be updated.
*/
bool _osdMessageChangeRequest;
/**
* The next OSD message.
*
* If this value is not empty, the OSD message will be set
* to it on the next frame.
*/
Common::U32String _osdMessageNextData;
/**
* Set the OSD message surface with the value of the next OSD message.
*/
void osdMessageUpdateSurface();
/**
* The OSD message's contents.
*/
Surface *_osdMessageSurface;
/**
* Current opacity level of the OSD message.
*/
uint8 _osdMessageAlpha;
/**
* When fading the OSD message has started.
*/
uint32 _osdMessageFadeStartTime;
enum {
kOSDMessageFadeOutDelay = 2 * 1000,
kOSDMessageFadeOutDuration = 500,
kOSDMessageInitialAlpha = 80
};
/**
* The OSD background activity icon's contents.
*/
Surface *_osdIconSurface;
enum {
kOSDIconTopMargin = 10,
kOSDIconRightMargin = 10
};
#endif
};
} // End of namespace OpenGL
#endif

View File

@@ -0,0 +1,48 @@
/* 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 "backends/graphics/opengl/pipelines/clut8.h"
#include "backends/graphics/opengl/shader.h"
#include "backends/graphics/opengl/framebuffer.h"
#include "graphics/opengl/debug.h"
namespace OpenGL {
#if !USE_FORCED_GLES
CLUT8LookUpPipeline::CLUT8LookUpPipeline()
: ShaderPipeline(ShaderMan.query(ShaderManager::kCLUT8LookUp)), _paletteTexture(nullptr) {
}
void CLUT8LookUpPipeline::drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) {
assert(isActive());
// Set the palette texture.
GL_CALL(glActiveTexture(GL_TEXTURE1));
if (_paletteTexture) {
_paletteTexture->bind();
}
GL_CALL(glActiveTexture(GL_TEXTURE0));
ShaderPipeline::drawTextureInternal(texture, coordinates, texcoords);
}
#endif // !USE_FORCED_GLES
} // End of namespace OpenGL

View File

@@ -0,0 +1,46 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H
#include "backends/graphics/opengl/pipelines/shader.h"
namespace OpenGL {
#if !USE_FORCED_GLES
class CLUT8LookUpPipeline : public ShaderPipeline {
public:
CLUT8LookUpPipeline();
void setPaletteTexture(const Texture *paletteTexture) { _paletteTexture = paletteTexture; }
protected:
void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) override;
private:
const Texture *_paletteTexture;
};
#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
#endif

View File

@@ -0,0 +1,88 @@
/* 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 "backends/graphics/opengl/pipelines/fixed.h"
#include "graphics/opengl/debug.h"
namespace OpenGL {
#if !USE_FORCED_GLES2
void FixedPipeline::activateInternal() {
Pipeline::activateInternal();
// Disable 3D properties.
GL_CALL(glDisable(GL_CULL_FACE));
GL_CALL(glDisable(GL_DEPTH_TEST));
GL_CALL(glDisable(GL_DITHER));
GL_CALL(glDisable(GL_LIGHTING));
GL_CALL(glDisable(GL_FOG));
GL_CALL(glShadeModel(GL_FLAT));
GL_CALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST));
GL_CALL(glEnableClientState(GL_VERTEX_ARRAY));
GL_CALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
#if !USE_FORCED_GLES
if (OpenGLContext.multitextureSupported) {
GL_CALL(glActiveTexture(GL_TEXTURE0));
}
#endif
GL_CALL(glEnable(GL_TEXTURE_2D));
GL_CALL(glColor4f(_r, _g, _b, _a));
}
void FixedPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
_r = r;
_g = g;
_b = b;
_a = a;
if (isActive()) {
GL_CALL(glColor4f(r, g, b, a));
}
}
void FixedPipeline::drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) {
assert(isActive());
texture.bind();
GL_CALL(glTexCoordPointer(2, GL_FLOAT, 0, texcoords));
GL_CALL(glVertexPointer(2, GL_FLOAT, 0, coordinates));
GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
}
void FixedPipeline::setProjectionMatrix(const Math::Matrix4 &projectionMatrix) {
assert(isActive());
GL_CALL(glMatrixMode(GL_PROJECTION));
GL_CALL(glLoadMatrixf(projectionMatrix.getData()));
GL_CALL(glMatrixMode(GL_MODELVIEW));
GL_CALL(glLoadIdentity());
GL_CALL(glMatrixMode(GL_TEXTURE));
GL_CALL(glLoadIdentity());
}
#endif // !USE_FORCED_GLES2
} // End of namespace OpenGL

View File

@@ -0,0 +1,48 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H
#include "backends/graphics/opengl/pipelines/pipeline.h"
namespace OpenGL {
#if !USE_FORCED_GLES2
class FixedPipeline : public Pipeline {
public:
FixedPipeline() : _r(0.f), _g(0.f), _b(0.f), _a(0.f) {}
void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override;
void setProjectionMatrix(const Math::Matrix4 &projectionMatrix) override;
protected:
void activateInternal() override;
void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) override;
GLfloat _r, _g, _b, _a;
};
#endif // !USE_FORCED_GLES2
} // End of namespace OpenGL
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,277 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_H
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_H
#include "graphics/opengl/system_headers.h"
#if !USE_FORCED_GLES
#include "backends/graphics/opengl/pipelines/shader.h"
#include "common/array.h"
#include "common/fs.h"
namespace Graphics {
struct Surface;
}
namespace OpenGL {
namespace LibRetro {
struct ShaderPreset;
struct ShaderPass;
} // End of namespace LibRetro
class TextureTarget;
class LibRetroTextureTarget;
/**
* Pipeline implementation using Libretro shader presets.
*/
class LibRetroPipeline : public Pipeline {
public:
LibRetroPipeline();
~LibRetroPipeline() override;
void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override;
void setProjectionMatrix(const Math::Matrix4 &projectionMatrix) override;
bool open(const Common::Path &shaderPath, Common::SearchSet &archSet);
void close();
/* Called by OpenGLGraphicsManager */
void enableLinearFiltering(bool enabled) { _linearFiltering = enabled; }
void setRotation(Common::RotationMode rotation) { if (_rotation != rotation) { _rotation = rotation; setPipelineState(); } }
/* Called by OpenGLGraphicsManager to setup the internal objects sizes */
void setDisplaySizes(uint inputWidth, uint inputHeight, const Common::Rect &outputRect);
/* Called by OpenGLGraphicsManager to indicate that next draws need to be scaled. */
void beginScaling();
/* Called by OpenGLGraphicsManager to indicate that next draws don't need to be scaled.
* This must be called to execute scaling. */
void finishScaling();
bool isAnimated() const { return _isAnimated; }
static bool isSupportedByContext() {
return OpenGLContext.shadersSupported
&& OpenGLContext.multitextureSupported
&& OpenGLContext.framebufferObjectSupported;
}
private:
void activateInternal() override;
void deactivateInternal() override;
void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) override;
bool loadTextures(Common::SearchSet &archSet);
bool loadPasses(Common::SearchSet &archSet);
void setPipelineState();
bool setupFBOs();
void setupPassUniforms(const uint id);
void setShaderTexUniforms(const Common::String &prefix, Shader *shader, const Texture &texture);
/* Pipelines used to draw all layers
* First before the scaler then after it to draw on screen
*/
ShaderPipeline _inputPipeline;
ShaderPipeline _outputPipeline;
bool _needsScaling;
const LibRetro::ShaderPreset *_shaderPreset;
uint _inputWidth;
uint _inputHeight;
Common::Rect _outputRect;
bool _linearFiltering;
Common::RotationMode _rotation;
/* Determines if preset depends on frameCount or from previous frames */
bool _isAnimated;
uint _frameCount;
Common::Array<LibRetroTextureTarget> _inputTargets;
uint _currentTarget;
struct LibRetroTexture {
LibRetroTexture() : textureData(nullptr), glTexture(nullptr) {}
LibRetroTexture(Graphics::Surface *tD, Texture *glTex) : textureData(tD), glTexture(glTex) {}
Common::String id;
Graphics::Surface *textureData;
Texture *glTexture;
};
LibRetroTexture loadTexture(const Common::Path &fileName, Common::Archive *container, Common::SearchSet &archSet);
typedef Common::Array<LibRetroTexture> TextureArray;
TextureArray _textures;
struct Pass {
Pass()
: shaderPass(nullptr), shader(nullptr), target(nullptr), texCoords(), texSamplers(),
inputTexture(nullptr), vertexCoord(), hasFrameCount(false), prevCount(0) {}
Pass(const LibRetro::ShaderPass *sP, Shader *s, TextureTarget *t)
: shaderPass(sP), shader(s), target(t), texCoords(), texSamplers(),
inputTexture(nullptr), vertexCoord(), hasFrameCount(false), prevCount(0) {}
const LibRetro::ShaderPass *shaderPass;
Shader *shader;
TextureTarget *target;
/**
* Description of texture coordinates bound to attribute.
*/
struct TexCoordAttribute {
/**
* Attribute name to bind data to.
*/
Common::String name;
enum Type {
/**
* 'index' denotes the 'index'th shader texture's coordinates.
*/
kTypeTexture,
/**
* 'index' denotes the texture coordinates given to pass 'index'.
*/
kTypePass,
/**
* 'index' denotes the texture coordinates of the 'index'th previous frame.
*/
kTypePrev
};
/**
* The type of the attribute.
*/
Type type;
/**
* Index for the texture coordinates to use.
*/
uint index;
TexCoordAttribute() : name(), type(), index() {}
TexCoordAttribute(const Common::String &n, Type t, uint i) : name(n), type(t), index(i) {}
};
typedef Common::Array<TexCoordAttribute> TexCoordAttributeArray;
TexCoordAttributeArray texCoords;
/**
* Build the 'texCoords' array.
*
* @param id Identifier of the current pass.
*/
void buildTexCoords(const uint id, const Common::StringArray &aliases);
void addTexCoord(const Common::String &prefix, const TexCoordAttribute::Type type, const uint index);
/**
* Description of a texture sampler.
*/
struct TextureSampler {
/**
* Texture unit to use.
*/
uint unit;
enum Type {
/**
* 'index' denotes the 'index'th shader texture.
*/
kTypeTexture,
/**
* 'index' denotes the input to pass 'index'.
*/
kTypePass,
/**
* 'index' denotes the input of the 'index'th previous frame.
*/
kTypePrev
};
/**
* Source type of the texture to bind.
*/
Type type;
/**
* Index of the texture.
*/
uint index;
TextureSampler() : unit(), type(), index() {}
TextureSampler(uint u, Type t, uint i) : unit(u), type(t), index(i) {}
};
typedef Common::Array<TextureSampler> TextureSamplerArray;
TextureSamplerArray texSamplers;
/**
* Build the 'texSamplers' array.
*
* @param id Identifier of the current pass.
* @param textures Array of shader textures available.
*/
void buildTexSamplers(const uint id, const TextureArray &textures, const Common::StringArray &aliases);
bool addTexSampler(const Common::String &name, uint *unit, const TextureSampler::Type type, const uint index, const bool prefixIsId = false);
/**
* Input texture of the pass.
*/
const Texture *inputTexture;
/**
* Vertex coordinates used for drawing.
*/
GLfloat vertexCoord[2*4];
/**
* Whether the shader has a FrameCount uniform or not
* Allows to speed up if it is not here
*/
bool hasFrameCount;
/**
* The number of previous frames this pass needs
*/
uint prevCount;
};
typedef Common::Array<Pass> PassArray;
PassArray _passes;
void renderPass(const Pass &pass);
void renderPassSetupCoordinates(const Pass &pass);
void renderPassSetupTextures(const Pass &pass);
};
} // End of namespace OpenGL
#endif // !USE_FORCED_GLES
#endif

View File

@@ -0,0 +1,573 @@
/* 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 "graphics/opengl/system_headers.h"
#if !USE_FORCED_GLES
#include "backends/graphics/opengl/pipelines/libretro/parser.h"
#include "common/file.h"
#include "common/hash-str.h"
#include "common/stream.h"
#include "common/algorithm.h"
#include "common/tokenizer.h"
#include "common/ptr.h"
#include "common/util.h"
#include "common/textconsole.h"
namespace OpenGL {
namespace LibRetro {
class PresetParser {
public:
ShaderPreset *parseStream(Common::SeekableReadStream &stream);
const Common::String &getErrorDesc() const { return _errorDesc; }
private:
bool parsePreset(Common::SeekableReadStream &stream);
bool lookUpValue(const Common::String &key, Common::String *value);
bool lookUpValue(const Common::String &key, bool *value);
bool lookUpValue(const Common::String &key, uint *value);
bool lookUpValue(const Common::String &key, float *value);
bool lookUpValue(const Common::String &key, FilteringMode *value, const FilteringMode defaultValue);
bool lookUpValue(const Common::String &key, ScaleType *value, const ScaleType defaultValue);
bool lookUpValueScale(const Common::String &key, float *floatValue, uint *uintValue, const ScaleType scaleType);
bool lookUpValue(const Common::String &key, WrapMode *value, const WrapMode defaultValue);
template<typename T, typename DefaultT>
bool lookUpValue(const Common::String &key, T *value, const DefaultT &defaultValue) {
if (_entries.contains(key)) {
return lookUpValue(key, value);
} else {
*value = defaultValue;
return true;
}
}
bool parseTextures();
bool parseTexture(const Common::String &id);
bool parsePasses();
bool parsePass(const uint id, const bool isLast);
bool parsePassScaleType(const uint id, const bool isLast, ShaderPass *pass);
bool parsePassScale(const uint id, ShaderPass *pass);
bool computeDefaultScale(const Common::String &key, float *floatValue, uint *uintValue, const ScaleType scaleType);
bool parseParameters();
typedef Common::HashMap<Common::String, Common::String> StringMap;
StringMap _entries;
Common::String _errorDesc;
Common::ScopedPtr<ShaderPreset> _shader;
};
ShaderPreset *PresetParser::parseStream(Common::SeekableReadStream &stream) {
_errorDesc.clear();
_entries.clear();
if (!parsePreset(stream)) {
return nullptr;
}
_shader.reset(new ShaderPreset);
if (!parseTextures()) {
return nullptr;
}
if (!parsePasses()) {
return nullptr;
}
if (!parseParameters()) {
return nullptr;
}
return _shader.release();
}
bool PresetParser::parsePreset(Common::SeekableReadStream &stream) {
while (!stream.eos()) {
Common::String line = stream.readLine();
if (stream.err()) {
_errorDesc = "Read error";
return false;
}
size_t sharpPos = line.findFirstOf('#');
if (sharpPos != line.npos) {
// Remove the end of line
line.erase(sharpPos);
}
if (line.empty()) {
continue;
}
bool empty = true;
for (uint i = 0; i < line.size(); i++) {
if (!Common::isSpace(line[i])) {
empty = false;
break;
}
}
if (empty)
continue;
// Split line into key, value pair.
Common::String::const_iterator equalIter = Common::find(line.begin(), line.end(), '=');
if (equalIter == line.end()) {
_errorDesc = "Could not find '=' in line '" + line + '\'';
return false;
}
Common::String key(line.begin(), equalIter);
Common::String value(equalIter + 1);
key.trim();
value.trim();
// Check whether the value is put in quotation marks. This might be
// useful when a path contains a whitespace. But Libretro's is not
// mentioning any exact format, but one example for 'textures'
// indicates quotes are supported in this place.
if (!value.empty() && value[0] == '"') {
if (value.size() < 2 || value.lastChar() != '"') {
_errorDesc = "Unmatched '\"' for value in line '" + line + '\'';
}
value.deleteLastChar();
value.deleteChar(0);
}
_entries[key] = value;
}
return true;
}
bool PresetParser::lookUpValue(const Common::String &key, Common::String *value) {
StringMap::const_iterator iter = _entries.find(key);
if (iter != _entries.end()) {
*value = iter->_value;
return true;
} else {
_errorDesc = "Missing key '" + key + "'";
return false;
}
}
bool PresetParser::lookUpValue(const Common::String &key, bool *value) {
StringMap::const_iterator iter = _entries.find(key);
if (iter != _entries.end()) {
if (Common::parseBool(iter->_value, *value)) {
return true;
} else {
_errorDesc = "Invalid boolean value for key '" + key + "': '" + iter->_value + '\'';
return false;
}
} else {
_errorDesc = "Missing key '" + key + "'";
return false;
}
}
bool PresetParser::lookUpValue(const Common::String &key, uint *value) {
StringMap::const_iterator iter = _entries.find(key);
if (iter != _entries.end()) {
char *endptr;
const unsigned long uintVal = strtoul(iter->_value.c_str(), &endptr, 0);
// Original libretro is quite laxist with int values and only checks errno
// This means that as long as there is some number at start, parsing won't fail
if (endptr == iter->_value.c_str() || uintVal >= UINT_MAX) {
_errorDesc = "Invalid unsigned integer value for key '" + key + "': '" + iter->_value + '\'';
return false;
} else {
if (*endptr != '\0') {
warning("Possibly invalid unsigned integer value for key '%s': '%s'", key.c_str(), iter->_value.c_str());
}
*value = uintVal;
return true;
}
} else {
_errorDesc = "Missing key '" + key + "'";
return false;
}
}
bool PresetParser::lookUpValue(const Common::String &key, float *value) {
StringMap::const_iterator iter = _entries.find(key);
if (iter != _entries.end()) {
char *endptr;
const double doubleVal = strtod(iter->_value.c_str(), &endptr);
if (*endptr != '\0') {
_errorDesc = "Invalid float value for key '" + key + "': '" + iter->_value + '\'';
return false;
} else {
*value = doubleVal;
return true;
}
} else {
_errorDesc = "Missing key '" + key + "'";
return false;
}
}
bool PresetParser::lookUpValue(const Common::String &key, FilteringMode *value, const FilteringMode defaultValue) {
StringMap::const_iterator iter = _entries.find(key);
if (iter != _entries.end()) {
bool isLinear;
if (Common::parseBool(iter->_value, isLinear)) {
*value = isLinear ? kFilteringModeLinear : kFilteringModeNearest;
return true;
} else {
_errorDesc = "Invalid filtering mode for key '" + key + "': '" + iter->_value + '\'';
return false;
}
} else {
*value = defaultValue;
return true;
}
}
bool PresetParser::lookUpValue(const Common::String &key, ScaleType *value, const ScaleType defaultValue) {
StringMap::const_iterator iter = _entries.find(key);
if (iter != _entries.end()) {
if (iter->_value == "source") {
*value = kScaleTypeSource;
return true;
} else if (iter->_value == "viewport") {
*value = kScaleTypeViewport;
return true;
} else if (iter->_value == "absolute") {
*value = kScaleTypeAbsolute;
return true;
} else {
_errorDesc = "Invalid scale type for key '" + key + "': '" + iter->_value + '\'';
return false;
}
} else {
*value = defaultValue;
return true;
}
}
bool PresetParser::lookUpValueScale(const Common::String &key, float *floatValue, uint *uintValue, const ScaleType scaleType) {
switch (scaleType) {
case kScaleTypeSource:
case kScaleTypeViewport:
return lookUpValue(key, floatValue, 1.0f);
case kScaleTypeAbsolute:
return lookUpValue(key, uintValue);
case kScaleTypeFull:
return true;
default:
_errorDesc = "Internal Error: Invalid scale type";
return false;
}
}
bool PresetParser::lookUpValue(const Common::String &key, WrapMode *value, const WrapMode defaultValue) {
StringMap::const_iterator iter = _entries.find(key);
if (iter != _entries.end()) {
if (iter->_value == "clamp_to_border") {
*value = kWrapModeBorder;
return true;
} else if (iter->_value == "clamp_to_edge") {
*value = kWrapModeEdge;
return true;
} else if (iter->_value == "repeat") {
*value = kWrapModeRepeat;
return true;
} else if (iter->_value == "mirrored_repeat") {
*value = kWrapModeMirroredRepeat;
return true;
} else {
_errorDesc = "Invalid wrap mode for key '" + key + "': '" + iter->_value + '\'';
return false;
}
} else {
*value = defaultValue;
return true;
}
}
bool PresetParser::parseTextures() {
Common::String textures;
if (!lookUpValue("textures", &textures)) {
return true;
}
// Parse all texture information from preset.
Common::StringTokenizer tokenizer(textures, ";");
while (!tokenizer.empty()) {
if (!parseTexture(tokenizer.nextToken())) {
return false;
}
}
return true;
}
bool PresetParser::parseTexture(const Common::String &id) {
Common::String fileName;
if (!lookUpValue(id, &fileName)) {
_errorDesc = "No file name specified for texture '" + id + '\'';
return false;
}
FilteringMode filteringMode;
if (!lookUpValue(id + "_linear", &filteringMode, kFilteringModeLinear)) {
return false;
}
WrapMode wrapMode;
if (!lookUpValue(id + "_wrap_mode", &wrapMode, kWrapModeBorder)) {
return false;
}
_shader->textures.push_back(ShaderTexture(id, fileName, filteringMode, wrapMode));
return true;
}
bool PresetParser::parsePasses() {
uint numShaders;
if (!lookUpValue("shaders", &numShaders)) {
return false;
}
for (uint shaderPass = 0; shaderPass < numShaders; ++shaderPass) {
if (!parsePass(shaderPass, shaderPass == numShaders - 1)) {
return false;
}
}
return true;
}
#define passKey(x) Common::String::format(x "%u", id)
bool PresetParser::parsePass(const uint id, const bool isLast) {
ShaderPass pass;
if (!lookUpValue(passKey("shader"), &pass.fileName)) {
_errorDesc = Common::String::format("No file name specified for pass '%u'", id);
return false;
}
if (!lookUpValue(passKey("alias"), &pass.alias)) {
_errorDesc.clear();
pass.alias.clear();
}
if (!lookUpValue(passKey("filter_linear"), &pass.filteringMode, kFilteringModeUnspecified)) {
return false;
}
if (!lookUpValue(passKey("wrap_mode"), &pass.wrapMode, kWrapModeBorder)) {
return false;
}
if (!lookUpValue(passKey("mipmap_input"), &pass.mipmapInput, false)) {
return false;
}
if (!lookUpValue(passKey("float_framebuffer"), &pass.floatFBO, false)) {
return false;
}
if (!lookUpValue(passKey("srgb_framebuffer"), &pass.srgbFBO, false)) {
return false;
}
if (!lookUpValue(passKey("frame_count_mod"), &pass.frameCountMod, 0)) {
return false;
}
if (!parsePassScaleType(id, isLast, &pass)) {
return false;
}
if (!parsePassScale(id, &pass)) {
return false;
}
_shader->passes.push_back(pass);
return true;
}
bool PresetParser::parsePassScaleType(const uint id, const bool isLast, ShaderPass *pass) {
// Parse scale type for the pass.
//
// This is a little more complicated because it is possible to specify the
// scale type per axis. However, a generic scale type overrides the axis
// scale types.
//
// Additionally, the default value for the passes vary. The last pass
// defaults to use full size, all other default to source scaling.
const ScaleType defaultScaleType = isLast ? kScaleTypeFull : kScaleTypeSource;
if (!lookUpValue(passKey("scale_type_x"), &pass->scaleTypeX, defaultScaleType)) {
return false;
}
if (!lookUpValue(passKey("scale_type_y"), &pass->scaleTypeY, defaultScaleType)) {
return false;
}
ScaleType scale_type;
// Small trick here: lookUpValue never returns kScaleTypeFull
if (!lookUpValue(passKey("scale_type"), &scale_type, kScaleTypeFull)) {
return false;
} else if (scale_type != kScaleTypeFull) {
pass->scaleTypeY = pass->scaleTypeX = scale_type;
}
return true;
}
bool PresetParser::parsePassScale(const uint id, ShaderPass *pass) {
// Parse actual scale value for the pass.
//
// Like for the scale type, 'scale' overrides 'scale_x'/'scale_y'.
if (_entries.contains(passKey("scale"))) {
if (!lookUpValueScale(passKey("scale"), &pass->scaleXFloat, &pass->scaleXUint, pass->scaleTypeX)) {
return false;
}
if (!lookUpValueScale(passKey("scale"), &pass->scaleYFloat, &pass->scaleYUint, pass->scaleTypeY)) {
return false;
}
return true;
}
if (_entries.contains(passKey("scale_x"))) {
if (!lookUpValueScale(passKey("scale_x"), &pass->scaleXFloat, &pass->scaleXUint, pass->scaleTypeX)) {
return false;
}
} else {
if (!computeDefaultScale(passKey("scale_x"), &pass->scaleXFloat, &pass->scaleXUint, pass->scaleTypeX)) {
return false;
}
}
if (_entries.contains(passKey("scale_y"))) {
if (!lookUpValueScale(passKey("scale_y"), &pass->scaleYFloat, &pass->scaleYUint, pass->scaleTypeY)) {
return false;
}
} else {
if (!computeDefaultScale(passKey("scale_y"), &pass->scaleYFloat, &pass->scaleYUint, pass->scaleTypeY)) {
return false;
}
}
return true;
}
#undef passKey
bool PresetParser::computeDefaultScale(const Common::String &key, float *floatValue, uint *uintValue, const ScaleType scaleType) {
switch (scaleType) {
case kScaleTypeSource:
case kScaleTypeViewport:
*floatValue = 1.0f;
return true;
case kScaleTypeAbsolute:
_errorDesc = "No value specified for scale '" + key + '\'';
return false;
case kScaleTypeFull:
return true;
default:
_errorDesc = "Internal Error: Invalid scale type";
return false;
}
}
bool PresetParser::parseParameters() {
Common::String parameters;
if (!lookUpValue("parameters", &parameters)) {
return true;
}
// Parse all texture information from preset.
Common::StringTokenizer tokenizer(parameters, ";");
while (!tokenizer.empty()) {
Common::String key = tokenizer.nextToken();
if (_entries.contains(key)) {
float value;
if (!lookUpValue(key, &value)) {
return false;
}
_shader->parameters[key] = value;
}
}
return true;
}
ShaderPreset *parsePreset(const Common::Path &shaderPreset, Common::SearchSet &archSet) {
Common::SeekableReadStream *stream;
Common::Archive *container = nullptr;
Common::Path basePath;
// First try SearchMan, then fallback to filesystem
if (archSet.hasFile(shaderPreset)) {
Common::ArchiveMemberPtr member = archSet.getMember(shaderPreset, &container);
stream = member->createReadStream();
basePath = shaderPreset.getParent();
} else {
Common::FSNode fsnode(shaderPreset);
if (!fsnode.exists() || !fsnode.isReadable() || fsnode.isDirectory()
|| !(stream = fsnode.createReadStream())) {
warning("LibRetro Preset Parsing: Invalid file path '%s'", shaderPreset.toString(Common::Path::kNativeSeparator).c_str());
return nullptr;
}
basePath = fsnode.getParent().getPath();
}
PresetParser parser;
ShaderPreset *shader = parser.parseStream(*stream);
delete stream;
if (!shader) {
warning("LibRetro Preset Parsing: Error while parsing file '%s': %s", shaderPreset.toString().c_str(), parser.getErrorDesc().c_str());
return nullptr;
}
shader->container = container;
shader->basePath = basePath;
return shader;
}
} // End of namespace LibRetro
} // End of namespace OpenGL
#endif // !USE_FORCED_GLES

View File

@@ -0,0 +1,39 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_PARSER_H
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_PARSER_H
#include "graphics/opengl/system_headers.h"
#if !USE_FORCED_GLES
#include "backends/graphics/opengl/pipelines/libretro/types.h"
namespace OpenGL {
namespace LibRetro {
ShaderPreset *parsePreset(const Common::Path &shaderPreset, Common::SearchSet &archSet);
} // End of namespace LibRetro
} // End of namespace OpenGL
#endif // !USE_FORCED_GLES
#endif

View File

@@ -0,0 +1,132 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_TYPES_H
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_TYPES_H
#include "graphics/opengl/system_headers.h"
#include "graphics/opengl/texture.h"
#if !USE_FORCED_GLES
#include "common/str.h"
#include "common/array.h"
#include "common/fs.h"
namespace OpenGL {
namespace LibRetro {
typedef Common::HashMap<Common::String, float, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> UniformsMap;
enum FilteringMode {
kFilteringModeUnspecified,
kFilteringModeNearest,
kFilteringModeLinear
};
struct ShaderTexture {
ShaderTexture() : id(), fileName(), filteringMode(kFilteringModeUnspecified) {}
ShaderTexture(const Common::String &i, const Common::String &fN, FilteringMode fM, WrapMode wM)
: id(i), fileName(fN), filteringMode(fM), wrapMode(wM) {}
Common::String id;
Common::String fileName;
FilteringMode filteringMode;
WrapMode wrapMode;
};
enum ScaleType {
kScaleTypeSource,
kScaleTypeViewport,
kScaleTypeAbsolute,
kScaleTypeFull
};
inline void applyScale(const ScaleType type,
const float source, const float viewport,
const float scaleFloat, const uint scaleUint,
float *output) {
switch (type) {
case kScaleTypeSource:
*output = source * scaleFloat;
break;
case kScaleTypeViewport:
*output = viewport * scaleFloat;
break;
case kScaleTypeAbsolute:
*output = scaleUint;
break;
case kScaleTypeFull:
*output = viewport;
break;
}
}
struct ShaderPass {
Common::String fileName;
Common::String alias;
FilteringMode filteringMode;
WrapMode wrapMode;
bool mipmapInput;
bool floatFBO;
bool srgbFBO;
uint frameCountMod;
ScaleType scaleTypeX;
ScaleType scaleTypeY;
float scaleXFloat;
float scaleYFloat;
uint scaleXUint;
uint scaleYUint;
void applyScale(const float sourceW, const float sourceH,
const float viewportW, const float viewportH,
float *outputW, float *outputH) const {
OpenGL::LibRetro::applyScale(scaleTypeX, sourceW, viewportW, scaleXFloat, scaleXUint, outputW);
OpenGL::LibRetro::applyScale(scaleTypeY, sourceH, viewportH, scaleYFloat, scaleYUint, outputH);
}
};
struct ShaderPreset {
Common::Archive *container;
Common::Path basePath;
typedef Common::Array<ShaderTexture> TextureArray;
TextureArray textures;
typedef Common::Array<ShaderPass> PassArray;
PassArray passes;
UniformsMap parameters;
};
} // End of namespace LibRetro
} // End of namespace OpenGL
#endif // !USE_FORCED_GLES
#endif

View File

@@ -0,0 +1,81 @@
/* 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 "backends/graphics/opengl/pipelines/pipeline.h"
#include "backends/graphics/opengl/framebuffer.h"
namespace OpenGL {
Pipeline *Pipeline::activePipeline = nullptr;
Pipeline::Pipeline()
: _activeFramebuffer(nullptr) {
}
void Pipeline::activate() {
if (activePipeline == this) {
return;
}
if (activePipeline) {
activePipeline->deactivate();
}
activePipeline = this;
activateInternal();
}
void Pipeline::activateInternal() {
if (_activeFramebuffer) {
_activeFramebuffer->activate(this);
}
}
void Pipeline::deactivate() {
assert(isActive());
deactivateInternal();
activePipeline = nullptr;
}
void Pipeline::deactivateInternal() {
if (_activeFramebuffer) {
_activeFramebuffer->deactivate();
}
}
Framebuffer *Pipeline::setFramebuffer(Framebuffer *framebuffer) {
Framebuffer *oldFramebuffer = _activeFramebuffer;
if (isActive() && oldFramebuffer) {
oldFramebuffer->deactivate();
}
_activeFramebuffer = framebuffer;
if (isActive() && _activeFramebuffer) {
_activeFramebuffer->activate(this);
}
return oldFramebuffer;
}
} // End of namespace OpenGL

View File

@@ -0,0 +1,167 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H
#include "graphics/opengl/system_headers.h"
#include "graphics/opengl/texture.h"
#include "math/matrix4.h"
namespace OpenGL {
class Framebuffer;
/**
* Interface for OpenGL pipeline functionality.
*
* This encapsulates differences in various rendering pipelines used for
* OpenGL, OpenGL ES 1, and OpenGL ES 2.
*/
class Pipeline {
public:
/**
* Deactivate any pipeline.
*/
static void disable() { if (activePipeline) activePipeline->deactivate(); }
Pipeline();
virtual ~Pipeline() { if (isActive()) deactivate(); }
/**
* Activate the pipeline.
*
* This sets the OpenGL state to make use of drawing with the given
* OpenGL pipeline.
*/
void activate();
/**
* Deactivate the pipeline.
*/
void deactivate();
/**
* Set framebuffer to render to.
*
* Client is responsible for any memory management related to framebuffer.
*
* @param framebuffer Framebuffer to activate.
* @return Formerly active framebuffer.
*/
Framebuffer *setFramebuffer(Framebuffer *framebuffer);
/**
* Set modulation color.
*
* @param r Red component in [0,1].
* @param g Green component in [0,1].
* @param b Blue component in [0,1].
* @param a Alpha component in [0,1].
*/
virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) = 0;
/**
* Draw a texture rectangle to the currently active framebuffer.
*
* @param texture Texture to use for drawing.
* @param coordinates x1, y1, x2, y2 coordinates where to draw the texture.
*/
inline void drawTexture(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) {
drawTextureInternal(texture, coordinates, texcoords);
}
inline void drawTexture(const Texture &texture, const GLfloat *coordinates) {
drawTextureInternal(texture, coordinates, texture.getTexCoords());
}
inline void drawTexture(const Texture &texture, GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
const GLfloat coordinates[4*2] = {
x, y,
x + w, y,
x, y + h,
x + w, y + h
};
drawTextureInternal(texture, coordinates, texture.getTexCoords());
}
inline void drawTexture(const Texture &texture, GLfloat x, GLfloat y, GLfloat w, GLfloat h, const Common::Rect &clip) {
const GLfloat coordinates[4*2] = {
x, y,
x + w, y,
x, y + h,
x + w, y + h
};
const uint tw = texture.getWidth(),
th = texture.getHeight();
if (tw == 0 || th == 0) {
// Nothing to display
return;
}
const GLfloat texcoords[4*2] = {
(float)clip.left / tw, (float)clip.top / th,
(float)clip.right / tw, (float)clip.top / th,
(float)clip.left / tw, (float)clip.bottom / th,
(float)clip.right / tw, (float)clip.bottom / th
};
drawTextureInternal(texture, coordinates, texcoords);
}
/**
* Set the projection matrix.
*
* This is intended to be only ever be used by Framebuffer subclasses.
*/
virtual void setProjectionMatrix(const Math::Matrix4 &projectionMatrix) = 0;
protected:
/**
* Activate the pipeline.
*
* This sets the OpenGL state to make use of drawing with the given
* OpenGL pipeline.
*/
virtual void activateInternal();
/**
* Deactivate the pipeline.
*/
virtual void deactivateInternal();
virtual void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) = 0;
bool isActive() const { return activePipeline == this; }
Framebuffer *_activeFramebuffer;
private:
/** Currently active rendering pipeline. */
static Pipeline *activePipeline;
};
} // End of namespace OpenGL
#endif

View File

@@ -0,0 +1,110 @@
/* 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 "backends/graphics/opengl/pipelines/shader.h"
#include "backends/graphics/opengl/shader.h"
#include "backends/graphics/opengl/framebuffer.h"
#include "graphics/opengl/debug.h"
namespace OpenGL {
// A 4 elements with 2 components vector of floats
static const int kCoordinatesSize = 4 * 2 * sizeof(float);
#if !USE_FORCED_GLES
ShaderPipeline::ShaderPipeline(Shader *shader)
: _activeShader(shader), _colorAttributes(), _colorDirty(true) {
// Use the same VBO for vertices and texcoords as we modify them at the same time
_coordsVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, kCoordinatesSize, nullptr, GL_STATIC_DRAW);
_activeShader->enableVertexAttribute("position", _coordsVBO, 2, GL_FLOAT, GL_FALSE, 0, 0);
_texcoordsVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, kCoordinatesSize, nullptr, GL_STATIC_DRAW);
_activeShader->enableVertexAttribute("texCoordIn", _texcoordsVBO, 2, GL_FLOAT, GL_FALSE, 0, 0);
_colorVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(_colorAttributes), nullptr, GL_DYNAMIC_DRAW);
_activeShader->enableVertexAttribute("blendColorIn", _colorVBO, 4, GL_FLOAT, GL_FALSE, 0, 0);
}
ShaderPipeline::~ShaderPipeline() {
delete _activeShader;
OpenGL::Shader::freeBuffer(_coordsVBO);
OpenGL::Shader::freeBuffer(_texcoordsVBO);
OpenGL::Shader::freeBuffer(_colorVBO);
}
void ShaderPipeline::activateInternal() {
Pipeline::activateInternal();
// Disable 3D properties.
GL_CALL(glDisable(GL_CULL_FACE));
GL_CALL(glDisable(GL_DEPTH_TEST));
GL_CALL(glDisable(GL_DITHER));
if (OpenGLContext.multitextureSupported) {
GL_CALL(glActiveTexture(GL_TEXTURE0));
}
_activeShader->use();
}
void ShaderPipeline::deactivateInternal() {
_activeShader->unbind();
Pipeline::deactivateInternal();
}
void ShaderPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
GLfloat *dst = _colorAttributes;
for (uint i = 0; i < 4; ++i) {
*dst++ = r;
*dst++ = g;
*dst++ = b;
*dst++ = a;
}
_colorDirty = true;
}
void ShaderPipeline::drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) {
assert(isActive());
texture.bind();
if (_colorDirty) {
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, _colorVBO));
GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(_colorAttributes), _colorAttributes));
_colorDirty = false;
}
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, _coordsVBO));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, kCoordinatesSize, coordinates, GL_STATIC_DRAW));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, _texcoordsVBO));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, kCoordinatesSize, texcoords, GL_STATIC_DRAW));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
}
void ShaderPipeline::setProjectionMatrix(const Math::Matrix4 &projectionMatrix) {
assert(isActive());
_activeShader->setUniform("projection", projectionMatrix);
}
#endif // !USE_FORCED_GLES
} // End of namespace OpenGL

View File

@@ -0,0 +1,59 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H
#include "backends/graphics/opengl/pipelines/pipeline.h"
namespace OpenGL {
#if !USE_FORCED_GLES
class Shader;
class ShaderPipeline : public Pipeline {
public:
ShaderPipeline(Shader *shader);
~ShaderPipeline() override;
void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override;
void setProjectionMatrix(const Math::Matrix4 &projectionMatrix) override;
protected:
void activateInternal() override;
void deactivateInternal() override;
void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) override;
GLuint _coordsVBO;
GLuint _texcoordsVBO;
GLuint _colorVBO;
GLfloat _colorAttributes[4*4];
bool _colorDirty;
Shader *const _activeShader;
};
#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
#endif

View File

@@ -0,0 +1,484 @@
/* 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/scummsys.h"
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#include "backends/graphics/opengl/renderer3d.h"
#include "backends/graphics/opengl/pipelines/pipeline.h"
#include "common/system.h"
#include "common/textconsole.h"
namespace OpenGL {
static void setupRenderbufferStorage(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
if (samples > 1) {
glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
return;
}
#endif
glRenderbufferStorage(target, internalformat, width, height);
}
// This constructor must not depend on any existing GL context
Renderer3D::Renderer3D() :
_texture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, false),
_renderBuffers{0, 0, 0}, _frameBuffers{0, 0}, _renderToFrameBuffer(false),
_samples(0), _stackLevel(0), _inOverlay(false),
_pendingScreenChangeWidth(-1), _pendingScreenChangeHeight(-1) {
_texture.enableLinearFiltering(true);
_texture.setFlip(true);
}
void Renderer3D::destroy() {
while (_stackLevel) {
enter3D();
}
if (_frameBuffers[0]) {
// Check that we did allocated some framebuffer before destroying them
// This avoids to call glDeleteFramebuffers and glDeleteRenderbuffers
// on platforms not supporting it
glDeleteFramebuffers(ARRAYSIZE(_frameBuffers), _frameBuffers);
glDeleteRenderbuffers(ARRAYSIZE(_renderBuffers), _renderBuffers);
memset(_renderBuffers, 0, sizeof(_renderBuffers));
memset(_frameBuffers, 0, sizeof(_frameBuffers));
}
_texture.destroy();
}
void Renderer3D::initSize(uint w, uint h, int samples, bool renderToFrameBuffer) {
_samples = samples;
_renderToFrameBuffer = renderToFrameBuffer;
if (!renderToFrameBuffer) {
destroy();
_texture.setSize(0, 0);
return;
}
_texture.setSize(w, h);
recreate();
}
void Renderer3D::resize(uint w, uint h) {
assert(!_stackLevel);
if (!_renderToFrameBuffer) {
return;
}
if (_inOverlay) {
// While the (GUI) overlay is active, the game doesn't renders
// So, instead of loosing the contents of the FBO because of a resize,
// just delay it to when we close the GUI.
_pendingScreenChangeWidth = w;
_pendingScreenChangeHeight = h;
return;
}
_texture.setSize(w, h);
setup();
// Rebind the framebuffer
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
if (_frameBuffers[1]) {
// We are using multisampling
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[1]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[0]);
} else if (_frameBuffers[0])
#endif
{
// Draw on framebuffer if one was setup
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[0]);
}
}
void Renderer3D::recreate() {
destroy();
if (!_renderToFrameBuffer) {
// No framebuffer was requested
return;
}
// A 1x antialiasing is not an antialiasing
if (_samples > 1) {
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
if (!OpenGLContext.framebufferObjectMultisampleSupported) {
warning("The current OpenGL context does not support multisample framebuffer objects");
_samples = 0;
}
if (_samples > OpenGLContext.multisampleMaxSamples) {
warning("Requested anti-aliasing with '%d' samples, but the current OpenGL context supports '%d' samples at most",
_samples, OpenGLContext.multisampleMaxSamples);
_samples = OpenGLContext.multisampleMaxSamples;
}
#else
warning("multisample framebuffer objects support is not compiled in");
_samples = 0;
#endif
} else {
_samples = 0;
}
setup();
// Context got destroyed
_stackLevel = 0;
}
void Renderer3D::setup() {
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
const bool multiSample = _samples > 1;
#else
const bool multiSample = false;
#endif
const uint w = _texture.getLogicalWidth();
const uint h = _texture.getLogicalHeight();
if (!_texture.getGLTexture()) {
_texture.create();
}
glBindTexture(GL_TEXTURE_2D, 0);
if (!_frameBuffers[0]) {
glGenFramebuffers(multiSample ? 2 : 1, _frameBuffers);
glGenRenderbuffers(multiSample ? 3 : 2, _renderBuffers);
}
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[multiSample ? 1 : 0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture.getGLTexture(), 0);
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
if (multiSample) {
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[0]);
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[2]);
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, GL_RGBA8, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffers[2]);
}
#endif
#ifdef EMSCRIPTEN
// See https://www.khronos.org/registry/webgl/specs/latest/1.0/#FBO_ATTACHMENTS
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[0]);
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, GL_DEPTH_STENCIL, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[0]);
#else
if (OpenGLContext.packedDepthStencilSupported) {
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[0]);
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, GL_DEPTH24_STENCIL8, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[0]);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[0]);
} else {
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[0]);
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, OpenGLContext.OESDepth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[0]);
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[1]);
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, GL_STENCIL_INDEX8, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[1]);
}
#endif
glBindRenderbuffer(GL_RENDERBUFFER, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
error("Framebuffer is not complete! status: %d", status);
}
if (multiSample) {
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[1]);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
error("Target framebuffer is not complete! status: %d", status);
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Renderer3D::leave3D() {
#if !USE_FORCED_GLES2
if (OpenGLContext.type == kContextGL) {
// Save current state (only available on OpenGL)
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT |
GL_LIGHTING_BIT | GL_PIXEL_MODE_BIT | GL_SCISSOR_BIT |
GL_TEXTURE_BIT | GL_TRANSFORM_BIT | GL_VIEWPORT_BIT);
glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT | GL_CLIENT_VERTEX_ARRAY_BIT);
// prepare view
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glMatrixMode(GL_TEXTURE);
glPushMatrix();
} else
#endif
{
// Save context by ourselves
#define CTX_STATE(gl_param) _save ## gl_param = glIsEnabled(gl_param)
#define CTX_BOOLEAN(gl_param) glGetBooleanv(gl_param, &_save ## gl_param)
#define CTX_INTEGER(gl_param, count) glGetIntegerv(gl_param, _save ## gl_param)
CTX_STATE(GL_BLEND);
CTX_STATE(GL_CULL_FACE);
CTX_STATE(GL_DEPTH_TEST);
CTX_STATE(GL_DITHER);
CTX_STATE(GL_POLYGON_OFFSET_FILL);
CTX_STATE(GL_SCISSOR_TEST);
CTX_STATE(GL_STENCIL_TEST);
CTX_BOOLEAN(GL_DEPTH_WRITEMASK);
CTX_INTEGER(GL_BLEND_SRC_RGB, 1);
CTX_INTEGER(GL_BLEND_DST_RGB, 1);
CTX_INTEGER(GL_BLEND_SRC_ALPHA, 1);
CTX_INTEGER(GL_BLEND_DST_ALPHA, 1);
CTX_INTEGER(GL_SCISSOR_BOX, 4);
CTX_INTEGER(GL_VIEWPORT, 4);
#undef CTX_INTEGER
#undef CTX_BOOLEAN
#undef CTX_STATE
}
_stackLevel++;
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
if (_frameBuffers[1]) {
// Frambuffer blit is impacted by scissor test, disable it
glDisable(GL_SCISSOR_TEST);
// We are using multisampling
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[0]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[1]);
const uint w = _texture.getLogicalWidth();
const uint h = _texture.getLogicalHeight();
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
} else if (_frameBuffers[0])
#endif
{
// Don't mess with the framebuffer if one was setup
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void Renderer3D::enter3D() {
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
if (_frameBuffers[1]) {
// We are using multisampling
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[1]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[0]);
} else if (_frameBuffers[0])
#endif
{
// Draw on framebuffer if one was setup
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[0]);
}
glBindTexture(GL_TEXTURE_2D, 0);
Pipeline::disable();
if (_stackLevel) {
#if !USE_FORCED_GLES2
if (OpenGLContext.type == kContextGL) {
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopClientAttrib();
glPopAttrib();
} else
#endif
{
#define CTX_STATE(gl_param) _save ## gl_param ? glEnable(gl_param) : glDisable(gl_param)
CTX_STATE(GL_BLEND);
CTX_STATE(GL_CULL_FACE);
CTX_STATE(GL_DEPTH_TEST);
CTX_STATE(GL_DITHER);
CTX_STATE(GL_POLYGON_OFFSET_FILL);
CTX_STATE(GL_SCISSOR_TEST);
CTX_STATE(GL_STENCIL_TEST);
glDepthMask(_saveGL_DEPTH_WRITEMASK);
glBlendFuncSeparate(_saveGL_BLEND_SRC_RGB[0], _saveGL_BLEND_DST_RGB[0],
_saveGL_BLEND_SRC_ALPHA[0], _saveGL_BLEND_DST_ALPHA[0]);
glScissor(_saveGL_SCISSOR_BOX[0], _saveGL_SCISSOR_BOX[1],
_saveGL_SCISSOR_BOX[2], _saveGL_SCISSOR_BOX[3]);
glViewport(_saveGL_VIEWPORT[0], _saveGL_VIEWPORT[1],
_saveGL_VIEWPORT[2], _saveGL_VIEWPORT[3]);
#undef CTX_STATE
}
_stackLevel--;
} else {
// 3D engine just starts, make sure the state is clean
glDisable(GL_BLEND);
if (OpenGLContext.imagingSupported) {
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ZERO);
}
glDisable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glDisable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_DITHER);
glDisable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.f, 0.f);
glDisable(GL_SCISSOR_TEST);
glScissor(0, 0, g_system->getWidth(), g_system->getHeight());
glDisable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 0, -1u);
glStencilMask(-1u);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glLineWidth(1.f);
glViewport(0, 0, g_system->getWidth(), g_system->getHeight());
#if !USE_FORCED_GLES2
if (OpenGLContext.type == kContextGL) {
glDisable(GL_ALPHA_TEST);
glAlphaFunc(GL_ALWAYS, 0);
glDisable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glDisable(GL_FOG);
glDisable(GL_LIGHTING);
glDisable(GL_LINE_SMOOTH);
glEnable(GL_MULTISAMPLE);
glDisable(GL_NORMALIZE);
glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_POLYGON_STIPPLE);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_TEXTURE_1D);
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_3D);
glDisable(GL_TEXTURE_CUBE_MAP);
glDisable(GL_TEXTURE_GEN_Q);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_EDGE_FLAG_ARRAY);
glDisableClientState(GL_FOG_COORD_ARRAY);
glDisableClientState(GL_INDEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
// The others targets are not modified by engines
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);
glLogicOp(GL_COPY);
glPointSize(1.f);
glShadeModel(GL_SMOOTH);
}
#endif
}
}
void Renderer3D::presentBuffer() {
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
if (!_frameBuffers[1]) {
// We are not using multisampling: contents are readily available
// The engine just has to read from the FBO or the backbuffer
return;
}
assert(_stackLevel == 0);
bool saveScissorTest = glIsEnabled(GL_SCISSOR_TEST);
// Frambuffer blit is impacted by scissor test, disable it
glDisable(GL_SCISSOR_TEST);
// Swap the framebuffers and blit
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[0]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[1]);
const uint w = _texture.getLogicalWidth();
const uint h = _texture.getLogicalHeight();
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Put back things as they were before
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[1]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[0]);
saveScissorTest ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST);
#endif
}
void Renderer3D::showOverlay(uint w, uint h) {
_inOverlay = true;
if (_frameBuffers[0]) {
// We have a framebuffer: the texture already contains an image
return;
}
_texture.create();
_texture.setSize(w, h);
Graphics::Surface background;
background.create(w, h, Texture::getRGBAPixelFormat());
glReadPixels(0, 0, background.w, background.h, GL_RGBA, GL_UNSIGNED_BYTE, background.getPixels());
_texture.updateArea(Common::Rect(w, h), background);
background.free();
}
void Renderer3D::hideOverlay() {
_inOverlay = false;
if (!_frameBuffers[0]) {
// We don't have a framebuffer: destroy the texture we used to store the background
_texture.destroy();
return;
}
// We have a framebuffer: resize the screen if we have a pending change
if (_pendingScreenChangeWidth >= 0 && _pendingScreenChangeHeight >= 0) {
resize(_pendingScreenChangeWidth, _pendingScreenChangeHeight);
_pendingScreenChangeWidth = -1;
_pendingScreenChangeHeight = -1;
}
}
} // End of namespace OpenGL
#endif

View File

@@ -0,0 +1,99 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_RENDERER3D_H
#define BACKENDS_GRAPHICS_OPENGL_RENDERER3D_H
#include "common/scummsys.h"
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#include "graphics/opengl/texture.h"
namespace OpenGL {
class Renderer3D {
public:
Renderer3D();
~Renderer3D() { destroy(); }
void initSize(uint w, uint h, int samples, bool renderToFrameBuffer);
void resize(uint w, uint h);
void recreate();
void destroy();
void leave3D();
void enter3D();
void presentBuffer();
void showOverlay(uint w, uint h);
void hideOverlay();
const Texture &getGLTexture() const { return _texture; }
bool hasTexture() const { return _texture.getGLTexture() != 0; }
void setRotation(Common::RotationMode rotation) { _texture.setRotation(rotation); }
protected:
void setup();
int _stackLevel;
bool _inOverlay;
int _pendingScreenChangeWidth;
int _pendingScreenChangeHeight;
bool _renderToFrameBuffer;
int _samples;
Texture _texture;
GLuint _renderBuffers[3];
GLuint _frameBuffers[2];
#define CTX_STATE(gl_param) GLboolean _save ## gl_param = false
#define CTX_BOOLEAN(gl_param) GLboolean _save ## gl_param = false
#define CTX_INTEGER(gl_param, count) GLint _save ## gl_param[count] = { 0 }
CTX_STATE(GL_BLEND);
CTX_STATE(GL_CULL_FACE);
CTX_STATE(GL_DEPTH_TEST);
CTX_STATE(GL_DITHER);
CTX_STATE(GL_POLYGON_OFFSET_FILL);
CTX_STATE(GL_SCISSOR_TEST);
CTX_STATE(GL_STENCIL_TEST);
CTX_BOOLEAN(GL_DEPTH_WRITEMASK);
CTX_INTEGER(GL_BLEND_SRC_RGB, 1);
CTX_INTEGER(GL_BLEND_DST_RGB, 1);
CTX_INTEGER(GL_BLEND_SRC_ALPHA, 1);
CTX_INTEGER(GL_BLEND_DST_ALPHA, 1);
CTX_INTEGER(GL_SCISSOR_BOX, 4);
CTX_INTEGER(GL_VIEWPORT, 4);
#undef CTX_INTEGER
#undef CTX_BOOLEAN
#undef CTX_STATE
};
} // End of namespace OpenGL
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#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 "backends/graphics/opengl/shader.h"
#include "graphics/opengl/debug.h"
#if !USE_FORCED_GLES
namespace Common {
DECLARE_SINGLETON(OpenGL::ShaderManager);
}
namespace OpenGL {
namespace {
#pragma mark - Builtin Shader Sources -
const char *const g_defaultShaderAttributes[] = {
"position", "texCoordIn", "blendColorIn", nullptr
};
const char *const g_defaultVertexShader =
"attribute vec4 position;\n"
"attribute vec2 texCoordIn;\n"
"attribute vec4 blendColorIn;\n"
"\n"
"uniform mat4 projection;\n"
"\n"
"varying vec2 texCoord;\n"
"varying vec4 blendColor;\n"
"\n"
"void main(void) {\n"
"\ttexCoord = texCoordIn;\n"
"\tblendColor = blendColorIn;\n"
"\tgl_Position = projection * position;\n"
"}\n";
const char *const g_defaultFragmentShader =
"varying vec2 texCoord;\n"
"varying vec4 blendColor;\n"
"\n"
"uniform sampler2D shaderTexture;\n"
"\n"
"void main(void) {\n"
"\tgl_FragColor = blendColor * texture2D(shaderTexture, texCoord);\n"
"}\n";
const char *const g_lookUpFragmentShader =
"varying vec2 texCoord;\n"
"varying vec4 blendColor;\n"
"\n"
"uniform sampler2D shaderTexture;\n"
"uniform sampler2D palette;\n"
"\n"
"const float scaleFactor = 255.0 / 256.0;\n"
"const float offsetFactor = 1.0 / (2.0 * 256.0);\n"
"\n"
"void main(void) {\n"
"\tvec4 index = texture2D(shaderTexture, texCoord);\n"
"\tgl_FragColor = blendColor * texture2D(palette, vec2(index.a * scaleFactor + offsetFactor, 0.0));\n"
"}\n";
} // End of anonymous namespace
ShaderManager::ShaderManager() {
for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
_builtIn[i] = nullptr;
}
}
ShaderManager::~ShaderManager() {
for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
delete _builtIn[i];
}
}
void ShaderManager::notifyDestroy() {
for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
delete _builtIn[i];
_builtIn[i] = nullptr;
}
}
void ShaderManager::notifyCreate() {
// Ensure everything is destroyed
notifyDestroy();
_builtIn[kDefault] = Shader::fromStrings("default", g_defaultVertexShader, g_defaultFragmentShader, g_defaultShaderAttributes, 110);
_builtIn[kCLUT8LookUp] = Shader::fromStrings("clut8lookup", g_defaultVertexShader, g_lookUpFragmentShader, g_defaultShaderAttributes, 110);
_builtIn[kCLUT8LookUp]->setUniform("palette", 1);
for (uint i = 0; i < kMaxUsages; ++i) {
_builtIn[i]->setUniform("shaderTexture", 0);
}
}
Shader *ShaderManager::query(ShaderUsage shader) const {
if (shader == kMaxUsages) {
warning("OpenGL: ShaderManager::query used with kMaxUsages");
return nullptr;
}
assert(_builtIn[shader]);
return _builtIn[shader]->clone();
}
} // End of namespace OpenGL
#endif // !USE_FORCED_GLES

View File

@@ -0,0 +1,79 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_SHADER_H
#define BACKENDS_GRAPHICS_OPENGL_SHADER_H
#include "graphics/opengl/system_headers.h"
#if !USE_FORCED_GLES
#include "common/singleton.h"
#include "graphics/opengl/shader.h"
namespace OpenGL {
class ShaderManager : public Common::Singleton<ShaderManager> {
public:
enum ShaderUsage {
/** Default shader implementing the GL fixed-function pipeline. */
kDefault = 0,
/** CLUT8 look up shader. */
kCLUT8LookUp,
/** Number of built-in shaders. Should not be used for query. */
kMaxUsages
};
/**
* Notify shader manager about context destruction.
*/
void notifyDestroy();
/**
* Notify shader manager about context creation.
*/
void notifyCreate();
/**
* Query a built-in shader.
* Shader returned must be destroyed by caller.
*/
Shader *query(ShaderUsage shader) const;
private:
friend class Common::Singleton<SingletonBaseType>;
ShaderManager();
~ShaderManager();
Shader *_builtIn[kMaxUsages];
};
} // End of namespace OpenGL
/** Shortcut for accessing the font manager. */
#define ShaderMan (OpenGL::ShaderManager::instance())
#endif // !USE_FORCED_GLES
#endif

View File

@@ -0,0 +1,652 @@
/* 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 "backends/graphics/opengl/texture.h"
#include "backends/graphics/opengl/shader.h"
#include "backends/graphics/opengl/pipelines/pipeline.h"
#include "backends/graphics/opengl/pipelines/clut8.h"
#include "backends/graphics/opengl/framebuffer.h"
#include "graphics/opengl/debug.h"
#include "common/algorithm.h"
#include "common/endian.h"
#include "common/rect.h"
#include "common/textconsole.h"
#include "graphics/blit.h"
#ifdef USE_SCALERS
#include "graphics/scalerplugin.h"
#endif
namespace OpenGL {
//
// Surface
//
Surface::Surface()
: _allDirty(false), _dirtyArea() {
}
void Surface::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) {
Graphics::Surface *dstSurf = getSurface();
assert(x + w <= (uint)dstSurf->w);
assert(y + h <= (uint)dstSurf->h);
addDirtyArea(Common::Rect(x, y, x + w, y + h));
const byte *src = (const byte *)srcPtr;
byte *dst = (byte *)dstSurf->getBasePtr(x, y);
const uint pitch = dstSurf->pitch;
const uint bytesPerPixel = dstSurf->format.bytesPerPixel;
if (srcPitch == pitch && x == 0 && w == (uint)dstSurf->w) {
memcpy(dst, src, h * pitch);
} else {
while (h-- > 0) {
memcpy(dst, src, w * bytesPerPixel);
dst += pitch;
src += srcPitch;
}
}
}
void Surface::fill(uint32 color) {
Graphics::Surface *dst = getSurface();
dst->fillRect(Common::Rect(dst->w, dst->h), color);
flagDirty();
}
void Surface::fill(const Common::Rect &r, uint32 color) {
Graphics::Surface *dst = getSurface();
dst->fillRect(r, color);
addDirtyArea(r);
}
void Surface::addDirtyArea(const Common::Rect &r) {
// *sigh* Common::Rect::extend behaves unexpected whenever one of the two
// parameters is an empty rect. Thus, we check whether the current dirty
// area is valid. In case it is not we simply use the parameters as new
// dirty area. Otherwise, we simply call extend.
if (_dirtyArea.isEmpty()) {
_dirtyArea = r;
} else {
_dirtyArea.extend(r);
}
}
Common::Rect Surface::getDirtyArea() const {
if (_allDirty) {
return Common::Rect(getWidth(), getHeight());
} else {
return _dirtyArea;
}
}
//
// Surface implementations
//
TextureSurface::TextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
: Surface(), _format(format), _glTexture(glIntFormat, glFormat, glType),
_textureData(), _userPixelData() {
}
TextureSurface::~TextureSurface() {
_textureData.free();
}
void TextureSurface::destroy() {
_glTexture.destroy();
}
void TextureSurface::recreate() {
_glTexture.create();
// In case image date exists assure it will be completely refreshed next
// time.
if (_textureData.getPixels()) {
flagDirty();
}
}
void TextureSurface::enableLinearFiltering(bool enable) {
_glTexture.enableLinearFiltering(enable);
}
void TextureSurface::setRotation(Common::RotationMode rotation) {
_glTexture.setRotation(rotation);
}
void TextureSurface::allocate(uint width, uint height) {
// Assure the texture can contain our user data.
_glTexture.setSize(width, height);
// In case the needed texture dimension changed we will reinitialize the
// texture data buffer.
if (_glTexture.getWidth() != (uint)_textureData.w || _glTexture.getHeight() != (uint)_textureData.h) {
// Create a buffer for the texture data.
_textureData.create(_glTexture.getWidth(), _glTexture.getHeight(), _format);
}
// Create a sub-buffer for raw access.
_userPixelData = _textureData.getSubArea(Common::Rect(width, height));
// The whole texture is dirty after we changed the size. This fixes
// multiple texture size changes without any actual update in between.
// Without this we might try to write a too big texture into the GL
// texture.
flagDirty();
}
void TextureSurface::updateGLTexture() {
if (!isDirty()) {
return;
}
Common::Rect dirtyArea = getDirtyArea();
updateGLTexture(dirtyArea);
}
void TextureSurface::updateGLTexture(Common::Rect &dirtyArea) {
// In case we use linear filtering we might need to duplicate the last
// pixel row/column to avoid glitches with filtering.
if (_glTexture.isLinearFilteringEnabled()) {
if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) {
uint height = dirtyArea.height();
const byte *src = (const byte *)_textureData.getBasePtr(_userPixelData.w - 1, dirtyArea.top);
byte *dst = (byte *)_textureData.getBasePtr(_userPixelData.w, dirtyArea.top);
while (height-- > 0) {
memcpy(dst, src, _textureData.format.bytesPerPixel);
dst += _textureData.pitch;
src += _textureData.pitch;
}
// Extend the dirty area.
++dirtyArea.right;
}
if (dirtyArea.bottom == _userPixelData.h && _userPixelData.h != _textureData.h) {
const byte *src = (const byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h - 1);
byte *dst = (byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h);
memcpy(dst, src, dirtyArea.width() * _textureData.format.bytesPerPixel);
// Extend the dirty area.
++dirtyArea.bottom;
}
}
_glTexture.updateArea(dirtyArea, _textureData);
// We should have handled everything, thus not dirty anymore.
clearDirty();
}
FakeTextureSurface::FakeTextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat)
: TextureSurface(glIntFormat, glFormat, glType, format),
_fakeFormat(fakeFormat),
_rgbData(),
_blitFunc(nullptr),
_palette(nullptr),
_mask(nullptr) {
if (_fakeFormat.isCLUT8()) {
_palette = new uint32[256]();
} else {
_blitFunc = Graphics::getFastBlitFunc(format, fakeFormat);
}
}
FakeTextureSurface::~FakeTextureSurface() {
delete[] _palette;
delete[] _mask;
_palette = nullptr;
_rgbData.free();
}
void FakeTextureSurface::allocate(uint width, uint height) {
TextureSurface::allocate(width, height);
// We only need to reinitialize our surface when the output size
// changed.
if (width == (uint)_rgbData.w && height == (uint)_rgbData.h) {
return;
}
_rgbData.create(width, height, getFormat());
}
void FakeTextureSurface::setMask(const byte *mask) {
if (mask) {
const uint numPixels = _rgbData.w * _rgbData.h;
if (!_mask)
_mask = new byte[numPixels];
memcpy(_mask, mask, numPixels);
} else {
delete[] _mask;
_mask = nullptr;
}
flagDirty();
}
void FakeTextureSurface::setColorKey(uint colorKey) {
if (!_palette)
return;
// The key color is set to black so the color value is pre-multiplied with the alpha value
// to avoid color fringes due to filtering.
// Erasing the color data is not a problem as the palette is always fully re-initialized
// before setting the key color.
uint32 *palette = _palette + colorKey;
*palette = 0;
// A palette changes means we need to refresh the whole surface.
flagDirty();
}
void FakeTextureSurface::setPalette(uint start, uint colors, const byte *palData) {
if (!_palette)
return;
Graphics::convertPaletteToMap(_palette + start, palData, colors, _format);
// A palette changes means we need to refresh the whole surface.
flagDirty();
}
void FakeTextureSurface::updateGLTexture() {
if (!isDirty()) {
return;
}
// Convert color space.
Graphics::Surface *outSurf = TextureSurface::getSurface();
const Common::Rect dirtyArea = getDirtyArea();
byte *dst = (byte *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top);
const byte *src = (const byte *)_rgbData.getBasePtr(dirtyArea.left, dirtyArea.top);
applyPaletteAndMask(dst, src, outSurf->pitch, _rgbData.pitch, _rgbData.w, dirtyArea, outSurf->format, _rgbData.format);
// Do generic handling of updating the texture.
TextureSurface::updateGLTexture();
}
void FakeTextureSurface::applyPaletteAndMask(byte *dst, const byte *src, uint dstPitch, uint srcPitch, uint srcWidth, const Common::Rect &dirtyArea, const Graphics::PixelFormat &dstFormat, const Graphics::PixelFormat &srcFormat) const {
if (_palette) {
Graphics::crossBlitMap(dst, src, dstPitch, srcPitch, dirtyArea.width(), dirtyArea.height(), dstFormat.bytesPerPixel, _palette);
} else if (_blitFunc) {
_blitFunc(dst, src, dstPitch, srcPitch, dirtyArea.width(), dirtyArea.height());
} else {
Graphics::crossBlit(dst, src, dstPitch, srcPitch, dirtyArea.width(), dirtyArea.height(), dstFormat, srcFormat);
}
if (_mask) {
uint maskPitch = srcWidth;
uint dirtyWidth = dirtyArea.width();
byte destBPP = dstFormat.bytesPerPixel;
const byte *maskRowStart = (_mask + dirtyArea.top * maskPitch + dirtyArea.left);
byte *dstRowStart = dst;
for (uint y = dirtyArea.top; y < static_cast<uint>(dirtyArea.bottom); y++) {
if (destBPP == 2) {
for (uint x = 0; x < dirtyWidth; x++) {
if (!maskRowStart[x])
reinterpret_cast<uint16 *>(dstRowStart)[x] = 0;
}
} else if (destBPP == 4) {
for (uint x = 0; x < dirtyWidth; x++) {
if (!maskRowStart[x])
reinterpret_cast<uint32 *>(dstRowStart)[x] = 0;
}
}
dstRowStart += dstPitch;
maskRowStart += maskPitch;
}
}
}
TextureSurfaceRGB555::TextureSurfaceRGB555()
: FakeTextureSurface(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) {
}
void TextureSurfaceRGB555::updateGLTexture() {
if (!isDirty()) {
return;
}
// Convert color space.
Graphics::Surface *outSurf = TextureSurface::getSurface();
const Common::Rect dirtyArea = getDirtyArea();
uint16 *dst = (uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top);
const uint dstAdd = outSurf->pitch - 2 * dirtyArea.width();
const uint16 *src = (const uint16 *)_rgbData.getBasePtr(dirtyArea.left, dirtyArea.top);
const uint srcAdd = _rgbData.pitch - 2 * dirtyArea.width();
for (int height = dirtyArea.height(); height > 0; --height) {
for (int width = dirtyArea.width(); width > 0; --width) {
const uint16 color = *src++;
*dst++ = ((color & 0x7C00) << 1) // R
| (((color & 0x03E0) << 1) | ((color & 0x0200) >> 4)) // G
| (color & 0x001F); // B
}
src = (const uint16 *)((const byte *)src + srcAdd);
dst = (uint16 *)((byte *)dst + dstAdd);
}
// Do generic handling of updating the texture.
TextureSurface::updateGLTexture();
}
#ifdef USE_SCALERS
ScaledTextureSurface::ScaledTextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat)
: FakeTextureSurface(glIntFormat, glFormat, glType, format, fakeFormat), _convData(nullptr), _scaler(nullptr), _scalerIndex(0), _scaleFactor(1), _extraPixels(0) {
}
ScaledTextureSurface::~ScaledTextureSurface() {
delete _scaler;
if (_convData) {
_convData->free();
delete _convData;
}
}
void ScaledTextureSurface::allocate(uint width, uint height) {
TextureSurface::allocate(width * _scaleFactor, height * _scaleFactor);
// We only need to reinitialize our surface when the output size
// changed.
if (width != (uint)_rgbData.w || height != (uint)_rgbData.h) {
_rgbData.create(width, height, _fakeFormat);
}
if (_format != _fakeFormat || _extraPixels != 0) {
if (!_convData)
_convData = new Graphics::Surface();
_convData->create(width + (_extraPixels * 2), height + (_extraPixels * 2), _format);
} else if (_convData) {
_convData->free();
delete _convData;
_convData = nullptr;
}
}
void ScaledTextureSurface::updateGLTexture() {
if (!isDirty()) {
return;
}
// Convert color space.
Graphics::Surface *outSurf = TextureSurface::getSurface();
Common::Rect dirtyArea = getDirtyArea();
// Extend the dirty region for scalers
// that "smear" the screen, e.g. 2xSAI
dirtyArea.grow(_extraPixels);
dirtyArea.clip(Common::Rect(0, 0, _rgbData.w, _rgbData.h));
const byte *src = (const byte *)_rgbData.getBasePtr(dirtyArea.left, dirtyArea.top);
uint srcPitch = _rgbData.pitch;
byte *dst;
uint dstPitch;
if (_convData) {
dst = (byte *)_convData->getBasePtr(dirtyArea.left + _extraPixels, dirtyArea.top + _extraPixels);
dstPitch = _convData->pitch;
applyPaletteAndMask(dst, src, dstPitch, srcPitch, _rgbData.w, dirtyArea, _convData->format, _rgbData.format);
src = dst;
srcPitch = dstPitch;
}
dst = (byte *)outSurf->getBasePtr(dirtyArea.left * _scaleFactor, dirtyArea.top * _scaleFactor);
dstPitch = outSurf->pitch;
if (_scaler && (uint)dirtyArea.height() >= _extraPixels) {
_scaler->scale(src, srcPitch, dst, dstPitch, dirtyArea.width(), dirtyArea.height(), dirtyArea.left, dirtyArea.top);
} else {
Graphics::scaleBlit(dst, src, dstPitch, srcPitch,
dirtyArea.width() * _scaleFactor, dirtyArea.height() * _scaleFactor,
dirtyArea.width(), dirtyArea.height(), outSurf->format);
}
dirtyArea.left *= _scaleFactor;
dirtyArea.right *= _scaleFactor;
dirtyArea.top *= _scaleFactor;
dirtyArea.bottom *= _scaleFactor;
// Do generic handling of updating the texture.
TextureSurface::updateGLTexture(dirtyArea);
}
void ScaledTextureSurface::setScaler(uint scalerIndex, int scaleFactor) {
const PluginList &scalerPlugins = ScalerMan.getPlugins();
const ScalerPluginObject &scalerPlugin = scalerPlugins[scalerIndex]->get<ScalerPluginObject>();
// If the scalerIndex has changed, change scaler plugins
if (_scaler && scalerIndex != _scalerIndex) {
delete _scaler;
_scaler = nullptr;
}
if (!_scaler) {
_scaler = scalerPlugin.createInstance(_format);
}
_scaler->setFactor(scaleFactor);
_scalerIndex = scalerIndex;
_scaleFactor = _scaler->getFactor();
_extraPixels = scalerPlugin.extraPixels();
}
#endif
#if !USE_FORCED_GLES
// _clut8Texture needs 8 bits internal precision, otherwise graphics glitches
// can occur. GL_ALPHA does not have any internal precision requirements.
// However, in practice (according to fuzzie) it's 8bit. If we run into
// problems, we need to switch to GL_R8 and GL_RED, but that is only supported
// for ARB_texture_rg and GLES3+ (EXT_rexture_rg does not support GL_R8).
TextureSurfaceCLUT8GPU::TextureSurfaceCLUT8GPU()
: _clut8Texture(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE),
_paletteTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE),
_target(new TextureTarget()), _clut8Pipeline(new CLUT8LookUpPipeline()),
_clut8Vertices(), _clut8Data(), _userPixelData(), _palette(),
_paletteDirty(false) {
// Allocate space for 256 colors.
_paletteTexture.setSize(256, 1);
// Setup pipeline.
_clut8Pipeline->setFramebuffer(_target);
_clut8Pipeline->setPaletteTexture(&_paletteTexture);
_clut8Pipeline->setColor(1.0f, 1.0f, 1.0f, 1.0f);
}
TextureSurfaceCLUT8GPU::~TextureSurfaceCLUT8GPU() {
delete _clut8Pipeline;
delete _target;
_clut8Data.free();
}
void TextureSurfaceCLUT8GPU::destroy() {
_clut8Texture.destroy();
_paletteTexture.destroy();
_target->destroy();
delete _clut8Pipeline;
_clut8Pipeline = nullptr;
}
void TextureSurfaceCLUT8GPU::recreate() {
_clut8Texture.create();
_paletteTexture.create();
_target->create();
// In case image date exists assure it will be completely refreshed next
// time.
if (_clut8Data.getPixels()) {
flagDirty();
_paletteDirty = true;
}
if (_clut8Pipeline == nullptr) {
_clut8Pipeline = new CLUT8LookUpPipeline();
// Setup pipeline.
_clut8Pipeline->setFramebuffer(_target);
_clut8Pipeline->setPaletteTexture(&_paletteTexture);
_clut8Pipeline->setColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
void TextureSurfaceCLUT8GPU::enableLinearFiltering(bool enable) {
_target->getTexture()->enableLinearFiltering(enable);
}
void TextureSurfaceCLUT8GPU::setRotation(Common::RotationMode rotation) {
_target->getTexture()->setRotation(rotation);
}
void TextureSurfaceCLUT8GPU::allocate(uint width, uint height) {
// Assure the texture can contain our user data.
_clut8Texture.setSize(width, height);
_target->setSize(width, height);
// In case the needed texture dimension changed we will reinitialize the
// texture data buffer.
if (_clut8Texture.getWidth() != (uint)_clut8Data.w || _clut8Texture.getHeight() != (uint)_clut8Data.h) {
// Create a buffer for the texture data.
_clut8Data.create(_clut8Texture.getWidth(), _clut8Texture.getHeight(), Graphics::PixelFormat::createFormatCLUT8());
}
// Create a sub-buffer for raw access.
_userPixelData = _clut8Data.getSubArea(Common::Rect(width, height));
// Setup structures for internal rendering to _glTexture.
_clut8Vertices[0] = 0;
_clut8Vertices[1] = 0;
_clut8Vertices[2] = width;
_clut8Vertices[3] = 0;
_clut8Vertices[4] = 0;
_clut8Vertices[5] = height;
_clut8Vertices[6] = width;
_clut8Vertices[7] = height;
// The whole texture is dirty after we changed the size. This fixes
// multiple texture size changes without any actual update in between.
// Without this we might try to write a too big texture into the GL
// texture.
flagDirty();
}
Graphics::PixelFormat TextureSurfaceCLUT8GPU::getFormat() const {
return Graphics::PixelFormat::createFormatCLUT8();
}
void TextureSurfaceCLUT8GPU::setColorKey(uint colorKey) {
// The key color is set to black so the color value is pre-multiplied with the alpha value
// to avoid color fringes due to filtering.
// Erasing the color data is not a problem as the palette is always fully re-initialized
// before setting the key color.
_palette[colorKey * 4 ] = 0x00;
_palette[colorKey * 4 + 1] = 0x00;
_palette[colorKey * 4 + 2] = 0x00;
_palette[colorKey * 4 + 3] = 0x00;
_paletteDirty = true;
}
void TextureSurfaceCLUT8GPU::setPalette(uint start, uint colors, const byte *palData) {
byte *dst = _palette + start * 4;
while (colors-- > 0) {
memcpy(dst, palData, 3);
dst[3] = 0xFF;
dst += 4;
palData += 3;
}
_paletteDirty = true;
}
const Texture &TextureSurfaceCLUT8GPU::getGLTexture() const {
return *_target->getTexture();
}
void TextureSurfaceCLUT8GPU::updateGLTexture() {
const bool needLookUp = Surface::isDirty() || _paletteDirty;
// Update CLUT8 texture if necessary.
if (Surface::isDirty()) {
_clut8Texture.updateArea(getDirtyArea(), _clut8Data);
clearDirty();
}
// Update palette if necessary.
if (_paletteDirty) {
Graphics::Surface palSurface;
palSurface.init(256, 1, 256, _palette, OpenGL::Texture::getRGBAPixelFormat());
_paletteTexture.updateArea(Common::Rect(256, 1), palSurface);
_paletteDirty = false;
}
// In case any data changed, do color look up and store result in _target.
if (needLookUp) {
lookUpColors();
}
}
void TextureSurfaceCLUT8GPU::lookUpColors() {
// Setup pipeline to do color look up.
_clut8Pipeline->activate();
// Do color look up.
_clut8Pipeline->drawTexture(_clut8Texture, _clut8Vertices);
_clut8Pipeline->deactivate();
}
#endif // !USE_FORCED_GLES
} // End of namespace OpenGL

View File

@@ -0,0 +1,341 @@
/* 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 BACKENDS_GRAPHICS_OPENGL_TEXTURE_H
#define BACKENDS_GRAPHICS_OPENGL_TEXTURE_H
#include "graphics/opengl/system_headers.h"
#include "graphics/opengl/context.h"
#include "graphics/opengl/texture.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "graphics/blit.h"
#include "common/rect.h"
#include "common/rotationmode.h"
class Scaler;
namespace OpenGL {
/**
* Interface for OpenGL implementations of a 2D surface.
*/
class Surface {
public:
Surface();
virtual ~Surface() {}
/**
* Destroy OpenGL description of surface.
*/
virtual void destroy() = 0;
/**
* Recreate OpenGL description of surface.
*/
virtual void recreate() = 0;
/**
* Enable or disable linear texture filtering.
*
* @param enable true to enable and false to disable.
*/
virtual void enableLinearFiltering(bool enable) = 0;
/**
* Sets the rotate parameter of the texture
*
* @param rotation How to rotate the texture
*/
virtual void setRotation(Common::RotationMode rotation) = 0;
/**
* Allocate storage for surface.
*
* @param width The desired logical width.
* @param height The desired logical height.
*/
virtual void allocate(uint width, uint height) = 0;
/**
* Assign a mask to the surface, where a byte value of 0 is black with 0 alpha and 1 is the normal color.
*
* @param mask The mask data.
*/
virtual void setMask(const byte *mask) {}
/**
* Copy image data to the surface.
*
* The format of the input data needs to match the format returned by
* getFormat.
*
* @param x X coordinate of upper left corner to copy data to.
* @param y Y coordinate of upper left corner to copy data to.
* @param w Width of the image data to copy.
* @param h Height of the image data to copy.
* @param src Pointer to image data.
* @param srcPitch The number of bytes in a row of the image data.
*/
void copyRectToTexture(uint x, uint y, uint w, uint h, const void *src, uint srcPitch);
/**
* Fill the surface with a fixed color.
*
* @param color Color value in format returned by getFormat.
*/
void fill(uint32 color);
void fill(const Common::Rect &r, uint32 color);
void flagDirty() { _allDirty = true; }
virtual bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); }
virtual uint getWidth() const = 0;
virtual uint getHeight() const = 0;
/**
* @return The logical format of the texture data.
*/
virtual Graphics::PixelFormat getFormat() const = 0;
virtual Graphics::Surface *getSurface() = 0;
virtual const Graphics::Surface *getSurface() const = 0;
/**
* @return Whether the surface is having a palette.
*/
virtual bool hasPalette() const { return false; }
/**
* Set color key for paletted textures.
*
* This needs to be called after any palette update affecting the color
* key. Calling this multiple times will result in multiple color indices
* to be treated as color keys.
*/
virtual void setColorKey(uint colorKey) {}
virtual void setPalette(uint start, uint colors, const byte *palData) {}
virtual void setScaler(uint scalerIndex, int scaleFactor) {}
/**
* Update underlying OpenGL texture to reflect current state.
*/
virtual void updateGLTexture() = 0;
/**
* Obtain underlying OpenGL texture.
*/
virtual const Texture &getGLTexture() const = 0;
protected:
void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); }
void addDirtyArea(const Common::Rect &r);
Common::Rect getDirtyArea() const;
private:
bool _allDirty;
Common::Rect _dirtyArea;
};
/**
* An OpenGL texture wrapper. It automatically takes care of all OpenGL
* texture handling issues and also provides access to the texture data.
*/
class TextureSurface : public Surface {
public:
/**
* Create a new texture with the specific internal format.
*
* @param glIntFormat The internal format to use.
* @param glFormat The input format.
* @param glType The input type.
* @param format The format used for the texture input.
*/
TextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
~TextureSurface() override;
void destroy() override;
void recreate() override;
void enableLinearFiltering(bool enable) override;
void setRotation(Common::RotationMode rotation) override;
void allocate(uint width, uint height) override;
uint getWidth() const override { return _userPixelData.w; }
uint getHeight() const override { return _userPixelData.h; }
/**
* @return The logical format of the texture data.
*/
Graphics::PixelFormat getFormat() const override { return _format; }
Graphics::Surface *getSurface() override { return &_userPixelData; }
const Graphics::Surface *getSurface() const override { return &_userPixelData; }
void updateGLTexture() override;
const Texture &getGLTexture() const override { return _glTexture; }
protected:
const Graphics::PixelFormat _format;
void updateGLTexture(Common::Rect &dirtyArea);
private:
Texture _glTexture;
Graphics::Surface _textureData;
Graphics::Surface _userPixelData;
};
class FakeTextureSurface : public TextureSurface {
public:
FakeTextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat);
~FakeTextureSurface() override;
void allocate(uint width, uint height) override;
void setMask(const byte *mask) override;
Graphics::PixelFormat getFormat() const override { return _fakeFormat; }
bool hasPalette() const override { return (_palette != nullptr); }
void setColorKey(uint colorKey) override;
void setPalette(uint start, uint colors, const byte *palData) override;
Graphics::Surface *getSurface() override { return &_rgbData; }
const Graphics::Surface *getSurface() const override { return &_rgbData; }
void updateGLTexture() override;
protected:
void applyPaletteAndMask(byte *dst, const byte *src, uint dstPitch, uint srcPitch, uint srcWidth, const Common::Rect &dirtyArea, const Graphics::PixelFormat &dstFormat, const Graphics::PixelFormat &srcFormat) const;
Graphics::Surface _rgbData;
Graphics::PixelFormat _fakeFormat;
Graphics::FastBlitFunc _blitFunc;
uint32 *_palette;
uint8 *_mask;
};
class TextureSurfaceRGB555 : public FakeTextureSurface {
public:
TextureSurfaceRGB555();
~TextureSurfaceRGB555() override {}
void updateGLTexture() override;
};
#ifdef USE_SCALERS
class ScaledTextureSurface : public FakeTextureSurface {
public:
ScaledTextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat);
~ScaledTextureSurface() override;
void allocate(uint width, uint height) override;
uint getWidth() const override { return _rgbData.w; }
uint getHeight() const override { return _rgbData.h; }
Graphics::PixelFormat getFormat() const override { return _fakeFormat; }
bool hasPalette() const override { return (_palette != nullptr); }
Graphics::Surface *getSurface() override { return &_rgbData; }
const Graphics::Surface *getSurface() const override { return &_rgbData; }
void updateGLTexture() override;
void setScaler(uint scalerIndex, int scaleFactor) override;
protected:
Graphics::Surface *_convData;
Scaler *_scaler;
uint _scalerIndex;
uint _extraPixels;
uint _scaleFactor;
};
#endif
#if !USE_FORCED_GLES
class TextureTarget;
class CLUT8LookUpPipeline;
class TextureSurfaceCLUT8GPU : public Surface {
public:
TextureSurfaceCLUT8GPU();
~TextureSurfaceCLUT8GPU() override;
void destroy() override;
void recreate() override;
void enableLinearFiltering(bool enable) override;
void setRotation(Common::RotationMode rotation) override;
void allocate(uint width, uint height) override;
bool isDirty() const override { return _paletteDirty || Surface::isDirty(); }
uint getWidth() const override { return _userPixelData.w; }
uint getHeight() const override { return _userPixelData.h; }
Graphics::PixelFormat getFormat() const override;
bool hasPalette() const override { return true; }
void setColorKey(uint colorKey) override;
void setPalette(uint start, uint colors, const byte *palData) override;
Graphics::Surface *getSurface() override { return &_userPixelData; }
const Graphics::Surface *getSurface() const override { return &_userPixelData; }
void updateGLTexture() override;
const Texture &getGLTexture() const override;
static bool isSupportedByContext() {
return OpenGLContext.shadersSupported
&& OpenGLContext.multitextureSupported
&& OpenGLContext.framebufferObjectSupported
// With 2^-8 precision this is too prone to approximation errors
&& OpenGLContext.textureLookupPrecision > 8;
}
private:
void lookUpColors();
Texture _clut8Texture;
Texture _paletteTexture;
TextureTarget *_target;
CLUT8LookUpPipeline *_clut8Pipeline;
GLfloat _clut8Vertices[4*2];
Graphics::Surface _clut8Data;
Graphics::Surface _userPixelData;
byte _palette[4 * 256];
bool _paletteDirty;
};
#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
/* 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 BACKENDS_GRAPHICS_OPENGLSDL_OPENGLSDL_GRAPHICS_H
#define BACKENDS_GRAPHICS_OPENGLSDL_OPENGLSDL_GRAPHICS_H
#include "backends/graphics/opengl/opengl-graphics.h"
#include "backends/graphics/sdl/sdl-graphics.h"
#include "backends/platform/sdl/sdl-sys.h"
#include "common/array.h"
#include "common/events.h"
class OpenGLSdlGraphicsManager : public OpenGL::OpenGLGraphicsManager, public SdlGraphicsManager {
public:
OpenGLSdlGraphicsManager(SdlEventSource *eventSource, SdlWindow *window);
virtual ~OpenGLSdlGraphicsManager();
bool hasFeature(OSystem::Feature f) const override;
void setFeatureState(OSystem::Feature f, bool enable) override;
bool getFeatureState(OSystem::Feature f) const override;
void initSize(uint w, uint h, const Graphics::PixelFormat *format) override;
void updateScreen() override;
float getHiDPIScreenFactor() const override;
// EventObserver API
bool notifyEvent(const Common::Event &event) override;
// SdlGraphicsManager API
void notifyVideoExpose() override;
void notifyResize(const int width, const int height) override;
#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0)
void *getImGuiTexture(const Graphics::Surface &image, const byte *palette, int palCount) override;
void freeImGuiTexture(void *texture) override;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 0)
void destroyingWindow() override;
#endif
protected:
bool loadVideoMode(uint requestedWidth, uint requestedHeight, bool resizable, int antialiasing) override;
void refreshScreen() override;
void handleResizeImpl(const int width, const int height) override;
bool saveScreenshot(const Common::Path &filename) const override;
bool canSwitchFullscreen() const override;
private:
bool setupMode(uint width, uint height);
void deinitOpenGLContext();
#if SDL_VERSION_ATLEAST(2, 0, 0)
int _glContextProfileMask, _glContextMajor, _glContextMinor;
SDL_GLContext _glContext;
#else
uint32 _lastVideoModeLoad;
#endif
#ifdef EMSCRIPTEN
/**
* See https://registry.khronos.org/webgl/specs/latest/1.0/#2 :
* " By default, after compositing the contents of the drawing buffer shall be cleared to their default values [...]
* Techniques like synchronous drawing buffer access (e.g., calling readPixels or toDataURL in the same function
* that renders to the drawing buffer) can be used to get the contents of the drawing buffer "
*
* This means we need to take the screenshot at the correct time, which we do by queueing taking the screenshot
* for the next frame instead of taking it right away.
*/
bool _queuedScreenshot = false;
void saveScreenshot() override;
#endif
OpenGL::ContextType _glContextType;
bool _resizable;
int _requestedAntialiasing;
int _effectiveAntialiasing;
uint _forceFrameUpdate = 0;
uint _lastRequestedWidth;
uint _lastRequestedHeight;
uint _graphicsScale;
bool _gotResize;
bool _vsync;
bool _wantsFullScreen;
uint _ignoreResizeEvents;
struct VideoMode {
VideoMode() : width(0), height(0) {}
VideoMode(uint w, uint h) : width(w), height(h) {}
bool operator<(const VideoMode &right) const {
if (width < right.width) {
return true;
} else if (width == right.width && height < right.height) {
return true;
} else {
return false;
}
}
bool operator==(const VideoMode &right) const {
return width == right.width && height == right.height;
}
bool operator!=(const VideoMode &right) const {
return !(*this == right);
}
uint width, height;
};
typedef Common::Array<VideoMode> VideoModeArray;
VideoModeArray _fullscreenVideoModes;
uint _desiredFullscreenWidth;
uint _desiredFullscreenHeight;
};
#endif

View File

@@ -0,0 +1,72 @@
/* 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/scummsys.h"
#if defined(OPENPANDORA)
#include "backends/graphics/openpandora/op-graphics.h"
#include "backends/events/openpandora/op-events.h"
#include "graphics/scaler/aspect.h"
#include "common/mutex.h"
#include "common/textconsole.h"
static SDL_Cursor *hiddenCursor;
OPGraphicsManager::OPGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
}
bool OPGraphicsManager::loadGFXMode() {
uint8_t hiddenCursorData = 0;
hiddenCursor = SDL_CreateCursor(&hiddenCursorData, &hiddenCursorData, 8, 1, 0, 0);
/* On the OpenPandora we need to work around an SDL assumption that
returns relative mouse coordinates when you get to the screen
edges using the touchscreen. The workaround is to set a blank
SDL cursor and not disable it (Hackish I know).
The root issues likes in the Windows Manager GRAB code in SDL.
That is why the issue is not seen on framebuffer devices like the
GP2X (there is no X window manager ;)).
*/
SDL_ShowCursor(SDL_ENABLE);
SDL_SetCursor(hiddenCursor);
return SurfaceSdlGraphicsManager::loadGFXMode();
}
void OPGraphicsManager::unloadGFXMode() {
uint8_t hiddenCursorData = 0;
hiddenCursor = SDL_CreateCursor(&hiddenCursorData, &hiddenCursorData, 8, 1, 0, 0);
// Free the hidden SDL cursor created in loadGFXMode
SDL_FreeCursor(hiddenCursor);
SurfaceSdlGraphicsManager::unloadGFXMode();
}
void OPGraphicsManager::showSystemMouseCursor(bool visible) {
}
#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/>.
*
*/
#ifndef BACKENDS_GRAPHICS_OP_H
#define BACKENDS_GRAPHICS_OP_H
#include "backends/graphics/surfacesdl/surfacesdl-graphics.h"
class OPGraphicsManager : public SurfaceSdlGraphicsManager {
public:
OPGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
bool loadGFXMode() override;
void unloadGFXMode() override;
void showSystemMouseCursor(bool visible) override;
};
#endif /* BACKENDS_GRAPHICS_OP_H */

View File

@@ -0,0 +1,77 @@
/* 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/scummsys.h"
#if defined(RISCOS) && defined(SDL_BACKEND)
#include "backends/graphics/riscossdl/riscossdl-graphics.h"
#include "common/translation.h"
static OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{"surfacesdl", _s("SDL Surface"), GFX_SURFACESDL},
{"palettesdl", _s("SDL Surface (forced 8bpp mode)"), GFX_PALETTESDL},
{nullptr, nullptr, 0}
};
const OSystem::GraphicsMode *RISCOSSdlGraphicsManager::getSupportedGraphicsModes() const {
return s_supportedGraphicsModes;
}
bool RISCOSSdlGraphicsManager::hasFeature(OSystem::Feature f) const {
if (f == OSystem::kFeatureVSync)
return true;
return SurfaceSdlGraphicsManager::hasFeature(f);
}
void RISCOSSdlGraphicsManager::initGraphicsSurface() {
Uint32 flags = 0;
int bpp = 0;
if (_videoMode.fullscreen)
flags |= SDL_FULLSCREEN;
if (_videoMode.vsync && _videoMode.fullscreen) {
flags |= SDL_HWSURFACE | SDL_DOUBLEBUF;
} else {
/* Hardware surfaces and double buffering aren't supported in windowed mode on RISC OS. */
flags |= SDL_SWSURFACE;
}
switch (_videoMode.mode) {
case GFX_SURFACESDL:
bpp = 16;
flags |= SDL_ANYFORMAT;
break;
case GFX_PALETTESDL:
bpp = 8;
flags |= SDL_HWPALETTE;
break;
default:
break;
}
_hwScreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, bpp, flags);
_isDoubleBuf = flags & SDL_DOUBLEBUF;
_isHwPalette = flags & SDL_HWPALETTE;
}
#endif

View File

@@ -0,0 +1,41 @@
/* 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 BACKENDS_GRAPHICS_RISCOSSDL_H
#define BACKENDS_GRAPHICS_RISCOSSDL_H
#include "backends/graphics/surfacesdl/surfacesdl-graphics.h"
enum {
GFX_PALETTESDL = 1
};
class RISCOSSdlGraphicsManager : public SurfaceSdlGraphicsManager {
public:
RISCOSSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window) : SurfaceSdlGraphicsManager(sdlEventSource, window) {}
const OSystem::GraphicsMode *getSupportedGraphicsModes() const override;
bool hasFeature(OSystem::Feature f) const override;
void initGraphicsSurface() override;
};
#endif /* BACKENDS_GRAPHICS_RISCOSSDL_H */

View File

@@ -0,0 +1,778 @@
/* 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 "backends/graphics/sdl/sdl-graphics.h"
#include "backends/platform/sdl/sdl-sys.h"
#include "backends/platform/sdl/sdl.h"
#include "backends/events/sdl/sdl-events.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymap.h"
#include "common/config-manager.h"
#include "common/fs.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "graphics/scaler/aspect.h"
#ifdef USE_OSD
#include "common/translation.h"
#endif
#ifdef EMSCRIPTEN
#include "backends/platform/sdl/emscripten/emscripten.h"
#endif
#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(3, 0, 0)
#include "backends/imgui/backends/imgui_impl_sdl3.h"
#ifdef USE_OPENGL
#include "backends/imgui/backends/imgui_impl_opengl3.h"
#endif
#ifdef USE_IMGUI_SDLRENDERER3
#include "backends/imgui/backends/imgui_impl_sdlrenderer3.h"
#endif
#elif defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0)
#include "backends/imgui/backends/imgui_impl_sdl2.h"
#ifdef USE_OPENGL
#include "backends/imgui/backends/imgui_impl_opengl3.h"
#endif
#ifdef USE_IMGUI_SDLRENDERER2
#include "backends/imgui/backends/imgui_impl_sdlrenderer2.h"
#endif
#endif
static void getMouseState(int *x, int *y) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
float fx, fy;
SDL_GetMouseState(&fx, &fy);
*x = static_cast<int>(fx);
*y = static_cast<int>(fy);
#else
SDL_GetMouseState(x, y);
#endif
}
SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source, SdlWindow *window)
: _eventSource(source), _window(window), _hwScreen(nullptr)
#if SDL_VERSION_ATLEAST(2, 0, 0)
, _allowWindowSizeReset(false), _hintedWidth(0), _hintedHeight(0), _lastFlags(0)
#endif
{
ConfMan.registerDefault("fullscreen_res", "desktop");
getMouseState(&_cursorX, &_cursorY);
}
void SdlGraphicsManager::activateManager() {
_eventSource->setGraphicsManager(this);
// Register the graphics manager as a event observer
g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 10, false);
}
void SdlGraphicsManager::deactivateManager() {
// Unregister the event observer
if (g_system->getEventManager()->getEventDispatcher()) {
g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
}
_eventSource->setGraphicsManager(nullptr);
}
SdlGraphicsManager::State SdlGraphicsManager::getState() const {
State state;
state.screenWidth = getWidth();
state.screenHeight = getHeight();
state.aspectRatio = getFeatureState(OSystem::kFeatureAspectRatioCorrection);
state.fullscreen = getFeatureState(OSystem::kFeatureFullscreenMode);
state.cursorPalette = getFeatureState(OSystem::kFeatureCursorPalette);
state.vsync = getFeatureState(OSystem::kFeatureVSync);
state.rotation = _rotationMode;
#ifdef USE_RGB_COLOR
state.pixelFormat = getScreenFormat();
#endif
return state;
}
bool SdlGraphicsManager::setState(const State &state) {
beginGFXTransaction();
#ifdef USE_RGB_COLOR
// When switching between the SDL and OpenGL graphics manager, the list
// of supported format changes. This means that the pixel format in the
// state may not be supported. In that case use the preferred supported
// pixel format instead.
Graphics::PixelFormat format = state.pixelFormat;
Common::List<Graphics::PixelFormat> supportedFormats = getSupportedFormats();
if (!supportedFormats.empty() && Common::find(supportedFormats.begin(), supportedFormats.end(), format) == supportedFormats.end())
format = supportedFormats.front();
initSize(state.screenWidth, state.screenHeight, &format);
#else
initSize(state.screenWidth, state.screenHeight, nullptr);
#endif
setFeatureState(OSystem::kFeatureAspectRatioCorrection, state.aspectRatio);
setFeatureState(OSystem::kFeatureFullscreenMode, state.fullscreen);
setFeatureState(OSystem::kFeatureCursorPalette, state.cursorPalette);
setFeatureState(OSystem::kFeatureVSync, state.vsync);
setRotationMode(state.rotation);
if (endGFXTransaction() != OSystem::kTransactionSuccess) {
return false;
} else {
return true;
}
}
Common::Rect SdlGraphicsManager::getPreferredFullscreenResolution() {
// Default to the desktop resolution, unless the user has set a
// resolution in the configuration file
const Common::String &fsres = ConfMan.get("fullscreen_res");
if (fsres != "desktop") {
uint newW, newH;
int converted = sscanf(fsres.c_str(), "%ux%u", &newW, &newH);
if (converted == 2) {
return Common::Rect(newW, newH);
} else {
warning("Could not parse 'fullscreen_res' option: expected WWWxHHH, got %s", fsres.c_str());
}
}
return _window->getDesktopResolution();
}
bool SdlGraphicsManager::defaultGraphicsModeConfig() const {
const Common::ConfigManager::Domain *transientDomain = ConfMan.getDomain(Common::ConfigManager::kTransientDomain);
if (transientDomain && transientDomain->contains("scaler")) {
const Common::String &mode = transientDomain->getVal("scaler");
if (!mode.equalsIgnoreCase("default")) {
return false;
}
}
const Common::ConfigManager::Domain *gameDomain = ConfMan.getActiveDomain();
if (gameDomain && gameDomain->contains("scaler")) {
const Common::String &mode = gameDomain->getVal("scaler");
if (!mode.equalsIgnoreCase("default")) {
return false;
}
}
return true;
}
void SdlGraphicsManager::initSizeHint(const Graphics::ModeList &modes) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
const bool useDefault = defaultGraphicsModeConfig();
// This gets called from engine before they call initGraphics(), which means we cannot use getScaleFactor()
// because the scale factor in the backend has not yet been updated to use the game settings. So directly
// read the game settings. This may be -1, which means we want to use the default. Fortunately runGame()
// in main.cpp sets the gaphics mode (OpenGL or SurfaceSDL) before starting the engine. So we already have
// the correct graphics manager and we can call getDefaultScaleFactor() here.
int scale = ConfMan.getInt("scale_factor");
if (scale == -1) {
scale = getDefaultScaleFactor();
}
int16 bestWidth = 0, bestHeight = 0;
const Graphics::ModeList::const_iterator end = modes.end();
for (Graphics::ModeList::const_iterator it = modes.begin(); it != end; ++it) {
int16 width = it->width, height = it->height;
// TODO: Normalize AR correction by passing a PAR in the mode list
// instead of checking the dimensions here like this, since not all
// 320x200/640x400 uses are with non-square pixels (e.g. DreamWeb).
if (ConfMan.getBool("aspect_ratio")) {
if ((width == 320 && height == 200) || (width == 640 && height == 400)) {
height = real2Aspect(height);
}
}
if (!useDefault || width <= 320) {
width *= scale;
height *= scale;
}
if (bestWidth < width) {
bestWidth = width;
}
if (bestHeight < height) {
bestHeight = height;
}
}
_hintedWidth = bestWidth;
_hintedHeight = bestHeight;
#endif
}
bool SdlGraphicsManager::showMouse(bool visible) {
if (visible == _cursorVisible) {
return visible;
}
bool showCursor = false;
if (visible) {
// _cursorX and _cursorY are currently always clipped to the active
// area, so we need to ask SDL where the system's mouse cursor is
// instead
int x, y;
getMouseState(&x, &y);
if (!_activeArea.drawRect.contains(Common::Point(x, y))) {
showCursor = true;
}
}
showSystemMouseCursor(showCursor);
return WindowedGraphicsManager::showMouse(visible);
}
bool SdlGraphicsManager::lockMouse(bool lock) {
return _window->lockMouse(lock);
}
bool SdlGraphicsManager::notifyMousePosition(Common::Point &mouse) {
bool showCursor = false;
// Currently on macOS we need to scale the events for HiDPI screen, but on
// Windows we do not. We can find out if we need to do it by querying the
// SDL window size vs the SDL drawable size.
float dpiScale = _window->getSdlDpiScalingFactor();
mouse.x = (int)(mouse.x * dpiScale + 0.5f);
mouse.y = (int)(mouse.y * dpiScale + 0.5f);
bool valid = true;
if (_activeArea.drawRect.contains(mouse)) {
_cursorLastInActiveArea = true;
} else {
// The right/bottom edges are not part of the drawRect. As the clipping
// is done in drawable area coordinates, but the mouse position is set
// in window coordinates, we need to subtract as many pixels from the
// edges as corresponds to one pixel in the window coordinates.
mouse.x = CLIP<int>(mouse.x, _activeArea.drawRect.left,
_activeArea.drawRect.right - (int)(1 * dpiScale + 0.5f));
mouse.y = CLIP<int>(mouse.y, _activeArea.drawRect.top,
_activeArea.drawRect.bottom - (int)(1 * dpiScale + 0.5f));
if (_window->mouseIsGrabbed() ||
// Keep the mouse inside the game area during dragging to prevent an
// event mismatch where the mouseup event gets lost because it is
// performed outside of the game area
(_cursorLastInActiveArea && SDL_GetMouseState(nullptr, nullptr) != 0)) {
setSystemMousePosition(mouse.x, mouse.y);
} else {
// Allow the in-game mouse to get a final movement event to the edge
// of the window if the mouse was moved out of the game area
if (_cursorLastInActiveArea) {
_cursorLastInActiveArea = false;
} else if (_cursorVisible) {
// Keep sending events to the game if the cursor is invisible,
// since otherwise if a game lets you skip a cutscene by
// clicking and the user moved the mouse outside the active
// area, the clicks wouldn't do anything, which would be
// confusing
valid = false;
}
if (_cursorVisible) {
showCursor = true;
}
}
}
showSystemMouseCursor(showCursor);
if (valid) {
setMousePosition(mouse.x, mouse.y);
mouse = convertWindowToVirtual(mouse.x, mouse.y);
}
return valid;
}
void SdlGraphicsManager::showSystemMouseCursor(bool visible) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (visible) {
SDL_ShowCursor();
} else {
SDL_HideCursor();
}
#else
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
#endif
}
void SdlGraphicsManager::setSystemMousePosition(const int x, const int y) {
assert(_window);
if (!_window->warpMouseInWindow(x, y)) {
const Common::Point mouse = convertWindowToVirtual(x, y);
_eventSource->fakeWarpMouse(mouse.x, mouse.y);
}
}
void SdlGraphicsManager::notifyActiveAreaChanged() {
_window->setMouseRect(_activeArea.drawRect);
}
void SdlGraphicsManager::handleResizeImpl(const int width, const int height) {
_forceRedraw = true;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool SdlGraphicsManager::createOrUpdateWindow(int width, int height, const Uint32 flags) {
if (!_window) {
return false;
}
// width *=3;
// height *=3;
// We only update the actual window when flags change (which usually means
// fullscreen mode is entered/exited), when updates are forced so that we
// do not reset the window size whenever a game makes a call to change the
// size or pixel format of the internal game surface (since a user may have
// resized the game window), or when the launcher is visible (since a user
// may change the scaler, which should reset the window size)
if (!_window->getSDLWindow() || _lastFlags != flags || _overlayVisible || _allowWindowSizeReset) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
const bool fullscreen = (flags & (SDL_WINDOW_FULLSCREEN)) != 0;
#else
const bool fullscreen = (flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0;
#endif
const bool maximized = (flags & SDL_WINDOW_MAXIMIZED);
if (!fullscreen && !maximized) {
if (_hintedWidth > width) {
width = _hintedWidth;
}
if (_hintedHeight > height) {
height = _hintedHeight;
}
}
if (!_window->createOrUpdateWindow(width, height, flags)) {
return false;
}
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (fullscreen) {
if (!SDL_SetWindowFullscreenMode(_window->getSDLWindow(), NULL))
return false;
if (!SDL_SyncWindow(_window->getSDLWindow()))
return false;
}
#endif
_lastFlags = flags;
_allowWindowSizeReset = false;
}
return true;
}
#endif
void SdlGraphicsManager::saveScreenshot() {
Common::String filename;
Common::Path screenshotsPath;
OSystem_SDL *sdl_g_system = dynamic_cast<OSystem_SDL*>(g_system);
if (sdl_g_system)
screenshotsPath = sdl_g_system->getScreenshotsPath();
// Use the name of the running target as a base for screenshot file names
Common::String currentTarget = ConfMan.getActiveDomainName();
#ifdef USE_PNG
const char *extension = "png";
#else
const char *extension = "bmp";
#endif
for (int n = 0;; n++) {
filename = Common::String::format("scummvm%s%s-%05d.%s", currentTarget.empty() ? "" : "-",
currentTarget.c_str(), n, extension);
Common::FSNode file = Common::FSNode(screenshotsPath.appendComponent(filename));
if (!file.exists()) {
break;
}
}
if (saveScreenshot(screenshotsPath.appendComponent(filename))) {
if (screenshotsPath.empty())
debug("Saved screenshot '%s' in current directory", filename.c_str());
else
debug("Saved screenshot '%s' in directory '%s'", filename.c_str(),
screenshotsPath.toString(Common::Path::kNativeSeparator).c_str());
#ifdef USE_OSD
if (!ConfMan.getBool("disable_saved_screenshot_osd"))
displayMessageOnOSD(Common::U32String::format(_("Saved screenshot '%s'"), filename.c_str()));
#endif
#ifdef EMSCRIPTEN
// Users can't access the virtual emscripten filesystem in the browser, so we export the generated screenshot file via OSystem_Emscripten::exportFile.
OSystem_Emscripten *emscripten_g_system = dynamic_cast<OSystem_Emscripten*>(g_system);
emscripten_g_system->exportFile(screenshotsPath.appendComponent(filename));
#endif
} else {
if (screenshotsPath.empty())
warning("Could not save screenshot in current directory");
else
warning("Could not save screenshot in directory '%s'", screenshotsPath.toString(Common::Path::kNativeSeparator).c_str());
#ifdef USE_OSD
displayMessageOnOSD(_("Could not save screenshot"));
#endif
}
}
bool SdlGraphicsManager::notifyEvent(const Common::Event &event) {
if (event.type != Common::EVENT_CUSTOM_BACKEND_ACTION_START) {
return false;
}
switch ((CustomEventAction) event.customType) {
case kActionToggleMouseCapture:
getWindow()->grabMouse(!getWindow()->mouseIsGrabbed());
return true;
case kActionToggleResizableWindow:
getWindow()->setResizable(!getWindow()->resizable());
return true;
case kActionToggleFullscreen:
toggleFullScreen();
return true;
case kActionSaveScreenshot:
saveScreenshot();
return true;
default:
return false;
}
}
void SdlGraphicsManager::toggleFullScreen() {
if (!g_system->hasFeature(OSystem::kFeatureFullscreenMode) ||
!canSwitchFullscreen()) {
return;
}
beginGFXTransaction();
setFeatureState(OSystem::kFeatureFullscreenMode, !getFeatureState(OSystem::kFeatureFullscreenMode));
endGFXTransaction();
#ifdef USE_OSD
if (getFeatureState(OSystem::kFeatureFullscreenMode))
displayMessageOnOSD(_("Fullscreen mode"));
else
displayMessageOnOSD(_("Windowed mode"));
#endif
}
Common::Keymap *SdlGraphicsManager::getKeymap() {
using namespace Common;
Keymap *keymap = new Keymap(Keymap::kKeymapTypeGlobal, "sdl-graphics", _("Graphics"));
Action *act;
if (g_system->hasFeature(OSystem::kFeatureFullscreenMode)) {
act = new Action("FULS", _("Toggle fullscreen"));
act->addDefaultInputMapping("A+RETURN");
act->addDefaultInputMapping("A+KP_ENTER");
act->setCustomBackendActionEvent(kActionToggleFullscreen);
keymap->addAction(act);
}
act = new Action("CAPT", _("Toggle mouse capture"));
act->addDefaultInputMapping("C+m");
act->setCustomBackendActionEvent(kActionToggleMouseCapture);
keymap->addAction(act);
act = new Action("RSZW", _("Toggle resizable window"));
act->addDefaultInputMapping("C+r");
act->setCustomBackendActionEvent(kActionToggleResizableWindow);
keymap->addAction(act);
act = new Action("SCRS", _("Save screenshot"));
act->addDefaultInputMapping("A+s");
act->setCustomBackendActionEvent(kActionSaveScreenshot);
keymap->addAction(act);
if (hasFeature(OSystem::kFeatureAspectRatioCorrection)) {
act = new Action("ASPT", _("Toggle aspect ratio correction"));
act->addDefaultInputMapping("C+A+a");
act->setCustomBackendActionEvent(kActionToggleAspectRatioCorrection);
keymap->addAction(act);
}
if (hasFeature(OSystem::kFeatureFilteringMode)) {
act = new Action("FILT", _("Toggle linear filtered scaling"));
act->addDefaultInputMapping("C+A+f");
act->setCustomBackendActionEvent(kActionToggleFilteredScaling);
keymap->addAction(act);
}
if (hasFeature(OSystem::kFeatureStretchMode)) {
act = new Action("STCH", _("Cycle through stretch modes"));
act->addDefaultInputMapping("C+A+s");
act->setCustomBackendActionEvent(kActionCycleStretchMode);
keymap->addAction(act);
}
act = new Action("SCL+", _("Increase the scale factor"));
act->addDefaultInputMapping("C+A+PLUS");
act->addDefaultInputMapping("C+A+KP_PLUS");
act->setCustomBackendActionEvent(kActionIncreaseScaleFactor);
keymap->addAction(act);
act = new Action("SCL-", _("Decrease the scale factor"));
act->addDefaultInputMapping("C+A+MINUS");
act->addDefaultInputMapping("C+A+KP_MINUS");
act->setCustomBackendActionEvent(kActionDecreaseScaleFactor);
keymap->addAction(act);
if (hasFeature(OSystem::kFeatureScalers)) {
act = new Action("FLTN", _("Switch to the next scaler"));
act->addDefaultInputMapping("C+A+0");
act->setCustomBackendActionEvent(kActionNextScaleFilter);
keymap->addAction(act);
act = new Action("FLTP", _("Switch to the previous scaler"));
act->addDefaultInputMapping("C+A+9");
act->setCustomBackendActionEvent(kActionPreviousScaleFilter);
keymap->addAction(act);
}
return keymap;
}
#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0)
void SdlGraphicsManager::setImGuiCallbacks(const ImGuiCallbacks &callbacks) {
if (_imGuiInited) {
if (_imGuiCallbacks.cleanup) {
_imGuiCallbacks.cleanup();
}
_imGuiInited = false;
}
_imGuiCallbacks = callbacks;
if (!_imGuiReady) {
return;
}
if (_imGuiCallbacks.init) {
_imGuiCallbacks.init();
}
_imGuiInited = true;
}
void SdlGraphicsManager::initImGui(SDL_Renderer *renderer, void *glContext) {
assert(!_imGuiReady);
_imGuiInited = false;
IMGUI_CHECKVERSION();
if (!ImGui::CreateContext()) {
return;
}
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
ImGui::StyleColorsDark();
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ImGuiStyle& style = ImGui::GetStyle();
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
io.IniFilename = nullptr;
_imGuiSDLRenderer = nullptr;
#ifdef USE_OPENGL
if (!_imGuiReady && glContext) {
// Only OpenGL and GLES2 are supported, not GLES
if ((OpenGLContext.type != OpenGL::kContextGL) &&
(OpenGLContext.type != OpenGL::kContextGLES2)) {
ImGui::DestroyContext();
return;
}
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (!ImGui_ImplSDL3_InitForOpenGL(_window->getSDLWindow(), glContext)) {
#else
if (!ImGui_ImplSDL2_InitForOpenGL(_window->getSDLWindow(), glContext)) {
#endif
ImGui::DestroyContext();
return;
}
const char *glslVersion;
if (OpenGLContext.type == OpenGL::kContextGLES2) {
glslVersion = "#version 100";
} else {
glslVersion = "#version 110";
}
if (!ImGui_ImplOpenGL3_Init(glslVersion)) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
ImGui_ImplSDL3_Shutdown();
#else
ImGui_ImplSDL2_Shutdown();
#endif
ImGui::DestroyContext();
return;
}
_imGuiReady = true;
}
#endif
#ifdef USE_IMGUI_SDLRENDERER3
if (!_imGuiReady && renderer) {
if (!ImGui_ImplSDL3_InitForSDLRenderer(_window->getSDLWindow(), renderer)) {
ImGui::DestroyContext();
return;
}
if (!ImGui_ImplSDLRenderer3_Init(renderer)) {
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
return;
}
_imGuiReady = true;
_imGuiSDLRenderer = renderer;
}
#elif defined(USE_IMGUI_SDLRENDERER2)
if (!_imGuiReady && renderer) {
if (!ImGui_ImplSDL2_InitForSDLRenderer(_window->getSDLWindow(), renderer)) {
ImGui::DestroyContext();
return;
}
if (!ImGui_ImplSDLRenderer2_Init(renderer)) {
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
return;
}
_imGuiReady = true;
_imGuiSDLRenderer = renderer;
}
#endif
if (!_imGuiReady) {
warning("No ImGui renderer has been found");
ImGui::DestroyContext();
return;
}
if (_imGuiCallbacks.init) {
_imGuiCallbacks.init();
}
_imGuiInited = true;
}
void SdlGraphicsManager::renderImGui() {
if (!_imGuiReady || !_imGuiCallbacks.render) {
return;
}
if (!_imGuiInited) {
if (_imGuiCallbacks.init) {
_imGuiCallbacks.init();
}
_imGuiInited = true;
}
#ifdef USE_IMGUI_SDLRENDERER3
if (_imGuiSDLRenderer) {
ImGui_ImplSDLRenderer3_NewFrame();
} else {
#elif defined(USE_IMGUI_SDLRENDERER2)
if (_imGuiSDLRenderer) {
ImGui_ImplSDLRenderer2_NewFrame();
} else {
#endif
#ifdef USE_OPENGL
ImGui_ImplOpenGL3_NewFrame();
#endif
#if defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)
}
#endif
#if SDL_VERSION_ATLEAST(3, 0, 0)
ImGui_ImplSDL3_NewFrame();
#else
ImGui_ImplSDL2_NewFrame();
#endif
ImGui::NewFrame();
_imGuiCallbacks.render();
ImGui::Render();
#ifdef USE_IMGUI_SDLRENDERER3
if (_imGuiSDLRenderer) {
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), _imGuiSDLRenderer);
} else {
#elif defined(USE_IMGUI_SDLRENDERER2)
if (_imGuiSDLRenderer) {
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), _imGuiSDLRenderer);
} else {
#endif
#ifdef USE_OPENGL
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow();
SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
#endif
#if defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)
}
#endif
}
void SdlGraphicsManager::destroyImGui() {
if (!_imGuiReady) {
return;
}
if (_imGuiCallbacks.cleanup) {
_imGuiCallbacks.cleanup();
}
_imGuiInited = false;
_imGuiReady = false;
#ifdef USE_IMGUI_SDLRENDERER3
if (_imGuiSDLRenderer) {
ImGui_ImplSDLRenderer3_Shutdown();
} else {
#elif defined(USE_IMGUI_SDLRENDERER2)
if (_imGuiSDLRenderer) {
ImGui_ImplSDLRenderer2_Shutdown();
} else {
#endif
#ifdef USE_OPENGL
ImGui_ImplOpenGL3_Shutdown();
#endif
#if defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3)
}
#endif
#if SDL_VERSION_ATLEAST(3, 0, 0)
ImGui_ImplSDL3_Shutdown();
#else
ImGui_ImplSDL2_Shutdown();
#endif
ImGui::DestroyContext();
}
#endif

View File

@@ -0,0 +1,241 @@
/* 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 BACKENDS_GRAPHICS_SDL_SDLGRAPHICS_H
#define BACKENDS_GRAPHICS_SDL_SDLGRAPHICS_H
#include "backends/graphics/windowed.h"
#include "backends/platform/sdl/sdl-window.h"
#include "common/events.h"
#include "common/rect.h"
class SdlEventSource;
#define USE_OSD 1
/**
* Base class for a SDL based graphics manager.
*/
class SdlGraphicsManager : virtual public WindowedGraphicsManager, public Common::EventObserver {
public:
SdlGraphicsManager(SdlEventSource *source, SdlWindow *window);
virtual ~SdlGraphicsManager() {}
/**
* Makes this graphics manager active. That means it should be ready to
* process inputs now. However, even without being active it should be
* able to query the supported modes and other bits.
*/
virtual void activateManager();
/**
* Makes this graphics manager inactive. This should allow another
* graphics manager to become active again.
*/
virtual void deactivateManager();
/**
* Notify the graphics manager that the graphics needs to be redrawn, since
* the application window was modified.
*
* This is basically called when SDL_VIDEOEXPOSE was received.
*/
virtual void notifyVideoExpose() = 0;
/**
* Notify the graphics manager about a resize event.
*
* It is noteworthy that the requested width/height should actually be set
* up as is and not changed by the graphics manager, since otherwise it may
* lead to odd behavior for certain window managers.
*
* It is only required to overwrite this method in case you want a
* resizable window. The default implementation just does nothing.
*
* @param width Requested window width.
* @param height Requested window height.
*/
virtual void notifyResize(const int width, const int height) {}
/**
* Notifies the graphics manager about a mouse position change.
*
* The passed point *must* be converted from window coordinates to virtual
* coordinates in order for the event to be processed correctly by the game
* engine. Just use `convertWindowToVirtual` for this unless you need to do
* something special.
*
* @param mouse The mouse position in window coordinates, which must be
* converted synchronously to virtual coordinates.
* @returns true if the mouse was in a valid position for the game and
* should cause the event to be sent to the game.
*/
virtual bool notifyMousePosition(Common::Point &mouse);
virtual bool showMouse(bool visible) override;
bool lockMouse(bool lock) override;
virtual bool saveScreenshot(const Common::Path &filename) const { return false; }
void saveScreenshot() override;
bool setRotationMode(Common::RotationMode rotation) override { _rotationMode = rotation; return true; }
// Override from Common::EventObserver
bool notifyEvent(const Common::Event &event) override;
/**
* A (subset) of the graphic manager's state. This is used when switching
* between different SDL graphic managers at runtime.
*/
struct State {
int screenWidth, screenHeight;
bool aspectRatio;
bool fullscreen;
bool cursorPalette;
bool vsync;
Common::RotationMode rotation;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat pixelFormat;
#endif
};
/**
* Gets the current state of the graphics manager.
*/
State getState() const;
/**
* Sets up a basic state of the graphics manager.
*/
bool setState(const State &state);
/**
* @returns the SDL window.
*/
SdlWindow *getWindow() const { return _window; }
void initSizeHint(const Graphics::ModeList &modes) override;
Common::Keymap *getKeymap();
protected:
enum CustomEventAction {
kActionToggleFullscreen = 100,
kActionToggleMouseCapture,
kActionToggleResizableWindow,
kActionSaveScreenshot,
kActionToggleAspectRatioCorrection,
kActionToggleFilteredScaling,
kActionCycleStretchMode,
kActionIncreaseScaleFactor,
kActionDecreaseScaleFactor,
kActionNextScaleFilter,
kActionPreviousScaleFilter
};
/** Obtain the user configured fullscreen resolution, or default to the desktop resolution */
Common::Rect getPreferredFullscreenResolution();
bool defaultGraphicsModeConfig() const;
/**
* Gets the dimensions of the window directly from SDL instead of from the
* values stored by the graphics manager.
*/
void getWindowSizeFromSdl(int *width, int *height) const {
#if SDL_VERSION_ATLEAST(3, 0, 0)
assert(_window);
SDL_GetWindowSizeInPixels(_window->getSDLWindow(), width, height);
#elif SDL_VERSION_ATLEAST(2, 0, 0)
assert(_window);
SDL_GL_GetDrawableSize(_window->getSDLWindow(), width, height);
#else
assert(_hwScreen);
if (width) {
*width = _hwScreen->w;
}
if (height) {
*height = _hwScreen->h;
}
#endif
}
virtual void showSystemMouseCursor(bool visible);
void setSystemMousePosition(const int x, const int y) override;
void notifyActiveAreaChanged() override;
void handleResizeImpl(const int width, const int height) override;
#if SDL_VERSION_ATLEAST(2, 0, 0)
public:
void unlockWindowSize() {
_allowWindowSizeReset = true;
_hintedWidth = 0;
_hintedHeight = 0;
}
// Called by SdlWindow when the window is about to be destroyed
virtual void destroyingWindow() {}
protected:
Uint32 _lastFlags;
bool _allowWindowSizeReset;
int _hintedWidth, _hintedHeight;
bool createOrUpdateWindow(const int width, const int height, const Uint32 flags);
#endif
SDL_Surface *_hwScreen;
SdlEventSource *_eventSource;
SdlWindow *_window;
/**
* @returns whether switching the fullscreen state is currently safe
*/
virtual bool canSwitchFullscreen() const { return false; }
private:
void toggleFullScreen();
#if defined(USE_IMGUI) && SDL_VERSION_ATLEAST(2, 0, 0)
public:
void setImGuiCallbacks(const ImGuiCallbacks &callbacks) override;
protected:
ImGuiCallbacks _imGuiCallbacks;
bool _imGuiReady = false;
bool _imGuiInited = false;
SDL_Renderer *_imGuiSDLRenderer = nullptr;
void initImGui(SDL_Renderer *renderer, void *glContext);
void renderImGui();
void destroyImGui();
#endif
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,496 @@
/* 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 BACKENDS_GRAPHICS_SURFACESDL_GRAPHICS_H
#define BACKENDS_GRAPHICS_SURFACESDL_GRAPHICS_H
#include "backends/graphics/graphics.h"
#include "backends/graphics/sdl/sdl-graphics.h"
#include "graphics/pixelformat.h"
#include "graphics/scaler.h"
#include "graphics/scalerplugin.h"
#include "common/events.h"
#include "common/mutex.h"
#include "backends/events/sdl/sdl-events.h"
#include "backends/platform/sdl/sdl-sys.h"
#ifndef RELEASE_BUILD
// Define this to allow for focus rectangle debugging
#define USE_SDL_DEBUG_FOCUSRECT
#endif
enum {
GFX_SURFACESDL = 0
};
/**
* SDL graphics manager
*/
class SurfaceSdlGraphicsManager : public SdlGraphicsManager {
public:
SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
virtual ~SurfaceSdlGraphicsManager();
bool hasFeature(OSystem::Feature f) const override;
void setFeatureState(OSystem::Feature f, bool enable) override;
bool getFeatureState(OSystem::Feature f) const override;
const OSystem::GraphicsMode *getSupportedGraphicsModes() const override;
int getDefaultGraphicsMode() const override;
bool setGraphicsMode(int mode, uint flags = OSystem::kGfxModeNoFlags) override;
int getGraphicsMode() const override;
uint getDefaultScaler() const override;
uint getDefaultScaleFactor() const override;
bool setScaler(uint mode, int factor) override;
uint getScaler() const override;
uint getScaleFactor() const override;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat getScreenFormat() const override { return _screenFormat; }
Common::List<Graphics::PixelFormat> getSupportedFormats() const override;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 0)
const OSystem::GraphicsMode *getSupportedStretchModes() const override;
int getDefaultStretchMode() const override;
bool setStretchMode(int mode) override;
int getStretchMode() const override;
#endif
void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL) override;
int getScreenChangeID() const override { return _screenChangeCount; }
void beginGFXTransaction() override;
OSystem::TransactionError endGFXTransaction() override;
int16 getHeight() const override;
int16 getWidth() const override;
protected:
// PaletteManager API
void setPalette(const byte *colors, uint start, uint num) override;
void grabPalette(byte *colors, uint start, uint num) const override;
virtual void initGraphicsSurface();
/**
* Convert from the SDL pixel format to Graphics::PixelFormat
* @param in The SDL pixel format to convert
* @param out A pixel format to be written to
*/
#if SDL_VERSION_ATLEAST(3, 0, 0)
Graphics::PixelFormat convertSDLPixelFormat(SDL_PixelFormat in) const;
#else
Graphics::PixelFormat convertSDLPixelFormat(SDL_PixelFormat *in) const;
#endif
public:
void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override;
Graphics::Surface *lockScreen() override;
void unlockScreen() override;
void fillScreen(uint32 col) override;
void fillScreen(const Common::Rect &r, uint32 col) override;
void updateScreen() override;
void setFocusRectangle(const Common::Rect& rect) override;
void clearFocusRectangle() override;
Graphics::PixelFormat getOverlayFormat() const override { return _overlayFormat; }
void clearOverlay() override;
void grabOverlay(Graphics::Surface &surface) const override;
void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
int16 getOverlayHeight() const override { return _videoMode.overlayHeight; }
int16 getOverlayWidth() const override { return _videoMode.overlayWidth; }
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = NULL) override;
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask, bool disableKeyColor);
void setCursorPalette(const byte *colors, uint start, uint num) override;
#ifdef USE_OSD
void displayMessageOnOSD(const Common::U32String &msg) override;
void displayActivityIconOnOSD(const Graphics::Surface *icon) override;
#endif
// Override from Common::EventObserver
bool notifyEvent(const Common::Event &event) override;
// SdlGraphicsManager interface
void notifyVideoExpose() override;
void notifyResize(const int width, const int height) override;
#if defined(USE_IMGUI) && (defined(USE_IMGUI_SDLRENDERER2) || defined(USE_IMGUI_SDLRENDERER3))
void *getImGuiTexture(const Graphics::Surface &image, const byte *palette, int palCount) override;
void freeImGuiTexture(void *texture) override;
#endif
protected:
#ifdef USE_OSD
/** Surface containing the OSD message */
SDL_Surface *_osdMessageSurface;
/** Transparency level of the OSD message */
uint8 _osdMessageAlpha;
/** When to start the fade out */
uint32 _osdMessageFadeStartTime;
/** Enum with OSD options */
enum {
kOSDFadeOutDelay = 2 * 1000, /** < Delay before the OSD is faded out (in milliseconds) */
kOSDFadeOutDuration = 500, /** < Duration of the OSD fade out (in milliseconds) */
kOSDInitialAlpha = 80 /** < Initial alpha level, in percent */
};
/** Screen rectangle where the OSD message is drawn */
SDL_Rect getOSDMessageRect() const;
/** Clear the currently displayed OSD message if any */
void removeOSDMessage();
/** Surface containing the OSD background activity icon */
SDL_Surface *_osdIconSurface;
/** Screen rectangle where the OSD background activity icon is drawn */
SDL_Rect getOSDIconRect() const;
void updateOSD();
void drawOSD();
#endif
class AspectRatio {
int _kw, _kh;
public:
AspectRatio() { _kw = _kh = 0; }
AspectRatio(int w, int h);
bool isAuto() const { return (_kw | _kh) == 0; }
int kw() const { return _kw; }
int kh() const { return _kh; }
};
static AspectRatio getDesiredAspectRatio();
bool gameNeedsAspectRatioCorrection() const override {
return _videoMode.aspectRatioCorrection;
}
int getGameRenderScale() const override {
return _videoMode.scaleFactor;
}
void handleResizeImpl(const int width, const int height) override;
virtual void setupHardwareSize();
void fixupResolutionForAspectRatio(AspectRatio desiredAspectRatio, int &width, int &height) const;
#if SDL_VERSION_ATLEAST(2, 0, 0)
/* SDL2 features a different API for 2D graphics. We create a wrapper
* around this API to keep the code paths as close as possible. */
SDL_Renderer *_renderer;
SDL_Texture *_screenTexture;
void deinitializeRenderer();
void recreateScreenTexture();
virtual SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);
virtual void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects);
int SDL_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors);
int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha);
int SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);
#endif
/** Unseen game screen */
SDL_Surface *_screen;
Graphics::PixelFormat _screenFormat;
Graphics::PixelFormat _cursorFormat;
#ifdef USE_RGB_COLOR
Common::List<Graphics::PixelFormat> _supportedFormats;
/**
* Update the list of supported pixel formats.
* This method is invoked by loadGFXMode().
*/
void detectSupportedFormats();
#endif
/** Temporary screen (for scalers) */
SDL_Surface *_tmpscreen;
/** Temporary screen (for scalers) */
SDL_Surface *_tmpscreen2;
SDL_Surface *_overlayscreen;
bool _useOldSrc;
Graphics::PixelFormat _overlayFormat;
bool _isDoubleBuf, _isHwPalette;
enum {
kTransactionNone = 0,
kTransactionActive = 1,
kTransactionRollback = 2
};
struct TransactionDetails {
bool sizeChanged;
bool needHotswap;
bool needUpdatescreen;
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool needTextureUpdate;
bool needDisplayResize;
#endif
#ifdef USE_RGB_COLOR
bool formatChanged;
#endif
TransactionDetails() {
sizeChanged = false;
needHotswap = false;
needUpdatescreen = false;
#if SDL_VERSION_ATLEAST(2, 0, 0)
needTextureUpdate = false;
needDisplayResize = false;
#endif
#ifdef USE_RGB_COLOR
formatChanged = false;
#endif
}
};
TransactionDetails _transactionDetails;
struct VideoState {
bool setup;
bool fullscreen;
bool aspectRatioCorrection;
AspectRatio desiredAspectRatio;
bool filtering;
int mode;
#if SDL_VERSION_ATLEAST(2, 0, 0)
int stretchMode;
#endif
bool vsync;
uint scalerIndex;
int scaleFactor;
int screenWidth, screenHeight;
int overlayWidth, overlayHeight;
int hardwareWidth, hardwareHeight;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat format;
#endif
VideoState() {
setup = false;
fullscreen = false;
aspectRatioCorrection = false;
// desiredAspectRatio set to (0, 0) by AspectRatio constructor
filtering = false;
mode = GFX_SURFACESDL;
#if SDL_VERSION_ATLEAST(2, 0, 0)
stretchMode = 0;
#endif
vsync = false;
scalerIndex = 0;
scaleFactor = 0;
screenWidth = 0;
screenHeight = 0;
overlayWidth = 0;
overlayHeight = 0;
hardwareWidth = 0;
hardwareHeight = 0;
#ifdef USE_RGB_COLOR
// format set to 0 values by Graphics::PixelFormat constructor
#endif
}
};
VideoState _videoMode, _oldVideoMode;
#if defined(WIN32) && !SDL_VERSION_ATLEAST(2, 0, 0)
/**
* Original BPP to restore the video mode on unload.
*
* This is required to make listing video modes for the OpenGL output work
* on Windows 8+. On these systems OpenGL modes are only available for
* 32bit formats. However, we setup a 16bit format and thus mode listings
* for OpenGL will return an empty list afterwards.
*
* In theory we might require this behavior on non-Win32 platforms too.
* However, SDL sometimes gives us invalid pixel formats for X11 outputs
* causing crashes when trying to setup the original pixel format.
* See bug #7038 "IRIX: X BadMatch when trying to start any 640x480 game".
*/
uint8 _originalBitsPerPixel;
#endif
int _transactionMode;
// Indicates whether it is needed to free _hwSurface in destructor
bool _displayDisabled;
const PluginList &_scalerPlugins;
ScalerPluginObject *_scalerPlugin;
Scaler *_scaler, *_mouseScaler;
uint _maxExtraPixels;
uint _extraPixels;
bool _screenIsLocked;
Graphics::Surface _framebuffer;
int _screenChangeCount;
enum {
NUM_DIRTY_RECT = 100,
MAX_SCALING = 3
};
// Dirty rect management
// When double-buffering we need to redraw both updates from
// current frame and previous frame. For convenience we copy
// them here before traversing the list.
SDL_Rect _dirtyRectList[2 * NUM_DIRTY_RECT];
int _numDirtyRects;
SDL_Rect _prevDirtyRectList[NUM_DIRTY_RECT];
int _numPrevDirtyRects;
struct MousePos {
// The size and hotspot of the original cursor image.
int16 w, h;
int16 hotX, hotY;
// The size and hotspot of the pre-scaled cursor image, in real
// coordinates.
int16 rW, rH;
int16 rHotX, rHotY;
// The size and hotspot of the pre-scaled cursor image, in game
// coordinates.
int16 vW, vH;
int16 vHotX, vHotY;
MousePos() : w(0), h(0), hotX(0), hotY(0),
rW(0), rH(0), rHotX(0), rHotY(0), vW(0), vH(0),
vHotX(0), vHotY(0)
{ }
};
SDL_Rect _mouseLastRect, _mouseNextRect;
MousePos _mouseCurState;
#ifdef USE_RGB_COLOR
uint32 _mouseKeyColor;
#else
byte _mouseKeyColor;
#endif
bool _disableMouseKeyColor;
byte _mappedMouseKeyColor;
bool _cursorDontScale;
bool _cursorPaletteDisabled;
SDL_Surface *_mouseOrigSurface;
SDL_Surface *_mouseSurface;
// Shake mode
// This is always set to 0 when building with SDL2.
int _currentShakeXOffset;
int _currentShakeYOffset;
// Palette data
SDL_Color *_currentPalette;
uint _paletteDirtyStart, _paletteDirtyEnd;
SDL_Color *_overlayPalette;
bool _isInOverlayPalette;
// Cursor palette data
SDL_Color *_cursorPalette;
/**
* Mutex which prevents multiple threads from interfering with each other
* when accessing the screen.
*/
Common::Mutex _graphicsMutex;
#ifdef USE_SDL_DEBUG_FOCUSRECT
bool _enableFocusRectDebugCode;
bool _enableFocusRect;
Common::Rect _focusRect;
#endif
virtual void addDirtyRect(int x, int y, int w, int h, bool inOverlay, bool realCoordinates = false);
virtual void drawMouse();
virtual void undrawMouse();
virtual void blitCursor();
virtual void internUpdateScreen();
virtual void updateScreen(SDL_Rect *dirtyRectList, int actualDirtyRects);
virtual bool loadGFXMode();
virtual void unloadGFXMode();
virtual bool hotswapGFXMode();
virtual void setAspectRatioCorrection(bool enable);
void setFilteringMode(bool enable);
void setVSync(bool enable);
bool saveScreenshot(const Common::Path &filename) const override;
virtual void setGraphicsModeIntern();
virtual void getDefaultResolution(uint &w, uint &h);
// In SurfaceSDL mode we never render in 3D and can always switch the fullscreen state
bool canSwitchFullscreen() const override { return true; }
private:
void setFullscreenMode(bool enable);
void handleScalerHotkeys(uint mode, int factor);
/**
* Converts the given point from the overlay's coordinate space to the
* game's coordinate space.
*/
Common::Point convertOverlayToGame(const int x, const int y) const {
if (getOverlayWidth() == 0 || getOverlayHeight() == 0) {
error("convertOverlayToGame called without a valid overlay");
}
return Common::Point(x * getWidth() / getOverlayWidth(),
y * getHeight() / getOverlayHeight());
}
/**
* Converts the given point from the game's coordinate space to the
* overlay's coordinate space.
*/
Common::Point convertGameToOverlay(const int x, const int y) const {
if (getWidth() == 0 || getHeight() == 0) {
error("convertGameToOverlay called without a valid overlay");
}
return Common::Point(x * getOverlayWidth() / getWidth(),
y * getOverlayHeight() / getHeight());
}
/**
* Special case for scalers that use the useOldSrc feature (currently
* only the Edge scalers). The variable is checked after closing the
* overlay, so that the creation of a new output buffer for the scaler
* can be triggered.
*/
bool _needRestoreAfterOverlay;
bool _prevForceRedraw;
bool _prevCursorNeedsRedraw;
};
#endif

View File

@@ -0,0 +1,689 @@
/* 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 BACKENDS_GRAPHICS_WINDOWED_H
#define BACKENDS_GRAPHICS_WINDOWED_H
#include "backends/graphics/graphics.h"
#include "common/frac.h"
#include "common/rect.h"
#include "common/config-manager.h"
#include "common/textconsole.h"
#include "graphics/scaler/aspect.h"
enum {
STRETCH_CENTER = 0,
STRETCH_INTEGRAL = 1,
STRETCH_INTEGRAL_AR = 2,
STRETCH_FIT = 3,
STRETCH_STRETCH = 4,
STRETCH_FIT_FORCE_ASPECT = 5
};
enum {
SCREEN_ALIGN_CENTER = 0,
SCREEN_ALIGN_LEFT = 1,
SCREEN_ALIGN_RIGHT = 2,
SCREEN_ALIGN_XMASK = 3,
SCREEN_ALIGN_MIDDLE = 0,
SCREEN_ALIGN_TOP = 4,
SCREEN_ALIGN_BOTTOM = 8,
SCREEN_ALIGN_YMASK = 12
};
class WindowedGraphicsManager : virtual public GraphicsManager {
public:
WindowedGraphicsManager() :
_windowWidth(0),
_windowHeight(0),
_screenAlign(SCREEN_ALIGN_CENTER | SCREEN_ALIGN_MIDDLE),
_rotationMode(Common::kRotationNormal),
_ignoreGameSafeArea(false),
_overlayVisible(false),
_overlayInGUI(false),
_gameScreenShakeXOffset(0),
_gameScreenShakeYOffset(0),
_forceRedraw(false),
_cursorVisible(false),
_cursorX(0),
_cursorY(0),
_cursorNeedsRedraw(false),
_cursorLastInActiveArea(true) {}
void showOverlay(bool inGUI) override {
_overlayInGUI = inGUI;
if (inGUI) {
_activeArea.drawRect = _overlayDrawRect;
_activeArea.width = getOverlayWidth();
_activeArea.height = getOverlayHeight();
} else {
_activeArea.drawRect = _gameDrawRect;
_activeArea.width = getWidth();
_activeArea.height = getHeight();
}
if (_overlayVisible)
return;
_overlayVisible = true;
_forceRedraw = true;
notifyActiveAreaChanged();
}
void hideOverlay() override {
if (!_overlayVisible)
return;
_overlayInGUI = false;
_activeArea.drawRect = _gameDrawRect;
_activeArea.width = getWidth();
_activeArea.height = getHeight();
_overlayVisible = false;
_forceRedraw = true;
notifyActiveAreaChanged();
}
bool isOverlayVisible() const override { return _overlayVisible; }
Common::Rect getSafeOverlayArea(int16 *width, int16 *height) const override {
Insets insets = getSafeAreaInsets();
// Create the overlay rect cut of the insets
// in the window coordinate space
// Make sure to avoid a negative size (and an invalid rect)
const int safeLeft = MAX(_overlayDrawRect.left, insets.left),
safeTop = MAX(_overlayDrawRect.top, insets.top);
Common::Rect safeArea(safeLeft, safeTop,
MAX(safeLeft, MIN((int)_overlayDrawRect.right, _windowWidth - insets.right)),
MAX(safeTop, MIN((int)_overlayDrawRect.bottom, _windowHeight - insets.bottom)));
// Convert this safe area in the overlay coordinate space
const int targetWidth = getOverlayWidth(),
targetHeight = getOverlayHeight(),
sourceWidth = _overlayDrawRect.width(),
sourceHeight = _overlayDrawRect.height();
if (width) *width = targetWidth;
if (height) *height = targetHeight;
int rotatedTargetWidth = targetWidth,
rotatedTargetHeight = targetHeight;
if (_rotationMode == Common::kRotation90 || _rotationMode == Common::kRotation270) {
SWAP(rotatedTargetWidth, rotatedTargetHeight);
}
// First make it relative to overlay origin and scale it
safeArea.left = ((safeArea.left - _overlayDrawRect.left) * rotatedTargetWidth) / sourceWidth;
safeArea.top = ((safeArea.top - _overlayDrawRect.top) * rotatedTargetHeight) / sourceHeight;
safeArea.right = ((safeArea.right - _overlayDrawRect.left) * rotatedTargetWidth) / sourceWidth;
safeArea.bottom = ((safeArea.bottom - _overlayDrawRect.top) * rotatedTargetHeight) / sourceHeight;
// Now rotate it
switch (_rotationMode) {
default:
case Common::kRotationNormal:
// Nothing to do
break;
case Common::kRotation90: {
int16 tmp = safeArea.left;
safeArea.left = safeArea.top;
safeArea.top = rotatedTargetWidth - safeArea.right;
//safeArea.right = targetWidth - (rotatedTargetHeight - safeArea.bottom);
safeArea.right = safeArea.bottom; // targetWidth == rotatedTargetHeight
safeArea.bottom = targetHeight - tmp;
break;
}
case Common::kRotation180: {
int16 tmp;
tmp = safeArea.left;
safeArea.left = rotatedTargetWidth - safeArea.right;
safeArea.right = rotatedTargetWidth - tmp;
tmp = safeArea.top;
safeArea.top = rotatedTargetHeight - safeArea.bottom;
safeArea.bottom = rotatedTargetHeight - tmp;
break;
}
case Common::kRotation270: {
int16 tmp = safeArea.left;
safeArea.left = rotatedTargetHeight - safeArea.bottom;
//safeArea.bottom = targetHeight - (rotatedTargetWidth - safeArea.right);
safeArea.bottom = safeArea.right; // targetHeight == rotatedTargetWidth
safeArea.right = targetWidth - safeArea.top;
safeArea.top = tmp;
break;
}
}
return safeArea;
}
void setShakePos(int shakeXOffset, int shakeYOffset) override {
if (_gameScreenShakeXOffset != shakeXOffset || _gameScreenShakeYOffset != shakeYOffset) {
_gameScreenShakeXOffset = shakeXOffset;
_gameScreenShakeYOffset = shakeYOffset;
recalculateDisplayAreas();
_cursorNeedsRedraw = true;
}
}
int getWindowWidth() const { return _windowWidth; }
int getWindowHeight() const { return _windowHeight; }
void setIgnoreGameSafeArea(bool ignoreGameSafeArea) {
if (_ignoreGameSafeArea == ignoreGameSafeArea) {
return;
}
_ignoreGameSafeArea = ignoreGameSafeArea;
Insets insets = getSafeAreaInsets();
if (insets.left == 0 &&
insets.top == 0 &&
insets.right == 0 &&
insets.bottom == 0) {
return;
}
handleResizeImpl(_windowWidth, _windowHeight);
}
protected:
/**
* @returns whether or not the game screen must have aspect ratio correction
* applied for correct rendering.
*/
virtual bool gameNeedsAspectRatioCorrection() const = 0;
/**
* Backend-specific implementation for updating internal surfaces that need
* to reflect the new window size.
*/
virtual void handleResizeImpl(const int width, const int height) = 0;
/**
* Converts the given point from the active virtual screen's coordinate
* space to the window's coordinate space (i.e. game-to-window or
* overlay-to-window).
*/
Common::Point convertVirtualToWindow(const int x, const int y) const {
const int targetX = _activeArea.drawRect.left;
const int targetY = _activeArea.drawRect.top;
const int targetWidth = _activeArea.drawRect.width();
const int targetHeight = _activeArea.drawRect.height();
const int sourceWidth = _activeArea.width;
const int sourceHeight = _activeArea.height;
if (sourceWidth == 0 || sourceHeight == 0) {
error("convertVirtualToWindow called without a valid draw rect");
}
int windowX, windowY;
switch (_rotationMode) {
default:
case Common::kRotationNormal:
windowX = targetX + (x * targetWidth + sourceWidth / 2) / sourceWidth;
windowY = targetY + (y * targetHeight + sourceHeight / 2) / sourceHeight;
break;
case Common::kRotation90:
windowX = targetX + ((y - (sourceHeight - 1)) * targetWidth + sourceHeight / 2) / sourceHeight;
windowY = targetY + (x * targetHeight + sourceWidth / 2) / sourceWidth;
break;
case Common::kRotation180:
windowX = targetX + ((x - (sourceWidth - 1)) * targetWidth + sourceWidth / 2) / sourceWidth;
windowY = targetY + ((y - (sourceHeight - 1)) * targetHeight + sourceHeight / 2) / sourceHeight;
break;
case Common::kRotation270:
windowX = targetX + (y * targetWidth + sourceHeight / 2) / sourceHeight;
windowY = targetY + ((x - (sourceWidth - 1)) * targetHeight + sourceWidth / 2) / sourceWidth;
break;
}
return Common::Point(CLIP<int>(windowX, targetX, targetX + targetWidth - 1),
CLIP<int>(windowY, targetY, targetY + targetHeight - 1));
}
/**
* Converts the given point from the window's coordinate space to the
* active virtual screen's coordinate space (i.e. window-to-game or
* window-to-overlay).
*/
Common::Point convertWindowToVirtual(int x, int y) const {
const int sourceX = _activeArea.drawRect.left;
const int sourceY = _activeArea.drawRect.top;
const int sourceMaxX = _activeArea.drawRect.right - 1;
const int sourceMaxY = _activeArea.drawRect.bottom - 1;
const int sourceWidth = _activeArea.drawRect.width();
const int sourceHeight = _activeArea.drawRect.height();
const int targetWidth = _activeArea.width;
const int targetHeight = _activeArea.height;
if (sourceWidth == 0 || sourceHeight == 0) {
error("convertWindowToVirtual called without a valid draw rect");
}
x = CLIP<int>(x, sourceX, sourceMaxX);
y = CLIP<int>(y, sourceY, sourceMaxY);
int virtualX, virtualY;
switch (_rotationMode) {
default:
case Common::kRotationNormal:
virtualX = ((x - sourceX) * targetWidth + sourceWidth / 2) / sourceWidth;
virtualY = ((y - sourceY) * targetHeight + sourceHeight / 2) / sourceHeight;
break;
case Common::kRotation90:
virtualY = targetHeight - 1 - ((x - sourceX) * targetHeight + sourceWidth / 2) / sourceWidth;
virtualX = ((y - sourceY) * targetWidth + sourceHeight / 2) / sourceHeight;
break;
case Common::kRotation180:
virtualX = targetWidth - 1 - ((x - sourceX) * targetWidth + sourceWidth / 2) / sourceWidth;
virtualY = targetHeight - 1 - ((y - sourceY) * targetHeight + sourceHeight / 2) / sourceHeight;
break;
case Common::kRotation270:
virtualY = ((x - sourceX) * targetHeight + sourceWidth / 2) / sourceWidth;
virtualX = targetWidth - 1 - ((y - sourceY) * targetWidth + sourceHeight / 2) / sourceHeight;
break;
}
return Common::Point(CLIP<int>(virtualX, 0, targetWidth - 1),
CLIP<int>(virtualY, 0, targetHeight - 1));
}
/**
* @returns the desired aspect ratio of the game surface.
*/
frac_t getDesiredGameAspectRatio() const {
if (getHeight() == 0 || gameNeedsAspectRatioCorrection()) {
return intToFrac(4) / 3;
}
return intToFrac(getWidth()) / getHeight();
}
/**
* @returns the scale used between the game size and the surface on which it is rendered.
*/
virtual int getGameRenderScale() const {
return 1;
}
struct Insets {
int16 left;
int16 top;
int16 right;
int16 bottom;
};
/**
* Returns the insets needed to get a safe area which does not interfere
* with any system UI elements such as the notch or home indicator on mobile devices.
*
* @return The safe area insets
*/
virtual Insets getSafeAreaInsets() const {
return {0, 0, 0, 0};
}
/**
* Called after the window has been updated with new dimensions.
*
* @param width The new width of the window, excluding window decoration.
* @param height The new height of the window, excluding window decoration.
*/
void handleResize(const int width, const int height) {
_windowWidth = width;
_windowHeight = height;
handleResizeImpl(width, height);
}
/**
* Recalculates the display areas for the game and overlay surfaces within
* the window.
*/
virtual void recalculateDisplayAreas() {
if (_windowHeight == 0) {
return;
}
// Compute a safe area rectangle out of the insets
Insets insets;
if (_ignoreGameSafeArea) {
insets = {0, 0, 0, 0};
} else {
insets = getSafeAreaInsets();
}
Common::Rect safeArea(insets.left, insets.top,
_windowWidth - insets.right,
_windowHeight - insets.bottom);
// Create a game draw rect using the safe are dimensions
populateDisplayAreaDrawRect(getDesiredGameAspectRatio(),
getWidth() * getGameRenderScale(), getHeight() * getGameRenderScale(),
safeArea, _gameDrawRect);
if (getOverlayHeight()) {
const int16 overlayWidth = getOverlayWidth(),
overlayHeight = getOverlayHeight();
const frac_t overlayAspect = intToFrac(overlayWidth) / overlayHeight;
populateDisplayAreaDrawRect(overlayAspect, overlayWidth, overlayHeight,
Common::Rect(_windowWidth, _windowHeight),_overlayDrawRect);
}
if (_overlayInGUI) {
_activeArea.drawRect = _overlayDrawRect;
_activeArea.width = getOverlayWidth();
_activeArea.height = getOverlayHeight();
} else {
_activeArea.drawRect = _gameDrawRect;
_activeArea.width = getWidth();
_activeArea.height = getHeight();
}
notifyActiveAreaChanged();
}
/**
* Sets the position of the hardware mouse cursor in the host system,
* relative to the window.
*
* @param x X coordinate in window coordinates.
* @param y Y coordinate in window coordinates.
*/
virtual void setSystemMousePosition(const int x, const int y) = 0;
/**
* Called whenever the active area has changed.
*/
virtual void notifyActiveAreaChanged() {}
bool showMouse(bool visible) override {
if (_cursorVisible == visible) {
return visible;
}
const bool last = _cursorVisible;
_cursorVisible = visible;
_cursorNeedsRedraw = true;
return last;
}
/**
* Move ("warp") the mouse cursor to the specified position.
*
* @param x The new X position of the mouse in virtual screen coordinates.
* @param y The new Y position of the mouse in virtual screen coordinates.
*/
void warpMouse(int x, int y) override {
// Check active coordinate instead of window coordinate to avoid warping
// the mouse if it is still within the same virtual pixel
const Common::Point virtualCursor = convertWindowToVirtual(_cursorX, _cursorY);
if (virtualCursor.x != x || virtualCursor.y != y) {
// Warping the mouse in SDL generates a mouse movement event, so
// `setMousePosition` would be called eventually through the
// `notifyMousePosition` callback if we *only* set the system mouse
// position here. However, this can cause problems with some games.
// For example, the cannon script in CoMI calls to warp the mouse
// twice each time the cannon is reloaded, and unless we update the
// mouse position immediately, the second call is ignored, which
// causes the cannon to change its aim.
const Common::Point windowCursor = convertVirtualToWindow(x, y);
setMousePosition(windowCursor.x, windowCursor.y);
setSystemMousePosition(windowCursor.x, windowCursor.y);
}
}
/**
* Sets the position of the rendered mouse cursor in the window.
*
* @param x X coordinate in window coordinates.
* @param y Y coordinate in window coordinates.
*/
void setMousePosition(int x, int y) {
if (_cursorX != x || _cursorY != y) {
_cursorNeedsRedraw = true;
}
_cursorX = x;
_cursorY = y;
}
/**
* The width of the window, excluding window decoration.
*/
int _windowWidth;
/**
* The height of the window, excluding window decoration.
*/
int _windowHeight;
/**
* How the overlay and game screens are aligned in the window.
* Centered vertically and horizontally by default.
*/
int _screenAlign;
/**
* How the screens need to be rotated on the screen
*/
Common::RotationMode _rotationMode;
/**
* Whether the safe area should be ignored for games
*/
bool _ignoreGameSafeArea;
/**
* Whether the overlay (i.e. launcher, including the out-of-game launcher)
* is visible or not.
*/
bool _overlayVisible;
/**
* Whether when overlay is shown, mouse coordinates depend on window or game screen size
*/
bool _overlayInGUI;
/**
* The offset by which the screen is moved horizontally.
*/
int _gameScreenShakeXOffset;
/**
* The offset by which the screen is moved vertically.
*/
int _gameScreenShakeYOffset;
/**
* The scaled draw rectangle for the game surface within the window.
*/
Common::Rect _gameDrawRect;
/**
* The scaled draw rectangle for the overlay (launcher) surface within the
* window.
*/
Common::Rect _overlayDrawRect;
/**
* Data about the display area of a virtual screen.
*/
struct DisplayArea {
/**
* The scaled area where the virtual screen is drawn within the window.
*/
Common::Rect drawRect;
/**
* The width of the virtual screen's unscaled coordinate space.
*/
int width;
/**
* The height of the virtual screen's unscaled coordinate space.
*/
int height;
};
/**
* Display area information about the currently active virtual screen. This
* will be the overlay screen when the overlay is active, and the game
* screen otherwise.
*/
DisplayArea _activeArea;
/**
* Whether the screen must be redrawn on the next frame.
*/
bool _forceRedraw;
/**
* Whether the cursor is actually visible.
*/
bool _cursorVisible;
/**
* Whether the mouse cursor needs to be redrawn on the next frame.
*/
bool _cursorNeedsRedraw;
/**
* Whether the last position of the system cursor was within the active area
* of the window.
*/
bool _cursorLastInActiveArea;
/**
* The position of the mouse cursor, in window coordinates.
*/
int _cursorX, _cursorY;
private:
void populateDisplayAreaDrawRect(const frac_t displayAspect, int originalWidth, int originalHeight, const Common::Rect &safeArea, Common::Rect &drawRect) const {
int mode = getStretchMode();
Common::Rect rotatedSafeArea(safeArea);
int rotatedWindowWidth = _windowWidth,
rotatedWindowHeight = _windowHeight;
if (_rotationMode == Common::kRotation90 || _rotationMode == Common::kRotation270) {
SWAP(rotatedSafeArea.left, rotatedSafeArea.top);
SWAP(rotatedSafeArea.right, rotatedSafeArea.bottom);
SWAP(rotatedWindowWidth, rotatedWindowHeight);
}
const int rotatedSafeWidth = rotatedSafeArea.width(),
rotatedSafeHeight = rotatedSafeArea.height();
// Mode Center = use original size, or divide by an integral amount if window is smaller than game surface
// Mode Integral = scale by an integral amount.
// Mode Fit = scale to fit the window while respecting the aspect ratio
// Mode Stretch = scale and stretch to fit the window without respecting the aspect ratio
// Mode Fit Force Aspect = scale to fit the window while forcing a 4:3 aspect ratio
int width = 0, height = 0;
if (mode == STRETCH_CENTER || mode == STRETCH_INTEGRAL || mode == STRETCH_INTEGRAL_AR) {
width = originalWidth;
height = intToFrac(width) / displayAspect;
if (width > rotatedSafeWidth || height > rotatedSafeHeight) {
int fac = 1 + MAX((width - 1) / rotatedSafeWidth, (height - 1) / rotatedSafeHeight);
width /= fac;
height /= fac;
} else if (mode == STRETCH_INTEGRAL) {
int fac = MIN(rotatedSafeWidth / width, rotatedSafeHeight / height);
width *= fac;
height *= fac;
} else if (mode == STRETCH_INTEGRAL_AR) {
int targetHeight = height;
int horizontalFac = rotatedSafeWidth / width;
do {
width = originalWidth * horizontalFac;
int verticalFac = (targetHeight * horizontalFac + originalHeight / 2) / originalHeight;
height = originalHeight * verticalFac;
--horizontalFac;
} while (horizontalFac > 0 && height > rotatedSafeHeight);
if (height > rotatedSafeHeight)
height = targetHeight;
}
} else {
frac_t windowAspect = intToFrac(rotatedSafeWidth) / rotatedSafeHeight;
width = rotatedSafeWidth;
height = rotatedSafeHeight;
if (mode == STRETCH_FIT_FORCE_ASPECT) {
frac_t ratio = intToFrac(4) / 3;
if (windowAspect < ratio)
height = intToFrac(width) / ratio;
else if (windowAspect > ratio)
width = fracToInt(height * ratio);
} else if (mode != STRETCH_STRETCH) {
if (windowAspect < displayAspect)
height = intToFrac(width) / displayAspect;
else if (windowAspect > displayAspect)
width = fracToInt(height * displayAspect);
}
}
int16 alignX, alignY;
switch (_screenAlign & SCREEN_ALIGN_XMASK) {
default:
case SCREEN_ALIGN_CENTER:
alignX = ((rotatedWindowWidth - width) / 2);
break;
case SCREEN_ALIGN_LEFT:
alignX = 0;
break;
case SCREEN_ALIGN_RIGHT:
alignX = (rotatedSafeArea.right - width);
break;
}
switch (_screenAlign & SCREEN_ALIGN_YMASK) {
default:
case SCREEN_ALIGN_MIDDLE:
alignY = ((rotatedWindowHeight - height) / 2);
break;
case SCREEN_ALIGN_TOP:
alignY = 0;
break;
case SCREEN_ALIGN_BOTTOM:
alignY = (rotatedSafeArea.bottom - height);
break;
}
rotatedSafeArea.constrain(alignX, alignY, width, height);
alignX += _gameScreenShakeXOffset * width / getWidth();
alignY += _gameScreenShakeYOffset * height / getHeight();
if (_rotationMode == Common::kRotation90 || _rotationMode == Common::kRotation270) {
drawRect.top = alignX;
drawRect.left = alignY;
drawRect.setWidth(height);
drawRect.setHeight(width);
} else {
drawRect.left = alignX;
drawRect.top = alignY;
drawRect.setWidth(width);
drawRect.setHeight(height);
}
}
};
#endif