mirror of https://github.com/encounter/SDL.git
Add SDL_UpdateNVTexture() to update NV12/21 Texture (bug #5430)
for renderer software, opengl, and opengles2
This commit is contained in:
parent
d72dbd9883
commit
be4cfd51c3
|
@ -450,6 +450,28 @@ extern DECLSPEC int SDLCALL SDL_UpdateYUVTexture(SDL_Texture * texture,
|
|||
const Uint8 *Uplane, int Upitch,
|
||||
const Uint8 *Vplane, int Vpitch);
|
||||
|
||||
/**
|
||||
* \brief Update a rectangle within a planar NV12 or NV21 texture with new pixel data.
|
||||
*
|
||||
* \param texture The texture to update
|
||||
* \param rect A pointer to the rectangle of pixels to update, or NULL to
|
||||
* update the entire texture.
|
||||
* \param Yplane The raw pixel data for the Y plane.
|
||||
* \param Ypitch The number of bytes between rows of pixel data for the Y plane.
|
||||
* \param UVplane The raw pixel data for the UV plane.
|
||||
* \param UVpitch The number of bytes between rows of pixel data for the UV plane.
|
||||
*
|
||||
* \return 0 on success, or -1 if the texture is not valid.
|
||||
*
|
||||
* \note You can use SDL_UpdateTexture() as long as your pixel data is
|
||||
* a contiguous block of NV12/21 planes in the proper order, but
|
||||
* this function is available if your pixel data is not contiguous.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDL_UpdateNVTexture(SDL_Texture * texture,
|
||||
const SDL_Rect * rect,
|
||||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *UVplane, int UVpitch);
|
||||
|
||||
/**
|
||||
* \brief Lock a portion of the texture for write-only pixel access.
|
||||
*
|
||||
|
|
|
@ -1628,6 +1628,59 @@ SDL_UpdateTextureYUVPlanar(SDL_Texture * texture, const SDL_Rect * rect,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
SDL_UpdateTextureNVPlanar(SDL_Texture * texture, const SDL_Rect * rect,
|
||||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *UVplane, int UVpitch)
|
||||
{
|
||||
SDL_Texture *native = texture->native;
|
||||
SDL_Rect full_rect;
|
||||
|
||||
if (SDL_SW_UpdateNVTexturePlanar(texture->yuv, rect, Yplane, Ypitch, UVplane, UVpitch) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
full_rect.x = 0;
|
||||
full_rect.y = 0;
|
||||
full_rect.w = texture->w;
|
||||
full_rect.h = texture->h;
|
||||
rect = &full_rect;
|
||||
|
||||
if (!rect->w || !rect->h) {
|
||||
return 0; /* nothing to do. */
|
||||
}
|
||||
|
||||
if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
|
||||
/* We can lock the texture and copy to it */
|
||||
void *native_pixels = NULL;
|
||||
int native_pitch = 0;
|
||||
|
||||
if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
|
||||
return -1;
|
||||
}
|
||||
SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
|
||||
rect->w, rect->h, native_pixels, native_pitch);
|
||||
SDL_UnlockTexture(native);
|
||||
} else {
|
||||
/* Use a temporary buffer for updating */
|
||||
const int temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
|
||||
const size_t alloclen = rect->h * temp_pitch;
|
||||
if (alloclen > 0) {
|
||||
void *temp_pixels = SDL_malloc(alloclen);
|
||||
if (!temp_pixels) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
|
||||
rect->w, rect->h, temp_pixels, temp_pitch);
|
||||
SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
|
||||
SDL_free(temp_pixels);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif /* SDL_HAVE_YUV */
|
||||
|
||||
int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect,
|
||||
|
@ -1697,6 +1750,68 @@ int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect,
|
|||
#endif
|
||||
}
|
||||
|
||||
int SDL_UpdateNVTexture(SDL_Texture * texture, const SDL_Rect * rect,
|
||||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *UVplane, int UVpitch)
|
||||
{
|
||||
#if SDL_HAVE_YUV
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Rect full_rect;
|
||||
|
||||
CHECK_TEXTURE_MAGIC(texture, -1);
|
||||
|
||||
if (!Yplane) {
|
||||
return SDL_InvalidParamError("Yplane");
|
||||
}
|
||||
if (!Ypitch) {
|
||||
return SDL_InvalidParamError("Ypitch");
|
||||
}
|
||||
if (!UVplane) {
|
||||
return SDL_InvalidParamError("UVplane");
|
||||
}
|
||||
if (!UVpitch) {
|
||||
return SDL_InvalidParamError("UVpitch");
|
||||
}
|
||||
|
||||
if (texture->format != SDL_PIXELFORMAT_NV12 &&
|
||||
texture->format != SDL_PIXELFORMAT_NV21) {
|
||||
return SDL_SetError("Texture format must by NV12 or NV21");
|
||||
}
|
||||
|
||||
if (!rect) {
|
||||
full_rect.x = 0;
|
||||
full_rect.y = 0;
|
||||
full_rect.w = texture->w;
|
||||
full_rect.h = texture->h;
|
||||
rect = &full_rect;
|
||||
}
|
||||
|
||||
if (!rect->w || !rect->h) {
|
||||
return 0; /* nothing to do. */
|
||||
}
|
||||
|
||||
if (texture->yuv) {
|
||||
return SDL_UpdateTextureNVPlanar(texture, rect, Yplane, Ypitch, UVplane, UVpitch);
|
||||
} else {
|
||||
SDL_assert(!texture->native);
|
||||
renderer = texture->renderer;
|
||||
SDL_assert(renderer->UpdateTextureNV);
|
||||
if (renderer->UpdateTextureNV) {
|
||||
if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return renderer->UpdateTextureNV(renderer, texture, rect, Yplane, Ypitch, UVplane, UVpitch);
|
||||
} else {
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
}
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if SDL_HAVE_YUV
|
||||
static int
|
||||
SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
|
||||
|
|
|
@ -136,6 +136,10 @@ struct SDL_Renderer
|
|||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *Uplane, int Upitch,
|
||||
const Uint8 *Vplane, int Vpitch);
|
||||
int (*UpdateTextureNV) (SDL_Renderer * renderer, SDL_Texture * texture,
|
||||
const SDL_Rect * rect,
|
||||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *UVplane, int UVpitch);
|
||||
int (*LockTexture) (SDL_Renderer * renderer, SDL_Texture * texture,
|
||||
const SDL_Rect * rect, void **pixels, int *pitch);
|
||||
void (*UnlockTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
|
||||
|
|
|
@ -299,6 +299,40 @@ SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int SDL_SW_UpdateNVTexturePlanar(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
||||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *UVplane, int UVpitch)
|
||||
{
|
||||
const Uint8 *src;
|
||||
Uint8 *dst;
|
||||
int row;
|
||||
size_t length;
|
||||
|
||||
/* Copy the Y plane */
|
||||
src = Yplane;
|
||||
dst = swdata->pixels + rect->y * swdata->w + rect->x;
|
||||
length = rect->w;
|
||||
for (row = 0; row < rect->h; ++row) {
|
||||
SDL_memcpy(dst, src, length);
|
||||
src += Ypitch;
|
||||
dst += swdata->w;
|
||||
}
|
||||
|
||||
/* Copy the UV or VU plane */
|
||||
src = UVplane;
|
||||
dst = swdata->pixels + swdata->h * swdata->w;
|
||||
dst += rect->y/2 * ((swdata->w + 1)/2) + rect->x/2;
|
||||
length = (rect->w + 1) / 2;
|
||||
length *= 2;
|
||||
for (row = 0; row < (rect->h + 1)/2; ++row) {
|
||||
SDL_memcpy(dst, src, length);
|
||||
src += UVpitch;
|
||||
dst += 2 * ((swdata->w + 1)/2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
SDL_SW_LockYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
||||
void **pixels, int *pitch)
|
||||
|
|
|
@ -55,6 +55,9 @@ int SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture * swdata, const SDL_Rect * r
|
|||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *Uplane, int Upitch,
|
||||
const Uint8 *Vplane, int Vpitch);
|
||||
int SDL_SW_UpdateNVTexturePlanar(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
||||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *UVplane, int UVpitch);
|
||||
int SDL_SW_LockYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
||||
void **pixels, int *pitch);
|
||||
void SDL_SW_UnlockYUVTexture(SDL_SW_YUVTexture * swdata);
|
||||
|
|
|
@ -728,6 +728,38 @@ GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
|
|||
return GL_CheckError("glTexSubImage2D()", renderer);
|
||||
}
|
||||
|
||||
static int
|
||||
GL_UpdateTextureNV(SDL_Renderer * renderer, SDL_Texture * texture,
|
||||
const SDL_Rect * rect,
|
||||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *UVplane, int UVpitch)
|
||||
{
|
||||
GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
|
||||
const GLenum textype = renderdata->textype;
|
||||
GL_TextureData *data = (GL_TextureData *) texture->driverdata;
|
||||
|
||||
GL_ActivateRenderer(renderer);
|
||||
|
||||
renderdata->drawstate.texture = NULL; /* we trash this state. */
|
||||
|
||||
renderdata->glBindTexture(textype, data->texture);
|
||||
renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Ypitch);
|
||||
renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w,
|
||||
rect->h, data->format, data->formattype,
|
||||
Yplane);
|
||||
|
||||
|
||||
renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, UVpitch / 2);
|
||||
renderdata->glBindTexture(textype, data->utexture);
|
||||
renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2,
|
||||
(rect->w + 1)/2, (rect->h + 1)/2,
|
||||
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, UVplane);
|
||||
|
||||
return GL_CheckError("glTexSubImage2D()", renderer);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
|
||||
const SDL_Rect * rect, void **pixels, int *pitch)
|
||||
|
@ -1590,6 +1622,7 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
|
|||
renderer->CreateTexture = GL_CreateTexture;
|
||||
renderer->UpdateTexture = GL_UpdateTexture;
|
||||
renderer->UpdateTextureYUV = GL_UpdateTextureYUV;
|
||||
renderer->UpdateTextureNV = GL_UpdateTextureNV;
|
||||
renderer->LockTexture = GL_LockTexture;
|
||||
renderer->UnlockTexture = GL_UnlockTexture;
|
||||
renderer->SetTextureScaleMode = GL_SetTextureScaleMode;
|
||||
|
|
|
@ -1748,6 +1748,48 @@ GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
|
|||
return GL_CheckError("glTexSubImage2D()", renderer);
|
||||
}
|
||||
|
||||
static int
|
||||
GLES2_UpdateTextureNV(SDL_Renderer * renderer, SDL_Texture * texture,
|
||||
const SDL_Rect * rect,
|
||||
const Uint8 *Yplane, int Ypitch,
|
||||
const Uint8 *UVplane, int UVpitch)
|
||||
{
|
||||
GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata;
|
||||
GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
|
||||
|
||||
GLES2_ActivateRenderer(renderer);
|
||||
|
||||
/* Bail out if we're supposed to update an empty rectangle */
|
||||
if (rect->w <= 0 || rect->h <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data->drawstate.texture = NULL; /* we trash this state. */
|
||||
|
||||
data->glBindTexture(tdata->texture_type, tdata->texture_u);
|
||||
GLES2_TexSubImage2D(data, tdata->texture_type,
|
||||
rect->x / 2,
|
||||
rect->y / 2,
|
||||
(rect->w + 1) / 2,
|
||||
(rect->h + 1) / 2,
|
||||
GL_LUMINANCE_ALPHA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
UVplane, UVpitch / 2, 1);
|
||||
|
||||
data->glBindTexture(tdata->texture_type, tdata->texture);
|
||||
GLES2_TexSubImage2D(data, tdata->texture_type,
|
||||
rect->x,
|
||||
rect->y,
|
||||
rect->w,
|
||||
rect->h,
|
||||
tdata->pixel_format,
|
||||
tdata->pixel_type,
|
||||
Yplane, Ypitch, 1);
|
||||
|
||||
return GL_CheckError("glTexSubImage2D()", renderer);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
|
||||
void **pixels, int *pitch)
|
||||
|
@ -2126,6 +2168,7 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
|
|||
renderer->CreateTexture = GLES2_CreateTexture;
|
||||
renderer->UpdateTexture = GLES2_UpdateTexture;
|
||||
renderer->UpdateTextureYUV = GLES2_UpdateTextureYUV;
|
||||
renderer->UpdateTextureNV = GLES2_UpdateTextureNV;
|
||||
renderer->LockTexture = GLES2_LockTexture;
|
||||
renderer->UnlockTexture = GLES2_UnlockTexture;
|
||||
renderer->SetTextureScaleMode = GLES2_SetTextureScaleMode;
|
||||
|
|
Loading…
Reference in New Issue