1
0
mirror of https://github.com/Relintai/sdl2_frt.git synced 2024-12-20 22:16:49 +01:00

Fixed bug 3857 - SDL_ConvertPixels misses YUV conversions

Sylvain

Few issues with YUV on SDL2 when using odd dimensions, and missing conversions from/back to YUV formats.

1) The big part is that SDL_ConvertPixels() does not convert to/from YUV in most cases. This now works with any format and also with odd dimensions,
  by adding two internal functions SDL_ConvertPixels_YUV_to_ARGB8888 and SDL_ConvertPixels_ARGB8888_to_YUV (could it be XRGB888 ?).
  The target format is hard coded to ARGB888 (which is the default in the internal of the software renderer).
  In case of different YUV conversion, it will do an intermediate conversion to a ARGB8888 buffer.

  SDL_ConvertPixels_YUV_to_ARGB8888 is somehow redundant with all the "Color*Dither*Mod*".
  But it allows some completeness of SDL_ConvertPixels to handle all YUV format.
  It also works with odd dimensions.

  Moreover, I did some benchmark(SDL_ConvertPixel vs Color32DitherYV12Mod1X and Color32DitherYUY2Mod1X).
  gcc-6.3 and clang-4.0. gcc performs better than clang. And, with gcc, SDL_ConvertPixels() performs better (20%) than the two C function Color32Dither*().
  For instance, to convert 10 times a 3888x2592 image, it takes ~195 ms with SDL_ConvertPixels and ~235 ms with Color32Dither*().
  Especially because of gcc vectorize feature that optimises all conversion loops (-ftree-loop-vectorize).

  Nb: I put no image pitch for the YUV buffers. because it complexify a little bit the code and the API :
  There would be some ambiguity when setting the pitch exactly to image width:
  would it a be pitch of image width (for luma and chroma). or just contiguous data ? (could set pitch=0 for the later).


2) Small issues with odd dimensions:
  If width "w" is odd, luma plane width is still "w" whereas chroma planes will be "(w + 1)/2". Almost the same for odd h.
  Solution is to strategically substitute "w" by "(w+1)/2" at the good places ...

- In the repository, SDL_ConvertPixels() handles YUV only if yuv source format is exactly the same as YUV destination format.
  It basically does a memcpy of pixels, but it's done incorrectly when width or height is odd (wrong size of chroma planes). This is fixed.

- SDL Renderers don't support odd width/height for YUV textures.
  This is fixed for software, opengl, opengles2. (opengles 1 does not support it and fallback to software rendering).
  This is *not* fixed for D3D and D3D11 ... (and others, psp ?)
  Only *two* Dither function are fixed ... not sure if others are really used.

- This is not possible to create a NV12/NV12 texture with the software renderer, whereas other renderers allow it.
  This is fixed, by using SDL_ConvertPixels underneath.

- It was not possible to SDL_UpdateTexture() of format NV12/NV21 with the software renderer. this is fixed.

Here's also two testcases:
- that do all combination of conversion.
- to test partial UpdateTexture
This commit is contained in:
Sam Lantinga 2017-10-06 16:50:24 -07:00
parent 827e985041
commit e9652b1987
4 changed files with 768 additions and 98 deletions

View File

