| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | |
| | | #include "SDL_hints.h" |
| | | #include "SDL_log.h" |
| | | #include "SDL_assert.h" |
| | | #include "SDL_opengl.h" |
| | | #include "../SDL_sysrender.h" |
| | | #include "SDL_shaders_gl.h" |
| | |
| | | static void GL_WindowEvent(SDL_Renderer * renderer, |
| | | const SDL_WindowEvent *event); |
| | | static int GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h); |
| | | static SDL_bool GL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode); |
| | | static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); |
| | | static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, |
| | | const SDL_Rect * rect, const void *pixels, |
| | |
| | | GLDEBUGPROCARB next_error_callback; |
| | | GLvoid *next_error_userparam; |
| | | |
| | | SDL_bool GL_ARB_texture_non_power_of_two_supported; |
| | | SDL_bool GL_ARB_texture_rectangle_supported; |
| | | struct { |
| | | GL_Shader shader; |
| | | Uint32 color; |
| | | int blendMode; |
| | | SDL_BlendMode blendMode; |
| | | } current; |
| | | |
| | | SDL_bool GL_EXT_framebuffer_object_supported; |
| | |
| | | int pitch; |
| | | SDL_Rect locked_rect; |
| | | |
| | | /* YV12 texture support */ |
| | | /* YUV texture support */ |
| | | SDL_bool yuv; |
| | | SDL_bool nv12; |
| | | GLuint utexture; |
| | | GLuint vtexture; |
| | | |
| | |
| | | |
| | | #if 0 |
| | | #define GL_CheckError(prefix, renderer) |
| | | #elif defined(_MSC_VER) |
| | | #define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, __FILE__, __LINE__, __FUNCTION__) |
| | | #else |
| | | #define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, __FILE__, __LINE__, __PRETTY_FUNCTION__) |
| | | #define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, SDL_FILE, SDL_LINE, SDL_FUNCTION) |
| | | #endif |
| | | |
| | | static int |
| | |
| | | do { \ |
| | | data->func = SDL_GL_GetProcAddress(#func); \ |
| | | if ( ! data->func ) { \ |
| | | return SDL_SetError("Couldn't load GL function %s: %s\n", #func, SDL_GetError()); \ |
| | | return SDL_SetError("Couldn't load GL function %s: %s", #func, SDL_GetError()); \ |
| | | } \ |
| | | } while ( 0 ); |
| | | #endif /* __SDL_NOGETPROCADDR__ */ |
| | |
| | | { |
| | | GL_RenderData *data = (GL_RenderData *) renderer->driverdata; |
| | | |
| | | if (SDL_CurrentContext != data->context) { |
| | | if (SDL_CurrentContext != data->context || |
| | | SDL_GL_GetCurrentContext() != data->context) { |
| | | if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) { |
| | | return -1; |
| | | } |
| | |
| | | { |
| | | GL_RenderData *data = (GL_RenderData *) renderer->driverdata; |
| | | |
| | | if (SDL_CurrentContext == data->context) { |
| | | if (SDL_GL_GetCurrentContext() == data->context) { |
| | | GL_UpdateViewport(renderer); |
| | | } else { |
| | | GL_ActivateRenderer(renderer); |
| | | } |
| | | |
| | | data->current.shader = SHADER_NONE; |
| | | data->current.color = 0; |
| | | data->current.blendMode = -1; |
| | | data->current.color = 0xffffffff; |
| | | data->current.blendMode = SDL_BLENDMODE_INVALID; |
| | | |
| | | data->glDisable(GL_DEPTH_TEST); |
| | | data->glDisable(GL_CULL_FACE); |
| | |
| | | } |
| | | |
| | | static void APIENTRY |
| | | GL_HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char *message, void *userParam) |
| | | GL_HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char *message, const void *userParam) |
| | | { |
| | | SDL_Renderer *renderer = (SDL_Renderer *) userParam; |
| | | GL_RenderData *data = (GL_RenderData *) renderer->driverdata; |
| | | |
| | | if (type == GL_DEBUG_TYPE_ERROR_ARB) { |
| | | /* Record this error */ |
| | | ++data->errors; |
| | | data->error_messages = SDL_realloc(data->error_messages, data->errors * sizeof(*data->error_messages)); |
| | | if (data->error_messages) { |
| | | int errors = data->errors + 1; |
| | | char **error_messages = SDL_realloc(data->error_messages, errors * sizeof(*data->error_messages)); |
| | | if (error_messages) { |
| | | data->errors = errors; |
| | | data->error_messages = error_messages; |
| | | data->error_messages[data->errors-1] = SDL_strdup(message); |
| | | } |
| | | } |
| | |
| | | { |
| | | SDL_Renderer *renderer; |
| | | GL_RenderData *data; |
| | | const char *hint; |
| | | GLint value; |
| | | Uint32 window_flags; |
| | | int profile_mask, major, minor; |
| | | int profile_mask = 0, major = 0, minor = 0; |
| | | SDL_bool changed_window = SDL_FALSE; |
| | | |
| | | SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask); |
| | | SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); |
| | |
| | | window_flags = SDL_GetWindowFlags(window); |
| | | if (!(window_flags & SDL_WINDOW_OPENGL) || |
| | | profile_mask == SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) { |
| | | |
| | | |
| | | changed_window = SDL_TRUE; |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR); |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR); |
| | | |
| | | if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) { |
| | | /* Uh oh, better try to put it back... */ |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); |
| | | SDL_RecreateWindow(window, window_flags); |
| | | return NULL; |
| | | goto error; |
| | | } |
| | | } |
| | | |
| | | renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); |
| | | if (!renderer) { |
| | | SDL_OutOfMemory(); |
| | | return NULL; |
| | | goto error; |
| | | } |
| | | |
| | | data = (GL_RenderData *) SDL_calloc(1, sizeof(*data)); |
| | | if (!data) { |
| | | GL_DestroyRenderer(renderer); |
| | | SDL_OutOfMemory(); |
| | | return NULL; |
| | | goto error; |
| | | } |
| | | |
| | | renderer->WindowEvent = GL_WindowEvent; |
| | | renderer->GetOutputSize = GL_GetOutputSize; |
| | | renderer->SupportsBlendMode = GL_SupportsBlendMode; |
| | | renderer->CreateTexture = GL_CreateTexture; |
| | | renderer->UpdateTexture = GL_UpdateTexture; |
| | | renderer->UpdateTextureYUV = GL_UpdateTextureYUV; |
| | |
| | | renderer->GL_BindTexture = GL_BindTexture; |
| | | renderer->GL_UnbindTexture = GL_UnbindTexture; |
| | | renderer->info = GL_RenderDriver.info; |
| | | renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); |
| | | renderer->info.flags = SDL_RENDERER_ACCELERATED; |
| | | renderer->driverdata = data; |
| | | renderer->window = window; |
| | | |
| | | data->context = SDL_GL_CreateContext(window); |
| | | if (!data->context) { |
| | | GL_DestroyRenderer(renderer); |
| | | return NULL; |
| | | goto error; |
| | | } |
| | | if (SDL_GL_MakeCurrent(window, data->context) < 0) { |
| | | GL_DestroyRenderer(renderer); |
| | | return NULL; |
| | | goto error; |
| | | } |
| | | |
| | | if (GL_LoadFunctions(data) < 0) { |
| | | GL_DestroyRenderer(renderer); |
| | | return NULL; |
| | | goto error; |
| | | } |
| | | |
| | | #ifdef __MACOSX__ |
| | |
| | | PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC) SDL_GL_GetProcAddress("glDebugMessageCallbackARB"); |
| | | |
| | | data->GL_ARB_debug_output_supported = SDL_TRUE; |
| | | data->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION_ARB, (GLvoid **)&data->next_error_callback); |
| | | data->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION_ARB, (GLvoid **)(char *)&data->next_error_callback); |
| | | data->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM_ARB, &data->next_error_userparam); |
| | | glDebugMessageCallbackARBFunc(GL_HandleDebugMessage, renderer); |
| | | |
| | |
| | | data->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); |
| | | } |
| | | |
| | | if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") |
| | | || SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) { |
| | | if (SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) { |
| | | data->GL_ARB_texture_non_power_of_two_supported = SDL_TRUE; |
| | | } else if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") || |
| | | SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) { |
| | | data->GL_ARB_texture_rectangle_supported = SDL_TRUE; |
| | | } |
| | | if (data->GL_ARB_texture_rectangle_supported) { |
| | | data->glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &value); |
| | | renderer->info.max_texture_width = value; |
| | | renderer->info.max_texture_height = value; |
| | |
| | | } |
| | | |
| | | /* Check for shader support */ |
| | | hint = SDL_GetHint(SDL_HINT_RENDER_OPENGL_SHADERS); |
| | | if (!hint || *hint != '0') { |
| | | if (SDL_GetHintBoolean(SDL_HINT_RENDER_OPENGL_SHADERS, SDL_TRUE)) { |
| | | data->shaders = GL_CreateShaderContext(); |
| | | } |
| | | SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s", |
| | |
| | | if (data->shaders && data->num_texture_units >= 3) { |
| | | renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12; |
| | | renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV; |
| | | renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV12; |
| | | renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV21; |
| | | } |
| | | |
| | | #ifdef __MACOSX__ |
| | |
| | | GL_ResetState(renderer); |
| | | |
| | | return renderer; |
| | | |
| | | error: |
| | | if (changed_window) { |
| | | /* Uh oh, better try to put it back... */ |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); |
| | | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); |
| | | SDL_RecreateWindow(window, window_flags); |
| | | } |
| | | return NULL; |
| | | } |
| | | |
| | | static void |
| | |
| | | GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h) |
| | | { |
| | | SDL_GL_GetDrawableSize(renderer->window, w, h); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | static GLenum GetBlendFunc(SDL_BlendFactor factor) |
| | | { |
| | | switch (factor) { |
| | | case SDL_BLENDFACTOR_ZERO: |
| | | return GL_ZERO; |
| | | case SDL_BLENDFACTOR_ONE: |
| | | return GL_ONE; |
| | | case SDL_BLENDFACTOR_SRC_COLOR: |
| | | return GL_SRC_COLOR; |
| | | case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: |
| | | return GL_ONE_MINUS_SRC_COLOR; |
| | | case SDL_BLENDFACTOR_SRC_ALPHA: |
| | | return GL_SRC_ALPHA; |
| | | case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: |
| | | return GL_ONE_MINUS_SRC_ALPHA; |
| | | case SDL_BLENDFACTOR_DST_COLOR: |
| | | return GL_DST_COLOR; |
| | | case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: |
| | | return GL_ONE_MINUS_DST_COLOR; |
| | | case SDL_BLENDFACTOR_DST_ALPHA: |
| | | return GL_DST_ALPHA; |
| | | case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: |
| | | return GL_ONE_MINUS_DST_ALPHA; |
| | | default: |
| | | return GL_INVALID_ENUM; |
| | | } |
| | | } |
| | | |
| | | static GLenum GetBlendEquation(SDL_BlendOperation operation) |
| | | { |
| | | switch (operation) { |
| | | case SDL_BLENDOPERATION_ADD: |
| | | return GL_FUNC_ADD; |
| | | case SDL_BLENDOPERATION_SUBTRACT: |
| | | return GL_FUNC_SUBTRACT; |
| | | case SDL_BLENDOPERATION_REV_SUBTRACT: |
| | | return GL_FUNC_REVERSE_SUBTRACT; |
| | | default: |
| | | return GL_INVALID_ENUM; |
| | | } |
| | | } |
| | | |
| | | static SDL_bool |
| | | GL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) |
| | | { |
| | | SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode); |
| | | SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode); |
| | | SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode); |
| | | SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode); |
| | | SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode); |
| | | SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode); |
| | | |
| | | if (GetBlendFunc(srcColorFactor) == GL_INVALID_ENUM || |
| | | GetBlendFunc(srcAlphaFactor) == GL_INVALID_ENUM || |
| | | GetBlendEquation(colorOperation) == GL_INVALID_ENUM || |
| | | GetBlendFunc(dstColorFactor) == GL_INVALID_ENUM || |
| | | GetBlendFunc(dstAlphaFactor) == GL_INVALID_ENUM || |
| | | GetBlendEquation(alphaOperation) == GL_INVALID_ENUM) { |
| | | return SDL_FALSE; |
| | | } |
| | | if (colorOperation != alphaOperation) { |
| | | return SDL_FALSE; |
| | | } |
| | | return SDL_TRUE; |
| | | } |
| | | |
| | | SDL_FORCE_INLINE int |
| | |
| | | break; |
| | | case SDL_PIXELFORMAT_YV12: |
| | | case SDL_PIXELFORMAT_IYUV: |
| | | case SDL_PIXELFORMAT_NV12: |
| | | case SDL_PIXELFORMAT_NV21: |
| | | *internalFormat = GL_LUMINANCE; |
| | | *format = GL_LUMINANCE; |
| | | *type = GL_UNSIGNED_BYTE; |
| | | break; |
| | | #ifdef __MACOSX__ |
| | | case SDL_PIXELFORMAT_UYVY: |
| | | *internalFormat = GL_RGB8; |
| | | *format = GL_YCBCR_422_APPLE; |
| | | *type = GL_UNSIGNED_SHORT_8_8_APPLE; |
| | | break; |
| | | *internalFormat = GL_RGB8; |
| | | *format = GL_YCBCR_422_APPLE; |
| | | *type = GL_UNSIGNED_SHORT_8_8_APPLE; |
| | | break; |
| | | #endif |
| | | default: |
| | | return SDL_FALSE; |
| | | } |
| | | return SDL_TRUE; |
| | | } |
| | | |
| | | static GLenum |
| | | GetScaleQuality(void) |
| | | { |
| | | const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); |
| | | |
| | | if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) { |
| | | return GL_NEAREST; |
| | | } else { |
| | | return GL_LINEAR; |
| | | } |
| | | } |
| | | |
| | | static int |
| | |
| | | GLenum scaleMode; |
| | | |
| | | GL_ActivateRenderer(renderer); |
| | | |
| | | if (texture->access == SDL_TEXTUREACCESS_TARGET && |
| | | !renderdata->GL_EXT_framebuffer_object_supported) { |
| | | return SDL_SetError("Render targets not supported by OpenGL"); |
| | | } |
| | | |
| | | if (!convert_format(renderdata, texture->format, &internalFormat, |
| | | &format, &type)) { |
| | |
| | | if (texture->format == SDL_PIXELFORMAT_YV12 || |
| | | texture->format == SDL_PIXELFORMAT_IYUV) { |
| | | /* Need to add size for the U and V planes */ |
| | | size += (2 * (texture->h * data->pitch) / 4); |
| | | size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); |
| | | } |
| | | if (texture->format == SDL_PIXELFORMAT_NV12 || |
| | | texture->format == SDL_PIXELFORMAT_NV21) { |
| | | /* Need to add size for the U/V plane */ |
| | | size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); |
| | | } |
| | | data->pixels = SDL_calloc(1, size); |
| | | if (!data->pixels) { |
| | |
| | | |
| | | GL_CheckError("", renderer); |
| | | renderdata->glGenTextures(1, &data->texture); |
| | | if (GL_CheckError("glGenTexures()", renderer) < 0) { |
| | | if (GL_CheckError("glGenTextures()", renderer) < 0) { |
| | | if (data->pixels) { |
| | | SDL_free(data->pixels); |
| | | } |
| | | SDL_free(data); |
| | | return -1; |
| | | } |
| | | texture->driverdata = data; |
| | | |
| | | if ((renderdata->GL_ARB_texture_rectangle_supported) |
| | | /* && texture->access != SDL_TEXTUREACCESS_TARGET */){ |
| | | if (renderdata->GL_ARB_texture_non_power_of_two_supported) { |
| | | data->type = GL_TEXTURE_2D; |
| | | texture_w = texture->w; |
| | | texture_h = texture->h; |
| | | data->texw = 1.0f; |
| | | data->texh = 1.0f; |
| | | } else if (renderdata->GL_ARB_texture_rectangle_supported) { |
| | | data->type = GL_TEXTURE_RECTANGLE_ARB; |
| | | texture_w = texture->w; |
| | | texture_h = texture->h; |
| | |
| | | |
| | | data->format = format; |
| | | data->formattype = type; |
| | | scaleMode = GetScaleQuality(); |
| | | scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR; |
| | | renderdata->glEnable(data->type); |
| | | renderdata->glBindTexture(data->type, data->texture); |
| | | renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode); |
| | |
| | | GL_CLAMP_TO_EDGE); |
| | | renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, |
| | | GL_CLAMP_TO_EDGE); |
| | | renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2, |
| | | texture_h/2, 0, format, type, NULL); |
| | | renderdata->glTexImage2D(data->type, 0, internalFormat, (texture_w+1)/2, |
| | | (texture_h+1)/2, 0, format, type, NULL); |
| | | |
| | | renderdata->glBindTexture(data->type, data->vtexture); |
| | | renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, |
| | |
| | | GL_CLAMP_TO_EDGE); |
| | | renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, |
| | | GL_CLAMP_TO_EDGE); |
| | | renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2, |
| | | texture_h/2, 0, format, type, NULL); |
| | | renderdata->glTexImage2D(data->type, 0, internalFormat, (texture_w+1)/2, |
| | | (texture_h+1)/2, 0, format, type, NULL); |
| | | |
| | | renderdata->glDisable(data->type); |
| | | } |
| | | |
| | | if (texture->format == SDL_PIXELFORMAT_NV12 || |
| | | texture->format == SDL_PIXELFORMAT_NV21) { |
| | | data->nv12 = SDL_TRUE; |
| | | |
| | | renderdata->glGenTextures(1, &data->utexture); |
| | | renderdata->glEnable(data->type); |
| | | |
| | | renderdata->glBindTexture(data->type, data->utexture); |
| | | renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, |
| | | scaleMode); |
| | | renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, |
| | | scaleMode); |
| | | renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, |
| | | GL_CLAMP_TO_EDGE); |
| | | renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, |
| | | GL_CLAMP_TO_EDGE); |
| | | renderdata->glTexImage2D(data->type, 0, GL_LUMINANCE_ALPHA, (texture_w+1)/2, |
| | | (texture_h+1)/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); |
| | | renderdata->glDisable(data->type); |
| | | } |
| | | |
| | |
| | | { |
| | | GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; |
| | | GL_TextureData *data = (GL_TextureData *) texture->driverdata; |
| | | const int texturebpp = SDL_BYTESPERPIXEL(texture->format); |
| | | |
| | | SDL_assert(texturebpp != 0); /* otherwise, division by zero later. */ |
| | | |
| | | GL_ActivateRenderer(renderer); |
| | | |
| | | renderdata->glEnable(data->type); |
| | | renderdata->glBindTexture(data->type, data->texture); |
| | | renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| | | renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, |
| | | (pitch / SDL_BYTESPERPIXEL(texture->format))); |
| | | renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / texturebpp)); |
| | | renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w, |
| | | rect->h, data->format, data->formattype, |
| | | pixels); |
| | | if (data->yuv) { |
| | | renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2)); |
| | | renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, ((pitch + 1) / 2)); |
| | | |
| | | /* Skip to the correct offset into the next texture */ |
| | | pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); |
| | |
| | | renderdata->glBindTexture(data->type, data->utexture); |
| | | } |
| | | renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, |
| | | rect->w/2, rect->h/2, |
| | | (rect->w+1)/2, (rect->h+1)/2, |
| | | data->format, data->formattype, pixels); |
| | | |
| | | /* Skip to the correct offset into the next texture */ |
| | | pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4); |
| | | pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); |
| | | if (texture->format == SDL_PIXELFORMAT_YV12) { |
| | | renderdata->glBindTexture(data->type, data->utexture); |
| | | } else { |
| | | renderdata->glBindTexture(data->type, data->vtexture); |
| | | } |
| | | renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, |
| | | rect->w/2, rect->h/2, |
| | | (rect->w+1)/2, (rect->h+1)/2, |
| | | data->format, data->formattype, pixels); |
| | | } |
| | | |
| | | if (data->nv12) { |
| | | renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, ((pitch + 1) / 2)); |
| | | |
| | | /* Skip to the correct offset into the next texture */ |
| | | pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); |
| | | renderdata->glBindTexture(data->type, data->utexture); |
| | | renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, |
| | | (rect->w + 1)/2, (rect->h + 1)/2, |
| | | GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels); |
| | | } |
| | | renderdata->glDisable(data->type); |
| | | |
| | |
| | | renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch); |
| | | renderdata->glBindTexture(data->type, data->utexture); |
| | | renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, |
| | | rect->w/2, rect->h/2, |
| | | (rect->w + 1)/2, (rect->h + 1)/2, |
| | | data->format, data->formattype, Uplane); |
| | | |
| | | renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch); |
| | | renderdata->glBindTexture(data->type, data->vtexture); |
| | | renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, |
| | | rect->w/2, rect->h/2, |
| | | (rect->w + 1)/2, (rect->h + 1)/2, |
| | | data->format, data->formattype, Vplane); |
| | | renderdata->glDisable(data->type); |
| | | |
| | |
| | | |
| | | GL_ActivateRenderer(renderer); |
| | | |
| | | if (!data->GL_EXT_framebuffer_object_supported) { |
| | | return SDL_SetError("Render targets not supported by OpenGL"); |
| | | } |
| | | |
| | | if (texture == NULL) { |
| | | data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); |
| | | return 0; |
| | |
| | | } else { |
| | | int w, h; |
| | | |
| | | SDL_GetRendererOutputSize(renderer, &w, &h); |
| | | SDL_GL_GetDrawableSize(renderer->window, &w, &h); |
| | | data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h), |
| | | renderer->viewport.w, renderer->viewport.h); |
| | | } |
| | |
| | | 0.0, 1.0); |
| | | } |
| | | } |
| | | data->glMatrixMode(GL_MODELVIEW); |
| | | |
| | | return GL_CheckError("", renderer); |
| | | } |
| | | |
| | | static int |
| | | GL_UpdateClipRect(SDL_Renderer * renderer) |
| | | { |
| | | const SDL_Rect *rect = &renderer->clip_rect; |
| | | GL_RenderData *data = (GL_RenderData *) renderer->driverdata; |
| | | |
| | | if (!SDL_RectEmpty(rect)) { |
| | | if (renderer->clipping_enabled) { |
| | | const SDL_Rect *rect = &renderer->clip_rect; |
| | | data->glEnable(GL_SCISSOR_TEST); |
| | | data->glScissor(rect->x, renderer->viewport.h - rect->y - rect->h, rect->w, rect->h); |
| | | if (renderer->target) { |
| | | data->glScissor(renderer->viewport.x + rect->x, renderer->viewport.y + rect->y, rect->w, rect->h); |
| | | } else { |
| | | int w, h; |
| | | |
| | | SDL_GL_GetDrawableSize(renderer->window, &w, &h); |
| | | data->glScissor(renderer->viewport.x + rect->x, h - renderer->viewport.y - rect->y - rect->h, rect->w, rect->h); |
| | | } |
| | | } else { |
| | | data->glDisable(GL_SCISSOR_TEST); |
| | | } |
| | |
| | | } |
| | | |
| | | static void |
| | | GL_SetBlendMode(GL_RenderData * data, int blendMode) |
| | | GL_SetBlendMode(GL_RenderData * data, SDL_BlendMode blendMode) |
| | | { |
| | | if (blendMode != data->current.blendMode) { |
| | | switch (blendMode) { |
| | | case SDL_BLENDMODE_NONE: |
| | | data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); |
| | | if (blendMode == SDL_BLENDMODE_NONE) { |
| | | data->glDisable(GL_BLEND); |
| | | break; |
| | | case SDL_BLENDMODE_BLEND: |
| | | data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); |
| | | } else { |
| | | data->glEnable(GL_BLEND); |
| | | data->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
| | | break; |
| | | case SDL_BLENDMODE_ADD: |
| | | data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); |
| | | data->glEnable(GL_BLEND); |
| | | data->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); |
| | | break; |
| | | case SDL_BLENDMODE_MOD: |
| | | data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); |
| | | data->glEnable(GL_BLEND); |
| | | data->glBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE); |
| | | break; |
| | | data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode)), |
| | | GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode)), |
| | | GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blendMode)), |
| | | GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blendMode))); |
| | | data->glBlendEquation(GetBlendEquation(SDL_GetBlendModeColorOperation(blendMode))); |
| | | } |
| | | data->current.blendMode = blendMode; |
| | | } |
| | |
| | | (GLfloat) renderer->b * inv255f, |
| | | (GLfloat) renderer->a * inv255f); |
| | | |
| | | if (renderer->clipping_enabled) { |
| | | data->glDisable(GL_SCISSOR_TEST); |
| | | } |
| | | |
| | | data->glClear(GL_COLOR_BUFFER_BIT); |
| | | |
| | | if (renderer->clipping_enabled) { |
| | | data->glEnable(GL_SCISSOR_TEST); |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | |
| | | } |
| | | |
| | | static int |
| | | GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, |
| | | const SDL_Rect * srcrect, const SDL_FRect * dstrect) |
| | | GL_SetupCopy(SDL_Renderer * renderer, SDL_Texture * texture) |
| | | { |
| | | GL_RenderData *data = (GL_RenderData *) renderer->driverdata; |
| | | GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; |
| | | GLfloat minx, miny, maxx, maxy; |
| | | GLfloat minu, maxu, minv, maxv; |
| | | |
| | | GL_ActivateRenderer(renderer); |
| | | |
| | | data->glEnable(texturedata->type); |
| | | if (texturedata->yuv) { |
| | | data->glActiveTextureARB(GL_TEXTURE2_ARB); |
| | | data->glBindTexture(texturedata->type, texturedata->vtexture); |
| | | |
| | | data->glActiveTextureARB(GL_TEXTURE1_ARB); |
| | | data->glBindTexture(texturedata->type, texturedata->utexture); |
| | | |
| | | data->glActiveTextureARB(GL_TEXTURE0_ARB); |
| | | } |
| | | if (texturedata->nv12) { |
| | | data->glActiveTextureARB(GL_TEXTURE1_ARB); |
| | | data->glBindTexture(texturedata->type, texturedata->utexture); |
| | | |
| | |
| | | |
| | | GL_SetBlendMode(data, texture->blendMode); |
| | | |
| | | if (texturedata->yuv) { |
| | | GL_SetShader(data, SHADER_YV12); |
| | | if (texturedata->yuv || texturedata->nv12) { |
| | | switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) { |
| | | case SDL_YUV_CONVERSION_JPEG: |
| | | if (texturedata->yuv) { |
| | | GL_SetShader(data, SHADER_YUV_JPEG); |
| | | } else if (texture->format == SDL_PIXELFORMAT_NV12) { |
| | | GL_SetShader(data, SHADER_NV12_JPEG); |
| | | } else { |
| | | GL_SetShader(data, SHADER_NV21_JPEG); |
| | | } |
| | | break; |
| | | case SDL_YUV_CONVERSION_BT601: |
| | | if (texturedata->yuv) { |
| | | GL_SetShader(data, SHADER_YUV_BT601); |
| | | } else if (texture->format == SDL_PIXELFORMAT_NV12) { |
| | | GL_SetShader(data, SHADER_NV12_BT601); |
| | | } else { |
| | | GL_SetShader(data, SHADER_NV21_BT601); |
| | | } |
| | | break; |
| | | case SDL_YUV_CONVERSION_BT709: |
| | | if (texturedata->yuv) { |
| | | GL_SetShader(data, SHADER_YUV_BT709); |
| | | } else if (texture->format == SDL_PIXELFORMAT_NV12) { |
| | | GL_SetShader(data, SHADER_NV12_BT709); |
| | | } else { |
| | | GL_SetShader(data, SHADER_NV21_BT709); |
| | | } |
| | | break; |
| | | default: |
| | | return SDL_SetError("Unsupported YUV conversion mode"); |
| | | } |
| | | } else { |
| | | GL_SetShader(data, SHADER_RGB); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | static int |
| | | GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, |
| | | const SDL_Rect * srcrect, const SDL_FRect * dstrect) |
| | | { |
| | | GL_RenderData *data = (GL_RenderData *) renderer->driverdata; |
| | | GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; |
| | | GLfloat minx, miny, maxx, maxy; |
| | | GLfloat minu, maxu, minv, maxv; |
| | | |
| | | GL_ActivateRenderer(renderer); |
| | | |
| | | if (GL_SetupCopy(renderer, texture) < 0) { |
| | | return -1; |
| | | } |
| | | |
| | | minx = dstrect->x; |
| | |
| | | |
| | | GL_ActivateRenderer(renderer); |
| | | |
| | | data->glEnable(texturedata->type); |
| | | if (texturedata->yuv) { |
| | | data->glActiveTextureARB(GL_TEXTURE2_ARB); |
| | | data->glBindTexture(texturedata->type, texturedata->vtexture); |
| | | |
| | | data->glActiveTextureARB(GL_TEXTURE1_ARB); |
| | | data->glBindTexture(texturedata->type, texturedata->utexture); |
| | | |
| | | data->glActiveTextureARB(GL_TEXTURE0_ARB); |
| | | } |
| | | data->glBindTexture(texturedata->type, texturedata->texture); |
| | | |
| | | if (texture->modMode) { |
| | | GL_SetColor(data, texture->r, texture->g, texture->b, texture->a); |
| | | } else { |
| | | GL_SetColor(data, 255, 255, 255, 255); |
| | | } |
| | | |
| | | GL_SetBlendMode(data, texture->blendMode); |
| | | |
| | | if (texturedata->yuv) { |
| | | GL_SetShader(data, SHADER_YV12); |
| | | } else { |
| | | GL_SetShader(data, SHADER_RGB); |
| | | if (GL_SetupCopy(renderer, texture) < 0) { |
| | | return -1; |
| | | } |
| | | |
| | | centerx = center->x; |
| | |
| | | Uint32 pixel_format, void * pixels, int pitch) |
| | | { |
| | | GL_RenderData *data = (GL_RenderData *) renderer->driverdata; |
| | | Uint32 temp_format = SDL_PIXELFORMAT_ARGB8888; |
| | | Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ARGB8888; |
| | | void *temp_pixels; |
| | | int temp_pitch; |
| | | GLint internalFormat; |
| | |
| | | |
| | | GL_ActivateRenderer(renderer); |
| | | |
| | | if (!convert_format(data, temp_format, &internalFormat, &format, &type)) { |
| | | return SDL_SetError("Texture format %s not supported by OpenGL", |
| | | SDL_GetPixelFormatName(temp_format)); |
| | | } |
| | | |
| | | if (!rect->w || !rect->h) { |
| | | return 0; /* nothing to do. */ |
| | | } |
| | | |
| | | temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format); |
| | | temp_pixels = SDL_malloc(rect->h * temp_pitch); |
| | | if (!temp_pixels) { |
| | | return SDL_OutOfMemory(); |
| | | } |
| | | |
| | | convert_format(data, temp_format, &internalFormat, &format, &type); |
| | | |
| | | SDL_GetRendererOutputSize(renderer, &w, &h); |
| | | |
| | |
| | | data->glPixelStorei(GL_PACK_ROW_LENGTH, |
| | | (temp_pitch / SDL_BYTESPERPIXEL(temp_format))); |
| | | |
| | | data->glReadPixels(rect->x, (h-rect->y)-rect->h, rect->w, rect->h, |
| | | format, type, temp_pixels); |
| | | data->glReadPixels(rect->x, renderer->target ? rect->y : (h-rect->y)-rect->h, |
| | | rect->w, rect->h, format, type, temp_pixels); |
| | | |
| | | if (GL_CheckError("glReadPixels()", renderer) < 0) { |
| | | SDL_free(temp_pixels); |
| | | return -1; |
| | | } |
| | | |
| | | /* Flip the rows to be top-down */ |
| | | length = rect->w * SDL_BYTESPERPIXEL(temp_format); |
| | | src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch; |
| | | dst = (Uint8*)temp_pixels; |
| | | tmp = SDL_stack_alloc(Uint8, length); |
| | | rows = rect->h / 2; |
| | | while (rows--) { |
| | | SDL_memcpy(tmp, dst, length); |
| | | SDL_memcpy(dst, src, length); |
| | | SDL_memcpy(src, tmp, length); |
| | | dst += temp_pitch; |
| | | src -= temp_pitch; |
| | | /* Flip the rows to be top-down if necessary */ |
| | | if (!renderer->target) { |
| | | length = rect->w * SDL_BYTESPERPIXEL(temp_format); |
| | | src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch; |
| | | dst = (Uint8*)temp_pixels; |
| | | tmp = SDL_stack_alloc(Uint8, length); |
| | | rows = rect->h / 2; |
| | | while (rows--) { |
| | | SDL_memcpy(tmp, dst, length); |
| | | SDL_memcpy(dst, src, length); |
| | | SDL_memcpy(src, tmp, length); |
| | | dst += temp_pitch; |
| | | src -= temp_pitch; |
| | | } |
| | | SDL_stack_free(tmp); |
| | | } |
| | | SDL_stack_free(tmp); |
| | | |
| | | status = SDL_ConvertPixels(rect->w, rect->h, |
| | | temp_format, temp_pixels, temp_pitch, |
| | |
| | | GL_RenderData *data = (GL_RenderData *) renderer->driverdata; |
| | | |
| | | if (data) { |
| | | if (data->context != NULL) { |
| | | /* make sure we delete the right resources! */ |
| | | GL_ActivateRenderer(renderer); |
| | | } |
| | | |
| | | GL_ClearErrors(renderer); |
| | | if (data->GL_ARB_debug_output_supported) { |
| | | PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC) SDL_GL_GetProcAddress("glDebugMessageCallbackARB"); |