233 lines
7.4 KiB
C

/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gl.h"
#include <mgba-util/math.h>
static const GLint _glVertices[] = {
0, 0,
1, 0,
1, 1,
0, 1
};
static const GLint _glTexCoords[] = {
0, 0,
1, 0,
1, 1,
0, 1
};
static inline void _initTex(void) {
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#ifndef _WIN32
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#endif
}
static void mGLContextInit(struct VideoBackend* v, WHandle handle) {
UNUSED(handle);
struct mGLContext* context = (struct mGLContext*) v;
memset(context->layerDims, 0, sizeof(context->layerDims));
glGenTextures(2, context->tex);
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
_initTex();
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
_initTex();
context->activeTex = 0;
glGenTextures(VIDEO_LAYER_MAX, context->tex);
int i;
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
glBindTexture(GL_TEXTURE_2D, context->layers[i]);
_initTex();
}
}
static inline void _setTexDims(const struct Rectangle* dims) {
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(dims->width), toPow2(dims->height), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(dims->width), toPow2(dims->height), 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
#endif
#elif defined(__BIG_ENDIAN__)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(dims->width), toPow2(dims->height), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(dims->width), toPow2(dims->height), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
#endif
}
static void mGLContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct Rectangle* dims) {
struct mGLContext* context = (struct mGLContext*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
context->layerDims[layer].x = dims->x;
context->layerDims[layer].y = dims->y;
if (dims->width == context->layerDims[layer].width && dims->height == context->layerDims[layer].height) {
return;
}
context->layerDims[layer].width = dims->width;
context->layerDims[layer].height = dims->height;
if (layer == VIDEO_LAYER_IMAGE) {
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
_setTexDims(dims);
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
_setTexDims(dims);
} else {
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
_setTexDims(dims);
}
}
static void mGLContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct Rectangle* dims) {
struct mGLContext* context = (struct mGLContext*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
memcpy(dims, &context->layerDims[layer], sizeof(*dims));
}
static void mGLContextDeinit(struct VideoBackend* v) {
struct mGLContext* context = (struct mGLContext*) v;
glDeleteTextures(2, context->tex);
glDeleteTextures(VIDEO_LAYER_MAX, context->layers);
}
static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
unsigned drawW = w;
unsigned drawH = h;
unsigned maxW;
unsigned maxH;
VideoBackendGetFrameSize(v, &maxW, &maxH);
if (v->lockAspectRatio) {
lockAspectRatioUInt(maxW, maxH, &drawW, &drawH);
}
if (v->lockIntegerScaling) {
lockIntegerRatioUInt(maxW, &drawW);
lockIntegerRatioUInt(maxH, &drawH);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
}
static void mGLContextClear(struct VideoBackend* v) {
UNUSED(v);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
static void _setFilter(struct VideoBackend* v) {
if (v->filter) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
}
static void _setFrame(struct Rectangle* dims, int frameW, int frameH) {
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glScissor(viewport[0] + dims->x * viewport[2] / frameW,
viewport[1] + dims->y * viewport[3] / frameH,
dims->width * viewport[2] / frameW,
dims->height * viewport[3] / frameH);
glTranslatef(dims->x, dims->y, 0);
glScalef(toPow2(dims->width), toPow2(dims->height), 1);
}
void mGLContextDrawFrame(struct VideoBackend* v) {
struct mGLContext* context = (struct mGLContext*) v;
glEnable(GL_TEXTURE_2D);
glEnable(GL_SCISSOR_TEST);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_INT, 0, _glVertices);
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
unsigned frameW, frameH;
VideoBackendGetFrameSize(v, &frameW, &frameH);
glOrtho(0, frameW, frameH, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
int layer;
for (layer = 0; layer < VIDEO_LAYER_IMAGE; ++layer) {
if (context->layerDims[layer].width < 1 || context->layerDims[layer].height < 1) {
continue;
}
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
_setFilter(v);
glPushMatrix();
_setFrame(&context->layerDims[layer], frameW, frameH);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glPopMatrix();
}
_setFrame(&context->layerDims[VIDEO_LAYER_IMAGE], frameW, frameH);
if (v->interframeBlending) {
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
glBlendColor(1, 1, 1, 0.5);
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex ^ 1]);
_setFilter(v);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glEnable(GL_BLEND);
}
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
_setFilter(v);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisable(GL_BLEND);
}
void mGLContextPostFrame(struct VideoBackend* v, enum VideoLayer layer, const void* frame) {
struct mGLContext* context = (struct mGLContext*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
if (layer == VIDEO_LAYER_IMAGE) {
context->activeTex ^= 1;
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
} else {
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
}
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, context->layerDims[layer].width, context->layerDims[layer].height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, context->layerDims[layer].width, context->layerDims[layer].height, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
#endif
#elif defined(__BIG_ENDIAN__)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, context->layerDims[layer].width, context->layerDims[layer].height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, context->layerDims[layer].width, context->layerDims[layer].height, GL_RGBA, GL_UNSIGNED_BYTE, frame);
#endif
}
void mGLContextCreate(struct mGLContext* context) {
context->d.init = mGLContextInit;
context->d.deinit = mGLContextDeinit;
context->d.setLayerDimensions = mGLContextSetLayerDimensions;
context->d.layerDimensions = mGLContextLayerDimensions;
context->d.contextResized = mGLContextResized;
context->d.swap = NULL;
context->d.clear = mGLContextClear;
context->d.setImage = mGLContextPostFrame;
context->d.drawFrame = mGLContextDrawFrame;
}