Mercurial > sdl-ios-xcode
diff src/render/opengl/SDL_renderer_gl.c @ 5157:fb424691cfc7
Moved the rendering code out to a separate directory in the hope that it can someday be completely decoupled from the rest of the library and be expanded to an awesome 2D on 3D library.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Wed, 02 Feb 2011 14:34:54 -0800 |
parents | src/video/SDL_renderer_gl.c@1435f8a6425c |
children | 307ccc9c135e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/render/opengl/SDL_renderer_gl.c Wed Feb 02 14:34:54 2011 -0800 @@ -0,0 +1,1285 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2010 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#if SDL_VIDEO_RENDER_OGL + +#include "SDL_opengl.h" +#include "../SDL_sysrender.h" + +#ifdef __MACOSX__ +#include <OpenGL/OpenGL.h> +#endif + + +/* OpenGL renderer implementation */ + +/* Details on optimizing the texture path on Mac OS X: + http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/chapter_10_section_2.html +*/ + +/* !!! FIXME: this should go in a higher level than the GL renderer. */ +static __inline__ int +bytes_per_pixel(const Uint32 format) +{ + if (!SDL_ISPIXELFORMAT_FOURCC(format)) { + return SDL_BYTESPERPIXEL(format); + } + + /* FOURCC format */ + switch (format) { + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_YUY2: + case SDL_PIXELFORMAT_UYVY: + case SDL_PIXELFORMAT_YVYU: + return 2; + default: + return 1; /* shouldn't ever hit this. */ + } +} + +/* Used to re-create the window with OpenGL capability */ +extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); + +static const float inv255f = 1.0f / 255.0f; + +static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags); +static void GL_WindowEvent(SDL_Renderer * renderer, + const SDL_WindowEvent *event); +static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); +static int GL_QueryTexturePixels(SDL_Renderer * renderer, + SDL_Texture * texture, void **pixels, + int *pitch); +static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, const void *pixels, + int pitch); +static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, int markDirty, void **pixels, + int *pitch); +static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); +static void GL_DirtyTexture(SDL_Renderer * renderer, SDL_Texture * texture, + int numrects, const SDL_Rect * rects); +static int GL_RenderClear(SDL_Renderer * renderer); +static int GL_RenderDrawPoints(SDL_Renderer * renderer, + const SDL_Point * points, int count); +static int GL_RenderDrawLines(SDL_Renderer * renderer, + const SDL_Point * points, int count); +static int GL_RenderFillRects(SDL_Renderer * renderer, + const SDL_Rect ** rects, int count); +static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_Rect * dstrect); +static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, + Uint32 pixel_format, void * pixels, int pitch); +static int GL_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, + Uint32 pixel_format, const void * pixels, int pitch); +static void GL_RenderPresent(SDL_Renderer * renderer); +static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); +static void GL_DestroyRenderer(SDL_Renderer * renderer); + + +SDL_RenderDriver GL_RenderDriver = { + GL_CreateRenderer, + { + "opengl", + (SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED), + 13, + { + SDL_PIXELFORMAT_RGB332, + SDL_PIXELFORMAT_RGB444, + SDL_PIXELFORMAT_RGB555, + SDL_PIXELFORMAT_ARGB4444, + SDL_PIXELFORMAT_ARGB1555, + SDL_PIXELFORMAT_RGB565, + SDL_PIXELFORMAT_RGB24, + SDL_PIXELFORMAT_BGR24, + SDL_PIXELFORMAT_RGB888, + SDL_PIXELFORMAT_BGR888, + SDL_PIXELFORMAT_ARGB8888, + SDL_PIXELFORMAT_ABGR8888, + SDL_PIXELFORMAT_ARGB2101010}, + 0, + 0} +}; + +typedef struct +{ + SDL_GLContext context; + SDL_bool updateSize; + SDL_bool GL_ARB_texture_rectangle_supported; + SDL_bool GL_EXT_paletted_texture_supported; + SDL_bool GL_APPLE_ycbcr_422_supported; + SDL_bool GL_MESA_ycbcr_texture_supported; + SDL_bool GL_ARB_fragment_program_supported; + int blendMode; + + /* OpenGL functions */ +#define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; +#include "../../video/SDL_glfuncs.h" +#undef SDL_PROC + + void (*glTextureRangeAPPLE) (GLenum target, GLsizei length, + const GLvoid * pointer); + + PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; + PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; + PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; + PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; + PFNGLGENPROGRAMSARBPROC glGenProgramsARB; + PFNGLBINDPROGRAMARBPROC glBindProgramARB; + PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; + + /* (optional) fragment programs */ + GLuint fragment_program_UYVY; +} GL_RenderData; + +typedef struct +{ + GLuint texture; + GLuint shader; + GLenum type; + GLfloat texw; + GLfloat texh; + GLenum format; + GLenum formattype; + Uint8 *palette; + void *pixels; + int pitch; + SDL_DirtyRectList dirty; + int HACK_RYAN_FIXME; +} GL_TextureData; + + +static void +GL_SetError(const char *prefix, GLenum result) +{ + const char *error; + + switch (result) { + case GL_NO_ERROR: + error = "GL_NO_ERROR"; + break; + case GL_INVALID_ENUM: + error = "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + error = "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + error = "GL_INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + error = "GL_STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + error = "GL_STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + error = "GL_OUT_OF_MEMORY"; + break; + case GL_TABLE_TOO_LARGE: + error = "GL_TABLE_TOO_LARGE"; + break; + default: + error = "UNKNOWN"; + break; + } + SDL_SetError("%s: %s", prefix, error); +} + +static int +GL_LoadFunctions(GL_RenderData * data) +{ +#ifdef __SDL_NOGETPROCADDR__ +#define SDL_PROC(ret,func,params) data->func=func; +#else +#define SDL_PROC(ret,func,params) \ + do { \ + data->func = SDL_GL_GetProcAddress(#func); \ + if ( ! data->func ) { \ + SDL_SetError("Couldn't load GL function %s: %s\n", #func, SDL_GetError()); \ + return -1; \ + } \ + } while ( 0 ); +#endif /* __SDL_NOGETPROCADDR__ */ + +#include "../../video/SDL_glfuncs.h" +#undef SDL_PROC + return 0; +} + +SDL_Renderer * +GL_CreateRenderer(SDL_Window * window, Uint32 flags) +{ + SDL_Renderer *renderer; + GL_RenderData *data; + GLint value; + Uint32 window_flags; + + window_flags = SDL_GetWindowFlags(window); + if (!(window_flags & SDL_WINDOW_OPENGL)) { + if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) { + return NULL; + } + } + + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + return NULL; + } + + data = (GL_RenderData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + GL_DestroyRenderer(renderer); + SDL_OutOfMemory(); + return NULL; + } + + renderer->WindowEvent = GL_WindowEvent; + renderer->CreateTexture = GL_CreateTexture; + renderer->QueryTexturePixels = GL_QueryTexturePixels; + renderer->UpdateTexture = GL_UpdateTexture; + renderer->LockTexture = GL_LockTexture; + renderer->UnlockTexture = GL_UnlockTexture; + renderer->DirtyTexture = GL_DirtyTexture; + renderer->RenderClear = GL_RenderClear; + renderer->RenderDrawPoints = GL_RenderDrawPoints; + renderer->RenderDrawLines = GL_RenderDrawLines; + renderer->RenderFillRects = GL_RenderFillRects; + renderer->RenderCopy = GL_RenderCopy; + renderer->RenderReadPixels = GL_RenderReadPixels; + renderer->RenderWritePixels = GL_RenderWritePixels; + renderer->RenderPresent = GL_RenderPresent; + renderer->DestroyTexture = GL_DestroyTexture; + renderer->DestroyRenderer = GL_DestroyRenderer; + renderer->info = GL_RenderDriver.info; + renderer->window = window; + renderer->driverdata = data; + + renderer->info.flags = SDL_RENDERER_ACCELERATED; + + if (GL_LoadFunctions(data) < 0) { + GL_DestroyRenderer(renderer); + return NULL; + } + + data->context = SDL_GL_CreateContext(window); + if (!data->context) { + GL_DestroyRenderer(renderer); + return NULL; + } + if (SDL_GL_MakeCurrent(window, data->context) < 0) { + GL_DestroyRenderer(renderer); + return NULL; + } +#ifdef __MACOSX__ + /* Enable multi-threaded rendering */ + /* Disabled until Ryan finishes his VBO/PBO code... + CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine); + */ +#endif + + if (flags & SDL_RENDERER_PRESENTVSYNC) { + SDL_GL_SetSwapInterval(1); + } else { + SDL_GL_SetSwapInterval(0); + } + if (SDL_GL_GetSwapInterval() > 0) { + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } + + data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); + renderer->info.max_texture_width = value; + data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); + renderer->info.max_texture_height = value; + + if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") + || SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) { + data->GL_ARB_texture_rectangle_supported = SDL_TRUE; + } + if (SDL_GL_ExtensionSupported("GL_APPLE_ycbcr_422")) { + data->GL_APPLE_ycbcr_422_supported = SDL_TRUE; + } + if (SDL_GL_ExtensionSupported("GL_MESA_ycbcr_texture")) { + data->GL_MESA_ycbcr_texture_supported = SDL_TRUE; + } + if (SDL_GL_ExtensionSupported("GL_APPLE_texture_range")) { + data->glTextureRangeAPPLE = + (void (*)(GLenum, GLsizei, const GLvoid *)) + SDL_GL_GetProcAddress("glTextureRangeAPPLE"); + } + + /* we might use fragment programs for YUV data, etc. */ + if (SDL_GL_ExtensionSupported("GL_ARB_fragment_program")) { + /* !!! FIXME: this doesn't check for errors. */ + /* !!! FIXME: this should really reuse the glfuncs.h stuff. */ + data->glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) + SDL_GL_GetProcAddress("glGetProgramivARB"); + data->glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) + SDL_GL_GetProcAddress("glGetProgramStringARB"); + data->glProgramLocalParameter4fvARB = + (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) + SDL_GL_GetProcAddress("glProgramLocalParameter4fvARB"); + data->glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) + SDL_GL_GetProcAddress("glDeleteProgramsARB"); + data->glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) + SDL_GL_GetProcAddress("glGenProgramsARB"); + data->glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) + SDL_GL_GetProcAddress("glBindProgramARB"); + data->glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) + SDL_GL_GetProcAddress("glProgramStringARB"); + data->GL_ARB_fragment_program_supported = SDL_TRUE; + } + + /* Set up parameters for rendering */ + data->blendMode = -1; + data->glDisable(GL_DEPTH_TEST); + data->glDisable(GL_CULL_FACE); + /* This ended up causing video discrepancies between OpenGL and Direct3D */ + /*data->glEnable(GL_LINE_SMOOTH);*/ + if (data->GL_ARB_texture_rectangle_supported) { + data->glEnable(GL_TEXTURE_RECTANGLE_ARB); + } else { + data->glEnable(GL_TEXTURE_2D); + } + data->updateSize = SDL_TRUE; + + return renderer; +} + +static SDL_GLContext SDL_CurrentContext = NULL; + +static int +GL_ActivateRenderer(SDL_Renderer * renderer) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + SDL_Window *window = renderer->window; + + if (SDL_CurrentContext != data->context) { + if (SDL_GL_MakeCurrent(window, data->context) < 0) { + return -1; + } + SDL_CurrentContext = data->context; + } + if (data->updateSize) { + int w, h; + + SDL_GetWindowSize(window, &w, &h); + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glMatrixMode(GL_MODELVIEW); + data->glLoadIdentity(); + data->glViewport(0, 0, w, h); + data->glOrtho(0.0, (GLdouble) w, (GLdouble) h, 0.0, 0.0, 1.0); + data->updateSize = SDL_FALSE; + } + return 0; +} + +static void +GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + + if (event->event == SDL_WINDOWEVENT_RESIZED) { + /* Rebind the context to the window area and update matrices */ + SDL_CurrentContext = NULL; + data->updateSize = SDL_TRUE; + } +} + +static __inline__ int +power_of_2(int input) +{ + int value = 1; + + while (value < input) { + value <<= 1; + } + return value; +} + + +//#define DEBUG_PROGRAM_COMPILE 1 + +static void +set_shader_error(GL_RenderData * data, const char *prefix) +{ + GLint pos = 0; + const GLubyte *errstr; + data->glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); + errstr = data->glGetString(GL_PROGRAM_ERROR_STRING_ARB); + SDL_SetError("%s: shader compile error at position %d: %s", + prefix, (int) pos, (const char *) errstr); +} + +static GLuint +compile_shader(GL_RenderData * data, GLenum shader_type, const char *_code) +{ + const int have_texture_rects = data->GL_ARB_texture_rectangle_supported; + const char *replacement = have_texture_rects ? "RECT" : "2D"; + const size_t replacementlen = SDL_strlen(replacement); + const char *token = "%TEXTURETARGET%"; + const size_t tokenlen = SDL_strlen(token); + char *code = NULL; + char *ptr = NULL; + GLuint program = 0; + + /* + * The TEX instruction needs a different target depending on what we use. + * To handle this, we use "%TEXTURETARGET%" and replace the string before + * compiling the shader. + */ + code = SDL_strdup(_code); + if (code == NULL) + return 0; + + for (ptr = SDL_strstr(code, token); ptr; ptr = SDL_strstr(ptr + 1, token)) { + SDL_memcpy(ptr, replacement, replacementlen); + SDL_memmove(ptr + replacementlen, ptr + tokenlen, + SDL_strlen(ptr + tokenlen) + 1); + } + +#if DEBUG_PROGRAM_COMPILE + printf("compiling shader:\n%s\n\n", code); +#endif + + data->glGetError(); /* flush any existing error state. */ + data->glGenProgramsARB(1, &program); + data->glBindProgramARB(shader_type, program); + data->glProgramStringARB(shader_type, GL_PROGRAM_FORMAT_ASCII_ARB, + (GLsizei)SDL_strlen(code), code); + + SDL_free(code); + + if (data->glGetError() == GL_INVALID_OPERATION) { +#if DEBUG_PROGRAM_COMPILE + GLint pos = 0; + const GLubyte *errstr; + data->glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); + errstr = data->glGetString(GL_PROGRAM_ERROR_STRING_ARB); + printf("program compile error at position %d: %s\n\n", + (int) pos, (const char *) errstr); +#endif + data->glBindProgramARB(shader_type, 0); + data->glDeleteProgramsARB(1, &program); + return 0; + } + + return program; +} + + +/* + * Fragment program that renders from UYVY textures. + * The UYVY to RGB equasion is: + * R = 1.164(Y-16) + 1.596(Cr-128) + * G = 1.164(Y-16) - 0.813(Cr-128) - 0.391(Cb-128) + * B = 1.164(Y-16) + 2.018(Cb-128) + * Byte layout is Cb, Y1, Cr, Y2, stored in the R, G, B, A channels. + * 4 bytes == 2 pixels: Y1/Cb/Cr, Y2/Cb/Cr + * + * !!! FIXME: this ignores blendmodes, etc. + * !!! FIXME: this could be more efficient...use a dot product for green, etc. + */ +static const char *fragment_program_UYVY_source_code = "!!ARBfp1.0\n" + /* outputs... */ + "OUTPUT outcolor = result.color;\n" + /* scratch registers... */ + "TEMP uyvy;\n" "TEMP luminance;\n" "TEMP work;\n" + /* Halve the coordinates to grab the correct 32 bits for the fragment. */ + "MUL work, fragment.texcoord, { 0.5, 1.0, 1.0, 1.0 };\n" + /* Sample the YUV texture. Cb, Y1, Cr, Y2, are stored in x, y, z, w. */ + "TEX uyvy, work, texture[0], %TEXTURETARGET%;\n" + /* Do subtractions (128/255, 16/255, 128/255, 16/255) */ + "SUB uyvy, uyvy, { 0.501960784313726, 0.06274509803922, 0.501960784313726, 0.06274509803922 };\n" + /* Choose the luminance component by texcoord. */ + /* !!! FIXME: laziness wins out for now... just average Y1 and Y2. */ + "ADD luminance, uyvy.yyyy, uyvy.wwww;\n" + "MUL luminance, luminance, { 0.5, 0.5, 0.5, 0.5 };\n" + /* Multiply luminance by its magic value. */ + "MUL luminance, luminance, { 1.164, 1.164, 1.164, 1.164 };\n" + /* uyvy.xyzw becomes Cr/Cr/Cb/Cb, with multiplications. */ + "MUL uyvy, uyvy.zzxx, { 1.596, -0.813, 2.018, -0.391 };\n" + /* Add luminance to Cr and Cb, store to RGB channels. */ + "ADD work.rgb, luminance, uyvy;\n" + /* Do final addition for Green channel. (!!! FIXME: this should be a DPH?) */ + "ADD work.g, work.g, uyvy.w;\n" + /* Make sure alpha channel is fully opaque. (!!! FIXME: blend modes!) */ + "MOV work.a, { 1.0 };\n" + /* Store out the final fragment color... */ + "MOV outcolor, work;\n" + /* ...and we're done! */ + "END\n"; + +static __inline__ SDL_bool +convert_format(GL_RenderData *renderdata, Uint32 pixel_format, + GLint* internalFormat, GLenum* format, GLenum* type) +{ + switch (pixel_format) { + case SDL_PIXELFORMAT_RGB332: + *internalFormat = GL_R3_G3_B2; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE_3_3_2; + break; + case SDL_PIXELFORMAT_RGB444: + *internalFormat = GL_RGB4; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case SDL_PIXELFORMAT_RGB555: + *internalFormat = GL_RGB5; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_5_5_1; + break; + case SDL_PIXELFORMAT_ARGB4444: + *internalFormat = GL_RGBA4; + *format = GL_BGRA; + *type = GL_UNSIGNED_SHORT_4_4_4_4_REV; + break; + case SDL_PIXELFORMAT_ARGB1555: + *internalFormat = GL_RGB5_A1; + *format = GL_BGRA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + break; + case SDL_PIXELFORMAT_RGB565: + *internalFormat = GL_RGB8; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + break; + case SDL_PIXELFORMAT_RGB24: + *internalFormat = GL_RGB8; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE; + break; + case SDL_PIXELFORMAT_RGB888: + *internalFormat = GL_RGB8; + *format = GL_BGRA; + *type = GL_UNSIGNED_BYTE; + break; + case SDL_PIXELFORMAT_BGR24: + *internalFormat = GL_RGB8; + *format = GL_BGR; + *type = GL_UNSIGNED_BYTE; + break; + case SDL_PIXELFORMAT_BGR888: + *internalFormat = GL_RGB8; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + break; + case SDL_PIXELFORMAT_ARGB8888: +#ifdef __MACOSX__ + *internalFormat = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; +#else + *internalFormat = GL_RGBA8; + *format = GL_BGRA; + *type = GL_UNSIGNED_BYTE; +#endif + break; + case SDL_PIXELFORMAT_ABGR8888: + *internalFormat = GL_RGBA8; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + break; + case SDL_PIXELFORMAT_ARGB2101010: + *internalFormat = GL_RGB10_A2; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_2_10_10_10_REV; + break; + case SDL_PIXELFORMAT_UYVY: + if (renderdata->GL_APPLE_ycbcr_422_supported) { + *internalFormat = GL_RGB; + *format = GL_YCBCR_422_APPLE; +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + *type = GL_UNSIGNED_SHORT_8_8_APPLE; +#else + *type = GL_UNSIGNED_SHORT_8_8_REV_APPLE; +#endif + } else if (renderdata->GL_MESA_ycbcr_texture_supported) { + *internalFormat = GL_YCBCR_MESA; + *format = GL_YCBCR_MESA; +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + *type = GL_UNSIGNED_SHORT_8_8_MESA; +#else + *type = GL_UNSIGNED_SHORT_8_8_REV_MESA; +#endif + } else if (renderdata->GL_ARB_fragment_program_supported) { + *internalFormat = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + } else { + return SDL_FALSE; + } + break; + case SDL_PIXELFORMAT_YUY2: + if (renderdata->GL_APPLE_ycbcr_422_supported) { + *internalFormat = GL_RGB; + *format = GL_YCBCR_422_APPLE; +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + *type = GL_UNSIGNED_SHORT_8_8_REV_APPLE; +#else + *type = GL_UNSIGNED_SHORT_8_8_APPLE; +#endif + } else if (renderdata->GL_MESA_ycbcr_texture_supported) { + *internalFormat = GL_YCBCR_MESA; + *format = GL_YCBCR_MESA; +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + *type = GL_UNSIGNED_SHORT_8_8_REV_MESA; +#else + *type = GL_UNSIGNED_SHORT_8_8_MESA; +#endif + } else { + return SDL_FALSE; + } + break; + default: + return SDL_FALSE; + } + return SDL_TRUE; +} + +static int +GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ + GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; + GL_TextureData *data; + GLint internalFormat; + GLenum format, type; + int texture_w, texture_h; + GLuint shader = 0; + GLenum result; + + GL_ActivateRenderer(renderer); + + if (!convert_format(renderdata, texture->format, &internalFormat, + &format, &type)) { + SDL_SetError("Texture format %s not supported by OpenGL", + SDL_GetPixelFormatName(texture->format)); + return -1; + } + if (texture->format == SDL_PIXELFORMAT_UYVY && + !renderdata->GL_APPLE_ycbcr_422_supported && + !renderdata->GL_MESA_ycbcr_texture_supported && + renderdata->GL_ARB_fragment_program_supported) { + if (renderdata->fragment_program_UYVY == 0) { + renderdata->fragment_program_UYVY = + compile_shader(renderdata, GL_FRAGMENT_PROGRAM_ARB, + fragment_program_UYVY_source_code); + if (renderdata->fragment_program_UYVY == 0) { + set_shader_error(renderdata, "UYVY"); + return -1; + } + } + shader = renderdata->fragment_program_UYVY; + } + + data = (GL_TextureData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + SDL_OutOfMemory(); + return -1; + } + + data->shader = shader; + + if (texture->access == SDL_TEXTUREACCESS_STREAMING) { + data->pitch = texture->w * bytes_per_pixel(texture->format); + data->pixels = SDL_malloc(texture->h * data->pitch); + if (!data->pixels) { + SDL_OutOfMemory(); + SDL_free(data); + return -1; + } + } + + texture->driverdata = data; + + renderdata->glGetError(); + renderdata->glGenTextures(1, &data->texture); + if (renderdata->GL_ARB_texture_rectangle_supported) { + data->type = GL_TEXTURE_RECTANGLE_ARB; + texture_w = texture->w; + texture_h = texture->h; + data->texw = (GLfloat) texture_w; + data->texh = (GLfloat) texture_h; + } else { + data->type = GL_TEXTURE_2D; + texture_w = power_of_2(texture->w); + texture_h = power_of_2(texture->h); + data->texw = (GLfloat) (texture->w) / texture_w; + data->texh = (GLfloat) texture->h / texture_h; + } + + /* YUV formats use RGBA but are really two bytes per pixel */ + if (internalFormat == GL_RGBA && bytes_per_pixel(texture->format) < 4) { + texture_w /= 2; + if (data->type == GL_TEXTURE_2D) { + data->texw *= 2.0f; + } + data->HACK_RYAN_FIXME = 2; + } else { + data->HACK_RYAN_FIXME = 1; + } + + data->format = format; + data->formattype = type; + renderdata->glEnable(data->type); + renderdata->glBindTexture(data->type, data->texture); + renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); + renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, + GL_LINEAR); + renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); +#ifdef __MACOSX__ +#ifndef GL_TEXTURE_STORAGE_HINT_APPLE +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#endif +#ifndef STORAGE_CACHED_APPLE +#define STORAGE_CACHED_APPLE 0x85BE +#endif +#ifndef STORAGE_SHARED_APPLE +#define STORAGE_SHARED_APPLE 0x85BF +#endif + if (texture->access == SDL_TEXTUREACCESS_STREAMING) { + renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE, + GL_STORAGE_SHARED_APPLE); + } else { + renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE, + GL_STORAGE_CACHED_APPLE); + } +/* This causes a crash in testoverlay for some reason. Apple bug? */ +#if 0 + if (texture->access == SDL_TEXTUREACCESS_STREAMING + && texture->format == SDL_PIXELFORMAT_ARGB8888) { + /* + if (renderdata->glTextureRangeAPPLE) { + renderdata->glTextureRangeAPPLE(data->type, + texture->h * data->pitch, + data->pixels); + } + */ + renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); + renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w, + texture_h, 0, format, type, data->pixels); + } else +#endif +#endif + { + renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w, + texture_h, 0, format, type, NULL); + } + renderdata->glDisable(data->type); + result = renderdata->glGetError(); + if (result != GL_NO_ERROR) { + GL_SetError("glTexImage2D()", result); + return -1; + } + return 0; +} + +static int +GL_QueryTexturePixels(SDL_Renderer * renderer, SDL_Texture * texture, + void **pixels, int *pitch) +{ + GL_TextureData *data = (GL_TextureData *) texture->driverdata; + + *pixels = data->pixels; + *pitch = data->pitch; + return 0; +} + +static void +SetupTextureUpdate(GL_RenderData * renderdata, SDL_Texture * texture, + int pitch) +{ + renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, + (pitch / bytes_per_pixel(texture->format)) / + ((GL_TextureData *) texture->driverdata)-> + HACK_RYAN_FIXME); +} + +static int +GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, const void *pixels, int pitch) +{ + GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; + GL_TextureData *data = (GL_TextureData *) texture->driverdata; + GLenum result; + + GL_ActivateRenderer(renderer); + + renderdata->glGetError(); + SetupTextureUpdate(renderdata, texture, pitch); + renderdata->glEnable(data->type); + renderdata->glBindTexture(data->type, data->texture); + renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w, + rect->h, data->format, data->formattype, + pixels); + renderdata->glDisable(data->type); + result = renderdata->glGetError(); + if (result != GL_NO_ERROR) { + GL_SetError("glTexSubImage2D()", result); + return -1; + } + return 0; +} + +static int +GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, int markDirty, void **pixels, + int *pitch) +{ + GL_TextureData *data = (GL_TextureData *) texture->driverdata; + + if (markDirty) { + SDL_AddDirtyRect(&data->dirty, rect); + } + + *pixels = + (void *) ((Uint8 *) data->pixels + rect->y * data->pitch + + rect->x * bytes_per_pixel(texture->format)); + *pitch = data->pitch; + return 0; +} + +static void +GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ +} + +static void +GL_DirtyTexture(SDL_Renderer * renderer, SDL_Texture * texture, int numrects, + const SDL_Rect * rects) +{ + GL_TextureData *data = (GL_TextureData *) texture->driverdata; + int i; + + for (i = 0; i < numrects; ++i) { + SDL_AddDirtyRect(&data->dirty, &rects[i]); + } +} + +static void +GL_SetBlendMode(GL_RenderData * data, int blendMode) +{ + if (blendMode != data->blendMode) { + switch (blendMode) { + case SDL_BLENDMODE_NONE: + data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + data->glDisable(GL_BLEND); + break; + case SDL_BLENDMODE_BLEND: + data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + data->glEnable(GL_BLEND); + data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case SDL_BLENDMODE_ADD: + data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + data->glEnable(GL_BLEND); + data->glBlendFunc(GL_SRC_ALPHA, GL_ONE); + break; + } + data->blendMode = blendMode; + } +} + +static int +GL_RenderClear(SDL_Renderer * renderer) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + + GL_ActivateRenderer(renderer); + + data->glClearColor((GLfloat) renderer->r * inv255f, + (GLfloat) renderer->g * inv255f, + (GLfloat) renderer->b * inv255f, + (GLfloat) renderer->a * inv255f); + + data->glClear(GL_COLOR_BUFFER_BIT); + + return 0; +} + +static int +GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_Point * points, + int count) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + int i; + + GL_ActivateRenderer(renderer); + + GL_SetBlendMode(data, renderer->blendMode); + + data->glColor4f((GLfloat) renderer->r * inv255f, + (GLfloat) renderer->g * inv255f, + (GLfloat) renderer->b * inv255f, + (GLfloat) renderer->a * inv255f); + + data->glBegin(GL_POINTS); + for (i = 0; i < count; ++i) { + data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); + } + data->glEnd(); + + return 0; +} + +static int +GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_Point * points, + int count) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + int i; + + GL_ActivateRenderer(renderer); + + GL_SetBlendMode(data, renderer->blendMode); + + data->glColor4f((GLfloat) renderer->r * inv255f, + (GLfloat) renderer->g * inv255f, + (GLfloat) renderer->b * inv255f, + (GLfloat) renderer->a * inv255f); + + if (count > 2 && + points[0].x == points[count-1].x && points[0].y == points[count-1].y) { + data->glBegin(GL_LINE_LOOP); + /* GL_LINE_LOOP takes care of the final segment */ + --count; + for (i = 0; i < count; ++i) { + data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); + } + data->glEnd(); + } else { +#if defined(__APPLE__) || defined(__WIN32__) +#else + int x1, y1, x2, y2; +#endif + + data->glBegin(GL_LINE_STRIP); + for (i = 0; i < count; ++i) { + data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); + } + data->glEnd(); + + /* The line is half open, so we need one more point to complete it. + * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html + * If we have to, we can use vertical line and horizontal line textures + * for vertical and horizontal lines, and then create custom textures + * for diagonal lines and software render those. It's terrible, but at + * least it would be pixel perfect. + */ + data->glBegin(GL_POINTS); +#if defined(__APPLE__) || defined(__WIN32__) + /* Mac OS X and Windows seem to always leave the second point open */ + data->glVertex2f(0.5f + points[count-1].x, 0.5f + points[count-1].y); +#else + /* Linux seems to leave the right-most or bottom-most point open */ + x1 = points[0].x; + y1 = points[0].y; + x2 = points[count-1].x; + y2 = points[count-1].y; + + if (x1 > x2) { + data->glVertex2f(0.5f + x1, 0.5f + y1); + } else if (x2 > x1) { + data->glVertex2f(0.5f + x2, 0.5f + y2); + } else if (y1 > y2) { + data->glVertex2f(0.5f + x1, 0.5f + y1); + } else if (y2 > y1) { + data->glVertex2f(0.5f + x2, 0.5f + y2); + } +#endif + data->glEnd(); + } + + return 0; +} + +static int +GL_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect ** rects, int count) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + int i; + + GL_ActivateRenderer(renderer); + + GL_SetBlendMode(data, renderer->blendMode); + + data->glColor4f((GLfloat) renderer->r * inv255f, + (GLfloat) renderer->g * inv255f, + (GLfloat) renderer->b * inv255f, + (GLfloat) renderer->a * inv255f); + + for (i = 0; i < count; ++i) { + const SDL_Rect *rect = rects[i]; + + data->glRecti(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h); + } + + return 0; +} + +static int +GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_Rect * dstrect) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; + int minx, miny, maxx, maxy; + GLfloat minu, maxu, minv, maxv; + + GL_ActivateRenderer(renderer); + + if (texturedata->dirty.list) { + SDL_DirtyRect *dirty; + void *pixels; + int bpp = bytes_per_pixel(texture->format); + int pitch = texturedata->pitch; + + SetupTextureUpdate(data, texture, pitch); + data->glEnable(texturedata->type); + data->glBindTexture(texturedata->type, texturedata->texture); + for (dirty = texturedata->dirty.list; dirty; dirty = dirty->next) { + SDL_Rect *rect = &dirty->rect; + pixels = + (void *) ((Uint8 *) texturedata->pixels + rect->y * pitch + + rect->x * bpp); + data->glTexSubImage2D(texturedata->type, 0, rect->x, rect->y, + rect->w / texturedata->HACK_RYAN_FIXME, + rect->h, texturedata->format, + texturedata->formattype, pixels); + } + SDL_ClearDirtyRects(&texturedata->dirty); + } + + minx = dstrect->x; + miny = dstrect->y; + maxx = dstrect->x + dstrect->w; + maxy = dstrect->y + dstrect->h; + + minu = (GLfloat) srcrect->x / texture->w; + minu *= texturedata->texw; + maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w; + maxu *= texturedata->texw; + minv = (GLfloat) srcrect->y / texture->h; + minv *= texturedata->texh; + maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h; + maxv *= texturedata->texh; + + data->glEnable(texturedata->type); + data->glBindTexture(texturedata->type, texturedata->texture); + + if (texture->modMode) { + data->glColor4f((GLfloat) texture->r * inv255f, + (GLfloat) texture->g * inv255f, + (GLfloat) texture->b * inv255f, + (GLfloat) texture->a * inv255f); + } else { + data->glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } + + GL_SetBlendMode(data, texture->blendMode); + + /* Set up the shader for the copy, if any */ + if (texturedata->shader) { + data->glEnable(GL_FRAGMENT_PROGRAM_ARB); + data->glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, texturedata->shader); + } + + data->glBegin(GL_TRIANGLE_STRIP); + data->glTexCoord2f(minu, minv); + data->glVertex2f((GLfloat) minx, (GLfloat) miny); + data->glTexCoord2f(maxu, minv); + data->glVertex2f((GLfloat) maxx, (GLfloat) miny); + data->glTexCoord2f(minu, maxv); + data->glVertex2f((GLfloat) minx, (GLfloat) maxy); + data->glTexCoord2f(maxu, maxv); + data->glVertex2f((GLfloat) maxx, (GLfloat) maxy); + data->glEnd(); + + if (texturedata->shader) { + data->glDisable(GL_FRAGMENT_PROGRAM_ARB); + } + + data->glDisable(texturedata->type); + + return 0; +} + +static int +GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, + Uint32 pixel_format, void * pixels, int pitch) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + SDL_Window *window = renderer->window; + GLint internalFormat; + GLenum format, type; + Uint8 *src, *dst, *tmp; + int w, h, length, rows; + + GL_ActivateRenderer(renderer); + + if (!convert_format(data, pixel_format, &internalFormat, &format, &type)) { + /* FIXME: Do a temp copy to a format that is supported */ + SDL_SetError("Unsupported pixel format"); + return -1; + } + + SDL_GetWindowSize(window, &w, &h); + + data->glPixelStorei(GL_PACK_ALIGNMENT, 1); + data->glPixelStorei(GL_PACK_ROW_LENGTH, + (pitch / bytes_per_pixel(pixel_format))); + + data->glReadPixels(rect->x, (h-rect->y)-rect->h, rect->w, rect->h, + format, type, pixels); + + /* Flip the rows to be top-down */ + length = rect->w * bytes_per_pixel(pixel_format); + src = (Uint8*)pixels + (rect->h-1)*pitch; + dst = (Uint8*)pixels; + tmp = SDL_stack_alloc(Uint8, length); + rows = rect->h / 2; + while (rows--) { + SDL_memcpy(tmp, dst, length); + SDL_memcpy(dst, src, length); + SDL_memcpy(src, tmp, length); + dst += pitch; + src -= pitch; + } + SDL_stack_free(tmp); + + return 0; +} + +static int +GL_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, + Uint32 pixel_format, const void * pixels, int pitch) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + SDL_Window *window = renderer->window; + GLint internalFormat; + GLenum format, type; + Uint8 *src, *dst, *tmp; + int w, h, length, rows; + + GL_ActivateRenderer(renderer); + + if (!convert_format(data, pixel_format, &internalFormat, &format, &type)) { + /* FIXME: Do a temp copy to a format that is supported */ + SDL_SetError("Unsupported pixel format"); + return -1; + } + + SDL_GetWindowSize(window, &w, &h); + + data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + data->glPixelStorei(GL_UNPACK_ROW_LENGTH, + (pitch / bytes_per_pixel(pixel_format))); + + /* Flip the rows to be bottom-up */ + length = rect->h * rect->w * pitch; + tmp = SDL_stack_alloc(Uint8, length); + src = (Uint8*)pixels + (rect->h-1)*pitch; + dst = (Uint8*)tmp; + rows = rect->h; + while (rows--) { + SDL_memcpy(dst, src, pitch); + dst += pitch; + src -= pitch; + } + + data->glRasterPos2i(rect->x, (h-rect->y)); + data->glDrawPixels(rect->w, rect->h, format, type, tmp); + SDL_stack_free(tmp); + + return 0; +} + +static void +GL_RenderPresent(SDL_Renderer * renderer) +{ + GL_ActivateRenderer(renderer); + + SDL_GL_SwapWindow(renderer->window); +} + +static void +GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ + GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; + GL_TextureData *data = (GL_TextureData *) texture->driverdata; + + GL_ActivateRenderer(renderer); + + if (!data) { + return; + } + if (data->texture) { + renderdata->glDeleteTextures(1, &data->texture); + } + if (data->palette) { + SDL_free(data->palette); + } + if (data->pixels) { + SDL_free(data->pixels); + } + SDL_FreeDirtyRects(&data->dirty); + SDL_free(data); + texture->driverdata = NULL; +} + +static void +GL_DestroyRenderer(SDL_Renderer * renderer) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + + if (data) { + if (data->context) { + if (data->GL_ARB_fragment_program_supported) { + data->glDisable(GL_FRAGMENT_PROGRAM_ARB); + data->glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0); + if (data->fragment_program_UYVY && + data->fragment_program_UYVY != ~0) { + data->glDeleteProgramsARB(1, + &data->fragment_program_UYVY); + } + } + + /* SDL_GL_MakeCurrent(0, NULL); *//* doesn't do anything */ + SDL_GL_DeleteContext(data->context); + } + SDL_free(data); + } + SDL_free(renderer); +} + +#endif /* SDL_VIDEO_RENDER_OGL */ + +/* vi: set ts=4 sw=4 expandtab: */