2011-03-04 21:41:55 +01:00

531 lines
12 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#if defined(__ANDROID__)
#include "backends/platform/android/android.h"
#include "backends/platform/android/jni.h"
static inline GLfixed xdiv(int numerator, int denominator) {
assert(numerator < (1 << 16));
return (numerator << 16) / denominator;
}
const OSystem::GraphicsMode *OSystem_Android::getSupportedGraphicsModes() const {
static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{ "default", "Default", 1 },
{ 0, 0, 0 },
};
return s_supportedGraphicsModes;
}
int OSystem_Android::getDefaultGraphicsMode() const {
return 1;
}
bool OSystem_Android::setGraphicsMode(const char *mode) {
ENTER("%s", mode);
return true;
}
bool OSystem_Android::setGraphicsMode(int mode) {
ENTER("%d", mode);
return true;
}
int OSystem_Android::getGraphicsMode() const {
return 1;
}
void OSystem_Android::initSurface() {
LOGD("initializing surface");
assert(!JNI::haveSurface());
_screen_changeid = JNI::surface_changeid;
_egl_surface_width = JNI::egl_surface_width;
_egl_surface_height = JNI::egl_surface_height;
assert(_egl_surface_width > 0 && _egl_surface_height > 0);
JNI::initSurface();
// Initialise OpenGLES context.
GLESTexture::initGLExtensions();
if (_game_texture)
_game_texture->reinit();
if (_overlay_texture)
_overlay_texture->reinit();
if (_mouse_texture)
_mouse_texture->reinit();
}
void OSystem_Android::deinitSurface() {
if (!JNI::haveSurface())
return;
LOGD("deinitializing surface");
_screen_changeid = JNI::surface_changeid;
_egl_surface_width = 0;
_egl_surface_height = 0;
// release texture resources
if (_game_texture)
_game_texture->release();
if (_overlay_texture)
_overlay_texture->release();
if (_mouse_texture)
_mouse_texture->release();
JNI::deinitSurface();
}
void OSystem_Android::initViewport() {
LOGD("initializing viewport");
assert(JNI::haveSurface());
// Turn off anything that looks like 3D ;)
GLCALL(glDisable(GL_CULL_FACE));
GLCALL(glDisable(GL_DEPTH_TEST));
GLCALL(glDisable(GL_LIGHTING));
GLCALL(glDisable(GL_FOG));
GLCALL(glDisable(GL_DITHER));
GLCALL(glShadeModel(GL_FLAT));
GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST));
GLCALL(glEnable(GL_BLEND));
GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
GLCALL(glEnableClientState(GL_VERTEX_ARRAY));
GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
GLCALL(glEnable(GL_TEXTURE_2D));
GLCALL(glViewport(0, 0, _egl_surface_width, _egl_surface_height));
GLCALL(glMatrixMode(GL_PROJECTION));
GLCALL(glLoadIdentity());
GLCALL(glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1));
GLCALL(glMatrixMode(GL_MODELVIEW));
GLCALL(glLoadIdentity());
clearFocusRectangle();
}
void OSystem_Android::initSize(uint width, uint height,
const Graphics::PixelFormat *format) {
ENTER("%d, %d, %p", width, height, format);
GLTHREADCHECK;
_game_texture->allocBuffer(width, height);
_game_texture->fillBuffer(0);
int overlay_width = _egl_surface_width;
int overlay_height = _egl_surface_height;
// the 'normal' theme layout uses a max height of 400 pixels. if the
// surface is too big we use only a quarter of the size so that the widgets
// don't get too small. if the surface height has less than 800 pixels, this
// enforces the 'lowres' layout, which will be scaled back up by factor 2x,
// but this looks way better than the 'normal' layout scaled by some
// calculated factors
if (overlay_height > 480) {
overlay_width /= 2;
overlay_height /= 2;
}
LOGI("overlay size is %ux%u", overlay_width, overlay_height);
_overlay_texture->allocBuffer(overlay_width, overlay_height);
// Don't know mouse size yet - it gets reallocated in
// setMouseCursor. We need the palette allocated before
// setMouseCursor however, so just take a guess at the desired
// size (it's small).
_mouse_texture->allocBuffer(20, 20);
}
int OSystem_Android::getScreenChangeID() const {
return _screen_changeid;
}
int16 OSystem_Android::getHeight() {
return _game_texture->height();
}
int16 OSystem_Android::getWidth() {
return _game_texture->width();
}
void OSystem_Android::setPalette(const byte *colors, uint start, uint num) {
ENTER("%p, %u, %u", colors, start, num);
GLTHREADCHECK;
if (!_use_mouse_palette)
_setCursorPalette(colors, start, num);
memcpy(_game_texture->palette() + start * 3, colors, num * 3);
}
void OSystem_Android::grabPalette(byte *colors, uint start, uint num) {
ENTER("%p, %u, %u", colors, start, num);
GLTHREADCHECK;
memcpy(colors, _game_texture->palette_const() + start * 3, num * 3);
}
void OSystem_Android::copyRectToScreen(const byte *buf, int pitch,
int x, int y, int w, int h) {
ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h);
GLTHREADCHECK;
_game_texture->updateBuffer(x, y, w, h, buf, pitch);
}
void OSystem_Android::updateScreen() {
//ENTER();
GLTHREADCHECK;
if (!JNI::haveSurface())
return;
if (!_force_redraw &&
!_game_texture->dirty() &&
!_overlay_texture->dirty() &&
!_mouse_texture->dirty())
return;
_force_redraw = false;
GLCALL(glPushMatrix());
if (_shake_offset != 0 ||
(!_focus_rect.isEmpty() &&
!Common::Rect(_game_texture->width(),
_game_texture->height()).contains(_focus_rect))) {
// These are the only cases where _game_texture doesn't
// cover the entire screen.
GLCALL(glClearColorx(0, 0, 0, 1 << 16));
GLCALL(glClear(GL_COLOR_BUFFER_BIT));
// Move everything up by _shake_offset (game) pixels
GLCALL(glTranslatex(0, -_shake_offset << 16, 0));
}
if (_focus_rect.isEmpty()) {
_game_texture->drawTexture(0, 0, _egl_surface_width,
_egl_surface_height);
} else {
GLCALL(glPushMatrix());
GLCALL(glScalex(xdiv(_egl_surface_width, _focus_rect.width()),
xdiv(_egl_surface_height, _focus_rect.height()),
1 << 16));
GLCALL(glTranslatex(-_focus_rect.left << 16,
-_focus_rect.top << 16, 0));
GLCALL(glScalex(xdiv(_game_texture->width(), _egl_surface_width),
xdiv(_game_texture->height(), _egl_surface_height),
1 << 16));
_game_texture->drawTexture(0, 0, _egl_surface_width,
_egl_surface_height);
GLCALL(glPopMatrix());
}
int cs = _mouse_targetscale;
if (_show_overlay) {
// ugly, but the modern theme sets a wacko factor, only god knows why
cs = 1;
GLCALL(_overlay_texture->drawTexture(0, 0, _egl_surface_width,
_egl_surface_height));
}
if (_show_mouse) {
GLCALL(glPushMatrix());
// Scale up ScummVM -> OpenGL (pixel) coordinates
int texwidth, texheight;
if (_show_overlay) {
texwidth = getOverlayWidth();
texheight = getOverlayHeight();
} else {
texwidth = getWidth();
texheight = getHeight();
}
GLCALL(glScalex(xdiv(_egl_surface_width, texwidth),
xdiv(_egl_surface_height, texheight),
1 << 16));
GLCALL(glTranslatex((-_mouse_hotspot.x * cs) << 16,
(-_mouse_hotspot.y * cs) << 16,
0));
// Note the extra half texel to position the mouse in
// the middle of the x,y square:
const Common::Point& mouse = getEventManager()->getMousePos();
GLCALL(glTranslatex((mouse.x << 16) | 1 << 15,
(mouse.y << 16) | 1 << 15, 0));
GLCALL(glScalex(cs << 16, cs << 16, 1 << 16));
_mouse_texture->drawTexture();
GLCALL(glPopMatrix());
}
GLCALL(glPopMatrix());
if (!JNI::swapBuffers())
LOGW("swapBuffers failed: 0x%x", glGetError());
}
Graphics::Surface *OSystem_Android::lockScreen() {
ENTER();
GLTHREADCHECK;
Graphics::Surface *surface = _game_texture->surface();
assert(surface->pixels);
return surface;
}
void OSystem_Android::unlockScreen() {
ENTER();
GLTHREADCHECK;
assert(_game_texture->dirty());
}
void OSystem_Android::setShakePos(int shake_offset) {
ENTER("%d", shake_offset);
if (_shake_offset != shake_offset) {
_shake_offset = shake_offset;
_force_redraw = true;
}
}
void OSystem_Android::fillScreen(uint32 col) {
ENTER("%u", col);
GLTHREADCHECK;
assert(col < 256);
_game_texture->fillBuffer(col);
}
void OSystem_Android::setFocusRectangle(const Common::Rect& rect) {
ENTER("%d, %d, %d, %d", rect.left, rect.top, rect.right, rect.bottom);
if (_enable_zoning) {
_focus_rect = rect;
_force_redraw = true;
}
}
void OSystem_Android::clearFocusRectangle() {
ENTER();
if (_enable_zoning) {
_focus_rect = Common::Rect();
_force_redraw = true;
}
}
void OSystem_Android::showOverlay() {
ENTER();
_show_overlay = true;
_force_redraw = true;
}
void OSystem_Android::hideOverlay() {
ENTER();
_show_overlay = false;
_force_redraw = true;
}
void OSystem_Android::clearOverlay() {
ENTER();
GLTHREADCHECK;
_overlay_texture->fillBuffer(0);
// breaks more than it fixes, disabled for now
// Shouldn't need this, but works around a 'blank screen' bug on Nexus1
//updateScreen();
}
void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) {
ENTER("%p, %d", buf, pitch);
GLTHREADCHECK;
// We support overlay alpha blending, so the pixel data here
// shouldn't actually be used. Let's fill it with zeros, I'm sure
// it will be fine...
const Graphics::Surface *surface = _overlay_texture->surface_const();
assert(surface->bytesPerPixel == sizeof(buf[0]));
uint h = surface->h;
do {
memset(buf, 0, surface->w * sizeof(buf[0]));
// This 'pitch' is pixels not bytes
buf += pitch;
} while (--h);
}
void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch,
int x, int y, int w, int h) {
ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h);
GLTHREADCHECK;
const Graphics::Surface *surface = _overlay_texture->surface_const();
assert(surface->bytesPerPixel == sizeof(buf[0]));
// This 'pitch' is pixels not bytes
_overlay_texture->updateBuffer(x, y, w, h, buf, pitch * sizeof(buf[0]));
// Shouldn't need this, but works around a 'blank screen' bug on Nexus1?
//updateScreen();
}
int16 OSystem_Android::getOverlayHeight() {
return _overlay_texture->height();
}
int16 OSystem_Android::getOverlayWidth() {
return _overlay_texture->width();
}
bool OSystem_Android::showMouse(bool visible) {
ENTER("%d", visible);
_show_mouse = visible;
return true;
}
void OSystem_Android::warpMouse(int x, int y) {
ENTER("%d, %d", x, y);
// We use only the eventmanager's idea of the current mouse
// position, so there is nothing extra to do here.
}
void OSystem_Android::setMouseCursor(const byte *buf, uint w, uint h,
int hotspotX, int hotspotY,
uint32 keycolor, int cursorTargetScale,
const Graphics::PixelFormat *format) {
ENTER("%p, %u, %u, %d, %d, %u, %d, %p", buf, w, h, hotspotX, hotspotY,
keycolor, cursorTargetScale, format);
GLTHREADCHECK;
assert(keycolor < 256);
_mouse_texture->allocBuffer(w, h);
// Update palette alpha based on keycolor
byte *palette = _mouse_texture->palette();
uint i = 256;
do {
palette[3] = 0xff;
palette += 4;
} while (--i);
palette = _mouse_texture->palette();
palette[keycolor * 4 + 3] = 0x00;
if (w == 0 || h == 0)
return;
_mouse_texture->updateBuffer(0, 0, w, h, buf, w);
_mouse_hotspot = Common::Point(hotspotX, hotspotY);
_mouse_targetscale = cursorTargetScale;
}
void OSystem_Android::_setCursorPalette(const byte *colors,
uint start, uint num) {
byte *palette = _mouse_texture->palette() + start * 4;
do {
for (int i = 0; i < 3; ++i)
palette[i] = colors[i];
// Leave alpha untouched to preserve keycolor
palette += 4;
colors += 3;
} while (--num);
}
void OSystem_Android::setCursorPalette(const byte *colors,
uint start, uint num) {
ENTER("%p, %u, %u", colors, start, num);
GLTHREADCHECK;
_setCursorPalette(colors, start, num);
_use_mouse_palette = true;
}
void OSystem_Android::disableCursorPalette(bool disable) {
ENTER("%d", disable);
_use_mouse_palette = !disable;
}
#endif