Bug 551378 - Implement 4:4:4 colorspace conversion in YCbCr layers code - r=roc
--- a/gfx/layers/ImageLayers.h
+++ b/gfx/layers/ImageLayers.h
@@ -214,19 +214,16 @@ protected:
* 4:2:0 - CbCr width and height is half that of Y.
*
* The color format is detected based on the height/width ratios
* defined above.
*
* The Image that is rendered is the picture region defined by
* mPicX, mPicY and mPicSize. The size of the rendered image is
* mPicSize, not mYSize or mCbCrSize.
- *
- * Note: The color-conversion code does not currently support 4:4:4
- * and an error is raised in this case. See bug 551378.
*/
class THEBES_API PlanarYCbCrImage : public Image {
public:
struct Data {
// Luminance buffer
PRUint8* mYChannel;
PRInt32 mYStride;
gfxIntSize mYSize;
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -130,17 +130,17 @@ BasicPlanarYCbCrImage::SetData(const Dat
if (!mBuffer) {
// out of memory
return;
}
gfx::YUVType type = gfx::YV12;
if (aData.mYSize.width == aData.mCbCrSize.width &&
aData.mYSize.height == aData.mCbCrSize.height) {
- NS_ERROR("YCbCr 4:4:4 format not supported");
+ type = gfx::YV24;
}
else if (aData.mYSize.width / 2 == aData.mCbCrSize.width &&
aData.mYSize.height == aData.mCbCrSize.height) {
type = gfx::YV16;
}
else if (aData.mYSize.width / 2 == aData.mCbCrSize.width &&
aData.mYSize.height / 2 == aData.mCbCrSize.height ) {
type = gfx::YV12;
--- a/gfx/ycbcr/README
+++ b/gfx/ycbcr/README
@@ -10,8 +10,10 @@ convert.patch: Change Chromium code to b
Add runtime CPU detection for MMX
Move default C implementation to work on all platforms.
picture_region.patch: Change Chromium code to allow a picture region.
The YUV conversion will convert within this
picture region only.
remove_scale.patch: Removes Chromium scaling code.
+
+yv24.patch: Adds YCbCr 4:4:4 support
--- a/gfx/ycbcr/update.sh
+++ b/gfx/ycbcr/update.sh
@@ -4,8 +4,9 @@ cp $1/media/base/yuv_convert.cc yuv_conv
cp $1/media/base/yuv_row.h .
cp $1/media/base/yuv_row_linux.cc yuv_row_linux.cpp
cp $1/media/base/yuv_row_mac.cc yuv_row_mac.cpp
cp $1/media/base/yuv_row_win.cc yuv_row_win.cpp
cp $1/media/base/yuv_row_linux.cc yuv_row_c.cpp
patch -p3 <convert.patch
patch -p3 <picture_region.patch
patch -p3 <remove_scale.patch
+patch -p3 <yv24.patch
--- a/gfx/ycbcr/yuv_convert.cpp
+++ b/gfx/ycbcr/yuv_convert.cpp
@@ -6,16 +6,17 @@
// http://www.fourcc.org/yuv.php
// The actual conversion is best described here
// http://en.wikipedia.org/wiki/YUV
// An article on optimizing YUV conversion using tables instead of multiplies
// http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
//
// YV12 is a full plane of Y and a half height, half width chroma planes
// YV16 is a full plane of Y and a full height, half width chroma planes
+// YV24 is a full plane of Y and a full height, full width chroma planes
//
// ARGB pixel format is output, which on little endian is stored as BGRA.
// The alpha is set to 255, allowing the application to use RGBA or RGB32.
#include "yuv_convert.h"
// Header for low level row functions.
#include "yuv_row.h"
@@ -33,50 +34,55 @@ void ConvertYCbCrToRGB32(const uint8* y_
int pic_x,
int pic_y,
int pic_width,
int pic_height,
int y_pitch,
int uv_pitch,
int rgb_pitch,
YUVType yuv_type) {
- unsigned int y_shift = yuv_type;
- bool has_mmx = supports_mmx();
- bool odd_pic_x = pic_x % 2 != 0;
+ unsigned int y_shift = yuv_type == YV12 ? 1 : 0;
+ unsigned int x_shift = yuv_type == YV24 ? 0 : 1;
+ // There is no optimized YV24 MMX routine so we check for this and
+ // fall back to the C code.
+ bool has_mmx = supports_mmx() && yuv_type != YV24;
+ bool odd_pic_x = yuv_type != YV24 && pic_x % 2 != 0;
int x_width = odd_pic_x ? pic_width - 1 : pic_width;
for (int y = pic_y; y < pic_height + pic_y; ++y) {
uint8* rgb_row = rgb_buf + (y - pic_y) * rgb_pitch;
const uint8* y_ptr = y_buf + y * y_pitch + pic_x;
- const uint8* u_ptr = u_buf + (y >> y_shift) * uv_pitch + (pic_x >> 1);
- const uint8* v_ptr = v_buf + (y >> y_shift) * uv_pitch + (pic_x >> 1);
+ const uint8* u_ptr = u_buf + (y >> y_shift) * uv_pitch + (pic_x >> x_shift);
+ const uint8* v_ptr = v_buf + (y >> y_shift) * uv_pitch + (pic_x >> x_shift);
if (odd_pic_x) {
// Handle the single odd pixel manually and use the
// fast routines for the remaining.
FastConvertYUVToRGB32Row_C(y_ptr++,
u_ptr++,
v_ptr++,
rgb_row,
- 1);
+ 1,
+ x_shift);
rgb_row += 4;
}
if (has_mmx)
FastConvertYUVToRGB32Row(y_ptr,
u_ptr,
v_ptr,
rgb_row,
x_width);
else
FastConvertYUVToRGB32Row_C(y_ptr,
u_ptr,
v_ptr,
rgb_row,
- x_width);
+ x_width,
+ x_shift);
}
// MMX used for FastConvertYUVToRGB32Row requires emms instruction.
if (has_mmx)
EMMS();
}
} // namespace gfx
--- a/gfx/ycbcr/yuv_convert.h
+++ b/gfx/ycbcr/yuv_convert.h
@@ -9,18 +9,19 @@
namespace mozilla {
namespace gfx {
// Type of YUV surface.
// The value of these enums matter as they are used to shift vertical indices.
enum YUVType {
- YV16 = 0, // YV16 is half width and full height chroma channels.
- YV12 = 1 // YV12 is half width and half height chroma channels.
+ YV12 = 0, // YV12 is half width and half height chroma channels.
+ YV16 = 1, // YV16 is half width and full height chroma channels.
+ YV24 = 2 // YV24 is full width and full height chroma channels.
};
// Convert a frame of YUV to 32 bit ARGB.
// Pass in YV16/YV12 depending on source format
void ConvertYCbCrToRGB32(const uint8* yplane,
const uint8* uplane,
const uint8* vplane,
uint8* rgbframe,
--- a/gfx/ycbcr/yuv_row.h
+++ b/gfx/ycbcr/yuv_row.h
@@ -20,17 +20,18 @@ void FastConvertYUVToRGB32Row(const uint
const uint8* v_buf,
uint8* rgb_buf,
int width);
void FastConvertYUVToRGB32Row_C(const uint8* y_buf,
const uint8* u_buf,
const uint8* v_buf,
uint8* rgb_buf,
- int width);
+ int width,
+ unsigned int x_shift);
} // extern "C"
// x64 uses MMX2 (SSE) so emms is not required.
#if !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC)
#if defined(_MSC_VER)
#define EMMS() __asm emms
--- a/gfx/ycbcr/yuv_row_c.cpp
+++ b/gfx/ycbcr/yuv_row_c.cpp
@@ -153,24 +153,29 @@ static inline void YuvPixel(uint8 y,
(clip(C298a + cr) << 16) |
(0xff000000);
}
void FastConvertYUVToRGB32Row_C(const uint8* y_buf,
const uint8* u_buf,
const uint8* v_buf,
uint8* rgb_buf,
- int width) {
+ int width,
+ unsigned int x_shift) {
for (int x = 0; x < width; x += 2) {
- uint8 u = u_buf[x >> 1];
- uint8 v = v_buf[x >> 1];
+ uint8 u = u_buf[x >> x_shift];
+ uint8 v = v_buf[x >> x_shift];
uint8 y0 = y_buf[x];
YuvPixel(y0, u, v, rgb_buf);
if ((x + 1) < width) {
uint8 y1 = y_buf[x + 1];
+ if (x_shift == 0) {
+ u = u_buf[x + 1];
+ v = v_buf[x + 1];
+ }
YuvPixel(y1, u, v, rgb_buf + 4);
}
rgb_buf += 8; // Advance 2 pixels.
}
}
} // extern "C"
new file mode 100644
--- /dev/null
+++ b/gfx/ycbcr/yv24.patch
@@ -0,0 +1,172 @@
+diff --git a/gfx/ycbcr/yuv_convert.cpp b/gfx/ycbcr/yuv_convert.cpp
+index b22e778..cdbb040 100644
+--- a/gfx/ycbcr/yuv_convert.cpp
++++ b/gfx/ycbcr/yuv_convert.cpp
+@@ -6,16 +6,17 @@
+ // http://www.fourcc.org/yuv.php
+ // The actual conversion is best described here
+ // http://en.wikipedia.org/wiki/YUV
+ // An article on optimizing YUV conversion using tables instead of multiplies
+ // http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
+ //
+ // YV12 is a full plane of Y and a half height, half width chroma planes
+ // YV16 is a full plane of Y and a full height, half width chroma planes
++// YV24 is a full plane of Y and a full height, full width chroma planes
+ //
+ // ARGB pixel format is output, which on little endian is stored as BGRA.
+ // The alpha is set to 255, allowing the application to use RGBA or RGB32.
+
+ #include "yuv_convert.h"
+
+ // Header for low level row functions.
+ #include "yuv_row.h"
+@@ -33,50 +34,55 @@ void ConvertYCbCrToRGB32(const uint8* y_buf,
+ int pic_x,
+ int pic_y,
+ int pic_width,
+ int pic_height,
+ int y_pitch,
+ int uv_pitch,
+ int rgb_pitch,
+ YUVType yuv_type) {
+- unsigned int y_shift = yuv_type;
+- bool has_mmx = supports_mmx();
+- bool odd_pic_x = pic_x % 2 != 0;
++ unsigned int y_shift = yuv_type == YV12 ? 1 : 0;
++ unsigned int x_shift = yuv_type == YV24 ? 0 : 1;
++ // There is no optimized YV24 MMX routine so we check for this and
++ // fall back to the C code.
++ bool has_mmx = supports_mmx() && yuv_type != YV24;
++ bool odd_pic_x = yuv_type != YV24 && pic_x % 2 != 0;
+ int x_width = odd_pic_x ? pic_width - 1 : pic_width;
+
+ for (int y = pic_y; y < pic_height + pic_y; ++y) {
+ uint8* rgb_row = rgb_buf + (y - pic_y) * rgb_pitch;
+ const uint8* y_ptr = y_buf + y * y_pitch + pic_x;
+- const uint8* u_ptr = u_buf + (y >> y_shift) * uv_pitch + (pic_x >> 1);
+- const uint8* v_ptr = v_buf + (y >> y_shift) * uv_pitch + (pic_x >> 1);
++ const uint8* u_ptr = u_buf + (y >> y_shift) * uv_pitch + (pic_x >> x_shift);
++ const uint8* v_ptr = v_buf + (y >> y_shift) * uv_pitch + (pic_x >> x_shift);
+
+ if (odd_pic_x) {
+ // Handle the single odd pixel manually and use the
+ // fast routines for the remaining.
+ FastConvertYUVToRGB32Row_C(y_ptr++,
+ u_ptr++,
+ v_ptr++,
+ rgb_row,
+- 1);
++ 1,
++ x_shift);
+ rgb_row += 4;
+ }
+
+ if (has_mmx)
+ FastConvertYUVToRGB32Row(y_ptr,
+ u_ptr,
+ v_ptr,
+ rgb_row,
+ x_width);
+ else
+ FastConvertYUVToRGB32Row_C(y_ptr,
+ u_ptr,
+ v_ptr,
+ rgb_row,
+- x_width);
++ x_width,
++ x_shift);
+ }
+
+ // MMX used for FastConvertYUVToRGB32Row requires emms instruction.
+ if (has_mmx)
+ EMMS();
+ }
+
+ } // namespace gfx
+diff --git a/gfx/ycbcr/yuv_convert.h b/gfx/ycbcr/yuv_convert.h
+index 6735b77..15fe381 100644
+--- a/gfx/ycbcr/yuv_convert.h
++++ b/gfx/ycbcr/yuv_convert.h
+@@ -9,18 +9,19 @@
+
+ namespace mozilla {
+
+ namespace gfx {
+
+ // Type of YUV surface.
+ // The value of these enums matter as they are used to shift vertical indices.
+ enum YUVType {
+- YV16 = 0, // YV16 is half width and full height chroma channels.
+- YV12 = 1 // YV12 is half width and half height chroma channels.
++ YV12 = 0, // YV12 is half width and half height chroma channels.
++ YV16 = 1, // YV16 is half width and full height chroma channels.
++ YV24 = 2 // YV24 is full width and full height chroma channels.
+ };
+
+ // Convert a frame of YUV to 32 bit ARGB.
+ // Pass in YV16/YV12 depending on source format
+ void ConvertYCbCrToRGB32(const uint8* yplane,
+ const uint8* uplane,
+ const uint8* vplane,
+ uint8* rgbframe,
+diff --git a/gfx/ycbcr/yuv_row.h b/gfx/ycbcr/yuv_row.h
+index 2a82972..d776dac 100644
+--- a/gfx/ycbcr/yuv_row.h
++++ b/gfx/ycbcr/yuv_row.h
+@@ -20,17 +20,18 @@ void FastConvertYUVToRGB32Row(const uint8* y_buf,
+ const uint8* v_buf,
+ uint8* rgb_buf,
+ int width);
+
+ void FastConvertYUVToRGB32Row_C(const uint8* y_buf,
+ const uint8* u_buf,
+ const uint8* v_buf,
+ uint8* rgb_buf,
+- int width);
++ int width,
++ unsigned int x_shift);
+
+
+ } // extern "C"
+
+ // x64 uses MMX2 (SSE) so emms is not required.
+ #if !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC)
+ #if defined(_MSC_VER)
+ #define EMMS() __asm emms
+diff --git a/gfx/ycbcr/yuv_row_c.cpp b/gfx/ycbcr/yuv_row_c.cpp
+index d3bdab4..36d9bda 100644
+--- a/gfx/ycbcr/yuv_row_c.cpp
++++ b/gfx/ycbcr/yuv_row_c.cpp
+@@ -153,24 +153,29 @@ static inline void YuvPixel(uint8 y,
+ (clip(C298a + cr) << 16) |
+ (0xff000000);
+ }
+
+ void FastConvertYUVToRGB32Row_C(const uint8* y_buf,
+ const uint8* u_buf,
+ const uint8* v_buf,
+ uint8* rgb_buf,
+- int width) {
++ int width,
++ unsigned int x_shift) {
+ for (int x = 0; x < width; x += 2) {
+- uint8 u = u_buf[x >> 1];
+- uint8 v = v_buf[x >> 1];
++ uint8 u = u_buf[x >> x_shift];
++ uint8 v = v_buf[x >> x_shift];
+ uint8 y0 = y_buf[x];
+ YuvPixel(y0, u, v, rgb_buf);
+ if ((x + 1) < width) {
+ uint8 y1 = y_buf[x + 1];
++ if (x_shift == 0) {
++ u = u_buf[x + 1];
++ v = v_buf[x + 1];
++ }
+ YuvPixel(y1, u, v, rgb_buf + 4);
+ }
+ rgb_buf += 8; // Advance 2 pixels.
+ }
+ }
+
+ } // extern "C"
+