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