@ -265,7 +265,18 @@ Color32DitherYV12Mod1X(int *colortab, Uint32 * rgb_2_pix,
int cr_r; int cr_r;
int crb_g; int crb_g;
int cb_b; int cb_b;
int cols_2 = cols / 2; int cols_2 = (cols + 1) / 2;
/* not even dimensions */
int skip_last_col = 0;
int skip_last_row = 0;
if ( (cols & 0x1) ) {
skip_last_col = 1;
}
if ( (rows & 0x1) ) {
skip_last_row = 1;
}
row1 = (unsigned int *) out; row1 = (unsigned int *) out;
row2 = row1 + cols + mod; row2 = row1 + cols + mod;
@ -273,7 +284,7 @@ Color32DitherYV12Mod1X(int *colortab, Uint32 * rgb_2_pix,
mod += cols + mod; mod += cols + mod;
y = rows / 2; y = (rows + 1) / 2;
while (y--) { while (y--) {
x = cols_2; x = cols_2;
while (x--) { while (x--) {
@ -290,20 +301,27 @@ Color32DitherYV12Mod1X(int *colortab, Uint32 * rgb_2_pix,
*row1++ = (rgb_2_pix[L + cr_r] | *row1++ = (rgb_2_pix[L + cr_r] |
rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]);
if (!(x == 0 && skip_last_col)) {
L = *lum++; L = *lum++;
*row1++ = (rgb_2_pix[L + cr_r] | *row1++ = (rgb_2_pix[L + cr_r] |
rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]);
} /* skip col */
if (!(y == 0 && skip_last_row)) {
/* Now, do second row. */ /* Now, do second row. */
L = *lum2++; L = *lum2++;
*row2++ = (rgb_2_pix[L + cr_r] | *row2++ = (rgb_2_pix[L + cr_r] |
rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]);
if (!(x == 1 && skip_last_col)) {
L = *lum2++; L = *lum2++;
*row2++ = (rgb_2_pix[L + cr_r] | *row2++ = (rgb_2_pix[L + cr_r] |
rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]);
} /* skip col */
} /* skip row */
} }
/* /*
@ -670,7 +688,12 @@ Color32DitherYUY2Mod1X(int *colortab, Uint32 * rgb_2_pix,
int cr_r; int cr_r;
int crb_g; int crb_g;
int cb_b; int cb_b;
int cols_2 = cols / 2; int cols_2 = (cols + 1) / 2;
/* not even dimensions */
int skip_last_col = 0;
if ( (cols & 0x1) ) {
skip_last_col = 1;
}
row = (unsigned int *) out; row = (unsigned int *) out;
y = rows; y = rows;
@ -693,9 +716,11 @@ Color32DitherYUY2Mod1X(int *colortab, Uint32 * rgb_2_pix,
L = *lum; L = *lum;
lum += 2; lum += 2;
if (!(x == 0 && skip_last_col)) {
*row++ = (rgb_2_pix[L + cr_r] | *row++ = (rgb_2_pix[L + cr_r] |
rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]);
} /* skip col */
} }
row += mod; row += mod;
@ -1022,6 +1047,13 @@ SDL_SW_SetupYUVDisplay(SDL_SW_YUVTexture * swdata, Uint32 target_format)
swdata->Display2X = Color32DitherYUY2Mod2X; swdata->Display2X = Color32DitherYUY2Mod2X;
} }
break; break;
case SDL_PIXELFORMAT_NV21:
case SDL_PIXELFORMAT_NV12:
/* no Display{1,2}X function */
swdata->Display1X = NULL;
swdata->Display2X = NULL;
break;
default: default:
/* We should never get here (caught above) */ /* We should never get here (caught above) */
break; break;
@ -1049,6 +1081,8 @@ SDL_SW_CreateYUVTexture(Uint32 format, int w, int h)
case SDL_PIXELFORMAT_YUY2: case SDL_PIXELFORMAT_YUY2:
case SDL_PIXELFORMAT_UYVY: case SDL_PIXELFORMAT_UYVY:
case SDL_PIXELFORMAT_YVYU: case SDL_PIXELFORMAT_YVYU:
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
break; break;
default: default:
SDL_SetError("Unsupported YUV format"); SDL_SetError("Unsupported YUV format");
@ -1065,7 +1099,35 @@ SDL_SW_CreateYUVTexture(Uint32 format, int w, int h)
swdata->target_format = SDL_PIXELFORMAT_UNKNOWN; swdata->target_format = SDL_PIXELFORMAT_UNKNOWN;
swdata->w = w; swdata->w = w;
swdata->h = h; swdata->h = h;
swdata->pixels = (Uint8 *) SDL_malloc(w * h * 2); {
const int sz_plane = w * h;
const int sz_plane_chroma = ((w + 1) / 2) * ((h + 1) / 2);
const int sz_plane_packed = ((w + 1) / 2) * h;
int dst_size = 0;
switch(format)
{
case SDL_PIXELFORMAT_YV12: /**< Planar mode: Y + V + U (3 planes) */
case SDL_PIXELFORMAT_IYUV: /**< Planar mode: Y + U + V (3 planes) */
dst_size = sz_plane + sz_plane_chroma + sz_plane_chroma;
break;
case SDL_PIXELFORMAT_YUY2: /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
case SDL_PIXELFORMAT_UYVY: /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
case SDL_PIXELFORMAT_YVYU: /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */
dst_size = 4 * sz_plane_packed;
break;
case SDL_PIXELFORMAT_NV12: /**< Planar mode: Y + U/V interleaved (2 planes) */
case SDL_PIXELFORMAT_NV21: /**< Planar mode: Y + V/U interleaved (2 planes) */
dst_size = sz_plane + sz_plane_chroma + sz_plane_chroma;
break;
default:
SDL_assert(0 && "We should never get here (caught above)");
break;
}
swdata->pixels = (Uint8 *) SDL_malloc(dst_size);
}
swdata->colortab = (int *) SDL_malloc(4 * 256 * sizeof(int)); swdata->colortab = (int *) SDL_malloc(4 * 256 * sizeof(int));
swdata->rgb_2_pix = (Uint32 *) SDL_malloc(3 * 768 * sizeof(Uint32)); swdata->rgb_2_pix = (Uint32 *) SDL_malloc(3 * 768 * sizeof(Uint32));
if (!swdata->pixels || !swdata->colortab || !swdata->rgb_2_pix) { if (!swdata->pixels || !swdata->colortab || !swdata->rgb_2_pix) {
@ -1095,18 +1157,27 @@ SDL_SW_CreateYUVTexture(Uint32 format, int w, int h)
case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_IYUV:
swdata->pitches[0] = w; swdata->pitches[0] = w;
swdata->pitches[1] = swdata->pitches[0] / 2; swdata->pitches[1] = (swdata->pitches[0] + 1) / 2;
swdata->pitches[2] = swdata->pitches[0] / 2; swdata->pitches[2] = (swdata->pitches[0] + 1) / 2;
swdata->planes[0] = swdata->pixels; swdata->planes[0] = swdata->pixels;
swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h; swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * h / 2; swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * ((h + 1) / 2);
break; break;
case SDL_PIXELFORMAT_YUY2: case SDL_PIXELFORMAT_YUY2:
case SDL_PIXELFORMAT_UYVY: case SDL_PIXELFORMAT_UYVY:
case SDL_PIXELFORMAT_YVYU: case SDL_PIXELFORMAT_YVYU:
swdata->pitches[0] = w * 2; swdata->pitches[0] = ((w + 1) / 2) * 4;
swdata->planes[0] = swdata->pixels; swdata->planes[0] = swdata->pixels;
break; break;
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
swdata->pitches[0] = w;
swdata->pitches[1] = 2 * ((swdata->pitches[0] + 1) / 2);
swdata->planes[0] = swdata->pixels;
swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
break;
default: default:
SDL_assert(0 && "We should never get here (caught above)"); SDL_assert(0 && "We should never get here (caught above)");
break; break;
@ -1135,7 +1206,7 @@ SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
if (rect->x == 0 && rect->y == 0 && if (rect->x == 0 && rect->y == 0 &&
rect->w == swdata->w && rect->h == swdata->h) { rect->w == swdata->w && rect->h == swdata->h) {
SDL_memcpy(swdata->pixels, pixels, SDL_memcpy(swdata->pixels, pixels,
(swdata->h * swdata->w) + (swdata->h * swdata->w) / 2); (swdata->h * swdata->w) + 2* ((swdata->h + 1) /2) * ((swdata->w + 1) / 2));
} else { } else {
Uint8 *src, *dst; Uint8 *src, *dst;
int row; int row;
@ -1150,28 +1221,28 @@ SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
src += pitch; src += pitch;
dst += swdata->w; dst += swdata->w;
} }
/* Copy the next plane */ /* Copy the next plane */
src = (Uint8 *) pixels + rect->h * pitch; src = (Uint8 *) pixels + rect->h * pitch;
dst = swdata->pixels + swdata->h * swdata->w; dst = swdata->pixels + swdata->h * swdata->w;
dst += rect->y/2 * swdata->w/2 + rect->x/2; dst += rect->y/2 * ((swdata->w + 1) / 2) + rect->x/2;
length = rect->w / 2; length = (rect->w + 1) / 2;
for (row = 0; row < rect->h/2; ++row) { for (row = 0; row < (rect->h + 1)/2; ++row) {
SDL_memcpy(dst, src, length); SDL_memcpy(dst, src, length);
src += pitch/2; src += (pitch + 1)/2;
dst += swdata->w/2; dst += (swdata->w + 1)/2;
} }
/* Copy the next plane */ /* Copy the next plane */
src = (Uint8 *) pixels + rect->h * pitch + (rect->h * pitch) / 4; src = (Uint8 *) pixels + rect->h * pitch + ((rect->h + 1) / 2) * ((pitch + 1) / 2);
dst = swdata->pixels + swdata->h * swdata->w + dst = swdata->pixels + swdata->h * swdata->w +
(swdata->h * swdata->w) / 4; ((swdata->h + 1)/2) * ((swdata->w+1) / 2);
dst += rect->y/2 * swdata->w/2 + rect->x/2; dst += rect->y/2 * ((swdata->w + 1)/2) + rect->x/2;
length = rect->w / 2; length = (rect->w + 1) / 2;
for (row = 0; row < rect->h/2; ++row) { for (row = 0; row < (rect->h + 1)/2; ++row) {
SDL_memcpy(dst, src, length); SDL_memcpy(dst, src, length);
src += pitch/2; src += (pitch + 1)/2;
dst += swdata->w/2; dst += (swdata->w + 1)/2;
} }
} }
break; break;
@ -1187,7 +1258,7 @@ SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
dst = dst =
swdata->planes[0] + rect->y * swdata->pitches[0] + swdata->planes[0] + rect->y * swdata->pitches[0] +
rect->x * 2; rect->x * 2;
length = rect->w * 2; length = 4 * ((rect->w + 1) / 2);
for (row = 0; row < rect->h; ++row) { for (row = 0; row < rect->h; ++row) {
SDL_memcpy(dst, src, length); SDL_memcpy(dst, src, length);
src += pitch; src += pitch;
@ -1195,6 +1266,42 @@ SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
} }
} }
break; break;
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
{
if (rect->x == 0 && rect->y == 0 && rect->w == swdata->w && rect->h == swdata->h) {
SDL_memcpy(swdata->pixels, pixels,
(swdata->h * swdata->w) + 2* ((swdata->h + 1) /2) * ((swdata->w + 1) / 2));
} else {
Uint8 *src, *dst;
int row;
size_t length;
/* Copy the Y plane */
src = (Uint8 *) pixels;
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 += pitch;
dst += swdata->w;
}
/* Copy the next plane */
src = (Uint8 *) pixels + rect->h * pitch;
dst = swdata->pixels + swdata->h * swdata->w;
dst += 2 * ((rect->y + 1)/2) * ((swdata->w + 1) / 2) + 2 * (rect->x/2);
length = 2 * ((rect->w + 1) / 2);
for (row = 0; row < (rect->h + 1)/2; ++row) {
SDL_memcpy(dst, src, length);
src += 2 * ((pitch + 1)/2);
dst += 2 * ((swdata->w + 1)/2);
}
}
}
break;
} }
return 0; return 0;
} }
@ -1226,14 +1333,14 @@ SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
dst = swdata->pixels + swdata->h * swdata->w; dst = swdata->pixels + swdata->h * swdata->w;
} else { } else {
dst = swdata->pixels + swdata->h * swdata->w + dst = swdata->pixels + swdata->h * swdata->w +
(swdata->h * swdata->w) / 4; ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
} }
dst += rect->y/2 * swdata->w/2 + rect->x/2; dst += rect->y/2 * ((swdata->w + 1)/2) + rect->x/2;
length = rect->w / 2; length = (rect->w + 1) / 2;
for (row = 0; row < rect->h/2; ++row) { for (row = 0; row < (rect->h + 1)/2; ++row) {
SDL_memcpy(dst, src, length); SDL_memcpy(dst, src, length);
src += Upitch; src += Upitch;
dst += swdata->w/2; dst += (swdata->w + 1)/2;
} }
/* Copy the V plane */ /* Copy the V plane */
@ -1242,14 +1349,14 @@ SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
dst = swdata->pixels + swdata->h * swdata->w; dst = swdata->pixels + swdata->h * swdata->w;
} else { } else {
dst = swdata->pixels + swdata->h * swdata->w + dst = swdata->pixels + swdata->h * swdata->w +
(swdata->h * swdata->w) / 4; ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
} }
dst += rect->y/2 * swdata->w/2 + rect->x/2; dst += rect->y/2 * ((swdata->w + 1)/2) + rect->x/2;
length = rect->w / 2; length = (rect->w + 1) / 2;
for (row = 0; row < rect->h/2; ++row) { for (row = 0; row < (rect->h + 1)/2; ++row) {
SDL_memcpy(dst, src, length); SDL_memcpy(dst, src, length);
src += Vpitch; src += Vpitch;
dst += swdata->w/2; dst += (swdata->w + 1)/2;
} }
return 0; return 0;
} }
@ -1261,11 +1368,13 @@ SDL_SW_LockYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
switch (swdata->format) { switch (swdata->format) {
case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_IYUV:
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
if (rect if (rect
&& (rect->x != 0 || rect->y != 0 || rect->w != swdata->w && (rect->x != 0 || rect->y != 0 || rect->w != swdata->w
|| rect->h != swdata->h)) { || rect->h != swdata->h)) {
return SDL_SetError return SDL_SetError
("YV12 and IYUV textures only support full surface locks"); ("YV12, IYUV, NV12, NV21 textures only support full surface locks");
} }
break; break;
} }
@ -1383,6 +1492,12 @@ SDL_SW_CopyYUVToRGB(SDL_SW_YUVTexture * swdata, const SDL_Rect * srcrect,
Cr = lum + 1; Cr = lum + 1;
Cb = lum + 3; Cb = lum + 3;
break; break;
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
return SDL_ConvertPixels(swdata->w, swdata->h,
swdata->format, swdata->planes[0], swdata->pitches[0],
target_format, pixels, pitch);
break;
default: default:
return SDL_SetError("Unsupported YUV format in copy"); return SDL_SetError("Unsupported YUV format in copy");
} }

