src/render/opengl/SDL_render_gl.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 07 Jan 2012 21:01:33 -0800
changeset 6182 eb6796f3e598
parent 6171 7b9126fde949
child 6207 15ff38383cb7
permissions -rwxr-xr-x
Fixed crash if the rendering system couldn't create an OpenGL window.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED
    24 
    25 #include "SDL_hints.h"
    26 #include "SDL_log.h"
    27 #include "SDL_opengl.h"
    28 #include "../SDL_sysrender.h"
    29 #include "SDL_shaders_gl.h"
    30 
    31 #ifdef __MACOSX__
    32 #include <OpenGL/OpenGL.h>
    33 #endif
    34 
    35 
    36 /* OpenGL renderer implementation */
    37 
    38 /* Details on optimizing the texture path on Mac OS X:
    39    http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/opengl_texturedata.html
    40 */
    41 
    42 /* Used to re-create the window with OpenGL capability */
    43 extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
    44 
    45 static const float inv255f = 1.0f / 255.0f;
    46 
    47 static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags);
    48 static void GL_WindowEvent(SDL_Renderer * renderer,
    49                            const SDL_WindowEvent *event);
    50 static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    51 static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    52                             const SDL_Rect * rect, const void *pixels,
    53                             int pitch);
    54 static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    55                           const SDL_Rect * rect, void **pixels, int *pitch);
    56 static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    57 static int GL_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             /* Uh oh, better try to put it back... */
   243             SDL_RecreateWindow(window, window_flags);
   244             return NULL;
   245         }
   246     }
   247 
   248     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   249     if (!renderer) {
   250         SDL_OutOfMemory();
   251         return NULL;
   252     }
   253 
   254     data = (GL_RenderData *) SDL_calloc(1, sizeof(*data));
   255     if (!data) {
   256         GL_DestroyRenderer(renderer);
   257         SDL_OutOfMemory();
   258         return NULL;
   259     }
   260 
   261     renderer->WindowEvent = GL_WindowEvent;
   262     renderer->CreateTexture = GL_CreateTexture;
   263     renderer->UpdateTexture = GL_UpdateTexture;
   264     renderer->LockTexture = GL_LockTexture;
   265     renderer->UnlockTexture = GL_UnlockTexture;
   266     renderer->UpdateViewport = GL_UpdateViewport;
   267     renderer->RenderClear = GL_RenderClear;
   268     renderer->RenderDrawPoints = GL_RenderDrawPoints;
   269     renderer->RenderDrawLines = GL_RenderDrawLines;
   270     renderer->RenderFillRects = GL_RenderFillRects;
   271     renderer->RenderCopy = GL_RenderCopy;
   272     renderer->RenderReadPixels = GL_RenderReadPixels;
   273     renderer->RenderPresent = GL_RenderPresent;
   274     renderer->DestroyTexture = GL_DestroyTexture;
   275     renderer->DestroyRenderer = GL_DestroyRenderer;
   276     renderer->info = GL_RenderDriver.info;
   277     renderer->info.flags = SDL_RENDERER_ACCELERATED;
   278     renderer->driverdata = data;
   279     renderer->window = window;
   280 
   281     data->context = SDL_GL_CreateContext(window);
   282     if (!data->context) {
   283         GL_DestroyRenderer(renderer);
   284         return NULL;
   285     }
   286     if (SDL_GL_MakeCurrent(window, data->context) < 0) {
   287         GL_DestroyRenderer(renderer);
   288         return NULL;
   289     }
   290 
   291     if (GL_LoadFunctions(data) < 0) {
   292         GL_DestroyRenderer(renderer);
   293         return NULL;
   294     }
   295 
   296 #ifdef __MACOSX__
   297     /* Enable multi-threaded rendering */
   298     /* Disabled until Ryan finishes his VBO/PBO code...
   299        CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine);
   300      */
   301 #endif
   302 
   303     if (flags & SDL_RENDERER_PRESENTVSYNC) {
   304         SDL_GL_SetSwapInterval(1);
   305     } else {
   306         SDL_GL_SetSwapInterval(0);
   307     }
   308     if (SDL_GL_GetSwapInterval() > 0) {
   309         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
   310     }
   311 
   312     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
   313     renderer->info.max_texture_width = value;
   314     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
   315     renderer->info.max_texture_height = value;
   316 
   317     if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle")
   318         || SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) {
   319         data->GL_ARB_texture_rectangle_supported = SDL_TRUE;
   320     }
   321 
   322     /* Check for multitexture support */
   323     if (SDL_GL_ExtensionSupported("GL_ARB_multitexture")) {
   324         data->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB");
   325         if (data->glActiveTextureARB) {
   326             data->GL_ARB_multitexture_supported = SDL_TRUE;
   327             data->glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &data->num_texture_units);
   328         }
   329     }
   330 
   331     /* Check for shader support */
   332     hint = SDL_GetHint(SDL_HINT_RENDER_OPENGL_SHADERS);
   333     if (!hint || *hint != '0') {
   334         data->shaders = GL_CreateShaderContext();
   335     }
   336     SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s",
   337                 data->shaders ? "ENABLED" : "DISABLED");
   338 
   339     /* We support YV12 textures using 3 textures and a shader */
   340     if (data->shaders && data->num_texture_units >= 3) {
   341         renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
   342         renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
   343     }
   344 
   345     /* Set up parameters for rendering */
   346     GL_ResetState(renderer);
   347 
   348     return renderer;
   349 }
   350 
   351 static void
   352 GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   353 {
   354     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
   355         /* Rebind the context to the window area and update matrices */
   356         SDL_CurrentContext = NULL;
   357     }
   358 }
   359 
   360 static __inline__ int
   361 power_of_2(int input)
   362 {
   363     int value = 1;
   364 
   365     while (value < input) {
   366         value <<= 1;
   367     }
   368     return value;
   369 }
   370 
   371 static __inline__ SDL_bool
   372 convert_format(GL_RenderData *renderdata, Uint32 pixel_format,
   373                GLint* internalFormat, GLenum* format, GLenum* type)
   374 {
   375     switch (pixel_format) {
   376     case SDL_PIXELFORMAT_ARGB8888:
   377         *internalFormat = GL_RGBA8;
   378         *format = GL_BGRA;
   379         *type = GL_UNSIGNED_INT_8_8_8_8_REV;
   380         break;
   381     case SDL_PIXELFORMAT_YV12:
   382     case SDL_PIXELFORMAT_IYUV:
   383         *internalFormat = GL_LUMINANCE;
   384         *format = GL_LUMINANCE;
   385         *type = GL_UNSIGNED_BYTE;
   386         break;
   387     default:
   388         return SDL_FALSE;
   389     }
   390     return SDL_TRUE;
   391 }
   392 
   393 static GLenum
   394 GetScaleQuality(void)
   395 {
   396     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
   397 
   398     if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
   399         return GL_NEAREST;
   400     } else {
   401         return GL_LINEAR;
   402     }
   403 }
   404 
   405 static int
   406 GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   407 {
   408     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   409     GL_TextureData *data;
   410     GLint internalFormat;
   411     GLenum format, type;
   412     int texture_w, texture_h;
   413     GLenum scaleMode;
   414     GLenum result;
   415 
   416     GL_ActivateRenderer(renderer);
   417 
   418     if (!convert_format(renderdata, texture->format, &internalFormat,
   419                         &format, &type)) {
   420         SDL_SetError("Texture format %s not supported by OpenGL",
   421                      SDL_GetPixelFormatName(texture->format));
   422         return -1;
   423     }
   424 
   425     data = (GL_TextureData *) SDL_calloc(1, sizeof(*data));
   426     if (!data) {
   427         SDL_OutOfMemory();
   428         return -1;
   429     }
   430 
   431     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   432         size_t size;
   433         data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
   434         size = texture->h * data->pitch;
   435         if (texture->format == SDL_PIXELFORMAT_YV12 ||
   436             texture->format == SDL_PIXELFORMAT_IYUV) {
   437             /* Need to add size for the U and V planes */
   438             size += (2 * (texture->h * data->pitch) / 4);
   439         }
   440         data->pixels = SDL_calloc(1, size);
   441         if (!data->pixels) {
   442             SDL_OutOfMemory();
   443             SDL_free(data);
   444             return -1;
   445         }
   446     }
   447 
   448     texture->driverdata = data;
   449 
   450     renderdata->glGetError();
   451     renderdata->glGenTextures(1, &data->texture);
   452     if (renderdata->GL_ARB_texture_rectangle_supported) {
   453         data->type = GL_TEXTURE_RECTANGLE_ARB;
   454         texture_w = texture->w;
   455         texture_h = texture->h;
   456         data->texw = (GLfloat) texture_w;
   457         data->texh = (GLfloat) texture_h;
   458     } else {
   459         data->type = GL_TEXTURE_2D;
   460         texture_w = power_of_2(texture->w);
   461         texture_h = power_of_2(texture->h);
   462         data->texw = (GLfloat) (texture->w) / texture_w;
   463         data->texh = (GLfloat) texture->h / texture_h;
   464     }
   465 
   466     data->format = format;
   467     data->formattype = type;
   468     scaleMode = GetScaleQuality();
   469     renderdata->glEnable(data->type);
   470     renderdata->glBindTexture(data->type, data->texture);
   471     renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
   472     renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
   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_MIN_FILTER,
   528                                     scaleMode);
   529         renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
   530                                     scaleMode);
   531         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
   532                                     GL_CLAMP_TO_EDGE);
   533         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
   534                                     GL_CLAMP_TO_EDGE);
   535         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
   536                                  texture_h/2, 0, format, type, NULL);
   537 
   538         renderdata->glBindTexture(data->type, data->vtexture);
   539         renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
   540                                     scaleMode);
   541         renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
   542                                     scaleMode);
   543         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
   544                                     GL_CLAMP_TO_EDGE);
   545         renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
   546                                     GL_CLAMP_TO_EDGE);
   547         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
   548                                  texture_h/2, 0, format, type, NULL);
   549 
   550         renderdata->glDisable(data->type);
   551     }
   552     return 0;
   553 }
   554 
   555 static int
   556 GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   557                  const SDL_Rect * rect, const void *pixels, int pitch)
   558 {
   559     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   560     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   561     GLenum result;
   562 
   563     GL_ActivateRenderer(renderer);
   564 
   565     renderdata->glGetError();
   566     renderdata->glEnable(data->type);
   567     renderdata->glBindTexture(data->type, data->texture);
   568     renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   569     renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
   570                               (pitch / SDL_BYTESPERPIXEL(texture->format)));
   571     renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
   572                                 rect->h, data->format, data->formattype,
   573                                 pixels);
   574     if (data->yuv) {
   575         renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2));
   576 
   577         /* Skip to the correct offset into the next texture */
   578         pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
   579         if (texture->format == SDL_PIXELFORMAT_YV12) {
   580             renderdata->glBindTexture(data->type, data->vtexture);
   581         } else {
   582             renderdata->glBindTexture(data->type, data->utexture);
   583         }
   584         renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
   585                                     rect->w/2, rect->h/2,
   586                                     data->format, data->formattype, pixels);
   587 
   588         /* Skip to the correct offset into the next texture */
   589         pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4);
   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 
   859         data->glActiveTextureARB(GL_TEXTURE1_ARB);
   860         data->glBindTexture(texturedata->type, texturedata->utexture);
   861 
   862         data->glActiveTextureARB(GL_TEXTURE0_ARB);
   863     }
   864     data->glBindTexture(texturedata->type, texturedata->texture);
   865 
   866     if (texture->modMode) {
   867         GL_SetColor(data, texture->r, texture->g, texture->b, texture->a);
   868     } else {
   869         GL_SetColor(data, 255, 255, 255, 255);
   870     }
   871 
   872     GL_SetBlendMode(data, texture->blendMode);
   873 
   874     if (texturedata->yuv) {
   875         GL_SetShader(data, SHADER_YV12);
   876     } else {
   877         GL_SetShader(data, SHADER_RGB);
   878     }
   879 
   880     minx = dstrect->x;
   881     miny = dstrect->y;
   882     maxx = dstrect->x + dstrect->w;
   883     maxy = dstrect->y + dstrect->h;
   884 
   885     minu = (GLfloat) srcrect->x / texture->w;
   886     minu *= texturedata->texw;
   887     maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
   888     maxu *= texturedata->texw;
   889     minv = (GLfloat) srcrect->y / texture->h;
   890     minv *= texturedata->texh;
   891     maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
   892     maxv *= texturedata->texh;
   893 
   894     data->glBegin(GL_TRIANGLE_STRIP);
   895     data->glTexCoord2f(minu, minv);
   896     data->glVertex2f((GLfloat) minx, (GLfloat) miny);
   897     data->glTexCoord2f(maxu, minv);
   898     data->glVertex2f((GLfloat) maxx, (GLfloat) miny);
   899     data->glTexCoord2f(minu, maxv);
   900     data->glVertex2f((GLfloat) minx, (GLfloat) maxy);
   901     data->glTexCoord2f(maxu, maxv);
   902     data->glVertex2f((GLfloat) maxx, (GLfloat) maxy);
   903     data->glEnd();
   904 
   905     data->glDisable(texturedata->type);
   906 
   907     return 0;
   908 }
   909 
   910 static int
   911 GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   912                     Uint32 pixel_format, void * pixels, int pitch)
   913 {
   914     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   915     SDL_Window *window = renderer->window;
   916     Uint32 temp_format = SDL_PIXELFORMAT_ARGB8888;
   917     void *temp_pixels;
   918     int temp_pitch;
   919     GLint internalFormat;
   920     GLenum format, type;
   921     Uint8 *src, *dst, *tmp;
   922     int w, h, length, rows;
   923     int status;
   924 
   925     GL_ActivateRenderer(renderer);
   926 
   927     temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format);
   928     temp_pixels = SDL_malloc(rect->h * temp_pitch);
   929     if (!temp_pixels) {
   930         SDL_OutOfMemory();
   931         return -1;
   932     }
   933 
   934     convert_format(data, temp_format, &internalFormat, &format, &type);
   935 
   936     SDL_GetWindowSize(window, &w, &h);
   937 
   938     data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
   939     data->glPixelStorei(GL_PACK_ROW_LENGTH,
   940                         (temp_pitch / SDL_BYTESPERPIXEL(temp_format)));
   941 
   942     data->glReadPixels(rect->x, (h-rect->y)-rect->h, rect->w, rect->h,
   943                        format, type, temp_pixels);
   944 
   945     /* Flip the rows to be top-down */
   946     length = rect->w * SDL_BYTESPERPIXEL(temp_format);
   947     src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch;
   948     dst = (Uint8*)temp_pixels;
   949     tmp = SDL_stack_alloc(Uint8, length);
   950     rows = rect->h / 2;
   951     while (rows--) {
   952         SDL_memcpy(tmp, dst, length);
   953         SDL_memcpy(dst, src, length);
   954         SDL_memcpy(src, tmp, length);
   955         dst += temp_pitch;
   956         src -= temp_pitch;
   957     }
   958     SDL_stack_free(tmp);
   959 
   960     status = SDL_ConvertPixels(rect->w, rect->h,
   961                                temp_format, temp_pixels, temp_pitch,
   962                                pixel_format, pixels, pitch);
   963     SDL_free(temp_pixels);
   964 
   965     return status;
   966 }
   967 
   968 static void
   969 GL_RenderPresent(SDL_Renderer * renderer)
   970 {
   971     GL_ActivateRenderer(renderer);
   972 
   973     SDL_GL_SwapWindow(renderer->window);
   974 }
   975 
   976 static void
   977 GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   978 {
   979     GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   980     GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   981 
   982     GL_ActivateRenderer(renderer);
   983 
   984     if (!data) {
   985         return;
   986     }
   987     if (data->texture) {
   988         renderdata->glDeleteTextures(1, &data->texture);
   989     }
   990     if (data->yuv) {
   991         renderdata->glDeleteTextures(1, &data->utexture);
   992         renderdata->glDeleteTextures(1, &data->vtexture);
   993     }
   994     if (data->pixels) {
   995         SDL_free(data->pixels);
   996     }
   997     SDL_free(data);
   998     texture->driverdata = NULL;
   999 }
  1000 
  1001 static void
  1002 GL_DestroyRenderer(SDL_Renderer * renderer)
  1003 {
  1004     GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
  1005 
  1006     if (data) {
  1007         if (data->shaders) {
  1008             GL_DestroyShaderContext(data->shaders);
  1009         }
  1010         if (data->context) {
  1011             /* SDL_GL_MakeCurrent(0, NULL); *//* doesn't do anything */
  1012             SDL_GL_DeleteContext(data->context);
  1013         }
  1014         SDL_free(data);
  1015     }
  1016     SDL_free(renderer);
  1017 }
  1018 
  1019 #endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */
  1020 
  1021 /* vi: set ts=4 sw=4 expandtab: */