• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 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_internal.h"
22 
23 #if SDL_VIDEO_RENDER_OGL_ES && !SDL_RENDER_DISABLED
24 
25 #include "SDL_hints.h"
26 #include "SDL_opengles.h"
27 #include "../SDL_sysrender.h"
28 
29 /* To prevent unnecessary window recreation,
30  * these should match the defaults selected in SDL_GL_ResetAttributes
31  */
32 
33 #define RENDERER_CONTEXT_MAJOR 1
34 #define RENDERER_CONTEXT_MINOR 1
35 
36 #if defined(SDL_VIDEO_DRIVER_PANDORA)
37 
38 /* Empty function stub to get OpenGL ES 1.x support without  */
39 /* OpenGL ES extension GL_OES_draw_texture supported         */
40 GL_API void GL_APIENTRY
glDrawTexiOES(GLint x,GLint y,GLint z,GLint width,GLint height)41 glDrawTexiOES(GLint x, GLint y, GLint z, GLint width, GLint height)
42 {
43     return;
44 }
45 
46 #endif /* SDL_VIDEO_DRIVER_PANDORA */
47 
48 /* OpenGL ES 1.1 renderer implementation, based on the OpenGL renderer */
49 
50 /* Used to re-create the window with OpenGL ES capability */
51 extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
52 
53 static const float inv255f = 1.0f / 255.0f;
54 
55 static SDL_Renderer *GLES_CreateRenderer(SDL_Window * window, Uint32 flags);
56 static void GLES_WindowEvent(SDL_Renderer * renderer,
57                              const SDL_WindowEvent *event);
58 static int GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
59 static int GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
60 static int GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
61                               const SDL_Rect * rect, const void *pixels,
62                               int pitch);
63 static int GLES_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
64                             const SDL_Rect * rect, void **pixels, int *pitch);
65 static void GLES_UnlockTexture(SDL_Renderer * renderer,
66                                SDL_Texture * texture);
67 static int GLES_SetRenderTarget(SDL_Renderer * renderer,
68                                  SDL_Texture * texture);
69 static int GLES_UpdateViewport(SDL_Renderer * renderer);
70 static int GLES_UpdateClipRect(SDL_Renderer * renderer);
71 static int GLES_RenderClear(SDL_Renderer * renderer);
72 static int GLES_RenderDrawPoints(SDL_Renderer * renderer,
73                                  const SDL_FPoint * points, int count);
74 static int GLES_RenderDrawLines(SDL_Renderer * renderer,
75                                 const SDL_FPoint * points, int count);
76 static int GLES_RenderFillRects(SDL_Renderer * renderer,
77                                 const SDL_FRect * rects, int count);
78 static int GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
79                            const SDL_Rect * srcrect,
80                            const SDL_FRect * dstrect);
81 static int GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
82                          const SDL_Rect * srcrect, const SDL_FRect * dstrect,
83                          const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
84 static int GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
85                     Uint32 pixel_format, void * pixels, int pitch);
86 static void GLES_RenderPresent(SDL_Renderer * renderer);
87 static void GLES_DestroyTexture(SDL_Renderer * renderer,
88                                 SDL_Texture * texture);
89 static void GLES_DestroyRenderer(SDL_Renderer * renderer);
90 static int GLES_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh);
91 static int GLES_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture);
92 
93 typedef struct GLES_FBOList GLES_FBOList;
94 
95 struct GLES_FBOList
96 {
97    Uint32 w, h;
98    GLuint FBO;
99    GLES_FBOList *next;
100 };
101 
102 
103 SDL_RenderDriver GLES_RenderDriver = {
104     GLES_CreateRenderer,
105     {
106      "opengles",
107      (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
108      1,
109      {SDL_PIXELFORMAT_ABGR8888},
110      0,
111      0}
112 };
113 
114 typedef struct
115 {
116     SDL_GLContext context;
117     struct {
118         Uint32 color;
119         int blendMode;
120         SDL_bool tex_coords;
121     } current;
122 
123 #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
124 #define SDL_PROC_OES SDL_PROC
125 #include "SDL_glesfuncs.h"
126 #undef SDL_PROC
127 #undef SDL_PROC_OES
128     SDL_bool GL_OES_framebuffer_object_supported;
129     GLES_FBOList *framebuffers;
130     GLuint window_framebuffer;
131 
132     SDL_bool GL_OES_blend_func_separate_supported;
133 } GLES_RenderData;
134 
135 typedef struct
136 {
137     GLuint texture;
138     GLenum type;
139     GLfloat texw;
140     GLfloat texh;
141     GLenum format;
142     GLenum formattype;
143     void *pixels;
144     int pitch;
145     GLES_FBOList *fbo;
146 } GLES_TextureData;
147 
148 static int
GLES_SetError(const char * prefix,GLenum result)149 GLES_SetError(const char *prefix, GLenum result)
150 {
151     const char *error;
152 
153     switch (result) {
154     case GL_NO_ERROR:
155         error = "GL_NO_ERROR";
156         break;
157     case GL_INVALID_ENUM:
158         error = "GL_INVALID_ENUM";
159         break;
160     case GL_INVALID_VALUE:
161         error = "GL_INVALID_VALUE";
162         break;
163     case GL_INVALID_OPERATION:
164         error = "GL_INVALID_OPERATION";
165         break;
166     case GL_STACK_OVERFLOW:
167         error = "GL_STACK_OVERFLOW";
168         break;
169     case GL_STACK_UNDERFLOW:
170         error = "GL_STACK_UNDERFLOW";
171         break;
172     case GL_OUT_OF_MEMORY:
173         error = "GL_OUT_OF_MEMORY";
174         break;
175     default:
176         error = "UNKNOWN";
177         break;
178     }
179     return SDL_SetError("%s: %s", prefix, error);
180 }
181 
GLES_LoadFunctions(GLES_RenderData * data)182 static int GLES_LoadFunctions(GLES_RenderData * data)
183 {
184 #if SDL_VIDEO_DRIVER_UIKIT
185 #define __SDL_NOGETPROCADDR__
186 #elif SDL_VIDEO_DRIVER_ANDROID
187 #define __SDL_NOGETPROCADDR__
188 #elif SDL_VIDEO_DRIVER_PANDORA
189 #define __SDL_NOGETPROCADDR__
190 #endif
191 
192 #ifdef __SDL_NOGETPROCADDR__
193 #define SDL_PROC(ret,func,params) data->func=func;
194 #define SDL_PROC_OES(ret,func,params) data->func=func;
195 #else
196 #define SDL_PROC(ret,func,params) \
197     do { \
198         data->func = SDL_GL_GetProcAddress(#func); \
199         if ( ! data->func ) { \
200             return SDL_SetError("Couldn't load GLES function %s: %s\n", #func, SDL_GetError()); \
201         } \
202     } while ( 0 );
203 #define SDL_PROC_OES(ret,func,params) \
204     do { \
205         data->func = SDL_GL_GetProcAddress(#func); \
206     } while ( 0 );
207 #endif /* __SDL_NOGETPROCADDR__ */
208 
209 #include "SDL_glesfuncs.h"
210 #undef SDL_PROC
211 #undef SDL_PROC_OES
212     return 0;
213 }
214 
215 static SDL_GLContext SDL_CurrentContext = NULL;
216 
217 GLES_FBOList *
GLES_GetFBO(GLES_RenderData * data,Uint32 w,Uint32 h)218 GLES_GetFBO(GLES_RenderData *data, Uint32 w, Uint32 h)
219 {
220    GLES_FBOList *result = data->framebuffers;
221    while ((result) && ((result->w != w) || (result->h != h)) ) {
222        result = result->next;
223    }
224    if (result == NULL) {
225        result = SDL_malloc(sizeof(GLES_FBOList));
226        result->w = w;
227        result->h = h;
228        data->glGenFramebuffersOES(1, &result->FBO);
229        result->next = data->framebuffers;
230        data->framebuffers = result;
231    }
232    return result;
233 }
234 
235 
236 static int
GLES_ActivateRenderer(SDL_Renderer * renderer)237 GLES_ActivateRenderer(SDL_Renderer * renderer)
238 {
239     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
240 
241     if (SDL_CurrentContext != data->context) {
242         if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) {
243             return -1;
244         }
245         SDL_CurrentContext = data->context;
246 
247         GLES_UpdateViewport(renderer);
248     }
249     return 0;
250 }
251 
252 /* This is called if we need to invalidate all of the SDL OpenGL state */
253 static void
GLES_ResetState(SDL_Renderer * renderer)254 GLES_ResetState(SDL_Renderer *renderer)
255 {
256     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
257 
258     if (SDL_CurrentContext == data->context) {
259         GLES_UpdateViewport(renderer);
260     } else {
261         GLES_ActivateRenderer(renderer);
262     }
263 
264     data->current.color = 0;
265     data->current.blendMode = -1;
266     data->current.tex_coords = SDL_FALSE;
267 
268     data->glDisable(GL_DEPTH_TEST);
269     data->glDisable(GL_CULL_FACE);
270 
271     data->glMatrixMode(GL_MODELVIEW);
272     data->glLoadIdentity();
273 
274     data->glEnableClientState(GL_VERTEX_ARRAY);
275     data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
276 }
277 
278 SDL_Renderer *
GLES_CreateRenderer(SDL_Window * window,Uint32 flags)279 GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
280 {
281 
282     SDL_Renderer *renderer;
283     GLES_RenderData *data;
284     GLint value;
285     Uint32 window_flags;
286     int profile_mask = 0, major = 0, minor = 0;
287     SDL_bool changed_window = SDL_FALSE;
288 
289     SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask);
290     SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
291     SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
292 
293     window_flags = SDL_GetWindowFlags(window);
294     if (!(window_flags & SDL_WINDOW_OPENGL) ||
295         profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) {
296 
297         changed_window = SDL_TRUE;
298         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
299         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
300         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
301 
302         if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
303             goto error;
304         }
305     }
306 
307     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
308     if (!renderer) {
309         SDL_OutOfMemory();
310         goto error;
311     }
312 
313     data = (GLES_RenderData *) SDL_calloc(1, sizeof(*data));
314     if (!data) {
315         GLES_DestroyRenderer(renderer);
316         SDL_OutOfMemory();
317         goto error;
318     }
319 
320     renderer->WindowEvent = GLES_WindowEvent;
321     renderer->GetOutputSize = GLES_GetOutputSize;
322     renderer->CreateTexture = GLES_CreateTexture;
323     renderer->UpdateTexture = GLES_UpdateTexture;
324     renderer->LockTexture = GLES_LockTexture;
325     renderer->UnlockTexture = GLES_UnlockTexture;
326     renderer->SetRenderTarget = GLES_SetRenderTarget;
327     renderer->UpdateViewport = GLES_UpdateViewport;
328     renderer->UpdateClipRect = GLES_UpdateClipRect;
329     renderer->RenderClear = GLES_RenderClear;
330     renderer->RenderDrawPoints = GLES_RenderDrawPoints;
331     renderer->RenderDrawLines = GLES_RenderDrawLines;
332     renderer->RenderFillRects = GLES_RenderFillRects;
333     renderer->RenderCopy = GLES_RenderCopy;
334     renderer->RenderCopyEx = GLES_RenderCopyEx;
335     renderer->RenderReadPixels = GLES_RenderReadPixels;
336     renderer->RenderPresent = GLES_RenderPresent;
337     renderer->DestroyTexture = GLES_DestroyTexture;
338     renderer->DestroyRenderer = GLES_DestroyRenderer;
339     renderer->GL_BindTexture = GLES_BindTexture;
340     renderer->GL_UnbindTexture = GLES_UnbindTexture;
341     renderer->info = GLES_RenderDriver.info;
342     renderer->info.flags = SDL_RENDERER_ACCELERATED;
343     renderer->driverdata = data;
344     renderer->window = window;
345 
346     data->context = SDL_GL_CreateContext(window);
347     if (!data->context) {
348         GLES_DestroyRenderer(renderer);
349         goto error;
350     }
351     if (SDL_GL_MakeCurrent(window, data->context) < 0) {
352         GLES_DestroyRenderer(renderer);
353         goto error;
354     }
355 
356     if (GLES_LoadFunctions(data) < 0) {
357         GLES_DestroyRenderer(renderer);
358         goto error;
359     }
360 
361     if (flags & SDL_RENDERER_PRESENTVSYNC) {
362         SDL_GL_SetSwapInterval(1);
363     } else {
364         SDL_GL_SetSwapInterval(0);
365     }
366     if (SDL_GL_GetSwapInterval() > 0) {
367         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
368     }
369 
370     value = 0;
371     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
372     renderer->info.max_texture_width = value;
373     value = 0;
374     data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
375     renderer->info.max_texture_height = value;
376 
377     /* Android does not report GL_OES_framebuffer_object but the functionality seems to be there anyway */
378     if (SDL_GL_ExtensionSupported("GL_OES_framebuffer_object") || data->glGenFramebuffersOES) {
379         data->GL_OES_framebuffer_object_supported = SDL_TRUE;
380         renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
381 
382         value = 0;
383         data->glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &value);
384         data->window_framebuffer = (GLuint)value;
385     }
386     data->framebuffers = NULL;
387 
388     if (SDL_GL_ExtensionSupported("GL_OES_blend_func_separate")) {
389         data->GL_OES_blend_func_separate_supported = SDL_TRUE;
390     }
391 
392     /* Set up parameters for rendering */
393     GLES_ResetState(renderer);
394 
395     return renderer;
396 
397 error:
398     if (changed_window) {
399         /* Uh oh, better try to put it back... */
400         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);
401         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
402         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
403         SDL_RecreateWindow(window, window_flags);
404     }
405     return NULL;
406 }
407 
408 static void
GLES_WindowEvent(SDL_Renderer * renderer,const SDL_WindowEvent * event)409 GLES_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
410 {
411     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
412 
413     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
414         event->event == SDL_WINDOWEVENT_SHOWN ||
415         event->event == SDL_WINDOWEVENT_HIDDEN) {
416         /* Rebind the context to the window area and update matrices */
417         SDL_CurrentContext = NULL;
418     }
419 
420     if (event->event == SDL_WINDOWEVENT_MINIMIZED) {
421         /* According to Apple documentation, we need to finish drawing NOW! */
422         data->glFinish();
423     }
424 }
425 
426 static int
GLES_GetOutputSize(SDL_Renderer * renderer,int * w,int * h)427 GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
428 {
429     SDL_GL_GetDrawableSize(renderer->window, w, h);
430     return 0;
431 }
432 
433 static SDL_INLINE int
power_of_2(int input)434 power_of_2(int input)
435 {
436     int value = 1;
437 
438     while (value < input) {
439         value <<= 1;
440     }
441     return value;
442 }
443 
444 static GLenum
GetScaleQuality(void)445 GetScaleQuality(void)
446 {
447     const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
448 
449     if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
450         return GL_NEAREST;
451     } else {
452         return GL_LINEAR;
453     }
454 }
455 
456 static int
GLES_CreateTexture(SDL_Renderer * renderer,SDL_Texture * texture)457 GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
458 {
459     GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
460     GLES_TextureData *data;
461     GLint internalFormat;
462     GLenum format, type;
463     int texture_w, texture_h;
464     GLenum scaleMode;
465     GLenum result;
466 
467     GLES_ActivateRenderer(renderer);
468 
469     switch (texture->format) {
470     case SDL_PIXELFORMAT_ABGR8888:
471         internalFormat = GL_RGBA;
472         format = GL_RGBA;
473         type = GL_UNSIGNED_BYTE;
474         break;
475     default:
476         return SDL_SetError("Texture format not supported");
477     }
478 
479     data = (GLES_TextureData *) SDL_calloc(1, sizeof(*data));
480     if (!data) {
481         return SDL_OutOfMemory();
482     }
483 
484     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
485         data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
486         data->pixels = SDL_calloc(1, texture->h * data->pitch);
487         if (!data->pixels) {
488             SDL_free(data);
489             return SDL_OutOfMemory();
490         }
491     }
492 
493 
494     if (texture->access == SDL_TEXTUREACCESS_TARGET) {
495         if (!renderdata->GL_OES_framebuffer_object_supported) {
496             SDL_free(data);
497             return SDL_SetError("GL_OES_framebuffer_object not supported");
498         }
499         data->fbo = GLES_GetFBO(renderer->driverdata, texture->w, texture->h);
500     } else {
501         data->fbo = NULL;
502     }
503 
504 
505     renderdata->glGetError();
506     renderdata->glEnable(GL_TEXTURE_2D);
507     renderdata->glGenTextures(1, &data->texture);
508     result = renderdata->glGetError();
509     if (result != GL_NO_ERROR) {
510         SDL_free(data);
511         return GLES_SetError("glGenTextures()", result);
512     }
513 
514     data->type = GL_TEXTURE_2D;
515     /* no NPOV textures allowed in OpenGL ES (yet) */
516     texture_w = power_of_2(texture->w);
517     texture_h = power_of_2(texture->h);
518     data->texw = (GLfloat) texture->w / texture_w;
519     data->texh = (GLfloat) texture->h / texture_h;
520 
521     data->format = format;
522     data->formattype = type;
523     scaleMode = GetScaleQuality();
524     renderdata->glBindTexture(data->type, data->texture);
525     renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
526     renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
527     renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
528     renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
529 
530     renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
531                              texture_h, 0, format, type, NULL);
532     renderdata->glDisable(GL_TEXTURE_2D);
533 
534     result = renderdata->glGetError();
535     if (result != GL_NO_ERROR) {
536         SDL_free(data);
537         return GLES_SetError("glTexImage2D()", result);
538     }
539 
540     texture->driverdata = data;
541     return 0;
542 }
543 
544 static int
GLES_UpdateTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,const void * pixels,int pitch)545 GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
546                    const SDL_Rect * rect, const void *pixels, int pitch)
547 {
548     GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
549     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
550     Uint8 *blob = NULL;
551     Uint8 *src;
552     int srcPitch;
553     int y;
554 
555     GLES_ActivateRenderer(renderer);
556 
557     /* Bail out if we're supposed to update an empty rectangle */
558     if (rect->w <= 0 || rect->h <= 0) {
559         return 0;
560     }
561 
562     /* Reformat the texture data into a tightly packed array */
563     srcPitch = rect->w * SDL_BYTESPERPIXEL(texture->format);
564     src = (Uint8 *)pixels;
565     if (pitch != srcPitch) {
566         blob = (Uint8 *)SDL_malloc(srcPitch * rect->h);
567         if (!blob) {
568             return SDL_OutOfMemory();
569         }
570         src = blob;
571         for (y = 0; y < rect->h; ++y) {
572             SDL_memcpy(src, pixels, srcPitch);
573             src += srcPitch;
574             pixels = (Uint8 *)pixels + pitch;
575         }
576         src = blob;
577     }
578 
579     /* Create a texture subimage with the supplied data */
580     renderdata->glGetError();
581     renderdata->glEnable(data->type);
582     renderdata->glBindTexture(data->type, data->texture);
583     renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
584     renderdata->glTexSubImage2D(data->type,
585                     0,
586                     rect->x,
587                     rect->y,
588                     rect->w,
589                     rect->h,
590                     data->format,
591                     data->formattype,
592                     src);
593     renderdata->glDisable(data->type);
594     SDL_free(blob);
595 
596     if (renderdata->glGetError() != GL_NO_ERROR) {
597         return SDL_SetError("Failed to update texture");
598     }
599     return 0;
600 }
601 
602 static int
GLES_LockTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,void ** pixels,int * pitch)603 GLES_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
604                  const SDL_Rect * rect, void **pixels, int *pitch)
605 {
606     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
607 
608     *pixels =
609         (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
610                   rect->x * SDL_BYTESPERPIXEL(texture->format));
611     *pitch = data->pitch;
612     return 0;
613 }
614 
615 static void
GLES_UnlockTexture(SDL_Renderer * renderer,SDL_Texture * texture)616 GLES_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
617 {
618     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
619     SDL_Rect rect;
620 
621     /* We do whole texture updates, at least for now */
622     rect.x = 0;
623     rect.y = 0;
624     rect.w = texture->w;
625     rect.h = texture->h;
626     GLES_UpdateTexture(renderer, texture, &rect, data->pixels, data->pitch);
627 }
628 
629 static int
GLES_SetRenderTarget(SDL_Renderer * renderer,SDL_Texture * texture)630 GLES_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
631 {
632     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
633     GLES_TextureData *texturedata = NULL;
634     GLenum status;
635 
636     GLES_ActivateRenderer(renderer);
637 
638     if (!data->GL_OES_framebuffer_object_supported) {
639         return SDL_SetError("Can't enable render target support in this renderer");
640     }
641 
642     if (texture == NULL) {
643         data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, data->window_framebuffer);
644         return 0;
645     }
646 
647     texturedata = (GLES_TextureData *) texture->driverdata;
648     data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, texturedata->fbo->FBO);
649     /* TODO: check if texture pixel format allows this operation */
650     data->glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, texturedata->type, texturedata->texture, 0);
651     /* Check FBO status */
652     status = data->glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
653     if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
654         return SDL_SetError("glFramebufferTexture2DOES() failed");
655     }
656     return 0;
657 }
658 
659 static int
GLES_UpdateViewport(SDL_Renderer * renderer)660 GLES_UpdateViewport(SDL_Renderer * renderer)
661 {
662     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
663 
664     if (SDL_CurrentContext != data->context) {
665         /* We'll update the viewport after we rebind the context */
666         return 0;
667     }
668 
669     if (renderer->target) {
670         data->glViewport(renderer->viewport.x, renderer->viewport.y,
671                          renderer->viewport.w, renderer->viewport.h);
672     } else {
673         int w, h;
674 
675         SDL_GL_GetDrawableSize(renderer->window, &w, &h);
676         data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h),
677                          renderer->viewport.w, renderer->viewport.h);
678     }
679 
680     data->glMatrixMode(GL_PROJECTION);
681     data->glLoadIdentity();
682     if (renderer->viewport.w && renderer->viewport.h) {
683         if (renderer->target) {
684             data->glOrthof((GLfloat) 0,
685                            (GLfloat) renderer->viewport.w,
686                            (GLfloat) 0,
687                            (GLfloat) renderer->viewport.h,
688                            0.0, 1.0);
689         } else {
690             data->glOrthof((GLfloat) 0,
691                            (GLfloat) renderer->viewport.w,
692                            (GLfloat) renderer->viewport.h,
693                            (GLfloat) 0,
694                            0.0, 1.0);
695         }
696     }
697     return 0;
698 }
699 
700 static int
GLES_UpdateClipRect(SDL_Renderer * renderer)701 GLES_UpdateClipRect(SDL_Renderer * renderer)
702 {
703     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
704 
705     if (SDL_CurrentContext != data->context) {
706         /* We'll update the clip rect after we rebind the context */
707         return 0;
708     }
709 
710     if (renderer->clipping_enabled) {
711         const SDL_Rect *rect = &renderer->clip_rect;
712         data->glEnable(GL_SCISSOR_TEST);
713         if (renderer->target) {
714             data->glScissor(renderer->viewport.x + rect->x, renderer->viewport.y + rect->y, rect->w, rect->h);
715         } else {
716             int w, h;
717 
718             SDL_GL_GetDrawableSize(renderer->window, &w, &h);
719             data->glScissor(renderer->viewport.x + rect->x, h - renderer->viewport.y - rect->y - rect->h, rect->w, rect->h);
720         }
721     } else {
722         data->glDisable(GL_SCISSOR_TEST);
723     }
724     return 0;
725 }
726 
727 static void
GLES_SetColor(GLES_RenderData * data,Uint8 r,Uint8 g,Uint8 b,Uint8 a)728 GLES_SetColor(GLES_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
729 {
730     Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
731 
732     if (color != data->current.color) {
733         data->glColor4f((GLfloat) r * inv255f,
734                         (GLfloat) g * inv255f,
735                         (GLfloat) b * inv255f,
736                         (GLfloat) a * inv255f);
737         data->current.color = color;
738     }
739 }
740 
741 static void
GLES_SetBlendMode(GLES_RenderData * data,int blendMode)742 GLES_SetBlendMode(GLES_RenderData * data, int blendMode)
743 {
744     if (blendMode != data->current.blendMode) {
745         switch (blendMode) {
746         case SDL_BLENDMODE_NONE:
747             data->glDisable(GL_BLEND);
748             break;
749         case SDL_BLENDMODE_BLEND:
750             data->glEnable(GL_BLEND);
751             if (data->GL_OES_blend_func_separate_supported) {
752                 data->glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
753             } else {
754                 data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
755             }
756             break;
757         case SDL_BLENDMODE_ADD:
758             data->glEnable(GL_BLEND);
759             if (data->GL_OES_blend_func_separate_supported) {
760                 data->glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
761             } else {
762                 data->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
763             }
764             break;
765         case SDL_BLENDMODE_MOD:
766             data->glEnable(GL_BLEND);
767             if (data->GL_OES_blend_func_separate_supported) {
768                 data->glBlendFuncSeparateOES(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE);
769             } else {
770                 data->glBlendFunc(GL_ZERO, GL_SRC_COLOR);
771             }
772             break;
773         }
774         data->current.blendMode = blendMode;
775     }
776 }
777 
778 static void
GLES_SetTexCoords(GLES_RenderData * data,SDL_bool enabled)779 GLES_SetTexCoords(GLES_RenderData * data, SDL_bool enabled)
780 {
781     if (enabled != data->current.tex_coords) {
782         if (enabled) {
783             data->glEnableClientState(GL_TEXTURE_COORD_ARRAY);
784         } else {
785             data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
786         }
787         data->current.tex_coords = enabled;
788     }
789 }
790 
791 static void
GLES_SetDrawingState(SDL_Renderer * renderer)792 GLES_SetDrawingState(SDL_Renderer * renderer)
793 {
794     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
795 
796     GLES_ActivateRenderer(renderer);
797 
798     GLES_SetColor(data, (GLfloat) renderer->r,
799                         (GLfloat) renderer->g,
800                         (GLfloat) renderer->b,
801                         (GLfloat) renderer->a);
802 
803     GLES_SetBlendMode(data, renderer->blendMode);
804 
805     GLES_SetTexCoords(data, SDL_FALSE);
806 }
807 
808 static int
GLES_RenderClear(SDL_Renderer * renderer)809 GLES_RenderClear(SDL_Renderer * renderer)
810 {
811     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
812 
813     GLES_ActivateRenderer(renderer);
814 
815     data->glClearColor((GLfloat) renderer->r * inv255f,
816                  (GLfloat) renderer->g * inv255f,
817                  (GLfloat) renderer->b * inv255f,
818                  (GLfloat) renderer->a * inv255f);
819 
820     if (renderer->clipping_enabled) {
821         data->glDisable(GL_SCISSOR_TEST);
822     }
823 
824     data->glClear(GL_COLOR_BUFFER_BIT);
825 
826     if (renderer->clipping_enabled) {
827         data->glEnable(GL_SCISSOR_TEST);
828     }
829 
830     return 0;
831 }
832 
833 static int
GLES_RenderDrawPoints(SDL_Renderer * renderer,const SDL_FPoint * points,int count)834 GLES_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
835                       int count)
836 {
837     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
838     GLfloat *vertices;
839     int idx;
840 
841     GLES_SetDrawingState(renderer);
842 
843     /* Emit the specified vertices as points */
844     vertices = SDL_stack_alloc(GLfloat, count * 2);
845     for (idx = 0; idx < count; ++idx) {
846         GLfloat x = points[idx].x + 0.5f;
847         GLfloat y = points[idx].y + 0.5f;
848 
849         vertices[idx * 2] = x;
850         vertices[(idx * 2) + 1] = y;
851     }
852 
853     data->glVertexPointer(2, GL_FLOAT, 0, vertices);
854     data->glDrawArrays(GL_POINTS, 0, count);
855     SDL_stack_free(vertices);
856     return 0;
857 }
858 
859 static int
GLES_RenderDrawLines(SDL_Renderer * renderer,const SDL_FPoint * points,int count)860 GLES_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
861                      int count)
862 {
863     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
864     GLfloat *vertices;
865     int idx;
866 
867     GLES_SetDrawingState(renderer);
868 
869     /* Emit a line strip including the specified vertices */
870     vertices = SDL_stack_alloc(GLfloat, count * 2);
871     for (idx = 0; idx < count; ++idx) {
872         GLfloat x = points[idx].x + 0.5f;
873         GLfloat y = points[idx].y + 0.5f;
874 
875         vertices[idx * 2] = x;
876         vertices[(idx * 2) + 1] = y;
877     }
878 
879     data->glVertexPointer(2, GL_FLOAT, 0, vertices);
880     if (count > 2 &&
881         points[0].x == points[count-1].x && points[0].y == points[count-1].y) {
882         /* GL_LINE_LOOP takes care of the final segment */
883         --count;
884         data->glDrawArrays(GL_LINE_LOOP, 0, count);
885     } else {
886         data->glDrawArrays(GL_LINE_STRIP, 0, count);
887         /* We need to close the endpoint of the line */
888         data->glDrawArrays(GL_POINTS, count-1, 1);
889     }
890     SDL_stack_free(vertices);
891 
892     return 0;
893 }
894 
895 static int
GLES_RenderFillRects(SDL_Renderer * renderer,const SDL_FRect * rects,int count)896 GLES_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects,
897                      int count)
898 {
899     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
900     int i;
901 
902     GLES_SetDrawingState(renderer);
903 
904     for (i = 0; i < count; ++i) {
905         const SDL_FRect *rect = &rects[i];
906         GLfloat minx = rect->x;
907         GLfloat maxx = rect->x + rect->w;
908         GLfloat miny = rect->y;
909         GLfloat maxy = rect->y + rect->h;
910         GLfloat vertices[8];
911         vertices[0] = minx;
912         vertices[1] = miny;
913         vertices[2] = maxx;
914         vertices[3] = miny;
915         vertices[4] = minx;
916         vertices[5] = maxy;
917         vertices[6] = maxx;
918         vertices[7] = maxy;
919 
920         data->glVertexPointer(2, GL_FLOAT, 0, vertices);
921         data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
922     }
923 
924     return 0;
925 }
926 
927 static int
GLES_RenderCopy(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect)928 GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
929                 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
930 {
931     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
932     GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
933     GLfloat minx, miny, maxx, maxy;
934     GLfloat minu, maxu, minv, maxv;
935     GLfloat vertices[8];
936     GLfloat texCoords[8];
937 
938     GLES_ActivateRenderer(renderer);
939 
940     data->glEnable(GL_TEXTURE_2D);
941 
942     data->glBindTexture(texturedata->type, texturedata->texture);
943 
944     if (texture->modMode) {
945         GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a);
946     } else {
947         GLES_SetColor(data, 255, 255, 255, 255);
948     }
949 
950     GLES_SetBlendMode(data, texture->blendMode);
951 
952     GLES_SetTexCoords(data, SDL_TRUE);
953 
954     minx = dstrect->x;
955     miny = dstrect->y;
956     maxx = dstrect->x + dstrect->w;
957     maxy = dstrect->y + dstrect->h;
958 
959     minu = (GLfloat) srcrect->x / texture->w;
960     minu *= texturedata->texw;
961     maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
962     maxu *= texturedata->texw;
963     minv = (GLfloat) srcrect->y / texture->h;
964     minv *= texturedata->texh;
965     maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
966     maxv *= texturedata->texh;
967 
968     vertices[0] = minx;
969     vertices[1] = miny;
970     vertices[2] = maxx;
971     vertices[3] = miny;
972     vertices[4] = minx;
973     vertices[5] = maxy;
974     vertices[6] = maxx;
975     vertices[7] = maxy;
976 
977     texCoords[0] = minu;
978     texCoords[1] = minv;
979     texCoords[2] = maxu;
980     texCoords[3] = minv;
981     texCoords[4] = minu;
982     texCoords[5] = maxv;
983     texCoords[6] = maxu;
984     texCoords[7] = maxv;
985 
986     data->glVertexPointer(2, GL_FLOAT, 0, vertices);
987     data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
988     data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
989 
990     data->glDisable(GL_TEXTURE_2D);
991 
992     return 0;
993 }
994 
995 static int
GLES_RenderCopyEx(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect,const double angle,const SDL_FPoint * center,const SDL_RendererFlip flip)996 GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
997                 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
998                 const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
999 {
1000 
1001     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1002     GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
1003     GLfloat minx, miny, maxx, maxy;
1004     GLfloat minu, maxu, minv, maxv;
1005     GLfloat centerx, centery;
1006     GLfloat vertices[8];
1007     GLfloat texCoords[8];
1008 
1009 
1010     GLES_ActivateRenderer(renderer);
1011 
1012     data->glEnable(GL_TEXTURE_2D);
1013 
1014     data->glBindTexture(texturedata->type, texturedata->texture);
1015 
1016     if (texture->modMode) {
1017         GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a);
1018     } else {
1019         GLES_SetColor(data, 255, 255, 255, 255);
1020     }
1021 
1022     GLES_SetBlendMode(data, texture->blendMode);
1023 
1024     GLES_SetTexCoords(data, SDL_TRUE);
1025 
1026     centerx = center->x;
1027     centery = center->y;
1028 
1029     /* Rotate and translate */
1030     data->glPushMatrix();
1031     data->glTranslatef(dstrect->x + centerx, dstrect->y + centery, 0.0f);
1032     data->glRotatef((GLfloat)angle, 0.0f, 0.0f, 1.0f);
1033 
1034     if (flip & SDL_FLIP_HORIZONTAL) {
1035         minx =  dstrect->w - centerx;
1036         maxx = -centerx;
1037     } else {
1038         minx = -centerx;
1039         maxx = dstrect->w - centerx;
1040     }
1041 
1042     if (flip & SDL_FLIP_VERTICAL) {
1043         miny = dstrect->h - centery;
1044         maxy = -centery;
1045     } else {
1046         miny = -centery;
1047         maxy = dstrect->h - centery;
1048     }
1049 
1050     minu = (GLfloat) srcrect->x / texture->w;
1051     minu *= texturedata->texw;
1052     maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
1053     maxu *= texturedata->texw;
1054     minv = (GLfloat) srcrect->y / texture->h;
1055     minv *= texturedata->texh;
1056     maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
1057     maxv *= texturedata->texh;
1058 
1059     vertices[0] = minx;
1060     vertices[1] = miny;
1061     vertices[2] = maxx;
1062     vertices[3] = miny;
1063     vertices[4] = minx;
1064     vertices[5] = maxy;
1065     vertices[6] = maxx;
1066     vertices[7] = maxy;
1067 
1068     texCoords[0] = minu;
1069     texCoords[1] = minv;
1070     texCoords[2] = maxu;
1071     texCoords[3] = minv;
1072     texCoords[4] = minu;
1073     texCoords[5] = maxv;
1074     texCoords[6] = maxu;
1075     texCoords[7] = maxv;
1076     data->glVertexPointer(2, GL_FLOAT, 0, vertices);
1077     data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
1078     data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1079     data->glPopMatrix();
1080     data->glDisable(GL_TEXTURE_2D);
1081 
1082     return 0;
1083 }
1084 
1085 static int
GLES_RenderReadPixels(SDL_Renderer * renderer,const SDL_Rect * rect,Uint32 pixel_format,void * pixels,int pitch)1086 GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
1087                     Uint32 pixel_format, void * pixels, int pitch)
1088 {
1089     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1090     Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ABGR8888;
1091     void *temp_pixels;
1092     int temp_pitch;
1093     Uint8 *src, *dst, *tmp;
1094     int w, h, length, rows;
1095     int status;
1096 
1097     GLES_ActivateRenderer(renderer);
1098 
1099     temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format);
1100     temp_pixels = SDL_malloc(rect->h * temp_pitch);
1101     if (!temp_pixels) {
1102         return SDL_OutOfMemory();
1103     }
1104 
1105     SDL_GetRendererOutputSize(renderer, &w, &h);
1106 
1107     data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
1108 
1109     data->glReadPixels(rect->x, renderer->target ? rect->y : (h-rect->y)-rect->h,
1110                        rect->w, rect->h, GL_RGBA, GL_UNSIGNED_BYTE, temp_pixels);
1111 
1112     /* Flip the rows to be top-down if necessary */
1113     if (!renderer->target) {
1114         length = rect->w * SDL_BYTESPERPIXEL(temp_format);
1115         src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch;
1116         dst = (Uint8*)temp_pixels;
1117         tmp = SDL_stack_alloc(Uint8, length);
1118         rows = rect->h / 2;
1119         while (rows--) {
1120             SDL_memcpy(tmp, dst, length);
1121             SDL_memcpy(dst, src, length);
1122             SDL_memcpy(src, tmp, length);
1123             dst += temp_pitch;
1124             src -= temp_pitch;
1125         }
1126         SDL_stack_free(tmp);
1127     }
1128 
1129     status = SDL_ConvertPixels(rect->w, rect->h,
1130                                temp_format, temp_pixels, temp_pitch,
1131                                pixel_format, pixels, pitch);
1132     SDL_free(temp_pixels);
1133 
1134     return status;
1135 }
1136 
1137 static void
GLES_RenderPresent(SDL_Renderer * renderer)1138 GLES_RenderPresent(SDL_Renderer * renderer)
1139 {
1140     GLES_ActivateRenderer(renderer);
1141 
1142     SDL_GL_SwapWindow(renderer->window);
1143 }
1144 
1145 static void
GLES_DestroyTexture(SDL_Renderer * renderer,SDL_Texture * texture)1146 GLES_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
1147 {
1148     GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
1149 
1150     GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
1151 
1152     GLES_ActivateRenderer(renderer);
1153 
1154     if (!data) {
1155         return;
1156     }
1157     if (data->texture) {
1158         renderdata->glDeleteTextures(1, &data->texture);
1159     }
1160     SDL_free(data->pixels);
1161     SDL_free(data);
1162     texture->driverdata = NULL;
1163 }
1164 
1165 static void
GLES_DestroyRenderer(SDL_Renderer * renderer)1166 GLES_DestroyRenderer(SDL_Renderer * renderer)
1167 {
1168     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1169 
1170     if (data) {
1171         if (data->context) {
1172             while (data->framebuffers) {
1173                GLES_FBOList *nextnode = data->framebuffers->next;
1174                data->glDeleteFramebuffersOES(1, &data->framebuffers->FBO);
1175                SDL_free(data->framebuffers);
1176                data->framebuffers = nextnode;
1177             }
1178             SDL_GL_DeleteContext(data->context);
1179         }
1180         SDL_free(data);
1181     }
1182     SDL_free(renderer);
1183 }
1184 
GLES_BindTexture(SDL_Renderer * renderer,SDL_Texture * texture,float * texw,float * texh)1185 static int GLES_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh)
1186 {
1187     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1188     GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
1189     GLES_ActivateRenderer(renderer);
1190 
1191     data->glEnable(GL_TEXTURE_2D);
1192     data->glBindTexture(texturedata->type, texturedata->texture);
1193 
1194     if (texw) {
1195         *texw = (float)texturedata->texw;
1196     }
1197     if (texh) {
1198         *texh = (float)texturedata->texh;
1199     }
1200 
1201     return 0;
1202 }
1203 
GLES_UnbindTexture(SDL_Renderer * renderer,SDL_Texture * texture)1204 static int GLES_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture)
1205 {
1206     GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1207     GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
1208     GLES_ActivateRenderer(renderer);
1209     data->glDisable(texturedata->type);
1210 
1211     return 0;
1212 }
1213 
1214 #endif /* SDL_VIDEO_RENDER_OGL_ES && !SDL_RENDER_DISABLED */
1215 
1216 /* vi: set ts=4 sw=4 expandtab: */
1217