src/render/opengl/SDL_render_gl.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 12 Feb 2011 00:25:02 -0800
changeset 5264 7ace5f8f432f
parent 5262 b530ef003506
child 5265 48724afcdc6e
permissions -rw-r--r--
Initial pass at shader YV12 support - doesn't quite work yet.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2011 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #if SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED
    25 
    26 #include "SDL_hints.h"
    27 #include "SDL_log.h"
    28 #include "SDL_opengl.h"
    29 #include "../SDL_sysrender.h"
    30 #include "SDL_shaders_gl.h"
    31 
    32 #ifdef __MACOSX__
    33 #include <OpenGL/OpenGL.h>
    34 #endif
    35 
    36 
    37 /* OpenGL renderer implementation */
    38 
    39 /* Details on optimizing the texture path on Mac OS X:
    40    http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/opengl_texturedata.html
    41 */
    42 
    43 /* Used to re-create the window with OpenGL capability */
    44 extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
    45 
    46 static const float inv255f = 1.0f / 255.0f;
    47 
    48 static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags);
    49 static void GL_WindowEvent(SDL_Renderer * renderer,
    50                            const SDL_WindowEvent *event);
    51 static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    52 static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    53                             const SDL_Rect * rect, const void *pixels,
    54                             int pitch);
    55 static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    56                           const SDL_Rect * rect, void **pixels, int *pitch);
    57 static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    58 static void GL_SetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect);
    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),
    80      1,
    81      {SDL_PIXELFORMAT_ARGB8888},
    82      0,
    83      0}
    84 };
    85 
    86 typedef struct
    87 {
    88     SDL_GLContext context;
    89     SDL_bool updateSize;
    90     SDL_bool GL_ARB_texture_rectangle_supported;
    91     int blendMode;
    92 
    93     /* OpenGL functions */
    94 #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
    95 #include "SDL_glfuncs.h"
    96 #undef SDL_PROC
    97 
    98     void (*glTextureRangeAPPLE) (GLenum target, GLsizei length,
    99                                  const GLvoid * pointer);
   100 
   101     /* Multitexture support */
   102     SDL_bool GL_ARB_multitexture_supported;
   103     PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
   104     int num_texture_units;
   105 
   106     /* Shader support */
   107     GL_ShaderContext *shaders;
   108 
   109 } GL_RenderData;
   110 
   111 typedef struct
   112 {
   113     GLuint texture;
   114     GLenum type;
   115     GLfloat texw;
   116     GLfloat texh;
   117     GLenum format;
   118     GLenum formattype;
   119     void *pixels;
   120     int pitch;
   121     SDL_Rect locked_rect;
   122 
   123     /* YV12 texture support */
   124     SDL_bool yuv;
   125     GLuint utexture;
   126     GLuint vtexture;
   127 } GL_TextureData;
   128 
   129 
   130 static void
   131 GL_SetError(const char *prefix, GLenum result)
   132 {
   133     const char *error;
   134 
   135     switch (result) {
   136     case GL_NO_ERROR:
   137         error = "GL_NO_ERROR";
   138         break;
   139     case GL_INVALID_ENUM:
   140         error = "GL_INVALID_ENUM";
   141         break;
   142     case GL_INVALID_VALUE:
   143         error = "GL_INVALID_VALUE";
   144         break;
   145     case GL_INVALID_OPERATION:
   146         error = "GL_INVALID_OPERATION";
   147         break;
   148     case GL_STACK_OVERFLOW:
   149         error = "GL_STACK_OVERFLOW";
   150         break;
   151     case GL_STACK_UNDERFLOW:
   152         error = "GL_STACK_UNDERFLOW";
   153         break;
   154     case GL_OUT_OF_MEMORY:
   155         error = "GL_OUT_OF_MEMORY";
   156         break;
   157     case GL_TABLE_TOO_LARGE:
   158         error = "GL_TABLE_TOO_LARGE";
   159         break;
   160     default:
   161         error = "UNKNOWN";
   162         break;
   163     }
   164     SDL_SetError("%s: %s", prefix, error);
   165 }
   166 
   167 static int
   168 GL_LoadFunctions(GL_RenderData * data)
   169 {
   170 #ifdef __SDL_NOGETPROCADDR__
   171 #define SDL_PROC(ret,func,params) data->func=func;
   172 #else
   173 #define SDL_PROC(ret,func,params) \
   174     do { \
   175         data->func = SDL_GL_GetProcAddress(#func); \
   176         if ( ! data->func ) { \
   177             SDL_SetError("Couldn't load GL function %s: %s\n", #func, SDL_GetError()); \
   178             return -1; \
   179         } \
   180     } while ( 0 );
   181 #endif /* __SDL_NOGETPROCADDR__ */
   182 
   183 #include "SDL_glfuncs.h"
   184 #undef SDL_PROC
   185     return 0;
   186 }
   187 
   188 SDL_Renderer *
   189 GL_CreateRenderer(SDL_Window * window, Uint32 flags)
   190 {
   191     SDL_Renderer *renderer;
   192     GL_RenderData *data;
   193     const char *hint;
   194     GLint value;
   195     Uint32 window_flags;
   196 
   197     window_flags = SDL_GetWindowFlags(window);
   198     if (!(window_flags & SDL_WINDOW_OPENGL)) {
   199         if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
   200             return NULL;
   201         }
   202     }
   203 
   204     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   205     if (!renderer) {
   206         SDL_OutOfMemory();
   207         return NULL;
   208     }
   209 
   210     data = (GL_RenderData *) SDL_calloc(1, sizeof(*data));
   211     if (!data) {
   212         GL_DestroyRenderer(renderer);
   213         SDL_OutOfMemory();
   214         return NULL;
   215     }
   216 
   217     renderer->WindowEvent = GL_WindowEvent;
   218     renderer->CreateTexture = GL_CreateTexture;
   219     renderer->UpdateTexture = GL_UpdateTexture;
   220     renderer->LockTexture = GL_LockTexture;
   221     renderer->UnlockTexture = GL_UnlockTexture;
   222     renderer->SetClipRect = GL_SetClipRect;
   223     renderer->RenderClear = GL_RenderClear;
   224     renderer->RenderDrawPoints = GL_RenderDrawPoints;
   225     renderer->RenderDrawLines = GL_RenderDrawLines;
   226     renderer->RenderFillRects = GL_RenderFillRects;
   227     renderer->RenderCopy = GL_RenderCopy;
   228     renderer->RenderReadPixels = GL_RenderReadPixels;
   229     renderer->RenderPresent = GL_RenderPresent;
   230     renderer->DestroyTexture = GL_DestroyTexture;
   231     renderer->DestroyRenderer = GL_DestroyRenderer;
   232     renderer->info = GL_RenderDriver.info;
   233     renderer->driverdata = data;
   234 
   235     renderer->info.flags = SDL_RENDERER_ACCELERATED;
   236 
   237     data->context = SDL_GL_CreateContext(window);
   238     if (!data->context) {
   239         GL_DestroyRenderer(renderer);
   240         return NULL;
   241     }
   242     if (SDL_GL_MakeCurrent(window, data->context) < 0) {
   243         GL_DestroyRenderer(renderer);
   244         return NULL;
   245     }
   246 
   247     if (GL_LoadFunctions(data) < 0) {
   248         GL_DestroyRenderer(renderer);
   249         return NULL;
   250     }
   251 
   252 #ifdef __MACOSX__
   253     /* Enable multi-threaded rendering */
   254     /* Disabled until Ryan finishes his VBO/PBO code...
   255        CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine);
   256      */
   257 #endif
   258 
   259     if (flags & SDL_RENDERER_PRESENTVSYNC) {
   260         SDL_GL_SetSwapInterval(1);
   261     } else {
   262         SDL_GL_SetSwapInterval(0);
   263     }
   264     if (SDL_GL_GetSwapInterval() > 0) {
   265         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
   266     }
   267 
   268     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
   269     renderer->info.max_texture_width = value;
   270     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
   271     renderer->info.max_texture_height = value;
   272 
   273     if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle")
   274         || SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) {
   275         data->GL_ARB_texture_rectangle_supported = SDL_TRUE;
   276     }
   277     if (SDL_GL_ExtensionSupported("GL_APPLE_texture_range")) {
   278         data->glTextureRangeAPPLE =
   279             (void (*)(GLenum, GLsizei, const GLvoid *))
   280             SDL_GL_GetProcAddress("glTextureRangeAPPLE");
   281     }
   282 
   283     /* Check for multitexture support */
   284     if (SDL_GL_ExtensionSupported("GL_ARB_multitexture")) {
   285         data->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB");
   286         if (data->glActiveTextureARB) {
   287             data->GL_ARB_multitexture_supported = SDL_TRUE;
   288             data->glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &data->num_texture_units);
   289         }
   290     }
   291 
   292     /* Check for shader support */
   293     hint = SDL_GetHint(SDL_HINT_RENDER_OPENGL_SHADERS);
   294     if (!hint || *hint != '0') {
   295         data->shaders = GL_CreateShaderContext();
   296     }
   297     SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s",
   298                 data->shaders ? "ENABLED" : "DISABLED");
   299 
   300     /* We support YV12 textures using 3 textures and a shader */
   301     if (data->shaders && data->num_texture_units >= 3) {
   302         renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
   303         renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
   304     }
   305 
   306     /* Set up parameters for rendering */
   307     data->blendMode = -1;
   308     data->glDisable(GL_DEPTH_TEST);
   309     data->glDisable(GL_CULL_FACE);
   310     /* This ended up causing video discrepancies between OpenGL and Direct3D */
   311     /*data->glEnable(GL_LINE_SMOOTH);*/
   312     data->updateSize = SDL_TRUE;
   313 
   314     return renderer;
   315 }
   316 
   317 static SDL_GLContext SDL_CurrentContext = NULL;
   318 
   319 static int
   320 GL_ActivateRenderer(SDL_Renderer * renderer)
   321 {
   322     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   323     SDL_Window *window = renderer->window;
   324 
   325     if (SDL_CurrentContext != data->context) {
   326         if (SDL_GL_MakeCurrent(window, data->context) < 0) {
   327             return -1;
   328         }
   329         SDL_CurrentContext = data->context;
   330     }
   331     if (data->updateSize) {
   332         int w, h;
   333 
   334         SDL_GetWindowSize(window, &w, &h);
   335         data->glMatrixMode(GL_PROJECTION);
   336         data->glLoadIdentity();
   337         data->glMatrixMode(GL_MODELVIEW);
   338         data->glLoadIdentity();
   339         data->glViewport(0, 0, w, h);
   340         data->glOrtho(0.0, (GLdouble) w, (GLdouble) h, 0.0, 0.0, 1.0);
   341         data->updateSize = SDL_FALSE;
   342     }
   343     return 0;
   344 }
   345 
   346 static void
   347 GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   348 {
   349     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   350 
   351     if (event->event == SDL_WINDOWEVENT_RESIZED) {
   352         /* Rebind the context to the window area and update matrices */
   353         SDL_CurrentContext = NULL;
   354         data->updateSize = SDL_TRUE;
   355     }
   356 }
   357 
   358 static __inline__ int
   359 power_of_2(int input)
   360 {
   361     int value = 1;
   362 
   363     while (value < input) {
   364         value <<= 1;
   365     }
   366     return value;
   367 }
   368 
   369 static __inline__ SDL_bool
   370 convert_format(GL_RenderData *renderdata, Uint32 pixel_format,
   371                GLint* internalFormat, GLenum* format, GLenum* type)
   372 {
   373     switch (pixel_format) {
   374     case SDL_PIXELFORMAT_ARGB8888:
   375         *internalFormat = GL_RGBA8;
   376         *format = GL_BGRA;
   377         *type = GL_UNSIGNED_INT_8_8_8_8_REV;
   378         break;
   379     case SDL_PIXELFORMAT_YV12:
   380     case SDL_PIXELFORMAT_IYUV:
   381         *internalFormat = GL_LUMINANCE;
   382         *format = GL_LUMINANCE;
   383         *type = GL_UNSIGNED_BYTE;
   384         break;
   385     default:
   386         return SDL_FALSE;
   387     }
   388     return SDL_TRUE;
   389 }
   390 
   391 static int
   392 GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   393 {
   394     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   395     GL_TextureData *data;
   396     GLint internalFormat;
   397     GLenum format, type;
   398     int texture_w, texture_h;
   399     GLenum result;
   400 
   401     GL_ActivateRenderer(renderer);
   402 
   403     if (!convert_format(renderdata, texture->format, &internalFormat,
   404                         &format, &type)) {
   405         SDL_SetError("Texture format %s not supported by OpenGL",
   406                      SDL_GetPixelFormatName(texture->format));
   407         return -1;
   408     }
   409 
   410     data = (GL_TextureData *) SDL_calloc(1, sizeof(*data));
   411     if (!data) {
   412         SDL_OutOfMemory();
   413         return -1;
   414     }
   415 
   416     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   417         size_t size;
   418         data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
   419         size = texture->h * data->pitch;
   420         if (texture->format == SDL_PIXELFORMAT_YV12 ||
   421             texture->format == SDL_PIXELFORMAT_IYUV) {
   422             /* Need to add size for the U and V planes */
   423             size += (2 * (texture->h * data->pitch) / 4);
   424         }
   425         data->pixels = SDL_malloc(size);
   426         if (!data->pixels) {
   427             SDL_OutOfMemory();
   428             SDL_free(data);
   429             return -1;
   430         }
   431     }
   432 
   433     texture->driverdata = data;
   434 
   435     renderdata->glGetError();
   436     renderdata->glGenTextures(1, &data->texture);
   437     if (renderdata->GL_ARB_texture_rectangle_supported) {
   438         data->type = GL_TEXTURE_RECTANGLE_ARB;
   439         texture_w = texture->w;
   440         texture_h = texture->h;
   441         data->texw = (GLfloat) texture_w;
   442         data->texh = (GLfloat) texture_h;
   443     } else {
   444         data->type = GL_TEXTURE_2D;
   445         texture_w = power_of_2(texture->w);
   446         texture_h = power_of_2(texture->h);
   447         data->texw = (GLfloat) (texture->w) / texture_w;
   448         data->texh = (GLfloat) texture->h / texture_h;
   449     }
   450 
   451     data->format = format;
   452     data->formattype = type;
   453     renderdata->glEnable(data->type);
   454     renderdata->glBindTexture(data->type, data->texture);
   455     renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
   456                                 GL_LINEAR);
   457     renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
   458                                 GL_LINEAR);
   459     renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
   460                                 GL_CLAMP_TO_EDGE);
   461     renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
   462                                 GL_CLAMP_TO_EDGE);
   463 #ifdef __MACOSX__
   464 #ifndef GL_TEXTURE_STORAGE_HINT_APPLE
   465 #define GL_TEXTURE_STORAGE_HINT_APPLE       0x85BC
   466 #endif
   467 #ifndef STORAGE_CACHED_APPLE
   468 #define STORAGE_CACHED_APPLE                0x85BE
   469 #endif
   470 #ifndef STORAGE_SHARED_APPLE
   471 #define STORAGE_SHARED_APPLE                0x85BF
   472 #endif
   473     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   474         renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
   475                                     GL_STORAGE_SHARED_APPLE);
   476     } else {
   477         renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
   478                                     GL_STORAGE_CACHED_APPLE);
   479     }
   480     if (texture->access == SDL_TEXTUREACCESS_STREAMING
   481         && texture->format == SDL_PIXELFORMAT_ARGB8888) {
   482         renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
   483         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
   484                                  texture_h, 0, format, type, data->pixels);
   485     }
   486     else
   487 #endif
   488     {
   489         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
   490                                  texture_h, 0, format, type, NULL);
   491     }
   492     renderdata->glDisable(data->type);
   493     result = renderdata->glGetError();
   494     if (result != GL_NO_ERROR) {
   495         GL_SetError("glTexImage2D()", result);
   496         return -1;
   497     }
   498 
   499     if (texture->format == SDL_PIXELFORMAT_YV12 ||
   500         texture->format == SDL_PIXELFORMAT_IYUV) {
   501         data->yuv = SDL_TRUE;
   502 
   503         renderdata->glGenTextures(1, &data->utexture);
   504         renderdata->glGenTextures(1, &data->vtexture);
   505         renderdata->glEnable(data->type);
   506 
   507         renderdata->glBindTexture(data->type, data->utexture);
   508         renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
   509                                     GL_LINEAR);
   510         renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
   511                                     GL_LINEAR);
   512         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
   513                                     GL_CLAMP_TO_EDGE);
   514         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
   515                                     GL_CLAMP_TO_EDGE);
   516         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
   517                                  texture_h/2, 0, format, type, NULL);
   518 
   519         renderdata->glBindTexture(data->type, data->vtexture);
   520         renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
   521                                     GL_LINEAR);
   522         renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
   523                                     GL_LINEAR);
   524         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
   525                                     GL_CLAMP_TO_EDGE);
   526         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
   527                                     GL_CLAMP_TO_EDGE);
   528         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
   529                                  texture_h/2, 0, format, type, NULL);
   530 
   531         renderdata->glDisable(data->type);
   532     }
   533     return 0;
   534 }
   535 
   536 static int
   537 GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   538                  const SDL_Rect * rect, const void *pixels, int pitch)
   539 {
   540     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   541     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   542     GLenum result;
   543 
   544     GL_ActivateRenderer(renderer);
   545 
   546     renderdata->glGetError();
   547     renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   548     renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
   549                               (pitch / SDL_BYTESPERPIXEL(texture->format)));
   550     renderdata->glEnable(data->type);
   551     renderdata->glBindTexture(data->type, data->texture);
   552     renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
   553                                 rect->h, data->format, data->formattype,
   554                                 pixels);
   555     if (data->yuv) {
   556         /* Skip to the top of the next texture */
   557         const void *top = (const void*)((const Uint8*)pixels + (texture->h-rect->y) * pitch - rect->x);
   558 
   559         /* Skip to the correct offset into the next texture */
   560         pixels = (const void*)((const Uint8*)top + (rect->y / 2) * pitch + rect->x / 2);
   561         if (texture->format == SDL_PIXELFORMAT_YV12) {
   562             renderdata->glBindTexture(data->type, data->vtexture);
   563         } else {
   564             renderdata->glBindTexture(data->type, data->utexture);
   565         }
   566         renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
   567                                     rect->w/2, rect->h/2,
   568                                     data->format, data->formattype, pixels);
   569 
   570         /* Skip to the top of the next texture */
   571         top = (const void*)((const Uint8*)top + (texture->h * pitch)/4);
   572 
   573         /* Skip to the correct offset into the next texture */
   574         pixels = (const void*)((const Uint8*)top + (rect->y / 2) * pitch + rect->x / 2);
   575         if (texture->format == SDL_PIXELFORMAT_YV12) {
   576             renderdata->glBindTexture(data->type, data->utexture);
   577         } else {
   578             renderdata->glBindTexture(data->type, data->vtexture);
   579         }
   580         renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
   581                                     rect->w/2, rect->h/2,
   582                                     data->format, data->formattype, pixels);
   583     }
   584     renderdata->glDisable(data->type);
   585     result = renderdata->glGetError();
   586     if (result != GL_NO_ERROR) {
   587         GL_SetError("glTexSubImage2D()", result);
   588         return -1;
   589     }
   590     return 0;
   591 }
   592 
   593 static int
   594 GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   595                const SDL_Rect * rect, void **pixels, int *pitch)
   596 {
   597     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   598 
   599     data->locked_rect = *rect;
   600     *pixels = 
   601         (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
   602                   rect->x * SDL_BYTESPERPIXEL(texture->format));
   603     *pitch = data->pitch;
   604     return 0;
   605 }
   606 
   607 static void
   608 GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   609 {
   610     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   611     const SDL_Rect *rect;
   612     void *pixels;
   613 
   614     rect = &data->locked_rect;
   615     pixels = 
   616         (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
   617                   rect->x * SDL_BYTESPERPIXEL(texture->format));
   618     GL_UpdateTexture(renderer, texture, rect, pixels, data->pitch);
   619 }
   620 
   621 static void
   622 GL_SetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
   623 {
   624     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   625 
   626     GL_ActivateRenderer(renderer);
   627 
   628     if (rect) {
   629         int w, h;
   630 
   631         SDL_GetWindowSize(renderer->window, &w, &h);
   632         data->glScissor(rect->x, (h-(rect->y+rect->h)), rect->w, rect->h);
   633         data->glEnable(GL_SCISSOR_TEST);
   634     } else {
   635         data->glDisable(GL_SCISSOR_TEST);
   636     }
   637 }
   638 
   639 static void
   640 GL_SetBlendMode(GL_RenderData * data, int blendMode)
   641 {
   642     if (blendMode != data->blendMode) {
   643         switch (blendMode) {
   644         case SDL_BLENDMODE_NONE:
   645             data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   646             data->glDisable(GL_BLEND);
   647             break;
   648         case SDL_BLENDMODE_BLEND:
   649             data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   650             data->glEnable(GL_BLEND);
   651             data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   652             break;
   653         case SDL_BLENDMODE_ADD:
   654             data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   655             data->glEnable(GL_BLEND);
   656             data->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
   657             break;
   658         case SDL_BLENDMODE_MOD:
   659             data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   660             data->glEnable(GL_BLEND);
   661             data->glBlendFunc(GL_ZERO, GL_SRC_COLOR);
   662             break;
   663         }
   664         data->blendMode = blendMode;
   665     }
   666 }
   667 
   668 static int
   669 GL_RenderClear(SDL_Renderer * renderer)
   670 {
   671     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   672 
   673     GL_ActivateRenderer(renderer);
   674 
   675     data->glClearColor((GLfloat) renderer->r * inv255f,
   676                        (GLfloat) renderer->g * inv255f,
   677                        (GLfloat) renderer->b * inv255f,
   678                        (GLfloat) renderer->a * inv255f);
   679 
   680     data->glClear(GL_COLOR_BUFFER_BIT);
   681 
   682     return 0;
   683 }
   684 
   685 static int
   686 GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_Point * points,
   687                     int count)
   688 {
   689     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   690     int i;
   691 
   692     GL_ActivateRenderer(renderer);
   693 
   694     GL_SetBlendMode(data, renderer->blendMode);
   695     GL_SelectShader(data->shaders, SHADER_SOLID);
   696 
   697     data->glColor4f((GLfloat) renderer->r * inv255f,
   698                     (GLfloat) renderer->g * inv255f,
   699                     (GLfloat) renderer->b * inv255f,
   700                     (GLfloat) renderer->a * inv255f);
   701 
   702     data->glBegin(GL_POINTS);
   703     for (i = 0; i < count; ++i) {
   704         data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   705     }
   706     data->glEnd();
   707 
   708     return 0;
   709 }
   710 
   711 static int
   712 GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_Point * points,
   713                    int count)
   714 {
   715     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   716     int i;
   717 
   718     GL_ActivateRenderer(renderer);
   719 
   720     GL_SetBlendMode(data, renderer->blendMode);
   721     GL_SelectShader(data->shaders, SHADER_SOLID);
   722 
   723     data->glColor4f((GLfloat) renderer->r * inv255f,
   724                     (GLfloat) renderer->g * inv255f,
   725                     (GLfloat) renderer->b * inv255f,
   726                     (GLfloat) renderer->a * inv255f);
   727 
   728     if (count > 2 && 
   729         points[0].x == points[count-1].x && points[0].y == points[count-1].y) {
   730         data->glBegin(GL_LINE_LOOP);
   731         /* GL_LINE_LOOP takes care of the final segment */
   732         --count;
   733         for (i = 0; i < count; ++i) {
   734             data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   735         }
   736         data->glEnd();
   737     } else {
   738 #if defined(__APPLE__) || defined(__WIN32__)
   739 #else
   740         int x1, y1, x2, y2;
   741 #endif
   742 
   743         data->glBegin(GL_LINE_STRIP);
   744         for (i = 0; i < count; ++i) {
   745             data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   746         }
   747         data->glEnd();
   748 
   749         /* The line is half open, so we need one more point to complete it.
   750          * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html
   751          * If we have to, we can use vertical line and horizontal line textures
   752          * for vertical and horizontal lines, and then create custom textures
   753          * for diagonal lines and software render those.  It's terrible, but at
   754          * least it would be pixel perfect.
   755          */
   756         data->glBegin(GL_POINTS);
   757 #if defined(__APPLE__) || defined(__WIN32__)
   758         /* Mac OS X and Windows seem to always leave the second point open */
   759         data->glVertex2f(0.5f + points[count-1].x, 0.5f + points[count-1].y);
   760 #else
   761         /* Linux seems to leave the right-most or bottom-most point open */
   762         x1 = points[0].x;
   763         y1 = points[0].y;
   764         x2 = points[count-1].x;
   765         y2 = points[count-1].y;
   766 
   767         if (x1 > x2) {
   768             data->glVertex2f(0.5f + x1, 0.5f + y1);
   769         } else if (x2 > x1) {
   770             data->glVertex2f(0.5f + x2, 0.5f + y2);
   771         } else if (y1 > y2) {
   772             data->glVertex2f(0.5f + x1, 0.5f + y1);
   773         } else if (y2 > y1) {
   774             data->glVertex2f(0.5f + x2, 0.5f + y2);
   775         }
   776 #endif
   777         data->glEnd();
   778     }
   779 
   780     return 0;
   781 }
   782 
   783 static int
   784 GL_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect ** rects, int count)
   785 {
   786     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   787     int i;
   788 
   789     GL_ActivateRenderer(renderer);
   790 
   791     GL_SetBlendMode(data, renderer->blendMode);
   792     GL_SelectShader(data->shaders, SHADER_SOLID);
   793 
   794     data->glColor4f((GLfloat) renderer->r * inv255f,
   795                     (GLfloat) renderer->g * inv255f,
   796                     (GLfloat) renderer->b * inv255f,
   797                     (GLfloat) renderer->a * inv255f);
   798 
   799     for (i = 0; i < count; ++i) {
   800         const SDL_Rect *rect = rects[i];
   801 
   802         data->glRecti(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
   803     }
   804 
   805     return 0;
   806 }
   807 
   808 static int
   809 GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   810               const SDL_Rect * srcrect, const SDL_Rect * dstrect)
   811 {
   812     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   813     GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
   814     int minx, miny, maxx, maxy;
   815     GLfloat minu, maxu, minv, maxv;
   816 
   817     GL_ActivateRenderer(renderer);
   818 
   819     minx = dstrect->x;
   820     miny = dstrect->y;
   821     maxx = dstrect->x + dstrect->w;
   822     maxy = dstrect->y + dstrect->h;
   823 
   824     minu = (GLfloat) srcrect->x / texture->w;
   825     minu *= texturedata->texw;
   826     maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
   827     maxu *= texturedata->texw;
   828     minv = (GLfloat) srcrect->y / texture->h;
   829     minv *= texturedata->texh;
   830     maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
   831     maxv *= texturedata->texh;
   832 
   833     data->glEnable(texturedata->type);
   834     if (texturedata->yuv) {
   835         data->glActiveTextureARB(GL_TEXTURE2_ARB);
   836         data->glBindTexture(texturedata->type, texturedata->vtexture);
   837         data->glActiveTextureARB(GL_TEXTURE1_ARB);
   838         data->glBindTexture(texturedata->type, texturedata->utexture);
   839         data->glActiveTextureARB(GL_TEXTURE0_ARB);
   840     }
   841     data->glBindTexture(texturedata->type, texturedata->texture);
   842 
   843     if (texture->modMode) {
   844         data->glColor4f((GLfloat) texture->r * inv255f,
   845                         (GLfloat) texture->g * inv255f,
   846                         (GLfloat) texture->b * inv255f,
   847                         (GLfloat) texture->a * inv255f);
   848     } else {
   849         data->glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
   850     }
   851 
   852     GL_SetBlendMode(data, texture->blendMode);
   853     if (texturedata->yuv) {
   854         GL_SelectShader(data->shaders, SHADER_YV12);
   855     } else {
   856         GL_SelectShader(data->shaders, SHADER_RGB);
   857     }
   858 
   859     data->glBegin(GL_TRIANGLE_STRIP);
   860     data->glTexCoord2f(minu, minv);
   861     data->glVertex2f((GLfloat) minx, (GLfloat) miny);
   862     data->glTexCoord2f(maxu, minv);
   863     data->glVertex2f((GLfloat) maxx, (GLfloat) miny);
   864     data->glTexCoord2f(minu, maxv);
   865     data->glVertex2f((GLfloat) minx, (GLfloat) maxy);
   866     data->glTexCoord2f(maxu, maxv);
   867     data->glVertex2f((GLfloat) maxx, (GLfloat) maxy);
   868     data->glEnd();
   869 
   870     data->glDisable(texturedata->type);
   871 
   872     return 0;
   873 }
   874 
   875 static int
   876 GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   877                     Uint32 pixel_format, void * pixels, int pitch)
   878 {
   879     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   880     SDL_Window *window = renderer->window;
   881     GLint internalFormat;
   882     GLenum format, type;
   883     Uint8 *src, *dst, *tmp;
   884     int w, h, length, rows;
   885 
   886     GL_ActivateRenderer(renderer);
   887 
   888     if (!convert_format(data, pixel_format, &internalFormat, &format, &type)) {
   889         /* FIXME: Do a temp copy to a format that is supported */
   890         SDL_SetError("Unsupported pixel format");
   891         return -1;
   892     }
   893 
   894     SDL_GetWindowSize(window, &w, &h);
   895 
   896     data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
   897     data->glPixelStorei(GL_PACK_ROW_LENGTH,
   898                         (pitch / SDL_BYTESPERPIXEL(pixel_format)));
   899 
   900     data->glReadPixels(rect->x, (h-rect->y)-rect->h, rect->w, rect->h,
   901                        format, type, pixels);
   902 
   903     /* Flip the rows to be top-down */
   904     length = rect->w * SDL_BYTESPERPIXEL(pixel_format);
   905     src = (Uint8*)pixels + (rect->h-1)*pitch;
   906     dst = (Uint8*)pixels;
   907     tmp = SDL_stack_alloc(Uint8, length);
   908     rows = rect->h / 2;
   909     while (rows--) {
   910         SDL_memcpy(tmp, dst, length);
   911         SDL_memcpy(dst, src, length);
   912         SDL_memcpy(src, tmp, length);
   913         dst += pitch;
   914         src -= pitch;
   915     }
   916     SDL_stack_free(tmp);
   917 
   918     return 0;
   919 }
   920 
   921 static void
   922 GL_RenderPresent(SDL_Renderer * renderer)
   923 {
   924     GL_ActivateRenderer(renderer);
   925 
   926     SDL_GL_SwapWindow(renderer->window);
   927 }
   928 
   929 static void
   930 GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   931 {
   932     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   933     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   934 
   935     GL_ActivateRenderer(renderer);
   936 
   937     if (!data) {
   938         return;
   939     }
   940     if (data->texture) {
   941         renderdata->glDeleteTextures(1, &data->texture);
   942     }
   943     if (data->yuv) {
   944         renderdata->glDeleteTextures(1, &data->utexture);
   945         renderdata->glDeleteTextures(1, &data->vtexture);
   946     }
   947     if (data->pixels) {
   948         SDL_free(data->pixels);
   949     }
   950     SDL_free(data);
   951     texture->driverdata = NULL;
   952 }
   953 
   954 static void
   955 GL_DestroyRenderer(SDL_Renderer * renderer)
   956 {
   957     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   958 
   959     if (data) {
   960         if (data->context) {
   961             /* SDL_GL_MakeCurrent(0, NULL); *//* doesn't do anything */
   962             SDL_GL_DeleteContext(data->context);
   963         }
   964         SDL_free(data);
   965     }
   966     SDL_free(renderer);
   967 }
   968 
   969 #endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */
   970 
   971 /* vi: set ts=4 sw=4 expandtab: */