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