aboutsummaryrefslogtreecommitdiff
path: root/backends/platform/android/video.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backends/platform/android/video.cpp')
-rw-r--r--backends/platform/android/video.cpp334
1 files changed, 334 insertions, 0 deletions
diff --git a/backends/platform/android/video.cpp b/backends/platform/android/video.cpp
new file mode 100644
index 0000000000..d4c002fbd0
--- /dev/null
+++ b/backends/platform/android/video.cpp
@@ -0,0 +1,334 @@
+/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/null/null.cpp $
+ * $Id: null.cpp 34912 2008-11-06 15:02:50Z fingolfin $
+ *
+ */
+
+#include "base/main.h"
+#include "graphics/surface.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <android/log.h>
+
+#include "common/rect.h"
+#include "common/array.h"
+#include "common/util.h"
+#include "common/tokenizer.h"
+
+#include "backends/platform/android/video.h"
+
+#undef LOG_TAG
+#define LOG_TAG "ScummVM-video"
+
+#if 0
+#define ENTER(args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, args)
+#else
+#define ENTER(args...) /**/
+#endif
+
+#if 0
+#define CHECK_GL_ERROR() checkGlError(__FILE__, __LINE__)
+static const char* getGlErrStr(GLenum error) {
+ switch (error) {
+ case GL_NO_ERROR: return "GL_NO_ERROR";
+ case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
+ case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
+ case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
+ case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
+ case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
+ }
+
+ static char buf[40];
+ snprintf(buf, sizeof(buf), "(Unknown GL error code 0x%x)", error);
+ return buf;
+}
+static void checkGlError(const char* file, int line) {
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR)
+ warning("%s:%d: GL error: %s", file, line, getGlErrStr(error));
+}
+#else
+#define CHECK_GL_ERROR() do {} while (false)
+#endif
+
+// Supported GL extensions
+static bool npot_supported = false;
+#ifdef GL_OES_draw_texture
+static bool draw_tex_supported = false;
+#endif
+
+static inline GLfixed xdiv(int numerator, int denominator) {
+ assert(numerator < (1<<16));
+ return (numerator << 16) / denominator;
+}
+
+template <class T>
+static T nextHigher2(T k) {
+ if (k == 0)
+ return 1;
+ --k;
+ for (uint i = 1; i < sizeof(T)*CHAR_BIT; i <<= 1)
+ k = k | k >> i;
+ return k + 1;
+}
+
+void GLESTexture::initGLExtensions() {
+ const char* ext_string =
+ reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
+ __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+ "Extensions: %s", ext_string);
+ Common::StringTokenizer tokenizer(ext_string, " ");
+ while (!tokenizer.empty()) {
+ Common::String token = tokenizer.nextToken();
+ if (token == "GL_ARB_texture_non_power_of_two")
+ npot_supported = true;
+#ifdef GL_OES_draw_texture
+ if (token == "GL_OES_draw_texture")
+ draw_tex_supported = true;
+#endif
+ }
+}
+
+GLESTexture::GLESTexture() :
+ _texture_width(0),
+ _texture_height(0),
+ _all_dirty(true)
+{
+ glGenTextures(1, &_texture_name);
+ // This all gets reset later in allocBuffer:
+ _surface.w = 0;
+ _surface.h = 0;
+ _surface.pitch = _texture_width;
+ _surface.pixels = NULL;
+ _surface.bytesPerPixel = 0;
+}
+
+GLESTexture::~GLESTexture() {
+ debug("Destroying texture %u", _texture_name);
+ glDeleteTextures(1, &_texture_name);
+}
+
+void GLESTexture::reinitGL() {
+ glGenTextures(1, &_texture_name);
+ setDirty();
+}
+
+void GLESTexture::allocBuffer(GLuint w, GLuint h) {
+ CHECK_GL_ERROR();
+ int bpp = bytesPerPixel();
+ _surface.w = w;
+ _surface.h = h;
+ _surface.bytesPerPixel = bpp;
+
+ if (w <= _texture_width && h <= _texture_height)
+ // Already allocated a sufficiently large buffer
+ return;
+
+ if (npot_supported) {
+ _texture_width = _surface.w;
+ _texture_height = _surface.h;
+ } else {
+ _texture_width = nextHigher2(_surface.w);
+ _texture_height = nextHigher2(_surface.h);
+ }
+ _surface.pitch = _texture_width * bpp;
+
+ // Allocate room for the texture now, but pixel data gets uploaded
+ // later (perhaps with multiple TexSubImage2D operations).
+ CHECK_GL_ERROR();
+ glBindTexture(GL_TEXTURE_2D, _texture_name);
+ CHECK_GL_ERROR();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ CHECK_GL_ERROR();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ CHECK_GL_ERROR();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ CHECK_GL_ERROR();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ CHECK_GL_ERROR();
+ glTexImage2D(GL_TEXTURE_2D, 0, glFormat(),
+ _texture_width, _texture_height,
+ 0, glFormat(), glType(), NULL);
+ CHECK_GL_ERROR();
+}
+
+void GLESTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h,
+ const void* buf, int pitch) {
+ ENTER("updateBuffer(%u, %u, %u, %u, %p, %d)", x, y, w, h, buf, pitch);
+ glBindTexture(GL_TEXTURE_2D, _texture_name);
+
+ setDirtyRect(Common::Rect(x, y, x+w, y+h));
+
+ if (static_cast<int>(w) * bytesPerPixel() == pitch) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h,
+ glFormat(), glType(), buf);
+ } else {
+ // GLES removed the ability to specify pitch, so we
+ // have to do this row by row.
+ const byte* src = static_cast<const byte*>(buf);
+ do {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y,
+ w, 1, glFormat(), glType(), src);
+ ++y;
+ src += pitch;
+ } while (--h);
+ }
+}
+
+void GLESTexture::fillBuffer(byte x) {
+ byte tmpbuf[_surface.h * _surface.w * bytesPerPixel()];
+ memset(tmpbuf, 0, _surface.h * _surface.w * bytesPerPixel());
+ glBindTexture(GL_TEXTURE_2D, _texture_name);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _surface.w, _surface.h,
+ glFormat(), glType(), tmpbuf);
+ setDirty();
+}
+
+void GLESTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
+ glBindTexture(GL_TEXTURE_2D, _texture_name);
+
+#ifdef GL_OES_draw_texture
+ // Great extension, but only works under specific conditions.
+ // Still a work-in-progress - disabled for now.
+ if (false && draw_tex_supported && paletteSize() == 0) {
+ //glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ const GLint crop[4] = {0, _surface.h, _surface.w, -_surface.h};
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ glDrawTexiOES(x, y, 0, w, h);
+ } else
+#endif
+ {
+ const GLfixed tex_width = xdiv(_surface.w, _texture_width);
+ const GLfixed tex_height = xdiv(_surface.h, _texture_height);
+ const GLfixed texcoords[] = {
+ 0, 0,
+ tex_width, 0,
+ 0, tex_height,
+ tex_width, tex_height,
+ };
+ glTexCoordPointer(2, GL_FIXED, 0, texcoords);
+
+ const GLshort vertices[] = {
+ x, y,
+ x+w, y,
+ x, y+h,
+ x+w, y+h,
+ };
+ glVertexPointer(2, GL_SHORT, 0, vertices);
+
+ assert(ARRAYSIZE(vertices) == ARRAYSIZE(texcoords));
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, ARRAYSIZE(vertices)/2);
+ }
+
+ _all_dirty = false;
+ _dirty_rect = Common::Rect();
+}
+
+GLESPaletteTexture::GLESPaletteTexture() :
+ GLESTexture(),
+ _texture(NULL)
+{
+}
+
+GLESPaletteTexture::~GLESPaletteTexture() {
+ delete[] _texture;
+}
+
+void GLESPaletteTexture::allocBuffer(GLuint w, GLuint h) {
+ CHECK_GL_ERROR();
+ int bpp = bytesPerPixel();
+ _surface.w = w;
+ _surface.h = h;
+ _surface.bytesPerPixel = bpp;
+
+ if (w <= _texture_width && h <= _texture_height)
+ // Already allocated a sufficiently large buffer
+ return;
+
+ if (npot_supported) {
+ _texture_width = _surface.w;
+ _texture_height = _surface.h;
+ } else {
+ _texture_width = nextHigher2(_surface.w);
+ _texture_height = nextHigher2(_surface.h);
+ }
+ _surface.pitch = _texture_width * bpp;
+
+ // Texture gets uploaded later (from drawTexture())
+
+ byte* new_buffer = new byte[paletteSize() +
+ _texture_width * _texture_height * bytesPerPixel()];
+ if (_texture) {
+ memcpy(new_buffer, _texture, paletteSize()); // preserve palette
+ delete[] _texture;
+ }
+ _texture = new_buffer;
+ _surface.pixels = _texture + paletteSize();
+}
+
+void GLESPaletteTexture::fillBuffer(byte x) {
+ assert(_surface.pixels);
+ memset(_surface.pixels, x, _surface.pitch * _surface.h);
+ setDirty();
+}
+
+void GLESPaletteTexture::updateBuffer(GLuint x, GLuint y,
+ GLuint w, GLuint h,
+ const void* buf, int pitch) {
+ _all_dirty = true;
+
+ const byte* src = static_cast<const byte*>(buf);
+ byte* dst = static_cast<byte*>(_surface.getBasePtr(x, y));
+ do {
+ memcpy(dst, src, w * bytesPerPixel());
+ dst += _surface.pitch;
+ src += pitch;
+ } while (--h);
+}
+
+void GLESPaletteTexture::uploadTexture() const {
+ const size_t texture_size =
+ paletteSize() + _texture_width * _texture_height * bytesPerPixel();
+ glCompressedTexImage2D(GL_TEXTURE_2D, 0, glType(),
+ _texture_width, _texture_height,
+ 0, texture_size, _texture);
+ CHECK_GL_ERROR();
+}
+
+void GLESPaletteTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
+ if (_all_dirty) {
+ glBindTexture(GL_TEXTURE_2D, _texture_name);
+ CHECK_GL_ERROR();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ CHECK_GL_ERROR();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ CHECK_GL_ERROR();
+ uploadTexture();
+ _all_dirty = false;
+ }
+
+ GLESTexture::drawTexture(x, y, w, h);
+}