From 6fef39d6b8f53906bb42eec1a818b5023dfca887 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 6 Aug 2014 11:34:54 -0700 Subject: [PATCH] Added NV12 and NV21 texture support for OpenGL and OpenGL ES 2.0 renderers --- include/SDL_pixels.h | 6 +- src/render/opengl/SDL_render_gl.c | 116 +++++++---- src/render/opengl/SDL_shaders_gl.c | 102 +++++++++- src/render/opengl/SDL_shaders_gl.h | 4 +- src/render/opengles2/SDL_render_gles2.c | 233 ++++++++++------------- src/render/opengles2/SDL_shaders_gles2.c | 82 ++++++++ src/render/opengles2/SDL_shaders_gles2.h | 4 +- src/test/SDL_test_common.c | 6 + src/video/SDL_pixels.c | 2 + src/video/SDL_surface.c | 35 +++- test/testautomation_pixels.c | 10 +- test/testoverlay2.c | 39 +++- 12 files changed, 456 insertions(+), 183 deletions(-) diff --git a/include/SDL_pixels.h b/include/SDL_pixels.h index 3131af7b74251..61e97c8d2751f 100644 --- a/include/SDL_pixels.h +++ b/include/SDL_pixels.h @@ -248,7 +248,11 @@ enum SDL_PIXELFORMAT_UYVY = /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */ SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'), SDL_PIXELFORMAT_YVYU = /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */ - SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U') + SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U'), + SDL_PIXELFORMAT_NV12 = /**< Planar mode: Y + U/V interleaved (2 planes) */ + SDL_DEFINE_PIXELFOURCC('N', 'V', '1', '2'), + SDL_PIXELFORMAT_NV21 = /**< Planar mode: Y + V/U interleaved (2 planes) */ + SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1') }; typedef struct SDL_Color diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index e1c78cb8ba179..e3a6b55bc6896 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -164,8 +164,9 @@ typedef struct int pitch; SDL_Rect locked_rect; - /* YV12 texture support */ + /* YUV texture support */ SDL_bool yuv; + SDL_bool nv12; GLuint utexture; GLuint vtexture; @@ -531,6 +532,8 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) 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__ @@ -611,16 +614,18 @@ convert_format(GL_RenderData *renderdata, Uint32 pixel_format, 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; @@ -672,6 +677,11 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) /* Need to add size for the U and V planes */ size += (2 * (texture->h * data->pitch) / 4); } + if (texture->format == SDL_PIXELFORMAT_NV12 || + texture->format == SDL_PIXELFORMAT_NV21) { + /* Need to add size for the U/V plane */ + size += ((texture->h * data->pitch) / 2); + } data->pixels = SDL_calloc(1, size); if (!data->pixels) { SDL_free(data); @@ -801,6 +811,27 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) 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/2, + texture_h/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); + renderdata->glDisable(data->type); + } + return GL_CheckError("", renderer); } @@ -848,6 +879,17 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, rect->w/2, rect->h/2, data->format, data->formattype, pixels); } + + if (data->nv12) { + renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 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, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels); + } renderdata->glDisable(data->type); return GL_CheckError("glTexSubImage2D()", renderer); @@ -1184,15 +1226,10 @@ GL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count) } 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) { @@ -1204,6 +1241,12 @@ GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, data->glActiveTextureARB(GL_TEXTURE0_ARB); } + if (texturedata->nv12) { + 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) { @@ -1215,10 +1258,33 @@ GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, GL_SetBlendMode(data, texture->blendMode); if (texturedata->yuv) { - GL_SetShader(data, SHADER_YV12); + GL_SetShader(data, SHADER_YUV); + } else if (texturedata->nv12) { + if (texture->format == SDL_PIXELFORMAT_NV12) { + GL_SetShader(data, SHADER_NV12); + } else { + GL_SetShader(data, SHADER_NV21); + } } 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; miny = dstrect->y; @@ -1263,30 +1329,8 @@ GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, 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; diff --git a/src/render/opengl/SDL_shaders_gl.c b/src/render/opengl/SDL_shaders_gl.c index b10a1a492e215..616f64aace459 100644 --- a/src/render/opengl/SDL_shaders_gl.c +++ b/src/render/opengl/SDL_shaders_gl.c @@ -113,7 +113,7 @@ static const char *shader_source[NUM_SHADERS][2] = "}" }, - /* SHADER_YV12 */ + /* SHADER_YUV */ { /* vertex shader */ "varying vec4 v_color;\n" @@ -162,6 +162,106 @@ static const char *shader_source[NUM_SHADERS][2] = "\n" " // That was easy. :) \n" " gl_FragColor = vec4(rgb, 1.0) * v_color;\n" +"}" + }, + + /* SHADER_NV12 */ + { + /* vertex shader */ +"varying vec4 v_color;\n" +"varying vec2 v_texCoord;\n" +"\n" +"void main()\n" +"{\n" +" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +" v_color = gl_Color;\n" +" v_texCoord = vec2(gl_MultiTexCoord0);\n" +"}", + /* fragment shader */ +"varying vec4 v_color;\n" +"varying vec2 v_texCoord;\n" +"uniform sampler2D tex0; // Y \n" +"uniform sampler2D tex1; // U/V \n" +"\n" +"// YUV offset \n" +"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" +"\n" +"// RGB coefficients \n" +"const vec3 Rcoeff = vec3(1.164, 0.000, 1.596);\n" +"const vec3 Gcoeff = vec3(1.164, -0.391, -0.813);\n" +"const vec3 Bcoeff = vec3(1.164, 2.018, 0.000);\n" +"\n" +"void main()\n" +"{\n" +" vec2 tcoord;\n" +" vec3 yuv, rgb;\n" +"\n" +" // Get the Y value \n" +" tcoord = v_texCoord;\n" +" yuv.x = texture2D(tex0, tcoord).r;\n" +"\n" +" // Get the U and V values \n" +" tcoord *= 0.5;\n" +" yuv.yz = texture2D(tex1, tcoord).ra;\n" +"\n" +" // Do the color transform \n" +" yuv += offset;\n" +" rgb.r = dot(yuv, Rcoeff);\n" +" rgb.g = dot(yuv, Gcoeff);\n" +" rgb.b = dot(yuv, Bcoeff);\n" +"\n" +" // That was easy. :) \n" +" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" +"}" + }, + + /* SHADER_NV21 */ + { + /* vertex shader */ +"varying vec4 v_color;\n" +"varying vec2 v_texCoord;\n" +"\n" +"void main()\n" +"{\n" +" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +" v_color = gl_Color;\n" +" v_texCoord = vec2(gl_MultiTexCoord0);\n" +"}", + /* fragment shader */ +"varying vec4 v_color;\n" +"varying vec2 v_texCoord;\n" +"uniform sampler2D tex0; // Y \n" +"uniform sampler2D tex1; // U/V \n" +"\n" +"// YUV offset \n" +"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" +"\n" +"// RGB coefficients \n" +"const vec3 Rcoeff = vec3(1.164, 0.000, 1.596);\n" +"const vec3 Gcoeff = vec3(1.164, -0.391, -0.813);\n" +"const vec3 Bcoeff = vec3(1.164, 2.018, 0.000);\n" +"\n" +"void main()\n" +"{\n" +" vec2 tcoord;\n" +" vec3 yuv, rgb;\n" +"\n" +" // Get the Y value \n" +" tcoord = v_texCoord;\n" +" yuv.x = texture2D(tex0, tcoord).r;\n" +"\n" +" // Get the U and V values \n" +" tcoord *= 0.5;\n" +" yuv.yz = texture2D(tex1, tcoord).ar;\n" +"\n" +" // Do the color transform \n" +" yuv += offset;\n" +" rgb.r = dot(yuv, Rcoeff);\n" +" rgb.g = dot(yuv, Gcoeff);\n" +" rgb.b = dot(yuv, Bcoeff);\n" +"\n" +" // That was easy. :) \n" +" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" "}" }, }; diff --git a/src/render/opengl/SDL_shaders_gl.h b/src/render/opengl/SDL_shaders_gl.h index fdd4db7fc04db..4604f05f7d11a 100644 --- a/src/render/opengl/SDL_shaders_gl.h +++ b/src/render/opengl/SDL_shaders_gl.h @@ -26,7 +26,9 @@ typedef enum { SHADER_NONE, SHADER_SOLID, SHADER_RGB, - SHADER_YV12, + SHADER_YUV, + SHADER_NV12, + SHADER_NV21, NUM_SHADERS } GL_Shader; diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 604b93fc7ace8..a32c738a31fd6 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -81,8 +81,9 @@ typedef struct GLES2_TextureData GLenum pixel_type; void *pixel_data; int pitch; - /* YV12 texture support */ + /* YUV texture support */ SDL_bool yuv; + SDL_bool nv12; GLenum texture_v; GLenum texture_u; GLES2_FBOList *fbo; @@ -151,7 +152,9 @@ typedef enum GLES2_IMAGESOURCE_TEXTURE_ARGB, GLES2_IMAGESOURCE_TEXTURE_RGB, GLES2_IMAGESOURCE_TEXTURE_BGR, - GLES2_IMAGESOURCE_TEXTURE_YUV + GLES2_IMAGESOURCE_TEXTURE_YUV, + GLES2_IMAGESOURCE_TEXTURE_NV12, + GLES2_IMAGESOURCE_TEXTURE_NV21 } GLES2_ImageSource; typedef struct GLES2_DriverContext @@ -488,6 +491,8 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) break; case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: format = GL_LUMINANCE; type = GL_UNSIGNED_BYTE; break; @@ -505,6 +510,7 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) data->pixel_format = format; data->pixel_type = type; data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12)); + data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21)); data->texture_u = 0; data->texture_v = 0; scaleMode = GetScaleQuality(); @@ -518,6 +524,10 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) /* Need to add size for the U and V planes */ size += (2 * (texture->h * data->pitch) / 4); } + if (data->nv12) { + /* Need to add size for the U/V plane */ + size += ((texture->h * data->pitch) / 2); + } data->pixel_data = SDL_calloc(1, size); if (!data->pixel_data) { SDL_free(data); @@ -557,6 +567,23 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) } } + if (data->nv12) { + renderdata->glGenTextures(1, &data->texture_u); + if (GL_CheckError("glGenTexures()", renderer) < 0) { + return -1; + } + renderdata->glActiveTexture(GL_TEXTURE1); + renderdata->glBindTexture(data->texture_type, data->texture_u); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + renderdata->glTexImage2D(data->texture_type, 0, GL_LUMINANCE_ALPHA, texture->w / 2, texture->h / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); + if (GL_CheckError("glTexImage2D()", renderer) < 0) { + return -1; + } + } + renderdata->glGenTextures(1, &data->texture); if (GL_CheckError("glGenTexures()", renderer) < 0) { return -1; @@ -673,6 +700,20 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect pixels, pitch / 2, 1); } + if (tdata->nv12) { + /* Skip to the correct offset into the next texture */ + pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); + data->glBindTexture(tdata->texture_type, tdata->texture_u); + GLES2_TexSubImage2D(data, tdata->texture_type, + rect->x / 2, + rect->y / 2, + rect->w / 2, + rect->h / 2, + GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, + pixels, pitch, 2); + } + return GL_CheckError("glTexSubImage2D()", renderer); } @@ -1093,6 +1134,12 @@ GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, SDL_BlendM case GLES2_IMAGESOURCE_TEXTURE_YUV: ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC; break; + case GLES2_IMAGESOURCE_TEXTURE_NV12: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV12_SRC; + break; + case GLES2_IMAGESOURCE_TEXTURE_NV21: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV21_SRC; + break; default: goto fault; } @@ -1432,20 +1479,15 @@ GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) } static int -GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, - const SDL_FRect *dstrect) +GLES2_SetupCopy(SDL_Renderer *renderer, SDL_Texture *texture) { GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; SDL_BlendMode blendMode; - GLfloat vertices[8]; - GLfloat texCoords[8]; GLES2_ProgramCacheEntry *program; Uint8 r, g, b, a; - GLES2_ActivateRenderer(renderer); - /* Activate an appropriate shader and set the projection matrix */ blendMode = texture->blendMode; if (renderer->target) { @@ -1505,11 +1547,22 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s break; } break; + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_YV12: + sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV; + break; + case SDL_PIXELFORMAT_NV12: + sourceType = GLES2_IMAGESOURCE_TEXTURE_NV12; + break; + case SDL_PIXELFORMAT_NV21: + sourceType = GLES2_IMAGESOURCE_TEXTURE_NV21; + break; + default: + return SDL_SetError("Unsupported texture format"); } } else sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; /* Texture formats match, use the non color mapping shader (even if the formats are not ABGR) */ - } - else { + } else { switch (texture->format) { case SDL_PIXELFORMAT_ARGB8888: @@ -1524,13 +1577,18 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s case SDL_PIXELFORMAT_BGR888: sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; break; - // TODO: new shader to change yv planes YV12 format case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV; break; + case SDL_PIXELFORMAT_NV12: + sourceType = GLES2_IMAGESOURCE_TEXTURE_NV12; + break; + case SDL_PIXELFORMAT_NV21: + sourceType = GLES2_IMAGESOURCE_TEXTURE_NV21; + break; default: - return -1; + return SDL_SetError("Unsupported texture format"); } } @@ -1548,6 +1606,12 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s data->glActiveTexture(GL_TEXTURE0); } + if (tdata->nv12) { + data->glActiveTexture(GL_TEXTURE1); + data->glBindTexture(tdata->texture_type, tdata->texture_u); + + data->glActiveTexture(GL_TEXTURE0); + } data->glBindTexture(tdata->texture_type, tdata->texture); /* Configure color modulation */ @@ -1578,6 +1642,23 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s GLES2_SetBlendMode(data, blendMode); GLES2_SetTexCoords(data, SDL_TRUE); + return 0; +} + +static int +GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, + const SDL_FRect *dstrect) +{ + GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; + GLfloat vertices[8]; + GLfloat texCoords[8]; + + GLES2_ActivateRenderer(renderer); + + if (GLES2_SetupCopy(renderer, texture) < 0) { + return -1; + } /* Emit the textured quad */ vertices[0] = dstrect->x; @@ -1609,10 +1690,6 @@ GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect { GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; - GLES2_ProgramCacheEntry *program; - Uint8 r, g, b, a; - SDL_BlendMode blendMode; GLfloat vertices[8]; GLfloat texCoords[8]; GLfloat translate[8]; @@ -1621,6 +1698,10 @@ GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect GLES2_ActivateRenderer(renderer); + if (GLES2_SetupCopy(renderer, texture) < 0) { + return -1; + } + data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_CENTER); data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE); fAngle[0] = fAngle[1] = fAngle[2] = fAngle[3] = (GLfloat)(360.0f - angle); @@ -1628,124 +1709,6 @@ GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect translate[0] = translate[2] = translate[4] = translate[6] = (center->x + dstrect->x); translate[1] = translate[3] = translate[5] = translate[7] = (center->y + dstrect->y); - /* Activate an appropriate shader and set the projection matrix */ - blendMode = texture->blendMode; - if (renderer->target) { - /* Check if we need to do color mapping between the source and render target textures */ - if (renderer->target->format != texture->format) { - switch (texture->format) - { - case SDL_PIXELFORMAT_ARGB8888: - switch (renderer->target->format) - { - case SDL_PIXELFORMAT_ABGR8888: - case SDL_PIXELFORMAT_BGR888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; - break; - case SDL_PIXELFORMAT_RGB888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; - break; - } - break; - case SDL_PIXELFORMAT_ABGR8888: - switch (renderer->target->format) - { - case SDL_PIXELFORMAT_ARGB8888: - case SDL_PIXELFORMAT_RGB888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; - break; - case SDL_PIXELFORMAT_BGR888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; - break; - } - break; - case SDL_PIXELFORMAT_RGB888: - switch (renderer->target->format) - { - case SDL_PIXELFORMAT_ABGR8888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; - break; - case SDL_PIXELFORMAT_ARGB8888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; - break; - case SDL_PIXELFORMAT_BGR888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; - break; - } - break; - case SDL_PIXELFORMAT_BGR888: - switch (renderer->target->format) - { - case SDL_PIXELFORMAT_ABGR8888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; - break; - case SDL_PIXELFORMAT_ARGB8888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB; - break; - case SDL_PIXELFORMAT_RGB888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; - break; - } - break; - } - } - else sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; /* Texture formats match, use the non color mapping shader (even if the formats are not ABGR) */ - } - else { - switch (texture->format) - { - case SDL_PIXELFORMAT_ARGB8888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; - break; - case SDL_PIXELFORMAT_ABGR8888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; - break; - case SDL_PIXELFORMAT_RGB888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB; - break; - case SDL_PIXELFORMAT_BGR888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; - break; - default: - return -1; - } - } - if (GLES2_SelectProgram(renderer, sourceType, blendMode) < 0) - return -1; - - /* Select the target texture */ - data->glBindTexture(tdata->texture_type, tdata->texture); - - /* Configure color modulation */ - /* !!! FIXME: grep for glUniform4f(), move that stuff to a subroutine, it's a lot of copy/paste. */ - g = texture->g; - a = texture->a; - - if (renderer->target && - (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 || - renderer->target->format == SDL_PIXELFORMAT_RGB888)) { - r = texture->b; - b = texture->r; - } else { - r = texture->r; - b = texture->b; - } - - program = data->current_program; - - if (!CompareColors(program->modulation_r, program->modulation_g, program->modulation_b, program->modulation_a, r, g, b, a)) { - data->glUniform4f(program->uniform_locations[GLES2_UNIFORM_MODULATION], r * inv255f, g * inv255f, b * inv255f, a * inv255f); - program->modulation_r = r; - program->modulation_g = g; - program->modulation_b = b; - program->modulation_a = a; - } - - /* Configure texture blending */ - GLES2_SetBlendMode(data, blendMode); - - GLES2_SetTexCoords(data, SDL_TRUE); - /* Emit the textured quad */ vertices[0] = dstrect->x; vertices[1] = dstrect->y; @@ -2066,6 +2029,8 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) 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; GLES2_ResetState(renderer); diff --git a/src/render/opengles2/SDL_shaders_gles2.c b/src/render/opengles2/SDL_shaders_gles2.c index bdc14aeda0d61..48040f968d737 100644 --- a/src/render/opengles2/SDL_shaders_gles2.c +++ b/src/render/opengles2/SDL_shaders_gles2.c @@ -150,6 +150,50 @@ static const Uint8 GLES2_FragmentSrc_TextureYUVSrc_[] = " \ } \ "; +/* NV12 to ABGR conversion */ +static const Uint8 GLES2_FragmentSrc_TextureNV12Src_[] = " \ + precision mediump float; \ + uniform sampler2D u_texture; \ + uniform sampler2D u_texture_u; \ + uniform vec4 u_modulation; \ + varying vec2 v_texCoord; \ + \ + void main() \ + { \ + mediump vec3 yuv; \ + lowp vec3 rgb; \ + yuv.x = texture2D(u_texture, v_texCoord).r; \ + yuv.yz = texture2D(u_texture_u, v_texCoord).ra - 0.5; \ + rgb = mat3( 1, 1, 1, \ + 0, -0.39465, 2.03211, \ + 1.13983, -0.58060, 0) * yuv; \ + gl_FragColor = vec4(rgb, 1); \ + gl_FragColor *= u_modulation; \ + } \ +"; + +/* NV21 to ABGR conversion */ +static const Uint8 GLES2_FragmentSrc_TextureNV21Src_[] = " \ + precision mediump float; \ + uniform sampler2D u_texture; \ + uniform sampler2D u_texture_u; \ + uniform vec4 u_modulation; \ + varying vec2 v_texCoord; \ + \ + void main() \ + { \ + mediump vec3 yuv; \ + lowp vec3 rgb; \ + yuv.x = texture2D(u_texture, v_texCoord).r; \ + yuv.yz = texture2D(u_texture_u, v_texCoord).ar - 0.5; \ + rgb = mat3( 1, 1, 1, \ + 0, -0.39465, 2.03211, \ + 1.13983, -0.58060, 0) * yuv; \ + gl_FragColor = vec4(rgb, 1); \ + gl_FragColor *= u_modulation; \ + } \ +"; + static const GLES2_ShaderInstance GLES2_VertexSrc_Default = { GL_VERTEX_SHADER, GLES2_SOURCE_SHADER, @@ -199,6 +243,20 @@ static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureYUVSrc = { GLES2_FragmentSrc_TextureYUVSrc_ }; +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV12Src = { + GL_FRAGMENT_SHADER, + GLES2_SOURCE_SHADER, + sizeof(GLES2_FragmentSrc_TextureNV12Src_), + GLES2_FragmentSrc_TextureNV12Src_ +}; + +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV21Src = { + GL_FRAGMENT_SHADER, + GLES2_SOURCE_SHADER, + sizeof(GLES2_FragmentSrc_TextureNV21Src_), + GLES2_FragmentSrc_TextureNV21Src_ +}; + /************************************************************************************************* * Vertex/fragment shader binaries (NVIDIA Tegra 1/2) * @@ -731,6 +789,20 @@ static GLES2_Shader GLES2_FragmentShader_TextureYUVSrc = { } }; +static GLES2_Shader GLES2_FragmentShader_TextureNV12Src = { + 1, + { + &GLES2_FragmentSrc_TextureNV12Src + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureNV21Src = { + 1, + { + &GLES2_FragmentSrc_TextureNV21Src + } +}; + /************************************************************************************************* * Shader selector * @@ -820,6 +892,16 @@ const GLES2_Shader *GLES2_GetShader(GLES2_ShaderType type, SDL_BlendMode blendMo return &GLES2_FragmentShader_TextureYUVSrc; } + case GLES2_SHADER_FRAGMENT_TEXTURE_NV12_SRC: + { + return &GLES2_FragmentShader_TextureNV12Src; + } + + case GLES2_SHADER_FRAGMENT_TEXTURE_NV21_SRC: + { + return &GLES2_FragmentShader_TextureNV21Src; + } + default: return NULL; } diff --git a/src/render/opengles2/SDL_shaders_gles2.h b/src/render/opengles2/SDL_shaders_gles2.h index 51bbbcc401d8e..d68f7d05cc889 100644 --- a/src/render/opengles2/SDL_shaders_gles2.h +++ b/src/render/opengles2/SDL_shaders_gles2.h @@ -47,7 +47,9 @@ typedef enum GLES2_SHADER_FRAGMENT_TEXTURE_ARGB_SRC, GLES2_SHADER_FRAGMENT_TEXTURE_BGR_SRC, GLES2_SHADER_FRAGMENT_TEXTURE_RGB_SRC, - GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC + GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_NV12_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_NV21_SRC } GLES2_ShaderType; #define GLES2_SOURCE_SHADER (GLenum)-1 diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index a2fe31984fb14..d05dda47b5603 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -572,6 +572,12 @@ SDLTest_PrintPixelFormat(Uint32 format) case SDL_PIXELFORMAT_YVYU: fprintf(stderr, "YVYU"); break; + case SDL_PIXELFORMAT_NV12: + fprintf(stderr, "NV12"); + break; + case SDL_PIXELFORMAT_NV21: + fprintf(stderr, "NV21"); + break; default: fprintf(stderr, "0x%8.8x", format); break; diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 9eb507e139ce6..0858daade8d9a 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -122,6 +122,8 @@ SDL_GetPixelFormatName(Uint32 format) CASE(SDL_PIXELFORMAT_YUY2) CASE(SDL_PIXELFORMAT_UYVY) CASE(SDL_PIXELFORMAT_YVYU) + CASE(SDL_PIXELFORMAT_NV12) + CASE(SDL_PIXELFORMAT_NV21) #undef CASE default: return "SDL_PIXELFORMAT_UNKNOWN"; diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index ccf89ca1c69b7..421e2b806a001 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -1005,7 +1005,7 @@ int SDL_ConvertPixels(int width, int height, SDL_Rect rect; void *nonconst_src = (void *) src; - /* Check to make sure we are bliting somewhere, so we don't crash */ + /* Check to make sure we are blitting somewhere, so we don't crash */ if (!dst) { return SDL_InvalidParamError("dst"); } @@ -1015,17 +1015,21 @@ int SDL_ConvertPixels(int width, int height, /* Fast path for same format copy */ if (src_format == dst_format) { - int bpp; + int bpp, i; if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { switch (src_format) { - case SDL_PIXELFORMAT_YV12: - case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YUY2: case SDL_PIXELFORMAT_UYVY: case SDL_PIXELFORMAT_YVYU: bpp = 2; break; + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + bpp = 1; + break; default: return SDL_SetError("Unknown FOURCC pixel format"); } @@ -1034,11 +1038,32 @@ int SDL_ConvertPixels(int width, int height, } width *= bpp; - while (height-- > 0) { + for (i = height; i--;) { SDL_memcpy(dst, src, width); src = (Uint8*)src + src_pitch; dst = (Uint8*)dst + dst_pitch; } + + if (src_format == SDL_PIXELFORMAT_YV12 || src_format == SDL_PIXELFORMAT_IYUV) { + /* U and V planes are a quarter the size of the Y plane */ + width /= 2; + height /= 2; + src_pitch /= 2; + dst_pitch /= 2; + for (i = height * 2; i--;) { + SDL_memcpy(dst, src, width); + src = (Uint8*)src + src_pitch; + dst = (Uint8*)dst + dst_pitch; + } + } else if (src_format == SDL_PIXELFORMAT_NV12 || src_format == SDL_PIXELFORMAT_NV21) { + /* U/V plane is half the height of the Y plane */ + height /= 2; + for (i = height; i--;) { + SDL_memcpy(dst, src, width); + src = (Uint8*)src + src_pitch; + dst = (Uint8*)dst + dst_pitch; + } + } return 0; } diff --git a/test/testautomation_pixels.c b/test/testautomation_pixels.c index 04e00deb27af5..7b88caaa9da6b 100644 --- a/test/testautomation_pixels.c +++ b/test/testautomation_pixels.c @@ -79,14 +79,16 @@ char* _RGBPixelFormatsVerbose[] = }; /* Definition of all Non-RGB formats used to test pixel conversions */ -const int _numNonRGBPixelFormats = 5; +const int _numNonRGBPixelFormats = 7; Uint32 _nonRGBPixelFormats[] = { SDL_PIXELFORMAT_YV12, SDL_PIXELFORMAT_IYUV, SDL_PIXELFORMAT_YUY2, SDL_PIXELFORMAT_UYVY, - SDL_PIXELFORMAT_YVYU + SDL_PIXELFORMAT_YVYU, + SDL_PIXELFORMAT_NV12, + SDL_PIXELFORMAT_NV21 }; char* _nonRGBPixelFormatsVerbose[] = { @@ -94,7 +96,9 @@ char* _nonRGBPixelFormatsVerbose[] = "SDL_PIXELFORMAT_IYUV", "SDL_PIXELFORMAT_YUY2", "SDL_PIXELFORMAT_UYVY", - "SDL_PIXELFORMAT_YVYU" + "SDL_PIXELFORMAT_YVYU", + "SDL_PIXELFORMAT_NV12", + "SDL_PIXELFORMAT_NV21" }; /* Definition of some invalid formats for negative tests */ diff --git a/test/testoverlay2.c b/test/testoverlay2.c index 441e1f338426e..d9bd827376998 100644 --- a/test/testoverlay2.c +++ b/test/testoverlay2.c @@ -206,6 +206,29 @@ ConvertRGBtoYV12(Uint8 *rgb, Uint8 *out, int w, int h, } } +void +ConvertRGBtoNV12(Uint8 *rgb, Uint8 *out, int w, int h, + int monochrome, int luminance) +{ + int x, y; + int yuv[3]; + Uint8 *op[2]; + + op[0] = out; + op[1] = op[0] + w*h; + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + RGBtoYUV(rgb, yuv, monochrome, luminance); + *(op[0]++) = yuv[0]; + if (x % 2 == 0 && y % 2 == 0) { + *(op[1]++) = yuv[1]; + *(op[1]++) = yuv[2]; + } + rgb += 3; + } + } +} + static void PrintUsage(char *argv0) { @@ -241,7 +264,11 @@ main(int argc, char **argv) int fps = 12; int fpsdelay; int nodelay = 0; +#ifdef TEST_NV12 + Uint32 pixel_format = SDL_PIXELFORMAT_NV12; +#else Uint32 pixel_format = SDL_PIXELFORMAT_YV12; +#endif int scale = 5; SDL_bool done = SDL_FALSE; @@ -371,7 +398,17 @@ main(int argc, char **argv) rgb[2] = MooseColors[frame[j]].b; rgb += 3; } - ConvertRGBtoYV12(MooseFrameRGB, MooseFrame[i], MOOSEPIC_W, MOOSEPIC_H, 0, 100); + switch (pixel_format) { + case SDL_PIXELFORMAT_YV12: + ConvertRGBtoYV12(MooseFrameRGB, MooseFrame[i], MOOSEPIC_W, MOOSEPIC_H, 0, 100); + break; + case SDL_PIXELFORMAT_NV12: + ConvertRGBtoNV12(MooseFrameRGB, MooseFrame[i], MOOSEPIC_W, MOOSEPIC_H, 0, 100); + break; + default: + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unsupported pixel format\n"); + break; + } } free(RawMooseData);