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