View File

@ -752,12 +752,12 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
if (texture->format == SDL_PIXELFORMAT_YV12 || if (texture->format == SDL_PIXELFORMAT_YV12 ||
texture->format == SDL_PIXELFORMAT_IYUV) { texture->format == SDL_PIXELFORMAT_IYUV) {
/* Need to add size for the U and V planes */ /* 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 || if (texture->format == SDL_PIXELFORMAT_NV12 ||
texture->format == SDL_PIXELFORMAT_NV21) { texture->format == SDL_PIXELFORMAT_NV21) {
/* Need to add size for the U/V plane */ /* Need to add size for the U/V plane */
size += ((texture->h * data->pitch) / 2); size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
} }
data->pixels = SDL_calloc(1, size); data->pixels = SDL_calloc(1, size);
if (!data->pixels) { if (!data->pixels) {
@ -875,8 +875,8 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
GL_CLAMP_TO_EDGE); GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE); GL_CLAMP_TO_EDGE);
renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2, renderdata->glTexImage2D(data->type, 0, internalFormat, (texture_w+1)/2,
texture_h/2, 0, format, type, NULL); (texture_h+1)/2, 0, format, type, NULL);
renderdata->glBindTexture(data->type, data->vtexture); renderdata->glBindTexture(data->type, data->vtexture);
renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
@ -887,8 +887,8 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
GL_CLAMP_TO_EDGE); GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE); GL_CLAMP_TO_EDGE);
renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2, renderdata->glTexImage2D(data->type, 0, internalFormat, (texture_w+1)/2,
texture_h/2, 0, format, type, NULL); (texture_h+1)/2, 0, format, type, NULL);
renderdata->glDisable(data->type); renderdata->glDisable(data->type);
} }
@ -909,8 +909,8 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
GL_CLAMP_TO_EDGE); GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE); GL_CLAMP_TO_EDGE);
renderdata->glTexImage2D(data->type, 0, GL_LUMINANCE_ALPHA, texture_w/2, renderdata->glTexImage2D(data->type, 0, GL_LUMINANCE_ALPHA, (texture_w+1)/2,
texture_h/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); (texture_h+1)/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
renderdata->glDisable(data->type); renderdata->glDisable(data->type);
} }
@ -937,7 +937,7 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
rect->h, data->format, data->formattype, rect->h, data->format, data->formattype,
pixels); pixels);
if (data->yuv) { 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 */ /* Skip to the correct offset into the next texture */
pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
@ -947,29 +947,29 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
renderdata->glBindTexture(data->type, data->utexture); renderdata->glBindTexture(data->type, data->utexture);
} }
renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, 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); data->format, data->formattype, pixels);
/* Skip to the correct offset into the next texture */ /* 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) { if (texture->format == SDL_PIXELFORMAT_YV12) {
renderdata->glBindTexture(data->type, data->utexture); renderdata->glBindTexture(data->type, data->utexture);
} else { } else {
renderdata->glBindTexture(data->type, data->vtexture); renderdata->glBindTexture(data->type, data->vtexture);
} }
renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, 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); data->format, data->formattype, pixels);
} }
if (data->nv12) { if (data->nv12) {
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 */ /* Skip to the correct offset into the next texture */
pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
renderdata->glBindTexture(data->type, data->utexture); renderdata->glBindTexture(data->type, data->utexture);
renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, 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,
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels); GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels);
} }
renderdata->glDisable(data->type); renderdata->glDisable(data->type);
@ -1000,13 +1000,13 @@ GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch); renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch);
renderdata->glBindTexture(data->type, data->utexture); renderdata->glBindTexture(data->type, data->utexture);
renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, 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); data->format, data->formattype, Uplane);
renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch); renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch);
renderdata->glBindTexture(data->type, data->vtexture); renderdata->glBindTexture(data->type, data->vtexture);
renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, 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); data->format, data->formattype, Vplane);
renderdata->glDisable(data->type); renderdata->glDisable(data->type);

