src/render/opengl/SDL_render_gl.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 22 Jan 2012 21:46:06 -0500
changeset 6260 fd494c5f305b
parent 6247 b6212690f78d
child 6320 6077a1310907
permissions -rwxr-xr-x
Fixed loading textures when the window starts hidden.
The viewport automatically resets to the window size when you programmatically resize the window.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED
    24 
    25 #include "SDL_hints.h"
    26 #include "SDL_log.h"
    27 #include "SDL_opengl.h"
    28 #include "../SDL_sysrender.h"
    29 #include "SDL_shaders_gl.h"
    30 
    31 #ifdef __MACOSX__
    32 #include <OpenGL/OpenGL.h>
    33 #endif
    34 
    35 
    36 /* OpenGL renderer implementation */
    37 
    38 /* Details on optimizing the texture path on Mac OS X:
    39    http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/opengl_texturedata.html
    40 */
    41 
    42 /* Used to re-create the window with OpenGL capability */
    43 extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
    44 
    45 static const float inv255f = 1.0f / 255.0f;
    46 
    47 static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags);
    48 static void GL_WindowEvent(SDL_Renderer * renderer,
    49                            const SDL_WindowEvent *event);
    50 static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    51 static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    52                             const SDL_Rect * rect, const void *pixels,
    53                             int pitch);
    54 static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    55                           const SDL_Rect * rect, void **pixels, int *pitch);
    56 static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    57 static int GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
    58 static int GL_UpdateViewport(SDL_Renderer * renderer);
    59 static int GL_RenderClear(SDL_Renderer * renderer);
    60 static int GL_RenderDrawPoints(SDL_Renderer * renderer,
    61                                const SDL_Point * points, int count);
    62 static int GL_RenderDrawLines(SDL_Renderer * renderer,
    63                               const SDL_Point * points, int count);
    64 static int GL_RenderFillRects(SDL_Renderer * renderer,
    65                               const SDL_Rect * rects, int count);
    66 static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
    67                          const SDL_Rect * srcrect, const SDL_Rect * dstrect);
    68 static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
    69                                Uint32 pixel_format, void * pixels, int pitch);
    70 static void GL_RenderPresent(SDL_Renderer * renderer);
    71 static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    72 static void GL_DestroyRenderer(SDL_Renderer * renderer);
    73 
    74 
    75 SDL_RenderDriver GL_RenderDriver = {
    76     GL_CreateRenderer,
    77     {
    78      "opengl",
    79      (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
    80      1,
    81      {SDL_PIXELFORMAT_ARGB8888},
    82      0,
    83      0}
    84 };
    85 
    86 typedef struct GL_FBOList GL_FBOList;
    87 
    88 struct GL_FBOList
    89 {
    90     Uint32 w, h;
    91     GLuint FBO;
    92     GL_FBOList *next;
    93 };
    94 
    95 typedef struct
    96 {
    97     SDL_GLContext context;
    98     SDL_bool GL_ARB_texture_rectangle_supported;
    99     struct {
   100         GL_Shader shader;
   101         Uint32 color;
   102         int blendMode;
   103     } current;
   104     
   105     SDL_bool GL_EXT_framebuffer_object_supported;
   106     GL_FBOList *framebuffers;
   107 
   108     /* OpenGL functions */
   109 #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
   110 #include "SDL_glfuncs.h"
   111 #undef SDL_PROC
   112 
   113     /* Multitexture support */
   114     SDL_bool GL_ARB_multitexture_supported;
   115     PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
   116     GLint num_texture_units;
   117     
   118     PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
   119     PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT;
   120     PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
   121     PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
   122     PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
   123 
   124     /* Shader support */
   125     GL_ShaderContext *shaders;
   126 
   127 } GL_RenderData;
   128 
   129 typedef struct
   130 {
   131     GLuint texture;
   132     GLenum type;
   133     GLfloat texw;
   134     GLfloat texh;
   135     GLenum format;
   136     GLenum formattype;
   137     void *pixels;
   138     int pitch;
   139     SDL_Rect locked_rect;
   140 
   141     /* YV12 texture support */
   142     SDL_bool yuv;
   143     GLuint utexture;
   144     GLuint vtexture;
   145     
   146     GL_FBOList *fbo;
   147 } GL_TextureData;
   148 
   149 
   150 static void
   151 GL_SetError(const char *prefix, GLenum result)
   152 {
   153     const char *error;
   154 
   155     switch (result) {
   156     case GL_NO_ERROR:
   157         error = "GL_NO_ERROR";
   158         break;
   159     case GL_INVALID_ENUM:
   160         error = "GL_INVALID_ENUM";
   161         break;
   162     case GL_INVALID_VALUE:
   163         error = "GL_INVALID_VALUE";
   164         break;
   165     case GL_INVALID_OPERATION:
   166         error = "GL_INVALID_OPERATION";
   167         break;
   168     case GL_STACK_OVERFLOW:
   169         error = "GL_STACK_OVERFLOW";
   170         break;
   171     case GL_STACK_UNDERFLOW:
   172         error = "GL_STACK_UNDERFLOW";
   173         break;
   174     case GL_OUT_OF_MEMORY:
   175         error = "GL_OUT_OF_MEMORY";
   176         break;
   177     case GL_TABLE_TOO_LARGE:
   178         error = "GL_TABLE_TOO_LARGE";
   179         break;
   180     default:
   181         error = "UNKNOWN";
   182         break;
   183     }
   184     SDL_SetError("%s: %s", prefix, error);
   185 }
   186 
   187 static int
   188 GL_LoadFunctions(GL_RenderData * data)
   189 {
   190 #ifdef __SDL_NOGETPROCADDR__
   191 #define SDL_PROC(ret,func,params) data->func=func;
   192 #else
   193 #define SDL_PROC(ret,func,params) \
   194     do { \
   195         data->func = SDL_GL_GetProcAddress(#func); \
   196         if ( ! data->func ) { \
   197             SDL_SetError("Couldn't load GL function %s: %s\n", #func, SDL_GetError()); \
   198             return -1; \
   199         } \
   200     } while ( 0 );
   201 #endif /* __SDL_NOGETPROCADDR__ */
   202 
   203 #include "SDL_glfuncs.h"
   204 #undef SDL_PROC
   205     return 0;
   206 }
   207 
   208 static SDL_GLContext SDL_CurrentContext = NULL;
   209 
   210 static int
   211 GL_ActivateRenderer(SDL_Renderer * renderer)
   212 {
   213     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   214 
   215     if (SDL_CurrentContext != data->context) {
   216         if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) {
   217             return -1;
   218         }
   219         SDL_CurrentContext = data->context;
   220 
   221         GL_UpdateViewport(renderer);
   222     }
   223     return 0;
   224 }
   225 
   226 /* This is called if we need to invalidate all of the SDL OpenGL state */
   227 static void
   228 GL_ResetState(SDL_Renderer *renderer)
   229 {
   230     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   231 
   232     if (SDL_CurrentContext == data->context) {
   233         GL_UpdateViewport(renderer);
   234     } else {
   235         GL_ActivateRenderer(renderer);
   236     }
   237 
   238     data->current.shader = SHADER_NONE;
   239     data->current.color = 0;
   240     data->current.blendMode = -1;
   241 
   242     data->glDisable(GL_DEPTH_TEST);
   243     data->glDisable(GL_CULL_FACE);
   244     /* This ended up causing video discrepancies between OpenGL and Direct3D */
   245     /*data->glEnable(GL_LINE_SMOOTH);*/
   246 
   247     data->glMatrixMode(GL_MODELVIEW);
   248     data->glLoadIdentity();
   249 }
   250 
   251 
   252 GL_FBOList *
   253 GL_GetFBO(GL_RenderData *data, Uint32 w, Uint32 h)
   254 {
   255     GL_FBOList *result = data->framebuffers;
   256 
   257     while (result && ((result->w != w) || (result->h != h))) {
   258         result = result->next;
   259     }
   260 
   261     if (!result) {
   262         result = SDL_malloc(sizeof(GL_FBOList));
   263         if (result) {
   264             result->w = w;
   265             result->h = h;
   266             data->glGenFramebuffersEXT(1, &result->FBO);
   267             result->next = data->framebuffers;
   268             data->framebuffers = result;
   269         }
   270     }
   271     return result;
   272 }
   273 
   274 SDL_Renderer *
   275 GL_CreateRenderer(SDL_Window * window, Uint32 flags)
   276 {
   277     SDL_Renderer *renderer;
   278     GL_RenderData *data;
   279     const char *hint;
   280     GLint value;
   281     Uint32 window_flags;
   282 
   283     window_flags = SDL_GetWindowFlags(window);
   284     if (!(window_flags & SDL_WINDOW_OPENGL)) {
   285         if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
   286             /* Uh oh, better try to put it back... */
   287             SDL_RecreateWindow(window, window_flags);
   288             return NULL;
   289         }
   290     }
   291 
   292     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   293     if (!renderer) {
   294         SDL_OutOfMemory();
   295         return NULL;
   296     }
   297 
   298     data = (GL_RenderData *) SDL_calloc(1, sizeof(*data));
   299     if (!data) {
   300         GL_DestroyRenderer(renderer);
   301         SDL_OutOfMemory();
   302         return NULL;
   303     }
   304 
   305     renderer->WindowEvent = GL_WindowEvent;
   306     renderer->CreateTexture = GL_CreateTexture;
   307     renderer->UpdateTexture = GL_UpdateTexture;
   308     renderer->LockTexture = GL_LockTexture;
   309     renderer->UnlockTexture = GL_UnlockTexture;
   310     renderer->SetRenderTarget = GL_SetRenderTarget;
   311     renderer->UpdateViewport = GL_UpdateViewport;
   312     renderer->RenderClear = GL_RenderClear;
   313     renderer->RenderDrawPoints = GL_RenderDrawPoints;
   314     renderer->RenderDrawLines = GL_RenderDrawLines;
   315     renderer->RenderFillRects = GL_RenderFillRects;
   316     renderer->RenderCopy = GL_RenderCopy;
   317     renderer->RenderReadPixels = GL_RenderReadPixels;
   318     renderer->RenderPresent = GL_RenderPresent;
   319     renderer->DestroyTexture = GL_DestroyTexture;
   320     renderer->DestroyRenderer = GL_DestroyRenderer;
   321     renderer->info = GL_RenderDriver.info;
   322     renderer->info.flags = SDL_RENDERER_ACCELERATED;
   323     renderer->driverdata = data;
   324     renderer->window = window;
   325 
   326     data->context = SDL_GL_CreateContext(window);
   327     if (!data->context) {
   328         GL_DestroyRenderer(renderer);
   329         return NULL;
   330     }
   331     if (SDL_GL_MakeCurrent(window, data->context) < 0) {
   332         GL_DestroyRenderer(renderer);
   333         return NULL;
   334     }
   335 
   336     if (GL_LoadFunctions(data) < 0) {
   337         GL_DestroyRenderer(renderer);
   338         return NULL;
   339     }
   340 
   341 #ifdef __MACOSX__
   342     /* Enable multi-threaded rendering */
   343     /* Disabled until Ryan finishes his VBO/PBO code...
   344        CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine);
   345      */
   346 #endif
   347 
   348     if (flags & SDL_RENDERER_PRESENTVSYNC) {
   349         SDL_GL_SetSwapInterval(1);
   350     } else {
   351         SDL_GL_SetSwapInterval(0);
   352     }
   353     if (SDL_GL_GetSwapInterval() > 0) {
   354         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
   355     }
   356 
   357     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
   358     renderer->info.max_texture_width = value;
   359     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
   360     renderer->info.max_texture_height = value;
   361 
   362     if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle")
   363         || SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) {
   364         data->GL_ARB_texture_rectangle_supported = SDL_TRUE;
   365     }
   366 
   367     /* Check for multitexture support */
   368     if (SDL_GL_ExtensionSupported("GL_ARB_multitexture")) {
   369         data->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB");
   370         if (data->glActiveTextureARB) {
   371             data->GL_ARB_multitexture_supported = SDL_TRUE;
   372             data->glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &data->num_texture_units);
   373         }
   374     }
   375 
   376     /* Check for shader support */
   377     hint = SDL_GetHint(SDL_HINT_RENDER_OPENGL_SHADERS);
   378     if (!hint || *hint != '0') {
   379         data->shaders = GL_CreateShaderContext();
   380     }
   381     SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s",
   382                 data->shaders ? "ENABLED" : "DISABLED");
   383 
   384     /* We support YV12 textures using 3 textures and a shader */
   385     if (data->shaders && data->num_texture_units >= 3) {
   386         renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
   387         renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
   388     }
   389     
   390     if (SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object")) {
   391         data->GL_EXT_framebuffer_object_supported = SDL_TRUE;
   392         data->glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)
   393             SDL_GL_GetProcAddress("glGenFramebuffersEXT");
   394         data->glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
   395             SDL_GL_GetProcAddress("glDeleteFramebuffersEXT");
   396         data->glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
   397             SDL_GL_GetProcAddress("glFramebufferTexture2DEXT");
   398         data->glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)
   399             SDL_GL_GetProcAddress("glBindFramebufferEXT");
   400         data->glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
   401             SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT");
   402         renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
   403     }
   404     data->framebuffers = NULL;
   405 
   406     /* Set up parameters for rendering */
   407     GL_ResetState(renderer);
   408 
   409     return renderer;
   410 }
   411 
   412 static void
   413 GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   414 {
   415     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
   416         event->event == SDL_WINDOWEVENT_SHOWN ||
   417         event->event == SDL_WINDOWEVENT_HIDDEN) {
   418         /* Rebind the context to the window area and update matrices */
   419         SDL_CurrentContext = NULL;
   420     }
   421 }
   422 
   423 static __inline__ int
   424 power_of_2(int input)
   425 {
   426     int value = 1;
   427 
   428     while (value < input) {
   429         value <<= 1;
   430     }
   431     return value;
   432 }
   433 
   434 static __inline__ SDL_bool
   435 convert_format(GL_RenderData *renderdata, Uint32 pixel_format,
   436                GLint* internalFormat, GLenum* format, GLenum* type)
   437 {
   438     switch (pixel_format) {
   439     case SDL_PIXELFORMAT_ARGB8888:
   440         *internalFormat = GL_RGBA8;
   441         *format = GL_BGRA;
   442         *type = GL_UNSIGNED_INT_8_8_8_8_REV;
   443         break;
   444     case SDL_PIXELFORMAT_YV12:
   445     case SDL_PIXELFORMAT_IYUV:
   446         *internalFormat = GL_LUMINANCE;
   447         *format = GL_LUMINANCE;
   448         *type = GL_UNSIGNED_BYTE;
   449         break;
   450     default:
   451         return SDL_FALSE;
   452     }
   453     return SDL_TRUE;
   454 }
   455 
   456 static GLenum
   457 GetScaleQuality(void)
   458 {
   459     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
   460 
   461     if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
   462         return GL_NEAREST;
   463     } else {
   464         return GL_LINEAR;
   465     }
   466 }
   467 
   468 static int
   469 GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   470 {
   471     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   472     GL_TextureData *data;
   473     GLint internalFormat;
   474     GLenum format, type;
   475     int texture_w, texture_h;
   476     GLenum scaleMode;
   477     GLenum result;
   478 
   479     GL_ActivateRenderer(renderer);
   480 
   481     if (!convert_format(renderdata, texture->format, &internalFormat,
   482                         &format, &type)) {
   483         SDL_SetError("Texture format %s not supported by OpenGL",
   484                      SDL_GetPixelFormatName(texture->format));
   485         return -1;
   486     }
   487 
   488     data = (GL_TextureData *) SDL_calloc(1, sizeof(*data));
   489     if (!data) {
   490         SDL_OutOfMemory();
   491         return -1;
   492     }
   493 
   494     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   495         size_t size;
   496         data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
   497         size = texture->h * data->pitch;
   498         if (texture->format == SDL_PIXELFORMAT_YV12 ||
   499             texture->format == SDL_PIXELFORMAT_IYUV) {
   500             /* Need to add size for the U and V planes */
   501             size += (2 * (texture->h * data->pitch) / 4);
   502         }
   503         data->pixels = SDL_calloc(1, size);
   504         if (!data->pixels) {
   505             SDL_OutOfMemory();
   506             SDL_free(data);
   507             return -1;
   508         }
   509     }
   510 
   511     texture->driverdata = data;
   512     
   513     if (texture->access == SDL_TEXTUREACCESS_TARGET) {
   514         data->fbo = GL_GetFBO(renderdata, texture->w, texture->h);
   515     } else {
   516         data->fbo = NULL;
   517     }
   518 
   519     renderdata->glGetError();
   520     renderdata->glGenTextures(1, &data->texture);
   521     if ((renderdata->GL_ARB_texture_rectangle_supported)
   522         /*&& texture->access != SDL_TEXTUREACCESS_TARGET*/){
   523         data->type = GL_TEXTURE_RECTANGLE_ARB;
   524         texture_w = texture->w;
   525         texture_h = texture->h;
   526         data->texw = (GLfloat) texture_w;
   527         data->texh = (GLfloat) texture_h;
   528     } else {
   529         data->type = GL_TEXTURE_2D;
   530         texture_w = power_of_2(texture->w);
   531         texture_h = power_of_2(texture->h);
   532         data->texw = (GLfloat) (texture->w) / texture_w;
   533         data->texh = (GLfloat) texture->h / texture_h;
   534     }
   535 
   536     data->format = format;
   537     data->formattype = type;
   538     scaleMode = GetScaleQuality();
   539     renderdata->glEnable(data->type);
   540     renderdata->glBindTexture(data->type, data->texture);
   541     renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
   542     renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
   543     /* According to the spec, CLAMP_TO_EDGE is the default for TEXTURE_RECTANGLE
   544        and setting it causes an INVALID_ENUM error in the latest NVidia drivers.
   545     */
   546     if (data->type != GL_TEXTURE_RECTANGLE_ARB) {
   547         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
   548                                     GL_CLAMP_TO_EDGE);
   549         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
   550                                     GL_CLAMP_TO_EDGE);
   551     }
   552 #ifdef __MACOSX__
   553 #ifndef GL_TEXTURE_STORAGE_HINT_APPLE
   554 #define GL_TEXTURE_STORAGE_HINT_APPLE       0x85BC
   555 #endif
   556 #ifndef STORAGE_CACHED_APPLE
   557 #define STORAGE_CACHED_APPLE                0x85BE
   558 #endif
   559 #ifndef STORAGE_SHARED_APPLE
   560 #define STORAGE_SHARED_APPLE                0x85BF
   561 #endif
   562     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   563         renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
   564                                     GL_STORAGE_SHARED_APPLE);
   565     } else {
   566         renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
   567                                     GL_STORAGE_CACHED_APPLE);
   568     }
   569     if (texture->access == SDL_TEXTUREACCESS_STREAMING
   570         && texture->format == SDL_PIXELFORMAT_ARGB8888
   571         && (texture->w % 8) == 0) {
   572         renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
   573         renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   574         renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
   575                           (data->pitch / SDL_BYTESPERPIXEL(texture->format)));
   576         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
   577                                  texture_h, 0, format, type, data->pixels);
   578         renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
   579     }
   580     else
   581 #endif
   582     {
   583         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
   584                                  texture_h, 0, format, type, NULL);
   585     }
   586     renderdata->glDisable(data->type);
   587     result = renderdata->glGetError();
   588     if (result != GL_NO_ERROR) {
   589         GL_SetError("glTexImage2D()", result);
   590         return -1;
   591     }
   592 
   593     if (texture->format == SDL_PIXELFORMAT_YV12 ||
   594         texture->format == SDL_PIXELFORMAT_IYUV) {
   595         data->yuv = SDL_TRUE;
   596 
   597         renderdata->glGenTextures(1, &data->utexture);
   598         renderdata->glGenTextures(1, &data->vtexture);
   599         renderdata->glEnable(data->type);
   600 
   601         renderdata->glBindTexture(data->type, data->utexture);
   602         renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
   603                                     scaleMode);
   604         renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
   605                                     scaleMode);
   606         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
   607                                     GL_CLAMP_TO_EDGE);
   608         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
   609                                     GL_CLAMP_TO_EDGE);
   610         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
   611                                  texture_h/2, 0, format, type, NULL);
   612 
   613         renderdata->glBindTexture(data->type, data->vtexture);
   614         renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
   615                                     scaleMode);
   616         renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
   617                                     scaleMode);
   618         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
   619                                     GL_CLAMP_TO_EDGE);
   620         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
   621                                     GL_CLAMP_TO_EDGE);
   622         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
   623                                  texture_h/2, 0, format, type, NULL);
   624 
   625         renderdata->glDisable(data->type);
   626     }
   627     return 0;
   628 }
   629 
   630 static int
   631 GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   632                  const SDL_Rect * rect, const void *pixels, int pitch)
   633 {
   634     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   635     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   636     GLenum result;
   637 
   638     GL_ActivateRenderer(renderer);
   639 
   640     renderdata->glGetError();
   641     renderdata->glEnable(data->type);
   642     renderdata->glBindTexture(data->type, data->texture);
   643     renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   644     renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
   645                               (pitch / SDL_BYTESPERPIXEL(texture->format)));
   646     renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
   647                                 rect->h, data->format, data->formattype,
   648                                 pixels);
   649     if (data->yuv) {
   650         renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2));
   651 
   652         /* Skip to the correct offset into the next texture */
   653         pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
   654         if (texture->format == SDL_PIXELFORMAT_YV12) {
   655             renderdata->glBindTexture(data->type, data->vtexture);
   656         } else {
   657             renderdata->glBindTexture(data->type, data->utexture);
   658         }
   659         renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
   660                                     rect->w/2, rect->h/2,
   661                                     data->format, data->formattype, pixels);
   662 
   663         /* Skip to the correct offset into the next texture */
   664         pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4);
   665         if (texture->format == SDL_PIXELFORMAT_YV12) {
   666             renderdata->glBindTexture(data->type, data->utexture);
   667         } else {
   668             renderdata->glBindTexture(data->type, data->vtexture);
   669         }
   670         renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
   671                                     rect->w/2, rect->h/2,
   672                                     data->format, data->formattype, pixels);
   673     }
   674     renderdata->glDisable(data->type);
   675     result = renderdata->glGetError();
   676     if (result != GL_NO_ERROR) {
   677         GL_SetError("glTexSubImage2D()", result);
   678         return -1;
   679     }
   680     return 0;
   681 }
   682 
   683 static int
   684 GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   685                const SDL_Rect * rect, void **pixels, int *pitch)
   686 {
   687     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   688 
   689     data->locked_rect = *rect;
   690     *pixels = 
   691         (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
   692                   rect->x * SDL_BYTESPERPIXEL(texture->format));
   693     *pitch = data->pitch;
   694     return 0;
   695 }
   696 
   697 static void
   698 GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   699 {
   700     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   701     const SDL_Rect *rect;
   702     void *pixels;
   703 
   704     rect = &data->locked_rect;
   705     pixels = 
   706         (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
   707                   rect->x * SDL_BYTESPERPIXEL(texture->format));
   708     GL_UpdateTexture(renderer, texture, rect, pixels, data->pitch);
   709 }
   710 
   711 static int
   712 GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
   713 {
   714     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;    
   715     GL_TextureData *texturedata;
   716     GLenum status;
   717 
   718     GL_ActivateRenderer(renderer);
   719     
   720     if (texture == NULL) {
   721         data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
   722         return 0;
   723     }
   724 
   725     texturedata = (GL_TextureData *) texture->driverdata;
   726     data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, texturedata->fbo->FBO);
   727     /* TODO: check if texture pixel format allows this operation */
   728     data->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, texturedata->type, texturedata->texture, 0);
   729     /* Check FBO status */
   730     status = data->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
   731     if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
   732         SDL_SetError("glFramebufferTexture2DEXT() failed");
   733         return -1;
   734     }
   735     return 0;
   736 }
   737 
   738 static int
   739 GL_UpdateViewport(SDL_Renderer * renderer)
   740 {
   741     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   742 
   743     if (SDL_CurrentContext != data->context) {
   744         /* We'll update the viewport after we rebind the context */
   745         return 0;
   746     }
   747 
   748     data->glViewport(renderer->viewport.x, renderer->viewport.y,
   749                      renderer->viewport.w, renderer->viewport.h);
   750 
   751     data->glMatrixMode(GL_PROJECTION);
   752     data->glLoadIdentity();
   753     if (renderer->target) {
   754         data->glOrtho((GLdouble) 0,
   755                       (GLdouble) renderer->viewport.w,
   756                       (GLdouble) 0,
   757                       (GLdouble) renderer->viewport.h,
   758                        0.0, 1.0);
   759     } else {
   760         data->glOrtho((GLdouble) 0,
   761                       (GLdouble) renderer->viewport.w,
   762                       (GLdouble) renderer->viewport.h,
   763                       (GLdouble) 0,
   764                        0.0, 1.0);
   765     }
   766     return 0;
   767 }
   768 
   769 static void
   770 GL_SetShader(GL_RenderData * data, GL_Shader shader)
   771 {
   772     if (data->shaders && shader != data->current.shader) {
   773         GL_SelectShader(data->shaders, shader);
   774         data->current.shader = shader;
   775     }
   776 }
   777 
   778 static void
   779 GL_SetColor(GL_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
   780 {
   781     Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
   782 
   783     if (color != data->current.color) {
   784         data->glColor4f((GLfloat) r * inv255f,
   785                         (GLfloat) g * inv255f,
   786                         (GLfloat) b * inv255f,
   787                         (GLfloat) a * inv255f);
   788         data->current.color = color;
   789     }
   790 }
   791 
   792 static void
   793 GL_SetBlendMode(GL_RenderData * data, int blendMode)
   794 {
   795     if (blendMode != data->current.blendMode) {
   796         switch (blendMode) {
   797         case SDL_BLENDMODE_NONE:
   798             data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   799             data->glDisable(GL_BLEND);
   800             break;
   801         case SDL_BLENDMODE_BLEND:
   802             data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   803             data->glEnable(GL_BLEND);
   804             data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   805             break;
   806         case SDL_BLENDMODE_ADD:
   807             data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   808             data->glEnable(GL_BLEND);
   809             data->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
   810             break;
   811         case SDL_BLENDMODE_MOD:
   812             data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   813             data->glEnable(GL_BLEND);
   814             data->glBlendFunc(GL_ZERO, GL_SRC_COLOR);
   815             break;
   816         }
   817         data->current.blendMode = blendMode;
   818     }
   819 }
   820 
   821 static void
   822 GL_SetDrawingState(SDL_Renderer * renderer)
   823 {
   824     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   825 
   826     GL_ActivateRenderer(renderer);
   827 
   828     GL_SetColor(data, renderer->r,
   829                       renderer->g,
   830                       renderer->b,
   831                       renderer->a);
   832 
   833     GL_SetBlendMode(data, renderer->blendMode);
   834 
   835     GL_SetShader(data, SHADER_SOLID);
   836 }
   837 
   838 static int
   839 GL_RenderClear(SDL_Renderer * renderer)
   840 {
   841     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   842 
   843     GL_ActivateRenderer(renderer);
   844 
   845     data->glClearColor((GLfloat) renderer->r * inv255f,
   846                        (GLfloat) renderer->g * inv255f,
   847                        (GLfloat) renderer->b * inv255f,
   848                        (GLfloat) renderer->a * inv255f);
   849 
   850     data->glClear(GL_COLOR_BUFFER_BIT);
   851 
   852     return 0;
   853 }
   854 
   855 static int
   856 GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_Point * points,
   857                     int count)
   858 {
   859     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   860     int i;
   861 
   862     GL_SetDrawingState(renderer);
   863 
   864     data->glBegin(GL_POINTS);
   865     for (i = 0; i < count; ++i) {
   866         data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   867     }
   868     data->glEnd();
   869 
   870     return 0;
   871 }
   872 
   873 static int
   874 GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_Point * points,
   875                    int count)
   876 {
   877     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   878     int i;
   879 
   880     GL_SetDrawingState(renderer);
   881 
   882     if (count > 2 && 
   883         points[0].x == points[count-1].x && points[0].y == points[count-1].y) {
   884         data->glBegin(GL_LINE_LOOP);
   885         /* GL_LINE_LOOP takes care of the final segment */
   886         --count;
   887         for (i = 0; i < count; ++i) {
   888             data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   889         }
   890         data->glEnd();
   891     } else {
   892 #if defined(__APPLE__) || defined(__WIN32__)
   893 #else
   894         int x1, y1, x2, y2;
   895 #endif
   896 
   897         data->glBegin(GL_LINE_STRIP);
   898         for (i = 0; i < count; ++i) {
   899             data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   900         }
   901         data->glEnd();
   902 
   903         /* The line is half open, so we need one more point to complete it.
   904          * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html
   905          * If we have to, we can use vertical line and horizontal line textures
   906          * for vertical and horizontal lines, and then create custom textures
   907          * for diagonal lines and software render those.  It's terrible, but at
   908          * least it would be pixel perfect.
   909          */
   910         data->glBegin(GL_POINTS);
   911 #if defined(__APPLE__) || defined(__WIN32__)
   912         /* Mac OS X and Windows seem to always leave the second point open */
   913         data->glVertex2f(0.5f + points[count-1].x, 0.5f + points[count-1].y);
   914 #else
   915         /* Linux seems to leave the right-most or bottom-most point open */
   916         x1 = points[0].x;
   917         y1 = points[0].y;
   918         x2 = points[count-1].x;
   919         y2 = points[count-1].y;
   920 
   921         if (x1 > x2) {
   922             data->glVertex2f(0.5f + x1, 0.5f + y1);
   923         } else if (x2 > x1) {
   924             data->glVertex2f(0.5f + x2, 0.5f + y2);
   925         } else if (y1 > y2) {
   926             data->glVertex2f(0.5f + x1, 0.5f + y1);
   927         } else if (y2 > y1) {
   928             data->glVertex2f(0.5f + x2, 0.5f + y2);
   929         }
   930 #endif
   931         data->glEnd();
   932     }
   933 
   934     return 0;
   935 }
   936 
   937 static int
   938 GL_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect * rects, int count)
   939 {
   940     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   941     int i;
   942 
   943     GL_SetDrawingState(renderer);
   944 
   945     for (i = 0; i < count; ++i) {
   946         const SDL_Rect *rect = &rects[i];
   947 
   948         data->glRecti(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
   949     }
   950 
   951     return 0;
   952 }
   953 
   954 static int
   955 GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   956               const SDL_Rect * srcrect, const SDL_Rect * dstrect)
   957 {
   958     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   959     GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
   960     int minx, miny, maxx, maxy;
   961     GLfloat minu, maxu, minv, maxv;
   962 
   963     GL_ActivateRenderer(renderer);
   964 
   965     data->glEnable(texturedata->type);
   966     if (texturedata->yuv) {
   967         data->glActiveTextureARB(GL_TEXTURE2_ARB);
   968         data->glBindTexture(texturedata->type, texturedata->vtexture);
   969 
   970         data->glActiveTextureARB(GL_TEXTURE1_ARB);
   971         data->glBindTexture(texturedata->type, texturedata->utexture);
   972 
   973         data->glActiveTextureARB(GL_TEXTURE0_ARB);
   974     }
   975     data->glBindTexture(texturedata->type, texturedata->texture);
   976 
   977     if (texture->modMode) {
   978         GL_SetColor(data, texture->r, texture->g, texture->b, texture->a);
   979     } else {
   980         GL_SetColor(data, 255, 255, 255, 255);
   981     }
   982 
   983     GL_SetBlendMode(data, texture->blendMode);
   984 
   985     if (texturedata->yuv) {
   986         GL_SetShader(data, SHADER_YV12);
   987     } else {
   988         GL_SetShader(data, SHADER_RGB);
   989     }
   990 
   991     minx = dstrect->x;
   992     miny = dstrect->y;
   993     maxx = dstrect->x + dstrect->w;
   994     maxy = dstrect->y + dstrect->h;
   995 
   996     minu = (GLfloat) srcrect->x / texture->w;
   997     minu *= texturedata->texw;
   998     maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
   999     maxu *= texturedata->texw;
  1000     minv = (GLfloat) srcrect->y / texture->h;
  1001     minv *= texturedata->texh;
  1002     maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
  1003     maxv *= texturedata->texh;
  1004 
  1005     data->glBegin(GL_TRIANGLE_STRIP);
  1006     data->glTexCoord2f(minu, minv);
  1007     data->glVertex2f((GLfloat) minx, (GLfloat) miny);
  1008     data->glTexCoord2f(maxu, minv);
  1009     data->glVertex2f((GLfloat) maxx, (GLfloat) miny);
  1010     data->glTexCoord2f(minu, maxv);
  1011     data->glVertex2f((GLfloat) minx, (GLfloat) maxy);
  1012     data->glTexCoord2f(maxu, maxv);
  1013     data->glVertex2f((GLfloat) maxx, (GLfloat) maxy);
  1014     data->glEnd();
  1015 
  1016     data->glDisable(texturedata->type);
  1017 
  1018     return 0;
  1019 }
  1020 
  1021 static int
  1022 GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  1023                     Uint32 pixel_format, void * pixels, int pitch)
  1024 {
  1025     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
  1026     SDL_Window *window = renderer->window;
  1027     Uint32 temp_format = SDL_PIXELFORMAT_ARGB8888;
  1028     void *temp_pixels;
  1029     int temp_pitch;
  1030     GLint internalFormat;
  1031     GLenum format, type;
  1032     Uint8 *src, *dst, *tmp;
  1033     int w, h, length, rows;
  1034     int status;
  1035 
  1036     GL_ActivateRenderer(renderer);
  1037 
  1038     temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format);
  1039     temp_pixels = SDL_malloc(rect->h * temp_pitch);
  1040     if (!temp_pixels) {
  1041         SDL_OutOfMemory();
  1042         return -1;
  1043     }
  1044 
  1045     convert_format(data, temp_format, &internalFormat, &format, &type);
  1046 
  1047     SDL_GetWindowSize(window, &w, &h);
  1048 
  1049     data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
  1050     data->glPixelStorei(GL_PACK_ROW_LENGTH,
  1051                         (temp_pitch / SDL_BYTESPERPIXEL(temp_format)));
  1052 
  1053     data->glReadPixels(rect->x, (h-rect->y)-rect->h, rect->w, rect->h,
  1054                        format, type, temp_pixels);
  1055 
  1056     /* Flip the rows to be top-down */
  1057     length = rect->w * SDL_BYTESPERPIXEL(temp_format);
  1058     src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch;
  1059     dst = (Uint8*)temp_pixels;
  1060     tmp = SDL_stack_alloc(Uint8, length);
  1061     rows = rect->h / 2;
  1062     while (rows--) {
  1063         SDL_memcpy(tmp, dst, length);
  1064         SDL_memcpy(dst, src, length);
  1065         SDL_memcpy(src, tmp, length);
  1066         dst += temp_pitch;
  1067         src -= temp_pitch;
  1068     }
  1069     SDL_stack_free(tmp);
  1070 
  1071     status = SDL_ConvertPixels(rect->w, rect->h,
  1072                                temp_format, temp_pixels, temp_pitch,
  1073                                pixel_format, pixels, pitch);
  1074     SDL_free(temp_pixels);
  1075 
  1076     return status;
  1077 }
  1078 
  1079 static void
  1080 GL_RenderPresent(SDL_Renderer * renderer)
  1081 {
  1082     GL_ActivateRenderer(renderer);
  1083 
  1084     SDL_GL_SwapWindow(renderer->window);
  1085 }
  1086 
  1087 static void
  1088 GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  1089 {
  1090     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
  1091     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
  1092 
  1093     GL_ActivateRenderer(renderer);
  1094 
  1095     if (!data) {
  1096         return;
  1097     }
  1098     if (data->texture) {
  1099         renderdata->glDeleteTextures(1, &data->texture);
  1100     }
  1101     if (data->yuv) {
  1102         renderdata->glDeleteTextures(1, &data->utexture);
  1103         renderdata->glDeleteTextures(1, &data->vtexture);
  1104     }
  1105     if (data->pixels) {
  1106         SDL_free(data->pixels);
  1107     }
  1108     SDL_free(data);
  1109     texture->driverdata = NULL;
  1110 }
  1111 
  1112 static void
  1113 GL_DestroyRenderer(SDL_Renderer * renderer)
  1114 {
  1115     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
  1116 
  1117     if (data) {
  1118         if (data->shaders) {
  1119             GL_DestroyShaderContext(data->shaders);
  1120         }
  1121         if (data->context) {
  1122             while (data->framebuffers) {
  1123                 GL_FBOList *nextnode = data->framebuffers->next;
  1124                 /* delete the framebuffer object */
  1125                 data->glDeleteFramebuffersEXT(1, &data->framebuffers->FBO);
  1126                 SDL_free(data->framebuffers);
  1127                 data->framebuffers = nextnode;
  1128             }            
  1129             /* SDL_GL_MakeCurrent(0, NULL); *//* doesn't do anything */
  1130             SDL_GL_DeleteContext(data->context);
  1131         }
  1132         SDL_free(data);
  1133     }
  1134     SDL_free(renderer);
  1135 }
  1136 
  1137 #endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */
  1138 
  1139 /* vi: set ts=4 sw=4 expandtab: */