2009-10-05 06:22:28 +02:00
|
|
|
// Copyright (c) 2009, Thomas Goyne
|
|
|
|
// All rights reserved.
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
|
|
// and/or other materials provided with the distribution.
|
|
|
|
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
|
|
// may be used to endorse or promote products derived from this software
|
|
|
|
// without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
//
|
|
|
|
// Aegisub Project http://www.aegisub.org/
|
|
|
|
//
|
2009-10-06 01:04:30 +02:00
|
|
|
// $Id$
|
2009-10-05 06:22:28 +02:00
|
|
|
|
|
|
|
/// @file video_out_gl.cpp
|
|
|
|
/// @brief OpenGL based video renderer
|
|
|
|
/// @ingroup video
|
|
|
|
///
|
|
|
|
|
2009-10-09 18:34:38 +02:00
|
|
|
#include "config.h"
|
|
|
|
|
2009-10-06 17:47:43 +02:00
|
|
|
#ifndef AGI_PRE
|
|
|
|
#include <wx/log.h>
|
|
|
|
#endif
|
|
|
|
|
2009-10-06 18:08:39 +02:00
|
|
|
// These must be included before local headers.
|
2009-10-05 06:22:28 +02:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <OpenGL/GL.h>
|
|
|
|
#include <OpenGL/glu.h>
|
|
|
|
#else
|
|
|
|
#include <GL/gl.h>
|
|
|
|
#include <GL/glu.h>
|
|
|
|
#endif
|
|
|
|
|
2009-10-06 18:08:39 +02:00
|
|
|
#include "video_out_gl.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "video_frame.h"
|
|
|
|
|
2009-10-19 03:11:02 +02:00
|
|
|
// Windows only has headers for OpenGL 1.1 and GL_CLAMP_TO_EDGE is 1.2
|
2009-10-05 06:22:28 +02:00
|
|
|
#ifndef GL_CLAMP_TO_EDGE
|
|
|
|
#define GL_CLAMP_TO_EDGE 0x812F
|
|
|
|
#endif
|
|
|
|
|
2009-12-01 01:32:43 +01:00
|
|
|
#define CHECK_INIT_ERROR(cmd) cmd; if (GLenum err = glGetError()) throw VideoOutInitException(_T(#cmd), err)
|
|
|
|
#define CHECK_ERROR(cmd) cmd; if (GLenum err = glGetError()) throw VideoOutRenderException(_T(#cmd), err)
|
|
|
|
|
2009-12-13 20:27:45 +01:00
|
|
|
/// @brief Structure tracking all precomputable information about a subtexture
|
|
|
|
struct VideoOutGL::TextureInfo {
|
|
|
|
/// The OpenGL texture id this is for
|
|
|
|
GLuint textureID;
|
|
|
|
/// The byte offset into the frame's data block
|
|
|
|
int dataOffset;
|
|
|
|
int sourceH;
|
|
|
|
int sourceW;
|
|
|
|
|
2010-01-24 20:05:20 +01:00
|
|
|
float destX1;
|
|
|
|
float destY1;
|
|
|
|
float destX2;
|
|
|
|
float destY2;
|
2009-12-13 20:27:45 +01:00
|
|
|
|
|
|
|
float texTop;
|
|
|
|
float texBottom;
|
|
|
|
float texLeft;
|
|
|
|
float texRight;
|
2010-01-24 20:05:20 +01:00
|
|
|
|
|
|
|
TextureInfo()
|
|
|
|
: textureID(0)
|
|
|
|
, dataOffset(0)
|
|
|
|
, sourceH(0)
|
|
|
|
, sourceW(0)
|
|
|
|
, destX1(0)
|
|
|
|
, destY1(0)
|
|
|
|
, destX2(0)
|
|
|
|
, destY2(0)
|
|
|
|
, texTop(0)
|
|
|
|
, texBottom(1.0f)
|
|
|
|
, texLeft(0)
|
|
|
|
, texRight(1.0f)
|
|
|
|
{ }
|
2009-12-13 20:27:45 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/// @brief Test if a texture can be created
|
|
|
|
/// @param width The width of the texture
|
|
|
|
/// @param height The height of the texture
|
|
|
|
/// @param format The texture's format
|
|
|
|
/// @return Whether the texture could be created.
|
|
|
|
static bool TestTexture(int width, int height, GLint format) {
|
|
|
|
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
|
|
|
|
while (glGetError()) { } // Silently swallow all errors as we don't care why it failed if it did
|
|
|
|
|
|
|
|
wxLogDebug("VideoOutGL::TestTexture: %dx%d\n", width, height);
|
|
|
|
return format != 0;
|
2009-10-05 06:22:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
VideoOutGL::VideoOutGL()
|
|
|
|
: maxTextureSize(0),
|
|
|
|
supportsRectangularTextures(false),
|
2009-10-19 03:11:02 +02:00
|
|
|
supportsGlClampToEdge(false),
|
2009-10-05 06:22:28 +02:00
|
|
|
internalFormat(0),
|
|
|
|
frameWidth(0),
|
|
|
|
frameHeight(0),
|
|
|
|
frameFormat(0),
|
2009-10-27 21:59:27 +01:00
|
|
|
frameFlipped(false),
|
2009-10-17 05:41:12 +02:00
|
|
|
textureIdList(),
|
|
|
|
textureList(),
|
2009-10-05 06:22:28 +02:00
|
|
|
textureCount(0),
|
|
|
|
textureRows(0),
|
2009-10-27 15:27:39 +01:00
|
|
|
textureCols(0)
|
2009-10-05 06:22:28 +02:00
|
|
|
{ }
|
|
|
|
|
2009-10-19 03:11:02 +02:00
|
|
|
/// @brief Runtime detection of required OpenGL capabilities
|
|
|
|
void VideoOutGL::DetectOpenGLCapabilities() {
|
|
|
|
if (maxTextureSize != 0) return;
|
|
|
|
|
|
|
|
// Test for supported internalformats
|
|
|
|
if (TestTexture(64, 64, GL_RGBA8)) internalFormat = GL_RGBA8;
|
|
|
|
else if (TestTexture(64, 64, GL_RGBA)) internalFormat = GL_RGBA;
|
2009-12-01 01:32:43 +01:00
|
|
|
else throw VideoOutInitException(L"Could not create a 64x64 RGB texture in any format.");
|
2009-10-19 03:11:02 +02:00
|
|
|
|
|
|
|
// Test for the maximum supported texture size
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
|
|
|
while (maxTextureSize > 64 && !TestTexture(maxTextureSize, maxTextureSize, internalFormat)) maxTextureSize >>= 1;
|
|
|
|
wxLogDebug("VideoOutGL::DetectOpenGLCapabilities: Maximum texture size is %dx%d\n", maxTextureSize, maxTextureSize);
|
|
|
|
|
|
|
|
// Test for rectangular texture support
|
|
|
|
supportsRectangularTextures = TestTexture(maxTextureSize, maxTextureSize >> 1, internalFormat);
|
|
|
|
|
|
|
|
// Test GL_CLAMP_TO_EDGE support
|
|
|
|
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
if (glGetError()) {
|
|
|
|
supportsGlClampToEdge = false;
|
|
|
|
wxLogDebug("VideoOutGL::DetectOpenGLCapabilities: Using GL_CLAMP\n");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
supportsGlClampToEdge = true;
|
|
|
|
wxLogDebug("VideoOutGL::DetectOpenGLCapabilities: Using GL_CLAMP_TO_EDGE\n");
|
|
|
|
}
|
|
|
|
}
|
2009-10-05 06:22:28 +02:00
|
|
|
|
|
|
|
/// @brief If needed, create the grid of textures for displaying frames of the given format
|
|
|
|
/// @param width The frame's width
|
|
|
|
/// @param height The frame's height
|
|
|
|
/// @param format The frame's format
|
|
|
|
/// @param bpp The frame's bytes per pixel
|
2009-10-27 15:27:39 +01:00
|
|
|
void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, bool flipped) {
|
2009-10-05 06:22:28 +02:00
|
|
|
// Do nothing if the frame size and format are unchanged
|
2009-10-27 21:59:27 +01:00
|
|
|
if (width == frameWidth && height == frameHeight && format == frameFormat && flipped == frameFlipped) return;
|
2010-01-24 20:05:20 +01:00
|
|
|
frameWidth = width;
|
|
|
|
frameHeight = height;
|
|
|
|
frameFormat = format;
|
|
|
|
frameFlipped = flipped;
|
2009-10-06 17:28:22 +02:00
|
|
|
wxLogDebug("VideoOutGL::InitTextures: Video size: %dx%d\n", width, height);
|
2009-10-05 06:22:28 +02:00
|
|
|
|
2009-10-19 03:11:02 +02:00
|
|
|
DetectOpenGLCapabilities();
|
2009-10-05 06:22:28 +02:00
|
|
|
|
|
|
|
// Clean up old textures
|
2009-10-17 05:41:12 +02:00
|
|
|
if (textureIdList.size() > 0) {
|
2009-12-01 01:32:43 +01:00
|
|
|
CHECK_INIT_ERROR(glDeleteTextures(textureIdList.size(), &textureIdList[0]));
|
2009-10-17 05:41:12 +02:00
|
|
|
textureIdList.clear();
|
|
|
|
textureList.clear();
|
2009-10-05 06:22:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-19 03:11:02 +02:00
|
|
|
// Create the textures
|
2009-10-05 06:22:28 +02:00
|
|
|
textureRows = (int)ceil(double(height) / maxTextureSize);
|
|
|
|
textureCols = (int)ceil(double(width) / maxTextureSize);
|
2009-10-17 05:41:12 +02:00
|
|
|
textureIdList.resize(textureRows * textureCols);
|
|
|
|
textureList.resize(textureRows * textureCols);
|
2009-12-01 01:32:43 +01:00
|
|
|
CHECK_INIT_ERROR(glGenTextures(textureIdList.size(), &textureIdList[0]));
|
2009-10-05 06:22:28 +02:00
|
|
|
|
|
|
|
// Calculate the position information for each texture
|
|
|
|
int sourceY = 0;
|
2010-01-24 20:05:20 +01:00
|
|
|
float destY = -1.0f;
|
2009-10-05 06:22:28 +02:00
|
|
|
for (int i = 0; i < textureRows; i++) {
|
|
|
|
int sourceX = 0;
|
2010-01-24 20:05:20 +01:00
|
|
|
float destX = -1.0f;
|
2009-10-05 06:22:28 +02:00
|
|
|
|
|
|
|
int sourceH = maxTextureSize;
|
2009-10-17 05:41:12 +02:00
|
|
|
int textureH = maxTextureSize;
|
2009-10-05 06:22:28 +02:00
|
|
|
// If the last row doesn't need a full texture, shrink it to the smallest one possible
|
2009-10-14 00:19:31 +02:00
|
|
|
if (i == textureRows - 1 && height % maxTextureSize > 0) {
|
|
|
|
sourceH = height % maxTextureSize;
|
2009-10-17 05:41:12 +02:00
|
|
|
textureH = SmallestPowerOf2(sourceH);
|
2009-10-05 06:22:28 +02:00
|
|
|
}
|
2010-01-24 20:05:20 +01:00
|
|
|
|
2009-10-05 06:22:28 +02:00
|
|
|
for (int j = 0; j < textureCols; j++) {
|
|
|
|
TextureInfo& ti = textureList[i * textureCols + j];
|
|
|
|
|
|
|
|
// Copy the current position information into the struct
|
2010-01-24 20:05:20 +01:00
|
|
|
ti.destX1 = destX;
|
|
|
|
ti.destY1 = destY;
|
2009-10-05 06:22:28 +02:00
|
|
|
ti.sourceH = sourceH;
|
2010-01-24 20:05:20 +01:00
|
|
|
ti.textureID = textureIdList[i * textureCols + j];
|
2009-10-05 06:22:28 +02:00
|
|
|
|
|
|
|
ti.sourceW = maxTextureSize;
|
2009-10-17 05:41:12 +02:00
|
|
|
int textureW = maxTextureSize;
|
2009-10-05 06:22:28 +02:00
|
|
|
// If the last column doesn't need a full texture, shrink it to the smallest one possible
|
2009-10-14 00:19:31 +02:00
|
|
|
if (j == textureCols - 1 && width % maxTextureSize > 0) {
|
|
|
|
ti.sourceW = width % maxTextureSize;
|
2009-10-17 05:41:12 +02:00
|
|
|
textureW = SmallestPowerOf2(ti.sourceW);
|
2009-10-05 06:22:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-17 05:41:12 +02:00
|
|
|
int w = textureW;
|
|
|
|
int h = textureH;
|
2009-10-05 06:22:28 +02:00
|
|
|
if (!supportsRectangularTextures) w = h = MAX(w, h);
|
2009-10-19 03:11:02 +02:00
|
|
|
|
2010-01-24 20:05:20 +01:00
|
|
|
CreateTexture(w, h, ti, format);
|
|
|
|
|
|
|
|
if (!supportsGlClampToEdge) {
|
2009-10-19 03:11:02 +02:00
|
|
|
// Stretch the texture a half pixel in each direction to eliminate the border
|
|
|
|
ti.texLeft = 1.0f / (2 * w);
|
|
|
|
ti.texTop = 1.0f / (2 * h);
|
|
|
|
}
|
|
|
|
|
2010-01-24 20:05:20 +01:00
|
|
|
ti.destX2 = ti.destX1 + w * 2.0f / width;
|
|
|
|
ti.destY2 = ti.destY1 + h * 2.0f / height;
|
2009-10-05 06:22:28 +02:00
|
|
|
|
2009-10-27 21:59:27 +01:00
|
|
|
ti.texRight = 1.0f - ti.texLeft;
|
|
|
|
if (flipped) {
|
2010-01-24 20:05:20 +01:00
|
|
|
ti.texBottom = 1.0f - ti.texTop;
|
|
|
|
ti.dataOffset = sourceY * width * bpp + sourceX * bpp;
|
|
|
|
}
|
|
|
|
else {
|
2009-10-27 21:59:27 +01:00
|
|
|
ti.texBottom = ti.texTop - float(h - ti.sourceH) / h;
|
|
|
|
ti.texTop = 1.0f - ti.texTop - float(h - ti.sourceH) / h;
|
|
|
|
|
|
|
|
ti.dataOffset = (height - sourceY - ti.sourceH) * width * bpp + sourceX * bpp;
|
|
|
|
}
|
|
|
|
|
2010-01-24 20:05:20 +01:00
|
|
|
destX = ti.destX2;
|
2009-10-05 06:22:28 +02:00
|
|
|
sourceX += ti.sourceW;
|
|
|
|
}
|
2010-01-24 20:05:20 +01:00
|
|
|
destY += sourceH * 2.0f / height;
|
2009-10-05 06:22:28 +02:00
|
|
|
sourceY += sourceH;
|
|
|
|
}
|
2010-01-24 20:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void VideoOutGL::CreateTexture(int w, int h, const TextureInfo& ti, GLenum format) {
|
|
|
|
CHECK_INIT_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
|
|
|
|
CHECK_INIT_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, GL_UNSIGNED_BYTE, NULL));
|
|
|
|
wxLogDebug("VideoOutGL::InitTextures: Using texture size: %dx%d\n", w, h);
|
|
|
|
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
|
|
|
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
2009-10-14 00:19:31 +02:00
|
|
|
|
2010-01-24 20:05:20 +01:00
|
|
|
GLint mode = supportsGlClampToEdge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
|
|
|
|
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode));
|
|
|
|
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode));
|
2009-10-05 06:22:28 +02:00
|
|
|
}
|
2010-01-24 20:05:20 +01:00
|
|
|
|
2009-10-27 15:27:39 +01:00
|
|
|
void VideoOutGL::UploadFrameData(const AegiVideoFrame& frame) {
|
2009-10-05 06:22:28 +02:00
|
|
|
if (frame.h == 0 || frame.w == 0) return;
|
|
|
|
|
|
|
|
GLuint format = frame.invertChannels ? GL_BGRA_EXT : GL_RGBA;
|
2009-10-27 15:27:39 +01:00
|
|
|
InitTextures(frame.w, frame.h, format, frame.GetBpp(0), frame.flipped);
|
2009-10-05 06:22:28 +02:00
|
|
|
|
|
|
|
// Set the row length, needed to be able to upload partial rows
|
2009-12-01 01:32:43 +01:00
|
|
|
CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.w));
|
2009-10-05 06:22:28 +02:00
|
|
|
|
2009-10-27 15:27:39 +01:00
|
|
|
for (unsigned i = 0; i < textureList.size(); i++) {
|
|
|
|
TextureInfo& ti = textureList[i];
|
|
|
|
|
2009-12-01 01:32:43 +01:00
|
|
|
CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
|
|
|
|
CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ti.sourceW,
|
|
|
|
ti.sourceH, format, GL_UNSIGNED_BYTE, frame.data[0] + ti.dataOffset));
|
2009-10-27 15:27:39 +01:00
|
|
|
}
|
|
|
|
|
2009-12-01 01:32:43 +01:00
|
|
|
CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
|
2009-10-27 15:27:39 +01:00
|
|
|
}
|
2010-01-24 20:05:20 +01:00
|
|
|
void VideoOutGL::SetViewport(int x, int y, int width, int height) {
|
|
|
|
CHECK_ERROR(glViewport(x, y, width, height));
|
|
|
|
}
|
2009-10-27 15:27:39 +01:00
|
|
|
|
|
|
|
void VideoOutGL::Render(int sw, int sh) {
|
2010-01-24 20:05:20 +01:00
|
|
|
// Clear the frame buffer
|
|
|
|
CHECK_ERROR(glClearColor(0,0,0,0));
|
|
|
|
CHECK_ERROR(glClearStencil(0));
|
|
|
|
CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK_ERROR(glShadeModel(GL_FLAT));
|
|
|
|
CHECK_ERROR(glDisable(GL_BLEND));
|
|
|
|
|
|
|
|
CHECK_ERROR(glMatrixMode(GL_PROJECTION));
|
|
|
|
CHECK_ERROR(glLoadIdentity());
|
|
|
|
|
|
|
|
// Render the current frame
|
2009-12-01 01:32:43 +01:00
|
|
|
CHECK_ERROR(glEnable(GL_TEXTURE_2D));
|
2009-10-27 15:27:39 +01:00
|
|
|
|
2009-10-17 05:41:12 +02:00
|
|
|
for (unsigned i = 0; i < textureList.size(); i++) {
|
2009-10-05 06:22:28 +02:00
|
|
|
TextureInfo& ti = textureList[i];
|
|
|
|
|
2009-12-01 01:32:43 +01:00
|
|
|
CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
|
|
|
|
CHECK_ERROR(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
|
2009-10-19 03:11:02 +02:00
|
|
|
|
2009-10-05 06:22:28 +02:00
|
|
|
glBegin(GL_QUADS);
|
2010-01-24 20:05:20 +01:00
|
|
|
glTexCoord2f(ti.texLeft, ti.texTop); glVertex2f(ti.destX1, ti.destY1);
|
|
|
|
glTexCoord2f(ti.texRight, ti.texTop); glVertex2f(ti.destX2, ti.destY1);
|
|
|
|
glTexCoord2f(ti.texRight, ti.texBottom); glVertex2f(ti.destX2, ti.destY2);
|
|
|
|
glTexCoord2f(ti.texLeft, ti.texBottom); glVertex2f(ti.destX1, ti.destY2);
|
2009-10-05 06:22:28 +02:00
|
|
|
glEnd();
|
2009-12-01 01:32:43 +01:00
|
|
|
if (GLenum err = glGetError()) throw VideoOutRenderException(L"GL_QUADS", err);
|
2009-10-05 06:22:28 +02:00
|
|
|
}
|
2009-12-01 01:32:43 +01:00
|
|
|
CHECK_ERROR(glDisable(GL_TEXTURE_2D));
|
2010-01-24 20:05:20 +01:00
|
|
|
|
|
|
|
CHECK_ERROR(glOrtho(0.0f, sw, sh, 0.0f, -1000.0f, 1000.0f));
|
|
|
|
CHECK_ERROR(glMatrixMode(GL_MODELVIEW));
|
|
|
|
CHECK_ERROR(glLoadIdentity());
|
2009-10-05 06:22:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
VideoOutGL::~VideoOutGL() {
|
2009-10-17 05:41:12 +02:00
|
|
|
if (textureIdList.size() > 0) {
|
|
|
|
glDeleteTextures(textureIdList.size(), &textureIdList[0]);
|
2009-10-05 06:22:28 +02:00
|
|
|
}
|
|
|
|
}
|