View File

@ -619,11 +619,11 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
size = texture->h * data->pitch; size = texture->h * data->pitch;
if (data->yuv) { if (data->yuv) {
/* Need to add size for the U and V planes */ /* 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 (data->nv12) { if (data->nv12) {
/* Need to add size for the U/V plane */ /* Need to add size for the U/V plane */
size += ((texture->h * data->pitch) / 2); size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
} }
data->pixel_data = SDL_calloc(1, size); data->pixel_data = SDL_calloc(1, size);
if (!data->pixel_data) { if (!data->pixel_data) {
@ -646,7 +646,7 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
renderdata->glTexImage2D(data->texture_type, 0, format, texture->w / 2, texture->h / 2, 0, format, type, NULL); renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL);
renderdata->glGenTextures(1, &data->texture_u); renderdata->glGenTextures(1, &data->texture_u);
if (GL_CheckError("glGenTexures()", renderer) < 0) { if (GL_CheckError("glGenTexures()", renderer) < 0) {
@ -658,7 +658,7 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
renderdata->glTexImage2D(data->texture_type, 0, format, texture->w / 2, texture->h / 2, 0, format, type, NULL); renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL);
if (GL_CheckError("glTexImage2D()", renderer) < 0) { if (GL_CheckError("glTexImage2D()", renderer) < 0) {
return -1; return -1;
} }
@ -675,7 +675,7 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, 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); renderdata->glTexImage2D(data->texture_type, 0, GL_LUMINANCE_ALPHA, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
if (GL_CheckError("glTexImage2D()", renderer) < 0) { if (GL_CheckError("glTexImage2D()", renderer) < 0) {
return -1; return -1;
} }
@ -775,14 +775,15 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect
GLES2_TexSubImage2D(data, tdata->texture_type, GLES2_TexSubImage2D(data, tdata->texture_type,
rect->x / 2, rect->x / 2,
rect->y / 2, rect->y / 2,
rect->w / 2, (rect->w + 1) / 2,
rect->h / 2, (rect->h + 1) / 2,
tdata->pixel_format, tdata->pixel_format,
tdata->pixel_type, tdata->pixel_type,
pixels, pitch / 2, 1); pixels, (pitch + 1) / 2, 1);
/* Skip to the correct offset into the next texture */ /* 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) { if (texture->format == SDL_PIXELFORMAT_YV12) {
data->glBindTexture(tdata->texture_type, tdata->texture_u); data->glBindTexture(tdata->texture_type, tdata->texture_u);
} else { } else {
@ -791,11 +792,11 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect
GLES2_TexSubImage2D(data, tdata->texture_type, GLES2_TexSubImage2D(data, tdata->texture_type,
rect->x / 2, rect->x / 2,
rect->y / 2, rect->y / 2,
rect->w / 2, (rect->w + 1) / 2,
rect->h / 2, (rect->h + 1) / 2,
tdata->pixel_format, tdata->pixel_format,
tdata->pixel_type, tdata->pixel_type,
pixels, pitch / 2, 1); pixels, (pitch + 1) / 2, 1);
} }
if (tdata->nv12) { if (tdata->nv12) {
@ -805,11 +806,11 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect
GLES2_TexSubImage2D(data, tdata->texture_type, GLES2_TexSubImage2D(data, tdata->texture_type,
rect->x / 2, rect->x / 2,
rect->y / 2, rect->y / 2,
rect->w / 2, (rect->w + 1) / 2,
rect->h / 2, (rect->h + 1) / 2,
GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
pixels, pitch, 2); pixels, 2 * ((pitch + 1) / 2), 2);
} }
return GL_CheckError("glTexSubImage2D()", renderer); return GL_CheckError("glTexSubImage2D()", renderer);
@ -836,8 +837,8 @@ GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
GLES2_TexSubImage2D(data, tdata->texture_type, GLES2_TexSubImage2D(data, tdata->texture_type,
rect->x / 2, rect->x / 2,
rect->y / 2, rect->y / 2,
rect->w / 2, (rect->w + 1) / 2,
rect->h / 2, (rect->h + 1) / 2,
tdata->pixel_format, tdata->pixel_format,
tdata->pixel_type, tdata->pixel_type,
Vplane, Vpitch, 1); Vplane, Vpitch, 1);
@ -846,8 +847,8 @@ GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
GLES2_TexSubImage2D(data, tdata->texture_type, GLES2_TexSubImage2D(data, tdata->texture_type,
rect->x / 2, rect->x / 2,
rect->y / 2, rect->y / 2,
rect->w / 2, (rect->w + 1) / 2,
rect->h / 2, (rect->h + 1) / 2,
tdata->pixel_format, tdata->pixel_format,
tdata->pixel_type, tdata->pixel_type,
Uplane, Upitch, 1); Uplane, Upitch, 1);

View File

@ -26,6 +26,17 @@
#include "SDL_RLEaccel_c.h" #include "SDL_RLEaccel_c.h"
#include "SDL_pixels_c.h" #include "SDL_pixels_c.h"
/* Private routines */
static int
SDL_ConvertPixels_YUV_to_ARGB8888(int width, int height,
Uint32 src_format, const void *src,
void *dst, int dst_pitch);
static int
SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height,
const void *src, int src_pitch,
Uint32 dst_format, void *dst);
/* Public routines */ /* Public routines */
/* /*
@ -1124,58 +1135,133 @@ int SDL_ConvertPixels(int width, int height,
/* Fast path for same format copy */ /* Fast path for same format copy */
if (src_format == dst_format) { if (src_format == dst_format) {
int bpp, i; int i;
if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { if (SDL_ISPIXELFORMAT_FOURCC(src_format)) {
switch (src_format) { switch (src_format) {
case SDL_PIXELFORMAT_YUY2: case SDL_PIXELFORMAT_YUY2:
case SDL_PIXELFORMAT_UYVY: case SDL_PIXELFORMAT_UYVY:
case SDL_PIXELFORMAT_YVYU: case SDL_PIXELFORMAT_YVYU:
bpp = 2; /* Packed planes */
width = 4 * ((width + 1) / 2);
for (i = height; i--;) {
SDL_memcpy(dst, src, width);
src = (const Uint8*)src + src_pitch;
dst = (Uint8*)dst + dst_pitch;
}
break; break;
case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_IYUV:
case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21: case SDL_PIXELFORMAT_NV21:
bpp = 1; {
/* Y plane */
for (i = height; i--;) {
SDL_memcpy(dst, src, width);
src = (const Uint8*)src + src_pitch;
dst = (Uint8*)dst + dst_pitch;
}
/* not sure the pitch is relevant here.
this also works to add the size of two chroma planes */
#if 0
SDL_memcpy(dst, src, 2 * ((width + 1)/2) * ((height+1)/2));
#else
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 = (width + 1) / 2;
height = (height + 1) / 2;
src_pitch = (src_pitch + 1) / 2;
dst_pitch = (dst_pitch + 1) / 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 = (height + 1) / 2;
width = (width + 1) / 2;
src_pitch = (src_pitch + 1) / 2;
dst_pitch = (dst_pitch + 1) / 2;
for (i = height; i--;) {
SDL_memcpy(dst, src, 2 * width);
src = (Uint8*)src + 2 * src_pitch;
dst = (Uint8*)dst + 2 * dst_pitch;
}
}
#endif
}
break; break;
default: default:
return SDL_SetError("Unknown FOURCC pixel format"); return SDL_SetError("Unknown FOURCC pixel format");
} }
} else { } else {
bpp = SDL_BYTESPERPIXEL(src_format); const int bpp = SDL_BYTESPERPIXEL(src_format);
} width *= bpp;
width *= bpp;
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--;) { for (i = height; i--;) {
SDL_memcpy(dst, src, width); SDL_memcpy(dst, src, width);
src = (Uint8*)src + src_pitch; src = (const Uint8*)src + src_pitch;
dst = (Uint8*)dst + dst_pitch; dst = (Uint8*)dst + dst_pitch;
} }
} }
return 0; return 0;
} }
/* FOURCC to Any */
if (SDL_ISPIXELFORMAT_FOURCC(src_format)) {
/* FOURCC to ARGB8888 */
if (dst_format == SDL_PIXELFORMAT_ARGB8888) {
SDL_ConvertPixels_YUV_to_ARGB8888(width, height, src_format, src, dst, dst_pitch);
return 0;
}
else /* FOURCC to not(ARGB8888) : need an intermediate conversion */
{
int ret;
void *tmp = SDL_malloc(width * height * 4);
if (tmp == NULL) {
return -1;
}
/* convert src/FOURCC to tmp/ARGB8888 */
SDL_ConvertPixels_YUV_to_ARGB8888(width, height, src_format, src, tmp, width * 4);
/* convert tmp/ARGB8888 to dst/dst_format */
ret = SDL_ConvertPixels(width, height, SDL_PIXELFORMAT_ARGB8888, tmp, width * 4, dst_format, dst, dst_pitch);
SDL_free(tmp);
return ret;
}
}
/* Any to FOURCC */
if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
/* ARGB8888 to FOURCC */
if (src_format == SDL_PIXELFORMAT_ARGB8888) {
SDL_ConvertPixels_ARGB8888_to_YUV(width, height, src, src_pitch, dst_format, dst);
return 0;
}
else /* not(ARGB8888) to FOURCC : need an intermediate conversion */
{
int ret;
void *tmp = SDL_malloc(width * height * 4);
if (tmp == NULL) {
return -1;
}
/* convert src/src_format to tmp/ARGB8888 */
ret = SDL_ConvertPixels(width, height, src_format, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, tmp, width * 4);
if (ret == -1) {
SDL_free(tmp);
return ret;
}
/* convert tmp/ARGB8888 to dst/FOURCC */
SDL_ConvertPixels_ARGB8888_to_YUV(width, height, tmp, width * 4, dst_format, dst);
SDL_free(tmp);
return 0;
}
}
if (!SDL_CreateSurfaceOnStack(width, height, src_format, nonconst_src, if (!SDL_CreateSurfaceOnStack(width, height, src_format, nonconst_src,
src_pitch, src_pitch,
&src_surface, &src_fmt, &src_blitmap)) { &src_surface, &src_fmt, &src_blitmap)) {
@ -1231,4 +1317,472 @@ SDL_FreeSurface(SDL_Surface * surface)
SDL_free(surface); SDL_free(surface);
} }
/* YUV-RGB conversion */
#define CLAMP(val) ((val) > 0 ? ((val) < 255 ? (val) : 255) : 0)
#define MAKE_Y(r, g, b) ((( 66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16)
#define MAKE_U(r, g, b) ((( -38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128)
#define MAKE_V(r, g, b) ((( 112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128)
#define MAKE_R(y, u, v) CLAMP(( 298 * ((y) - 16) + 409 * ((v) - 128) + 128) >> 8)
#define MAKE_G(y, u, v) CLAMP(( 298 * ((y) - 16) - 100 * ((u) - 128) - 208 * ((v) - 128) + 128) >> 8)
#define MAKE_B(y, u, v) CLAMP(( 298 * ((y) - 16) + 516 * ((u) - 128) + 128) >> 8)
static int
SDL_ConvertPixels_YUV_to_ARGB8888(int width, int height,
Uint32 src_format, const void *src,
void *dst, int dst_pitch)
{
const int sz_plane = width * height;
const int sz_plane_chroma = ((width + 1) / 2) * ((height + 1) / 2);
const int width_remainder = (width & 0x1);
const int width_half = width / 2;
const int curr_row_padding = dst_pitch - 4 * width;
int i, j;
Uint8 *curr_row = (Uint8*)dst;
// SDL_Log("SDL_ConvertPixels_YUV_to_ARGB8888 (from %s)", SDL_GetPixelFormatName(src_format));
#define WRITE_RGB_PIXEL(y, u, v) \
*((Uint32*)curr_row) = \
(MAKE_B((y), (u), (v)) \
| (MAKE_G((y), (u), (v)) << 8) \
| (MAKE_R((y), (u), (v)) << 16) \
| 0xff000000); \
curr_row += 4; \
switch (src_format)
{
case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV:
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
{
const Uint8 *plane_y = (const Uint8*)src;
if (src_format == SDL_PIXELFORMAT_YV12 || src_format == SDL_PIXELFORMAT_IYUV)
{
const Uint8 *plane_u = (src_format == SDL_PIXELFORMAT_YV12 ? plane_y + sz_plane + sz_plane_chroma : plane_y + sz_plane);
const Uint8 *plane_v = (src_format == SDL_PIXELFORMAT_YV12 ? plane_y + sz_plane : plane_y + sz_plane + sz_plane_chroma);
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
const Uint8 u = *plane_u++;
const Uint8 v = *plane_v++;
const Uint8 y = *plane_y++;
const Uint8 y1 = *plane_y++;
WRITE_RGB_PIXEL(y, u, v);
WRITE_RGB_PIXEL(y1, u, v);
}
if (width_remainder) {
const Uint8 u = *plane_u++;
const Uint8 v = *plane_v++;
const Uint8 y = *plane_y++;
WRITE_RGB_PIXEL(y, u, v);
}
/* Re-use the same line of chroma planes */
if ((j & 0x1) == 0x0) {
plane_u -= width_half + width_remainder;
plane_v -= width_half + width_remainder;
}
curr_row += curr_row_padding;
}
}
else if (src_format == SDL_PIXELFORMAT_NV12)
{
const Uint8 *plane_interleaved_uv = plane_y + sz_plane;
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
const Uint8 y = *plane_y++;
const Uint8 y1 = *plane_y++;
const Uint8 u = *plane_interleaved_uv++;
const Uint8 v = *plane_interleaved_uv++;
WRITE_RGB_PIXEL(y, u, v);
WRITE_RGB_PIXEL(y1, u, v);
}
if (width_remainder) {
const Uint8 y = *plane_y++;
const Uint8 u = *plane_interleaved_uv++;
const Uint8 v = *plane_interleaved_uv++;
WRITE_RGB_PIXEL(y, u, v);
}
/* Re-use the same line of chroma planes */
if ((j & 0x1) == 0x0) {
plane_interleaved_uv -= 2 * (width_half + width_remainder);
}
curr_row += curr_row_padding;
}
}
else /* src_format == SDL_PIXELFORMAT_NV21 */
{
const Uint8 *plane_interleaved_uv = plane_y + sz_plane;
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
const Uint8 y = *plane_y++;
const Uint8 y1 = *plane_y++;
const Uint8 v = *plane_interleaved_uv++;
const Uint8 u = *plane_interleaved_uv++;
WRITE_RGB_PIXEL(y, u, v);
WRITE_RGB_PIXEL(y1, u, v);
}
if (width_remainder) {
const Uint8 y = *plane_y++;
const Uint8 v = *plane_interleaved_uv++;
const Uint8 u = *plane_interleaved_uv++;
WRITE_RGB_PIXEL(y, u, v);
}
/* Re-use the same line of chroma planes */
if ((j & 0x1) == 0x0) {
plane_interleaved_uv -= 2 * (width_half + width_remainder);
}
curr_row += curr_row_padding;
}
}
}
break;
case SDL_PIXELFORMAT_YUY2:
case SDL_PIXELFORMAT_UYVY:
case SDL_PIXELFORMAT_YVYU:
{
const Uint8 *plane = (const Uint8 *)src;
#define READ_PACKED_YUV(var1, var2, var3, var4) \
const Uint8 var1 = plane[0]; \
const Uint8 var2 = plane[1]; \
const Uint8 var3 = plane[2]; \
const Uint8 var4 = plane[3]; \
plane += 4; \
if (src_format == SDL_PIXELFORMAT_YUY2) /* Y U Y1 V */
{
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_PACKED_YUV(y, u, y1, v);
WRITE_RGB_PIXEL(y, u, v);
WRITE_RGB_PIXEL(y1, u, v);
}
if (width_remainder) {
READ_PACKED_YUV(y, u, y1, v); /* y1 unused */
WRITE_RGB_PIXEL(y, u, v);
}
curr_row += curr_row_padding;
}
}
else if (src_format == SDL_PIXELFORMAT_UYVY) /* U Y V Y1 */
{
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_PACKED_YUV(u, y, v, y1);
WRITE_RGB_PIXEL(y, u, v);
WRITE_RGB_PIXEL(y1, u, v);
}
if (width_remainder) {
READ_PACKED_YUV(u, y, v, y1); /* y1 unused */
WRITE_RGB_PIXEL(y, u, v);
}
curr_row += curr_row_padding;
}
}
else if (src_format == SDL_PIXELFORMAT_YVYU) /* Y V Y1 U */
{
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_PACKED_YUV(y, v, y1, u);
WRITE_RGB_PIXEL(y, u, v);
WRITE_RGB_PIXEL(y1, u, v);
}
if (width_remainder) {
READ_PACKED_YUV(y, v, y1, u); /* y1 unused */
WRITE_RGB_PIXEL(y, u, v);
}
curr_row += curr_row_padding;
}
}
#undef READ_PACKED_YUV
}
break;
}
#undef WRITE_RGB_PIXEL
return 0;
}
static int
SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void *src, int src_pitch, Uint32 dst_format, void *dst)
{
const int src_pitch_x_2 = src_pitch * 2;
const int sz_plane = width * height;
const int sz_plane_chroma = ((width + 1) / 2) * ((height + 1) / 2);
const int height_half = height / 2;
const int height_remainder = (height & 0x1);
const int width_half = width / 2;
const int width_remainder = (width & 0x1);
int i, j;
// SDL_Log("SDL_ConvertPixels_ARGB8888_to_YUV (to %s)", SDL_GetPixelFormatName(dst_format));
switch (dst_format)
{
case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV:
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
{
const Uint8 *curr_row, *next_row;
Uint8 *plane_y = (Uint8*) dst;
Uint8 *plane_u = (dst_format == SDL_PIXELFORMAT_YV12 ? plane_y + sz_plane + sz_plane_chroma : plane_y + sz_plane);
Uint8 *plane_v = (dst_format == SDL_PIXELFORMAT_YV12 ? plane_y + sz_plane : plane_y + sz_plane + sz_plane_chroma);
Uint8 *plane_interleaved_uv = plane_y + sz_plane;
curr_row = (const Uint8*)src;
/* Write Y plane */
for (j = 0; j < height; j++) {
for (i = 0; i < width; i++) {
const Uint8 b = curr_row[4 * i + 0];
const Uint8 g = curr_row[4 * i + 1];
const Uint8 r = curr_row[4 * i + 2];
*plane_y++ = MAKE_Y(r, g, b);
}
curr_row += src_pitch;
}
curr_row = (const Uint8*)src;
next_row = (const Uint8*)src;
next_row += src_pitch;
#if 1
/* slightly faster */
#define READ_2x2_PIXELS \
const Uint32 p1 = ((Uint32 *)curr_row)[2 * i]; \
const Uint32 p2 = ((Uint32 *)curr_row)[2 * i + 1]; \
const Uint32 p3 = ((Uint32 *)next_row)[2 * i]; \
const Uint32 p4 = ((Uint32 *)next_row)[2 * i + 1]; \
const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff) + (p3 & 0x000000ff) + (p4 & 0x000000ff)) >> 2; \
const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00) + (p3 & 0x0000ff00) + (p4 & 0x0000ff00)) >> 10; \
const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000) + (p3 & 0x00ff0000) + (p4 & 0x00ff0000)) >> 18; \
#else
#define READ_2x2_PIXELS \
const Uint8 b = (curr_row[8 * i + 0] + curr_row[8 * i + 4] \
+ next_row[8 * i + 0] + next_row[8 * i + 4] ) >> 2; \
const Uint8 g = (curr_row[8 * i + 1] + curr_row[8 * i + 5] \
+ next_row[8 * i + 1] + next_row[8 * i + 5] ) >> 2; \
const Uint8 r = (curr_row[8 * i + 2] + curr_row[8 * i + 6] \
+ next_row[8 * i + 2] + next_row[8 * i + 6] ) >> 2; \
#endif
#define READ_2x1_PIXELS \
const Uint8 b = (curr_row[8 * i + 0] + next_row[8 * i + 0]) >> 1; \
const Uint8 g = (curr_row[8 * i + 1] + next_row[8 * i + 1]) >> 1; \
const Uint8 r = (curr_row[8 * i + 2] + next_row[8 * i + 2]) >> 1; \
#define READ_1x2_PIXELS \
const Uint8 b = (curr_row[8 * i + 0] + curr_row[8 * i + 4]) >> 1; \
const Uint8 g = (curr_row[8 * i + 1] + curr_row[8 * i + 5]) >> 1; \
const Uint8 r = (curr_row[8 * i + 2] + curr_row[8 * i + 6]) >> 1; \
#define READ_1x1_PIXEL \
const Uint8 b = curr_row[8 * i + 0]; \
const Uint8 g = curr_row[8 * i + 1]; \
const Uint8 r = curr_row[8 * i + 2]; \
if (dst_format == SDL_PIXELFORMAT_YV12 || dst_format == SDL_PIXELFORMAT_IYUV)
{
/* Write UV planes, not interleaved */
for (j = 0; j < height_half; j++) {
for (i = 0; i < width_half; i++) {
READ_2x2_PIXELS;
*plane_u++ = MAKE_U(r, g, b);
*plane_v++ = MAKE_V(r, g, b);
}
if (width_remainder) {
READ_2x1_PIXELS;
*plane_u++ = MAKE_U(r, g, b);
*plane_v++ = MAKE_V(r, g, b);
}
curr_row += src_pitch_x_2;
next_row += src_pitch_x_2;
}
if (height_remainder) {
for (i = 0; i < width_half; i++) {
READ_1x2_PIXELS;
*plane_u++ = MAKE_U(r, g, b);
*plane_v++ = MAKE_V(r, g, b);
}
if (width_remainder) {
READ_1x1_PIXEL;
*plane_u++ = MAKE_U(r, g, b);
*plane_v++ = MAKE_V(r, g, b);
}
}
}
else if (dst_format == SDL_PIXELFORMAT_NV12)
{
for (j = 0; j < height_half; j++) {
for (i = 0; i < width_half; i++) {
READ_2x2_PIXELS;
*plane_interleaved_uv++ = MAKE_U(r, g, b);
*plane_interleaved_uv++ = MAKE_V(r, g, b);
}
if (width_remainder) {
READ_2x1_PIXELS;
*plane_interleaved_uv++ = MAKE_U(r, g, b);
*plane_interleaved_uv++ = MAKE_V(r, g, b);
}
curr_row += src_pitch_x_2;
next_row += src_pitch_x_2;
}
if (height_remainder) {
for (i = 0; i < width_half; i++) {
READ_1x2_PIXELS;
*plane_interleaved_uv++ = MAKE_U(r, g, b);
*plane_interleaved_uv++ = MAKE_V(r, g, b);
}
if (width_remainder) {
READ_1x1_PIXEL;
*plane_interleaved_uv++ = MAKE_U(r, g, b);
*plane_interleaved_uv++ = MAKE_V(r, g, b);
}
}
}
else /* dst_format == SDL_PIXELFORMAT_NV21 */
{
for (j = 0; j < height_half; j++) {
for (i = 0; i < width_half; i++) {
READ_2x2_PIXELS;
*plane_interleaved_uv++ = MAKE_V(r, g, b);
*plane_interleaved_uv++ = MAKE_U(r, g, b);
}
if (width_remainder) {
READ_2x1_PIXELS;
*plane_interleaved_uv++ = MAKE_V(r, g, b);
*plane_interleaved_uv++ = MAKE_U(r, g, b);
}
curr_row += src_pitch_x_2;
next_row += src_pitch_x_2;
}
if (height_remainder) {
for (i = 0; i < width_half; i++) {
READ_1x2_PIXELS;
*plane_interleaved_uv++ = MAKE_V(r, g, b);
*plane_interleaved_uv++ = MAKE_U(r, g, b);
}
if (width_remainder) {
READ_1x1_PIXEL;
*plane_interleaved_uv++ = MAKE_V(r, g, b);
*plane_interleaved_uv++ = MAKE_U(r, g, b);
}
}
}
#undef READ_2x2_PIXELS
#undef READ_2x1_PIXELS
#undef READ_1x2_PIXELS
#undef READ_1x1_PIXEL
}
break;
case SDL_PIXELFORMAT_YUY2:
case SDL_PIXELFORMAT_UYVY:
case SDL_PIXELFORMAT_YVYU:
{
const Uint8 *curr_row = (const Uint8*) src;
Uint8 *plane = (Uint8*) dst;
#define READ_TWO_RGB_PIXELS \
const Uint8 b = curr_row[8 * i + 0]; \
const Uint8 g = curr_row[8 * i + 1]; \
const Uint8 r = curr_row[8 * i + 2]; \
const Uint8 b1 = curr_row[8 * i + 4]; \
const Uint8 g1 = curr_row[8 * i + 5]; \
const Uint8 r1 = curr_row[8 * i + 6]; \
const Uint8 B = (b + b1) >> 1; \
const Uint8 G = (g + g1) >> 1; \
const Uint8 R = (r + r1) >> 1; \
#define READ_ONE_RGB_PIXEL \
const Uint8 b = curr_row[8 * i + 0]; \
const Uint8 g = curr_row[8 * i + 1]; \
const Uint8 r = curr_row[8 * i + 2]; \
/* Write YUV plane, packed */
if (dst_format == SDL_PIXELFORMAT_YUY2)
{
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_TWO_RGB_PIXELS;
/* Y U Y1 V */
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_U(R, G, B);
*plane++ = MAKE_Y(r1, g1, b1);
*plane++ = MAKE_V(R, G, B);
}
if (width_remainder) {
READ_ONE_RGB_PIXEL;
/* Y U Y V */
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_U(r, g, b);
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(r, g, b);
}
curr_row += src_pitch;
}
}
else if (dst_format == SDL_PIXELFORMAT_UYVY)
{
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_TWO_RGB_PIXELS;
/* U Y V Y1 */
*plane++ = MAKE_U(R, G, B);
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(R, G, B);
*plane++ = MAKE_Y(r1, g1, b1);
}
if (width_remainder) {
READ_ONE_RGB_PIXEL;
/* U Y V Y */
*plane++ = MAKE_U(r, g, b);
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(r, g, b);
*plane++ = MAKE_Y(r, g, b);
}
curr_row += src_pitch;
}
}
else if (dst_format == SDL_PIXELFORMAT_YVYU)
{
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_TWO_RGB_PIXELS;
/* Y V Y1 U */
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(R, G, B);
*plane++ = MAKE_Y(r1, g1, b1);
*plane++ = MAKE_U(R, G, B);
}
if (width_remainder) {
READ_ONE_RGB_PIXEL;
/* Y V Y U */
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(r, g, b);
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_U(r, g, b);
}
curr_row += src_pitch;
}
}
#undef READ_TWO_RGB_PIXELS
#undef READ_ONE_RGB_PIXEL
}
break;
}
return 0;
}
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */