diff options
author | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-18 22:17:38 +0000 |
---|---|---|
committer | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-18 22:17:38 +0000 |
commit | be5a116b8a3465308174ccc4ab63351413d6dfe7 (patch) | |
tree | 2ba1202e427db0fda4d70037a5afda93e7f576fe /media/base | |
parent | 3090eeb5afd49e828739b492eb400d8fcf734784 (diff) | |
download | chromium_src-be5a116b8a3465308174ccc4ab63351413d6dfe7.zip chromium_src-be5a116b8a3465308174ccc4ab63351413d6dfe7.tar.gz chromium_src-be5a116b8a3465308174ccc4ab63351413d6dfe7.tar.bz2 |
YUV with clipping.
All functions do 2 pixels at a time.
90 and 270 rotations implemented.
YV16 refactored. YV12 code accepts a YuvType that allows the same code to support YV16 as well.
Special case for half size removed.
Special case for doubling added. 3.62 ms versus 8.62 for general purpose code.
Review URL: http://codereview.chromium.org/113407
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16334 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/base')
-rw-r--r-- | media/base/video_frame_impl_unittest.cc | 19 | ||||
-rw-r--r-- | media/base/yuv_convert.cc | 168 | ||||
-rw-r--r-- | media/base/yuv_convert.h | 72 | ||||
-rw-r--r-- | media/base/yuv_convert_unittest.cc | 194 | ||||
-rw-r--r-- | media/base/yuv_row.h | 62 | ||||
-rw-r--r-- | media/base/yuv_row_linux.cc | 88 | ||||
-rw-r--r-- | media/base/yuv_row_mac.cc | 88 | ||||
-rw-r--r-- | media/base/yuv_row_win.cc | 327 | ||||
-rw-r--r-- | media/base/yuv_scale.cc | 166 | ||||
-rw-r--r-- | media/base/yuv_scale.h | 64 | ||||
-rw-r--r-- | media/base/yuv_scale_unittest.cc | 134 |
11 files changed, 639 insertions, 743 deletions
diff --git a/media/base/video_frame_impl_unittest.cc b/media/base/video_frame_impl_unittest.cc index 352e2368..c674460 100644 --- a/media/base/video_frame_impl_unittest.cc +++ b/media/base/video_frame_impl_unittest.cc @@ -42,15 +42,16 @@ void ExpectFrameColor(media::VideoFrame* yv12_frame, uint32 expect_rgb_color) { EXPECT_EQ(rgb_surface.height, yuv_surface.height); EXPECT_EQ(rgb_surface.planes, expect_rgb_planes); - media::ConvertYV12ToRGB32(yuv_surface.data[VideoSurface::kYPlane], - yuv_surface.data[VideoSurface::kUPlane], - yuv_surface.data[VideoSurface::kVPlane], - rgb_surface.data[VideoSurface::kRGBPlane], - rgb_surface.width, - rgb_surface.height, - yuv_surface.strides[VideoSurface::kYPlane], - yuv_surface.strides[VideoSurface::kUPlane], - rgb_surface.strides[VideoSurface::kRGBPlane]); + media::ConvertYUVToRGB32(yuv_surface.data[VideoSurface::kYPlane], + yuv_surface.data[VideoSurface::kUPlane], + yuv_surface.data[VideoSurface::kVPlane], + rgb_surface.data[VideoSurface::kRGBPlane], + rgb_surface.width, + rgb_surface.height, + yuv_surface.strides[VideoSurface::kYPlane], + yuv_surface.strides[VideoSurface::kUPlane], + rgb_surface.strides[VideoSurface::kRGBPlane], + media::YV12); for (size_t row = 0; row < rgb_surface.height; ++row) { uint32* rgb_row_data = reinterpret_cast<uint32*>( diff --git a/media/base/yuv_convert.cc b/media/base/yuv_convert.cc index 788fd99..72f89a1 100644 --- a/media/base/yuv_convert.cc +++ b/media/base/yuv_convert.cc @@ -73,72 +73,150 @@ #define DCHECK(a) #endif +// Header for low level row functions. #include "media/base/yuv_row.h" namespace media { - -// Convert a frame of YV12 (aka YUV420) to 32 bit ARGB. -void ConvertYV12ToRGB32(const uint8* y_buf, +// Convert a frame of YUV to 32 bit ARGB. +void ConvertYUVToRGB32(const uint8* y_buf, const uint8* u_buf, const uint8* v_buf, uint8* rgb_buf, - size_t width, - size_t height, + int width, + int height, int y_pitch, int uv_pitch, - int rgb_pitch) { - // Image must be multiple of 2 in width. - DCHECK((width & 1) == 0); - // Check alignment. Use memalign to allocate the buffer if you hit this - // check: - DCHECK((reinterpret_cast<uintptr_t>(rgb_buf) & 7) == 0); + int rgb_pitch, + YUVType yuv_type) { + unsigned int y_shift = yuv_type; #ifdef _OPENMP #pragma omp parallel for #endif - for (int y = 0; y < static_cast<int>(height); ++y) { - uint8* d1 = rgb_buf + y * rgb_pitch; + for (int y = 0; y < height; ++y) { + uint8* rgb_row = rgb_buf + y * rgb_pitch; const uint8* y_ptr = y_buf + y * y_pitch; - const uint8* u_ptr = u_buf + y/2 * uv_pitch; - const uint8* v_ptr = v_buf + y/2 * uv_pitch; + const uint8* u_ptr = u_buf + (y >> y_shift) * uv_pitch; + const uint8* v_ptr = v_buf + (y >> y_shift) * uv_pitch; - ConvertYV12ToRGB32Row(y_ptr, - u_ptr, - v_ptr, - d1, - width); + FastConvertYUVToRGB32Row(y_ptr, + u_ptr, + v_ptr, + rgb_row, + width); } EMMS(); } -// Convert a frame of YV16 (aka YUV422) to 32 bit ARGB. -void ConvertYV16ToRGB32(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - size_t width, - size_t height, - int y_pitch, - int uv_pitch, - int rgb_pitch) { - // Image must be multiple of 2 in width. - DCHECK((width & 1) == 0); - // Check alignment. Use memalign to allocate the buffer if you hit this - // check: - DCHECK((reinterpret_cast<uintptr_t>(rgb_buf) & 7) == 0); +// Scale a frame of YUV to 32 bit ARGB. +void ScaleYUVToRGB32(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int height, + int scaled_width, + int scaled_height, + int y_pitch, + int uv_pitch, + int rgb_pitch, + YUVType yuv_type, + Rotate view_rotate) { + unsigned int y_shift = yuv_type; + // Diagram showing origin and direction of source sampling. + // ->0 4<- + // 7 3 + // + // 6 5 + // ->1 2<- + // Rotations that start at right side of image. + if ((view_rotate == ROTATE_180) || + (view_rotate == ROTATE_270) || + (view_rotate == MIRROR_ROTATE_0) || + (view_rotate == MIRROR_ROTATE_90)) { + y_buf += width - 1; + u_buf += width / 2 - 1; + v_buf += width / 2 - 1; + width = -width; + } + // Rotations that start at bottom of image. + if ((view_rotate == ROTATE_90) || + (view_rotate == ROTATE_180) || + (view_rotate == MIRROR_ROTATE_90) || + (view_rotate == MIRROR_ROTATE_180)) { + y_buf += (height - 1) * y_pitch; + u_buf += ((height >> y_shift) - 1) * uv_pitch; + v_buf += ((height >> y_shift) - 1) * uv_pitch; + height = -height; + } + + // Handle zero sized destination. + if (scaled_width == 0 || scaled_height == 0) + return; + int scaled_dx = width * 16 / scaled_width; + int scaled_dy = height * 16 / scaled_height; + + int scaled_dx_uv = scaled_dx; + + if ((view_rotate == ROTATE_90) || + (view_rotate == ROTATE_270)) { + int tmp = scaled_height; + scaled_height = scaled_width; + scaled_width = tmp; + tmp = height; + height = width; + width = tmp; + int original_dx = scaled_dx; + int original_dy = scaled_dy; + scaled_dx = ((original_dy >> 4) * y_pitch) << 4; + scaled_dx_uv = ((original_dy >> 4) * uv_pitch) << 4; + scaled_dy = original_dx; + if (view_rotate == ROTATE_90) { + y_pitch = -1; + uv_pitch = -1; + height = -height; + } else { + y_pitch = 1; + uv_pitch = 1; + } + } + #ifdef _OPENMP #pragma omp parallel for #endif - for (int y = 0; y < static_cast<int>(height); ++y) { - uint8* d1 = rgb_buf + y * rgb_pitch; - const uint8* y_ptr = y_buf + y * y_pitch; - const uint8* u_ptr = u_buf + y * uv_pitch; - const uint8* v_ptr = v_buf + y * uv_pitch; + for (int y = 0; y < scaled_height; ++y) { + uint8* dest_pixel = rgb_buf + y * rgb_pitch; + int scaled_y = (y * height / scaled_height); + const uint8* y_ptr = y_buf + scaled_y * y_pitch; + const uint8* u_ptr = u_buf + (scaled_y >> y_shift) * uv_pitch; + const uint8* v_ptr = v_buf + (scaled_y >> y_shift) * uv_pitch; - ConvertYV12ToRGB32Row(y_ptr, - u_ptr, - v_ptr, - d1, - width); +#if USE_MMX + if (scaled_width == (width * 2)) { + DoubleYUVToRGB32Row(y_ptr, u_ptr, v_ptr, + dest_pixel, scaled_width); + } else if ((scaled_dx & 15) == 0) { // Scaling by integer scale factor. + if (scaled_dx_uv == scaled_dx) { // Not rotated. + if (scaled_dx == 16) { // Not scaled + FastConvertYUVToRGB32Row(y_ptr, u_ptr, v_ptr, + dest_pixel, scaled_width); + } else { // Simple scale down. ie half + ConvertYUVToRGB32Row(y_ptr, u_ptr, v_ptr, + dest_pixel, scaled_width, scaled_dx >> 4); + } + } else { + RotateConvertYUVToRGB32Row(y_ptr, u_ptr, v_ptr, + dest_pixel, scaled_width, + scaled_dx >> 4, scaled_dx_uv >> 4); + } +#else + if (scaled_dx == 16) { // Not scaled + FastConvertYUVToRGB32Row(y_ptr, u_ptr, v_ptr, + dest_pixel, scaled_width); +#endif + } else { + ScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr, + dest_pixel, scaled_width, scaled_dx); + } } EMMS(); } diff --git a/media/base/yuv_convert.h b/media/base/yuv_convert.h index 5f3c5c2..a757070 100644 --- a/media/base/yuv_convert.h +++ b/media/base/yuv_convert.h @@ -9,29 +9,55 @@ namespace media { -// Convert a frame of YV12 (aka YUV420) to 32 bit ARGB. -void ConvertYV12ToRGB32(const uint8* yplane, - const uint8* uplane, - const uint8* vplane, - uint8* rgbframe, - size_t frame_width, - size_t frame_height, - int ystride, - int uvstride, - int rgbstride); - -// Convert a frame of YV16 (aka YUV422) to 32 bit ARGB. -void ConvertYV16ToRGB32(const uint8* yplane, - const uint8* uplane, - const uint8* vplane, - uint8* rgbframe, - size_t frame_width, - size_t frame_height, - int ystride, - int uvstride, - int rgbstride); +// 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. +}; + +// Mirror means flip the image horizontally, as in looking in a mirror. +// Rotate happens after mirroring. +enum Rotate { + ROTATE_0, // Rotation off. + ROTATE_90, // Rotate clockwise. + ROTATE_180, // Rotate upside down. + ROTATE_270, // Rotate counter clockwise. + MIRROR_ROTATE_0, // Mirror horizontally. + MIRROR_ROTATE_90, // Mirror then Rotate clockwise. + MIRROR_ROTATE_180, // Mirror vertically. + MIRROR_ROTATE_270, // Transpose. +}; + +// Convert a frame of YUV to 32 bit ARGB. +// Pass in YV16/YV12 depending on source format +void ConvertYUVToRGB32(const uint8* yplane, + const uint8* uplane, + const uint8* vplane, + uint8* rgbframe, + int frame_width, + int frame_height, + int ystride, + int uvstride, + int rgbstride, + YUVType yuv_type); + +// Scale a frame of YUV to 32 bit ARGB. +// Supports rotation and mirroring. +void ScaleYUVToRGB32(const uint8* yplane, + const uint8* uplane, + const uint8* vplane, + uint8* rgbframe, + int frame_width, + int frame_height, + int scaled_width, + int scaled_height, + int ystride, + int uvstride, + int rgbstride, + YUVType yuv_type, + Rotate view_rotate); +} // namespace media #endif // MEDIA_BASE_YUV_CONVERT_H_ - -} // namespace media diff --git a/media/base/yuv_convert_unittest.cc b/media/base/yuv_convert_unittest.cc index 960e0b3..a5b31d7 100644 --- a/media/base/yuv_convert_unittest.cc +++ b/media/base/yuv_convert_unittest.cc @@ -18,8 +18,11 @@ // yuvhalf -yv16 -skip 24 bali.yv16.1280_720.yuv bali.yv16.640_360.yuv // Size of raw image. +// Size of raw image. static const int kWidth = 640; static const int kHeight = 360; +static const int kScaledWidth = 1024; +static const int kScaledHeight = 768; static const int kBpp = 4; // Surface sizes. @@ -28,6 +31,15 @@ static const size_t kYUV16Size = kWidth * kHeight * 16 / 8; static const size_t kRGBSize = kWidth * kHeight * kBpp; static const size_t kRGBSizeConverted = kWidth * kHeight * kBpp; +namespace { +// DJB2 hash +unsigned int hash(unsigned char *s, size_t len, unsigned int hash = 5381) { + while (len--) + hash = hash * 33 + *s++; + return hash; +} +} + TEST(YUVConvertTest, YV12) { // Allocate all surfaces. scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); @@ -46,37 +58,27 @@ TEST(YUVConvertTest, YV12) { reinterpret_cast<char*>(yuv_bytes.get()), static_cast<int>(kYUV12Size))); - // Read RGB reference data from file. - FilePath rgb_url; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &rgb_url)); - rgb_url = rgb_url.Append(FILE_PATH_LITERAL("media")) - .Append(FILE_PATH_LITERAL("test")) - .Append(FILE_PATH_LITERAL("data")) - .Append(FILE_PATH_LITERAL("bali.yv12.640_360.rgb")); - EXPECT_EQ(static_cast<int>(kRGBSize), - file_util::ReadFile(rgb_url, - reinterpret_cast<char*>(rgb_bytes.get()), - static_cast<int>(kRGBSize))); - // Convert a frame of YUV to 32 bit ARGB. - media::ConvertYV12ToRGB32(yuv_bytes.get(), // Y - yuv_bytes.get() + kWidth * kHeight, // U - yuv_bytes.get() + kWidth * kHeight * 5 / 4, // V - rgb_converted_bytes.get(), // RGB output - kWidth, kHeight, // Dimensions - kWidth, // YStride - kWidth / 2, // UVStride - kWidth * kBpp); // RGBStride - -// TODO(fbarchard): The reference image was converted with MMX. -// Non-MMX implementations do not match exactly. In the future, -// Mac and Linux will use MMX as well and the unittest will be -// activated automatically. + media::ConvertYUVToRGB32(yuv_bytes.get(), // Y + yuv_bytes.get() + kWidth * kHeight, // U + yuv_bytes.get() + kWidth * kHeight * 5 / 4, // V + rgb_converted_bytes.get(), // RGB output + kWidth, kHeight, // Dimensions + kWidth, // YStride + kWidth / 2, // UVStride + kWidth * kBpp, // RGBStride + media::YV12); + + unsigned int rgb_hash = hash(rgb_converted_bytes.get(), kRGBSizeConverted); + + // To get this hash value, run once and examine the following EXPECT_EQ. + // Then plug new hash value into EXPECT_EQ statements. + + // TODO(fbarchard): Make reference code mimic MMX exactly #if USE_MMX - // Compare converted YUV to reference conversion file. - int rgb_diff = memcmp(rgb_converted_bytes.get(), rgb_bytes.get(), kRGBSize); - - EXPECT_EQ(0, rgb_diff); + EXPECT_EQ(2413171226u, rgb_hash); +#else + EXPECT_EQ(2936300063u, rgb_hash); #endif return; // This is here to allow you to put a break point on this line } @@ -99,37 +101,117 @@ TEST(YUVConvertTest, YV16) { reinterpret_cast<char*>(yuv_bytes.get()), static_cast<int>(kYUV16Size))); - // Read RGB reference data from file. - FilePath rgb_url; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &rgb_url)); - rgb_url = rgb_url.Append(FILE_PATH_LITERAL("media")) + // Convert a frame of YUV to 32 bit ARGB. + media::ConvertYUVToRGB32(yuv_bytes.get(), // Y + yuv_bytes.get() + kWidth * kHeight, // U + yuv_bytes.get() + kWidth * kHeight * 3 / 2, // V + rgb_converted_bytes.get(), // RGB output + kWidth, kHeight, // Dimensions + kWidth, // YStride + kWidth / 2, // UVStride + kWidth * kBpp, // RGBStride + media::YV16); + + unsigned int rgb_hash = hash(rgb_converted_bytes.get(), kRGBSizeConverted); + + // To get this hash value, run once and examine the following EXPECT_EQ. + // Then plug new hash value into EXPECT_EQ statements. + + // TODO(fbarchard): Make reference code mimic MMX exactly +#if USE_MMX + EXPECT_EQ(4222342047u, rgb_hash); +#else + EXPECT_EQ(106869773u, rgb_hash); +#endif + return; // This is here to allow you to put a break point on this line +} + +TEST(YuvScaleTest, Basic) { + // Read YUV reference data from file. + FilePath yuv_url; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); + yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) .Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("data")) - .Append(FILE_PATH_LITERAL("bali.yv16.640_360.rgb")); - EXPECT_EQ(static_cast<int>(kRGBSize), - file_util::ReadFile(rgb_url, - reinterpret_cast<char*>(rgb_bytes.get()), - static_cast<int>(kRGBSize))); - - // Convert a frame of YUV to 32 bit ARGB. - media::ConvertYV16ToRGB32(yuv_bytes.get(), // Y - yuv_bytes.get() + kWidth * kHeight, // U - yuv_bytes.get() + kWidth * kHeight * 3 / 2, // V - rgb_converted_bytes.get(), // RGB output - kWidth, kHeight, // Dimensions - kWidth, // YStride - kWidth / 2, // UVStride - kWidth * kBpp); // RGBStride - -// TODO(fbarchard): The reference image was converted with MMX. -// Non-MMX implementations do not match exactly. In the future, -// Mac and Linux will use MMX as well and the unittest will be -// activated automatically. + .Append(FILE_PATH_LITERAL("bali.yv12.640_360.yuv")); + const size_t size_of_yuv = kWidth * kHeight * 12 / 8; // 12 bpp. + uint8* yuv_bytes = new uint8[size_of_yuv]; + EXPECT_EQ(static_cast<int>(size_of_yuv), + file_util::ReadFile(yuv_url, + reinterpret_cast<char*>(yuv_bytes), + static_cast<int>(size_of_yuv))); + + // Scale a frame of YUV to 32 bit ARGB. + const size_t size_of_rgb_scaled = kScaledWidth * kScaledHeight * kBpp; + uint8* rgb_scaled_bytes = new uint8[size_of_rgb_scaled]; + + media::ScaleYUVToRGB32(yuv_bytes, // Y plane + yuv_bytes + kWidth * kHeight, // U plane + yuv_bytes + kWidth * kHeight * 5 / 4, // V plane + rgb_scaled_bytes, // Rgb output + kWidth, kHeight, // Dimensions + kScaledWidth, kScaledHeight, // Dimensions + kWidth, // YStride + kWidth / 2, // UvStride + kScaledWidth * kBpp, // RgbStride + media::YV12, + media::ROTATE_0); + + unsigned int rgb_hash = hash(rgb_scaled_bytes, size_of_rgb_scaled); + + // To get this hash value, run once and examine the following EXPECT_EQ. + // Then plug new hash value into EXPECT_EQ statements. + + // TODO(fbarchard): Make reference code mimic MMX exactly #if USE_MMX - // Compare converted YUV to reference conversion file. - int rgb_diff = memcmp(rgb_converted_bytes.get(), rgb_bytes.get(), kRGBSize); + EXPECT_EQ(4259656254u, rgb_hash); +#else + EXPECT_EQ(197274901u, rgb_hash); +#endif + return; // This is here to allow you to put a break point on this line +} - EXPECT_EQ(0, rgb_diff); +TEST(YV16ScaleTest, Basic) { + // Read YV16 reference data from file. + FilePath yuv_url; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); + yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .Append(FILE_PATH_LITERAL("bali.yv16.640_360.yuv")); + const size_t size_of_yuv = kWidth * kHeight * 16 / 8; // 16 bpp. + uint8* yuv_bytes = new uint8[size_of_yuv]; + EXPECT_EQ(static_cast<int>(size_of_yuv), + file_util::ReadFile(yuv_url, + reinterpret_cast<char*>(yuv_bytes), + static_cast<int>(size_of_yuv))); + + // Scale a frame of YUV to 32 bit ARGB. + const size_t size_of_rgb_scaled = kScaledWidth * kScaledHeight * kBpp; + uint8* rgb_scaled_bytes = new uint8[size_of_rgb_scaled]; + + media::ScaleYUVToRGB32(yuv_bytes, // Y plane + yuv_bytes + kWidth * kHeight, // U plane + yuv_bytes + kWidth * kHeight * 3 / 2, // V plane + rgb_scaled_bytes, // Rgb output + kWidth, kHeight, // Dimensions + kScaledWidth, kScaledHeight, // Dimensions + kWidth, // YStride + kWidth / 2, // UvStride + kScaledWidth * kBpp, // RgbStride + media::YV16, + media::ROTATE_0); + + unsigned int rgb_hash = hash(rgb_scaled_bytes, size_of_rgb_scaled); + + // To get this hash value, run once and examine the following EXPECT_EQ. + // Then plug new hash value into EXPECT_EQ statements. + + // TODO(fbarchard): Make reference code mimic MMX exactly +#if USE_MMX + EXPECT_EQ(974965419u, rgb_hash); +#else + EXPECT_EQ(2946450771u, rgb_hash); #endif return; } diff --git a/media/base/yuv_row.h b/media/base/yuv_row.h index 11fc71f..b8651f5 100644 --- a/media/base/yuv_row.h +++ b/media/base/yuv_row.h @@ -12,33 +12,55 @@ namespace media { -void ConvertYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width); +// Can only do 1x. +// This is the second fastest of the scalers. +void FastConvertYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width); -void HalfYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width); +// Can do 1x, half size or any scale down by an integer amount. +// Step can be negative (mirroring, rotate 180). +// This is the third fastest of the scalers. +void ConvertYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int step); + +// Rotate is like Convert, but applies different step to Y versus U and V. +// This allows rotation by 90 or 270, by stepping by stride. +// This is the forth fastest of the scalers. +void RotateConvertYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int ystep, + int uvstep); -void ScaleYV12ToRGB32Row(const uint8* y_buf, +// Doubler does 4 pixels at a time. Each pixel is replicated. +// This is the fastest of the scalers. +void DoubleYUVToRGB32Row(const uint8* y_buf, const uint8* u_buf, const uint8* v_buf, uint8* rgb_buf, - int width, - int scaled_dx); + int width); -void Half2Row(const uint8* in_row0, - const uint8* in_row1, - uint8* out_row, - int out_width); - -// MMX for Windows -// C++ code provided as a fall back. +// Handles arbitrary scaling up or down. +// Mirroring is supported, but not 90 or 270 degree rotation. +// Chroma is under sampled every 2 pixels for performance. +// This is the slowest of the scalers. +void ScaleYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int scaled_dx); +// MMX for Windows; C++ for other platforms. #ifndef USE_MMX #if defined(_MSC_VER) #define USE_MMX 1 diff --git a/media/base/yuv_row_linux.cc b/media/base/yuv_row_linux.cc index 4acc454..db7c753 100644 --- a/media/base/yuv_row_linux.cc +++ b/media/base/yuv_row_linux.cc @@ -150,101 +150,39 @@ static inline void YuvPixel(uint8 y, (0xff000000); } -void ConvertYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width) { +void FastConvertYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width) { for (int32 x = 0; x < static_cast<int32>(width); x += 2) { uint8 u = u_buf[x >> 1]; uint8 v = v_buf[x >> 1]; - int32 d = static_cast<int32>(u) - 128; - int32 e = static_cast<int32>(v) - 128; - - int32 cb = (516 * d + 128); - int32 cg = (- 100 * d - 208 * e + 128); - int32 cr = (409 * e + 128); - uint8 y0 = y_buf[x]; - int32 C298a = ((static_cast<int32>(y0) - 16) * 298 + 128); - *reinterpret_cast<uint32*>(rgb_buf) = clip(C298a + cb) | - (clip(C298a + cg) << 8) | - (clip(C298a + cr) << 16) | - 0xff000000; - uint8 y1 = y_buf[x + 1]; - int32 C298b = ((static_cast<int32>(y1) - 16) * 298 + 128); - *reinterpret_cast<uint32*>(rgb_buf + 4) = clip(C298b + cb) | - (clip(C298b + cg) << 8) | - (clip(C298b + cr) << 16) | - 0xff000000; + YuvPixel(y0, u, v, rgb_buf); + YuvPixel(y1, u, v, rgb_buf + 4); rgb_buf += 8; // Advance 2 pixels. } } -void HalfYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width) { - for (int32 x = 0; x < width; ++x) { - uint8 u = u_buf[x]; - uint8 v = v_buf[x]; - int32 d = static_cast<int32>(u) - 128; - int32 e = static_cast<int32>(v) - 128; - - int32 cb = (516 * d + 128); - int32 cg = (- 100 * d - 208 * e + 128); - int32 cr = (409 * e + 128); - - uint8 y0 = y_buf[x * 2 + 0]; - uint8 y1 = y_buf[x * 2 + 1]; - int32 C298a = ((static_cast<int32>((y0 + y1) >> 1) - 16) * 298 + 128); - *reinterpret_cast<uint32*>(rgb_buf) = clip(C298a + cb) | - (clip(C298a + cg) << 8) | - (clip(C298a + cr) << 16) | - 0xff000000; - - rgb_buf += 4; - } -} - // 28.4 fixed point is used. A shift by 4 isolates the integer. // A shift by 5 is used to further subsample the chrominence channels. // & 15 isolates the fixed point fraction. >> 2 to get the upper 2 bits, // for 1/4 pixel accurate interpolation. -void ScaleYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width, - int scaled_dx) { +void ScaleYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int scaled_dx) { int scaled_x = 0; for (int32 x = 0; x < width; ++x) { uint8 u = u_buf[scaled_x >> 5]; uint8 v = v_buf[scaled_x >> 5]; uint8 y0 = y_buf[scaled_x >> 4]; -#if MEDIA_BILINEAR_FILTER - uint8 y1 = y_buf[(scaled_x >> 4) + 1]; - switch ((scaled_x & 15) >> 2) { - case 1: // 75% first pixel, 25% second pixel. - y0 = (y0 + y0 + y0 + y1) >> 2; - break; - case 2: // 50/50 blend - y0 = (y0 + y1) >> 1; - break; - case 3: // 25% first pixel, 75% second pixel. - y0 = (y0 + y1 + y1 + y1) >> 2; - break; - default: - case 0: // 100% first pixel. - break; - } -#endif // MEDIA_BILINEAR_FILTER - YuvPixel(y0, u, v, rgb_buf); - rgb_buf += 4; scaled_x += scaled_dx; } diff --git a/media/base/yuv_row_mac.cc b/media/base/yuv_row_mac.cc index 4acc454..db7c753 100644 --- a/media/base/yuv_row_mac.cc +++ b/media/base/yuv_row_mac.cc @@ -150,101 +150,39 @@ static inline void YuvPixel(uint8 y, (0xff000000); } -void ConvertYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width) { +void FastConvertYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width) { for (int32 x = 0; x < static_cast<int32>(width); x += 2) { uint8 u = u_buf[x >> 1]; uint8 v = v_buf[x >> 1]; - int32 d = static_cast<int32>(u) - 128; - int32 e = static_cast<int32>(v) - 128; - - int32 cb = (516 * d + 128); - int32 cg = (- 100 * d - 208 * e + 128); - int32 cr = (409 * e + 128); - uint8 y0 = y_buf[x]; - int32 C298a = ((static_cast<int32>(y0) - 16) * 298 + 128); - *reinterpret_cast<uint32*>(rgb_buf) = clip(C298a + cb) | - (clip(C298a + cg) << 8) | - (clip(C298a + cr) << 16) | - 0xff000000; - uint8 y1 = y_buf[x + 1]; - int32 C298b = ((static_cast<int32>(y1) - 16) * 298 + 128); - *reinterpret_cast<uint32*>(rgb_buf + 4) = clip(C298b + cb) | - (clip(C298b + cg) << 8) | - (clip(C298b + cr) << 16) | - 0xff000000; + YuvPixel(y0, u, v, rgb_buf); + YuvPixel(y1, u, v, rgb_buf + 4); rgb_buf += 8; // Advance 2 pixels. } } -void HalfYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width) { - for (int32 x = 0; x < width; ++x) { - uint8 u = u_buf[x]; - uint8 v = v_buf[x]; - int32 d = static_cast<int32>(u) - 128; - int32 e = static_cast<int32>(v) - 128; - - int32 cb = (516 * d + 128); - int32 cg = (- 100 * d - 208 * e + 128); - int32 cr = (409 * e + 128); - - uint8 y0 = y_buf[x * 2 + 0]; - uint8 y1 = y_buf[x * 2 + 1]; - int32 C298a = ((static_cast<int32>((y0 + y1) >> 1) - 16) * 298 + 128); - *reinterpret_cast<uint32*>(rgb_buf) = clip(C298a + cb) | - (clip(C298a + cg) << 8) | - (clip(C298a + cr) << 16) | - 0xff000000; - - rgb_buf += 4; - } -} - // 28.4 fixed point is used. A shift by 4 isolates the integer. // A shift by 5 is used to further subsample the chrominence channels. // & 15 isolates the fixed point fraction. >> 2 to get the upper 2 bits, // for 1/4 pixel accurate interpolation. -void ScaleYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width, - int scaled_dx) { +void ScaleYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int scaled_dx) { int scaled_x = 0; for (int32 x = 0; x < width; ++x) { uint8 u = u_buf[scaled_x >> 5]; uint8 v = v_buf[scaled_x >> 5]; uint8 y0 = y_buf[scaled_x >> 4]; -#if MEDIA_BILINEAR_FILTER - uint8 y1 = y_buf[(scaled_x >> 4) + 1]; - switch ((scaled_x & 15) >> 2) { - case 1: // 75% first pixel, 25% second pixel. - y0 = (y0 + y0 + y0 + y1) >> 2; - break; - case 2: // 50/50 blend - y0 = (y0 + y1) >> 1; - break; - case 3: // 25% first pixel, 75% second pixel. - y0 = (y0 + y1 + y1 + y1) >> 2; - break; - default: - case 0: // 100% first pixel. - break; - } -#endif // MEDIA_BILINEAR_FILTER - YuvPixel(y0, u, v, rgb_buf); - rgb_buf += 4; scaled_x += scaled_dx; } diff --git a/media/base/yuv_row_win.cc b/media/base/yuv_row_win.cc index beae687..53dadc4 100644 --- a/media/base/yuv_row_win.cc +++ b/media/base/yuv_row_win.cc @@ -246,11 +246,11 @@ MMX_ALIGNED(int16 coefficients_RGB_V[256][4]) = { #pragma warning(disable: 4799) __declspec(naked) -void ConvertYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width) { +void FastConvertYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width) { __asm { pushad mov edx, [esp + 32 + 4] // Y @@ -258,17 +258,17 @@ void ConvertYV12ToRGB32Row(const uint8* y_buf, mov esi, [esp + 32 + 12] // V mov ebp, [esp + 32 + 16] // rgb mov ecx, [esp + 32 + 20] // width - shr ecx, 1 + jmp wend wloop : - movzx eax, byte ptr [edi] // NOLINT + movzx eax, byte ptr [edi] add edi, 1 - movzx ebx, byte ptr [esi] // NOLINT + movzx ebx, byte ptr [esi] add esi, 1 movq mm0, [coefficients_RGB_U + 8 * eax] - movzx eax, byte ptr [edx] // NOLINT + movzx eax, byte ptr [edx] paddsw mm0, [coefficients_RGB_V + 8 * ebx] - movzx ebx, byte ptr [edx + 1] // NOLINT + movzx ebx, byte ptr [edx + 1] movq mm1, [coefficients_RGB_Y + 8 * eax] add edx, 2 movq mm2, [coefficients_RGB_Y + 8 * ebx] @@ -277,10 +277,26 @@ void ConvertYV12ToRGB32Row(const uint8* y_buf, psraw mm1, 6 psraw mm2, 6 packuswb mm1, mm2 - movntq [ebp], mm1 // NOLINT + movntq [ebp], mm1 add ebp, 8 - sub ecx, 1 - jnz wloop + wend : + sub ecx, 2 + jns wloop + + and ecx, 1 // odd number of pixels? + jz wdone + + movzx eax, byte ptr [edi] + movq mm0, [coefficients_RGB_U + 8 * eax] + movzx eax, byte ptr [esi] + paddsw mm0, [coefficients_RGB_V + 8 * eax] + movzx eax, byte ptr [edx] + movq mm1, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + psraw mm1, 6 + packuswb mm1, mm1 + movd [ebp], mm1 + wdone : popad ret @@ -288,11 +304,12 @@ void ConvertYV12ToRGB32Row(const uint8* y_buf, } __declspec(naked) -void HalfYV12ToRGB32Row(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width) { +void ConvertYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int step) { __asm { pushad mov edx, [esp + 32 + 4] // Y @@ -300,28 +317,47 @@ void HalfYV12ToRGB32Row(const uint8* y_buf, mov esi, [esp + 32 + 12] // V mov ebp, [esp + 32 + 16] // rgb mov ecx, [esp + 32 + 20] // width + mov ebx, [esp + 32 + 24] // step + jmp wend wloop : movzx eax, byte ptr [edi] - add edi, 1 - movzx ebx, byte ptr [esi] - add esi, 1 + add edi, ebx movq mm0, [coefficients_RGB_U + 8 * eax] + movzx eax, byte ptr [esi] + add esi, ebx + paddsw mm0, [coefficients_RGB_V + 8 * eax] movzx eax, byte ptr [edx] - paddsw mm0, [coefficients_RGB_V + 8 * ebx] -#if MEDIA_BILINEAR_FILTER - movzx ebx, byte ptr [edx + 1] - add ebx, eax - shr ebx, 1 -#endif - paddsw mm0, [coefficients_RGB_Y + 8 * eax] - add edx, 2 - psraw mm0, 6 - packuswb mm0, mm0 - movd [ebp], mm0 - add ebp, 4 - sub ecx, 1 - jnz wloop + add edx, ebx + movq mm1, [coefficients_RGB_Y + 8 * eax] + movzx eax, byte ptr [edx] + add edx, ebx + movq mm2, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + paddsw mm2, mm0 + psraw mm1, 6 + psraw mm2, 6 + packuswb mm1, mm2 + movntq [ebp], mm1 + add ebp, 8 + wend : + sub ecx, 2 + jns wloop + + and ecx, 1 // odd number of pixels? + jz wdone + + movzx eax, byte ptr [edi] + movq mm0, [coefficients_RGB_U + 8 * eax] + movzx eax, byte ptr [esi] + paddsw mm0, [coefficients_RGB_V + 8 * eax] + movzx eax, byte ptr [edx] + movq mm1, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + psraw mm1, 6 + packuswb mm1, mm1 + movd [ebp], mm1 + wdone : popad ret @@ -329,12 +365,74 @@ void HalfYV12ToRGB32Row(const uint8* y_buf, } __declspec(naked) -void ScaleYV12ToRGB32Row(const uint8* y_buf, +void RotateConvertYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int ystep, + int uvstep) { + __asm { + pushad + mov edx, [esp + 32 + 4] // Y + mov edi, [esp + 32 + 8] // U + mov esi, [esp + 32 + 12] // V + mov ebp, [esp + 32 + 16] // rgb + mov ecx, [esp + 32 + 20] // width + jmp wend + + wloop : + movzx eax, byte ptr [edi] + mov ebx, [esp + 32 + 28] // uvstep + add edi, ebx + movq mm0, [coefficients_RGB_U + 8 * eax] + movzx eax, byte ptr [esi] + add esi, ebx + paddsw mm0, [coefficients_RGB_V + 8 * eax] + movzx eax, byte ptr [edx] + mov ebx, [esp + 32 + 24] // ystep + add edx, ebx + movq mm1, [coefficients_RGB_Y + 8 * eax] + movzx eax, byte ptr [edx] + add edx, ebx + movq mm2, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + paddsw mm2, mm0 + psraw mm1, 6 + psraw mm2, 6 + packuswb mm1, mm2 + movntq [ebp], mm1 + add ebp, 8 + wend : + sub ecx, 2 + jns wloop + + and ecx, 1 // odd number of pixels? + jz wdone + + movzx eax, byte ptr [edi] + movq mm0, [coefficients_RGB_U + 8 * eax] + movzx eax, byte ptr [esi] + paddsw mm0, [coefficients_RGB_V + 8 * eax] + movzx eax, byte ptr [edx] + movq mm1, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + psraw mm1, 6 + packuswb mm1, mm1 + movd [ebp], mm1 + wdone : + + popad + ret + } +} + +__declspec(naked) +void DoubleYUVToRGB32Row(const uint8* y_buf, const uint8* u_buf, const uint8* v_buf, uint8* rgb_buf, - int width, - int dx) { + int width) { __asm { pushad mov edx, [esp + 32 + 4] // Y @@ -342,62 +440,139 @@ void ScaleYV12ToRGB32Row(const uint8* y_buf, mov esi, [esp + 32 + 12] // V mov ebp, [esp + 32 + 16] // rgb mov ecx, [esp + 32 + 20] // width - xor eax, eax // x + jmp wend wloop : - mov ebx, eax - sar ebx, 5 - movzx ebx, byte ptr [edi + ebx] - movq mm0, [coefficients_RGB_U + 8 * ebx] - mov ebx, eax - sar ebx, 5 - movzx ebx, byte ptr [esi + ebx] + movzx eax, byte ptr [edi] + add edi, 1 + movzx ebx, byte ptr [esi] + add esi, 1 + movq mm0, [coefficients_RGB_U + 8 * eax] + movzx eax, byte ptr [edx] paddsw mm0, [coefficients_RGB_V + 8 * ebx] - mov ebx, eax - sar ebx, 4 - movzx ebx, byte ptr [edx + ebx] + movq mm1, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + psraw mm1, 6 + packuswb mm1, mm1 + punpckldq mm1, mm1 + movntq [ebp], mm1 + + movzx ebx, byte ptr [edx + 1] + add edx, 2 paddsw mm0, [coefficients_RGB_Y + 8 * ebx] psraw mm0, 6 packuswb mm0, mm0 - movd [ebp], mm0 + punpckldq mm0, mm0 + movntq [ebp+8], mm0 + add ebp, 16 + wend : + sub ecx, 4 + jns wloop + + add ecx, 4 + jz wdone + + movzx eax, byte ptr [edi] + movq mm0, [coefficients_RGB_U + 8 * eax] + movzx eax, byte ptr [esi] + paddsw mm0, [coefficients_RGB_V + 8 * eax] + movzx eax, byte ptr [edx] + movq mm1, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + psraw mm1, 6 + packuswb mm1, mm1 + jmp wend1 + + wloop1 : + movd [ebp], mm1 add ebp, 4 - add eax, [esp + 32 + 24] // x += dx + wend1 : sub ecx, 1 - jnz wloop - + jns wloop1 + wdone : popad ret } } - +// This version does general purpose scaling by any amount, up or down. +// The only thing it can not do it rotation by 90 or 270. +// For performance the chroma is under sampled, reducing cost of a 3x +// 1080p scale from 8.4 ms to 5.4 ms. __declspec(naked) -void Half2Row(const uint8* in_row0, - const uint8* in_row1, - uint8* out_row, - int out_width) { +void ScaleYUVToRGB32Row(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int width, + int dx) { __asm { pushad - mov esi, [esp + 32 + 4] // row0 - mov ebx, [esp + 32 + 8] // row1 - mov edi, [esp + 32 + 12] // out - mov ecx, [esp + 32 + 16] // width + mov edx, [esp + 32 + 4] // Y + mov edi, [esp + 32 + 8] // U + mov esi, [esp + 32 + 12] // V + mov ebp, [esp + 32 + 16] // rgb + mov ecx, [esp + 32 + 20] // width + xor ebx, ebx // x + jmp wend wloop : - movzx eax, byte ptr [esi] - movzx edx, byte ptr [esi+1] - add esi, 2 - add eax, edx - movzx edx, byte ptr [ebx] - add eax, edx - movzx edx, byte ptr [ebx+1] - add eax, edx - add ebx, 2 - shr eax, 2 - mov [edi], al - add edi, 1 - sub ecx, 1 - jnz wloop + mov eax, ebx + sar eax, 5 + movzx eax, byte ptr [edi + eax] + movq mm0, [coefficients_RGB_U + 8 * eax] + mov eax, ebx + sar eax, 5 + movzx eax, byte ptr [esi + eax] + paddsw mm0, [coefficients_RGB_V + 8 * eax] + mov eax, ebx + add ebx, [esp + 32 + 24] // x += dx + sar eax, 4 + movzx eax, byte ptr [edx + eax] + movq mm1, [coefficients_RGB_Y + 8 * eax] + mov eax, ebx + add ebx, [esp + 32 + 24] // x += dx + sar eax, 4 + movzx eax, byte ptr [edx + eax] + movq mm2, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + paddsw mm2, mm0 + psraw mm1, 6 + psraw mm2, 6 + packuswb mm1, mm2 + movntq [ebp], mm1 + add ebp, 8 + wend : + sub ecx, 2 + jns wloop + + and ecx, 1 // odd number of pixels? + jz wdone + + mov eax, ebx + sar eax, 5 + movzx eax, byte ptr [edi + eax] + movq mm0, [coefficients_RGB_U + 8 * eax] + mov eax, ebx + sar eax, 5 + movzx eax, byte ptr [esi + eax] + paddsw mm0, [coefficients_RGB_V + 8 * eax] + mov eax, ebx + sar eax, 4 + movzx eax, byte ptr [edx + eax] + movq mm1, [coefficients_RGB_Y + 8 * eax] + mov eax, ebx + sar eax, 4 + movzx eax, byte ptr [edx + eax] + movq mm2, [coefficients_RGB_Y + 8 * eax] + paddsw mm1, mm0 + paddsw mm2, mm0 + psraw mm1, 6 + psraw mm2, 6 + packuswb mm1, mm2 + movd [ebp], mm1 + + wdone : popad ret diff --git a/media/base/yuv_scale.cc b/media/base/yuv_scale.cc deleted file mode 100644 index ffe4f05..0000000 --- a/media/base/yuv_scale.cc +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/base/yuv_scale.h" -// yuv_row.h included to detect USE_MMX -#include "media/base/yuv_row.h" - -#ifdef _OPENMP -#include <omp.h> -#endif - -#ifdef _DEBUG -#include "base/logging.h" -#else -#define DCHECK(a) -#endif - -// TODO(fbarchard): Determine is HALF_TEST will be used or removed. -// Half test is a prototype function that may or may not be useful in the -// future. It is slower, but higher quality. The low level function -// Half2Row() is in yuv_row.h and yuv_row_win.cc. -// The function is small, so it has been left in, but if it turns out to -// be useless, it should be removed in the future. -// #define HALF_TEST 1 - -namespace media { - -// Scale a frame of YV12 (aka YUV420) to 32 bit ARGB. -void ScaleYV12ToRGB32(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width, - int height, - int scaled_width, - int scaled_height, - int y_pitch, - int uv_pitch, - int rgb_pitch, - Rotate view_rotate) { - // Rotations that start at right side of image. - if ((view_rotate == ROTATE_180) || - (view_rotate == ROTATE_270) || - (view_rotate == MIRROR_ROTATE_0) || - (view_rotate == MIRROR_ROTATE_90)) { - y_buf += width - 1; - u_buf += width / 2 - 1; - v_buf += width / 2 - 1; - width = -width; - } - // Rotations that start at bottom of image. - if ((view_rotate == ROTATE_90) || - (view_rotate == ROTATE_180) || - (view_rotate == MIRROR_ROTATE_90) || - (view_rotate == MIRROR_ROTATE_180)) { - y_buf += (height - 1) * y_pitch; - u_buf += (height / 2 - 1) * uv_pitch; - v_buf += (height / 2 - 1) * uv_pitch; - height = -height; - } - // Only these rotations are implemented. - DCHECK((view_rotate == ROTATE_0) || - (view_rotate == ROTATE_180) || - (view_rotate == MIRROR_ROTATE_0) || - (view_rotate == MIRROR_ROTATE_180)); - - int scaled_dx = width * 16 / scaled_width; -#ifdef _OPENMP -#pragma omp parallel for -#endif - for (int y = 0; y < scaled_height; ++y) { - uint8* dest_pixel = rgb_buf + y * rgb_pitch; - int scaled_y = (y * height / scaled_height); - const uint8* y_ptr = y_buf + scaled_y * y_pitch; - const uint8* u_ptr = u_buf + scaled_y / 2 * uv_pitch; - const uint8* v_ptr = v_buf + scaled_y / 2 * uv_pitch; - if (scaled_width == width) { - ConvertYV12ToRGB32Row(y_ptr, u_ptr, v_ptr, - dest_pixel, scaled_width); - } else if (scaled_width == (width / 2)) { -#if HALF_TEST - uint8 y_half[2048]; - uint8 u_half[1024]; - uint8 v_half[1024]; - Half2Row(y_ptr, y_ptr + y_pitch, y_half, scaled_width); - Half2Row(u_ptr, u_ptr + uv_pitch, u_half, scaled_width / 2); - Half2Row(v_ptr, v_ptr + uv_pitch, v_half, scaled_width / 2); - ConvertYV12ToRGB32Row(y_half, u_half, v_half, - dest_pixel, scaled_width); -#else - HalfYV12ToRGB32Row(y_ptr, u_ptr, v_ptr, - dest_pixel, scaled_width); -#endif - } else { - ScaleYV12ToRGB32Row(y_ptr, u_ptr, v_ptr, - dest_pixel, scaled_width, scaled_dx); - } - } - EMMS(); -} - -// Scale a frame of YV16 (aka YUV422) to 32 bit ARGB. -void ScaleYV16ToRGB32(const uint8* y_buf, - const uint8* u_buf, - const uint8* v_buf, - uint8* rgb_buf, - int width, - int height, - int scaled_width, - int scaled_height, - int y_pitch, - int uv_pitch, - int rgb_pitch, - Rotate view_rotate) { - // Rotations that start at right side of image. - if ((view_rotate == ROTATE_180) || - (view_rotate == ROTATE_270) || - (view_rotate == MIRROR_ROTATE_0) || - (view_rotate == MIRROR_ROTATE_90)) { - y_buf += width - 1; - u_buf += width / 2 - 1; - v_buf += width / 2 - 1; - width = -width; - } - // Rotations that start at bottom of image. - if ((view_rotate == ROTATE_90) || - (view_rotate == ROTATE_180) || - (view_rotate == MIRROR_ROTATE_90) || - (view_rotate == MIRROR_ROTATE_180)) { - y_buf += (height - 1) * y_pitch; - u_buf += (height - 1) * uv_pitch; - v_buf += (height - 1) * uv_pitch; - height = -height; - } - // Only these rotations are implemented. - DCHECK((view_rotate == ROTATE_0) || - (view_rotate == ROTATE_180) || - (view_rotate == MIRROR_ROTATE_0) || - (view_rotate == MIRROR_ROTATE_180)); - int scaled_dx = width * 16 / scaled_width; -#ifdef _OPENMP -#pragma omp parallel for -#endif - for (int y = 0; y < scaled_height; ++y) { - uint8* dest_pixel = rgb_buf + y * rgb_pitch; - int scaled_y = (y * height / scaled_height); - const uint8* y_ptr = y_buf + scaled_y * y_pitch; - const uint8* u_ptr = u_buf + scaled_y * uv_pitch; - const uint8* v_ptr = v_buf + scaled_y * uv_pitch; - if (scaled_width == width) { - ConvertYV12ToRGB32Row(y_ptr, u_ptr, v_ptr, - dest_pixel, scaled_width); - } else if (scaled_width == (width / 2)) { - HalfYV12ToRGB32Row(y_ptr, u_ptr, v_ptr, - dest_pixel, scaled_width); - } else { - ScaleYV12ToRGB32Row(y_ptr, u_ptr, v_ptr, - dest_pixel, scaled_width, scaled_dx); - } - } - EMMS(); -} - -} // namespace media - diff --git a/media/base/yuv_scale.h b/media/base/yuv_scale.h deleted file mode 100644 index 8913620..0000000 --- a/media/base/yuv_scale.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_BASE_YUV_SCALE_H_ -#define MEDIA_BASE_YUV_SCALE_H_ - -#include "base/basictypes.h" - -namespace media { - -// Mirror means flip the image horizontally, as in looking in a mirror. -// Rotate happens after mirroring. - -enum Rotate { - ROTATE_0, // Rotation off. - ROTATE_90, // Rotate clockwise. - ROTATE_180, // Rotate upside down. - ROTATE_270, // Rotate counter clockwise. - MIRROR_ROTATE_0, // Mirror horizontally. - MIRROR_ROTATE_90, // Mirror then Rotate clockwise. - MIRROR_ROTATE_180, // Mirror vertically. - MIRROR_ROTATE_270, // Transpose. -}; - -// Diagram showing origin and direction of source sampling. -// ->0 4<- -// 7 3 -// -// 6 5 -// ->1 2<- - -// Scale a frame of YV12 (aka YUV420) to 32 bit ARGB. -void ScaleYV12ToRGB32(const uint8* yplane, - const uint8* uplane, - const uint8* vplane, - uint8* rgbframe, - int frame_width, - int frame_height, - int scaled_width, - int scaled_height, - int ystride, - int uvstride, - int rgbstride, - Rotate view_rotate); - -// Scale a frame of YV16 (aka YUV422) to 32 bit ARGB. -void ScaleYV16ToRGB32(const uint8* yplane, - const uint8* uplane, - const uint8* vplane, - uint8* rgbframe, - int frame_width, - int frame_height, - int scaled_width, - int scaled_height, - int ystride, - int uvstride, - int rgbstride, - Rotate view_rotate); - -} // namespace media - -#endif // MEDIA_BASE_YUV_SCALE_H_ - diff --git a/media/base/yuv_scale_unittest.cc b/media/base/yuv_scale_unittest.cc deleted file mode 100644 index 2be0b7b..0000000 --- a/media/base/yuv_scale_unittest.cc +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/base_paths.h" -#include "base/file_util.h" -#include "media/base/yuv_scale.h" -#include "media/base/yuv_row.h" -#include "testing/gtest/include/gtest/gtest.h" - -// Reference images were created with the following steps -// ffmpeg -vframes 25 -i bali.mov -vcodec rawvideo -pix_fmt yuv420p -an -// bali.yv12.1280_720.yuv -// yuvhalf -yv12 -skip 24 bali.yv12.1280_720.yuv bali.yv12.640_360.yuv - -// ffmpeg -vframes 25 -i bali.mov -vcodec rawvideo -pix_fmt yuv422p -an -// bali.yv16.1280_720.yuv -// yuvhalf -yv16 -skip 24 bali.yv16.1280_720.yuv bali.yv16.640_360.yuv - -// After running the scale functions in the unit test below, a hash -// is taken and compared to a reference value. -// If the image or code changes, the hash will require an update. -// A process outside the unittest is needed to confirm the new code -// and data are correctly working. At this time, the author used -// the media_player with the same parameters and visually inspected the -// quality. Once satisified that the quality is correct, the hash -// can be updated and used to ensure quality remains the same on -// all future builds and ports. - -// Size of raw image. -static const int kWidth = 640; -static const int kHeight = 360; -static const int kScaledWidth = 1024; -static const int kScaledHeight = 768; -static const int kBpp = 4; - -// TODO(fbarchard): Move hash function to somewhere common. -namespace { -// DJB2 hash -unsigned int hash(unsigned char *s, size_t len, unsigned int hash = 5381) { - while (len--) - hash = hash * 33 + *s++; - return hash; -} -} - -TEST(YuvScaleTest, Basic) { - // Read YUV reference data from file. - FilePath yuv_url; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); - yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) - .Append(FILE_PATH_LITERAL("test")) - .Append(FILE_PATH_LITERAL("data")) - .Append(FILE_PATH_LITERAL("bali.yv12.640_360.yuv")); - const size_t size_of_yuv = kWidth * kHeight * 12 / 8; // 12 bpp. - uint8* yuv_bytes = new uint8[size_of_yuv]; - EXPECT_EQ(static_cast<int>(size_of_yuv), - file_util::ReadFile(yuv_url, - reinterpret_cast<char*>(yuv_bytes), - static_cast<int>(size_of_yuv))); - - // Scale a frame of YUV to 32 bit ARGB. - const size_t size_of_rgb_scaled = kScaledWidth * kScaledHeight * kBpp; - uint8* rgb_scaled_bytes = new uint8[size_of_rgb_scaled]; - - media::ScaleYV12ToRGB32(yuv_bytes, // Y plane - yuv_bytes + kWidth * kHeight, // U plane - yuv_bytes + kWidth * kHeight * 5 / 4, // V plane - rgb_scaled_bytes, // Rgb output - kWidth, kHeight, // Dimensions - kScaledWidth, kScaledHeight, // Dimensions - kWidth, // YStride - kWidth / 2, // UvStride - kScaledWidth * kBpp, // RgbStride - media::ROTATE_0); - - unsigned int rgb_hash = hash(rgb_scaled_bytes, size_of_rgb_scaled); - - // To get this hash value, run once and examine the following EXPECT_EQ. - // Then plug new hash value into EXPECT_EQ statements. - - // TODO(fbarchard): Make reference code mimic MMX exactly -#if USE_MMX - EXPECT_EQ(379971680u, rgb_hash); -#else - EXPECT_EQ(197274901u, rgb_hash); -#endif - return; // This is here to allow you to put a break point on this line -} - -TEST(YV16ScaleTest, Basic) { - // Read YV16 reference data from file. - FilePath yuv_url; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); - yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) - .Append(FILE_PATH_LITERAL("test")) - .Append(FILE_PATH_LITERAL("data")) - .Append(FILE_PATH_LITERAL("bali.yv16.640_360.yuv")); - const size_t size_of_yuv = kWidth * kHeight * 16 / 8; // 16 bpp. - uint8* yuv_bytes = new uint8[size_of_yuv]; - EXPECT_EQ(static_cast<int>(size_of_yuv), - file_util::ReadFile(yuv_url, - reinterpret_cast<char*>(yuv_bytes), - static_cast<int>(size_of_yuv))); - - // Scale a frame of YUV to 32 bit ARGB. - const size_t size_of_rgb_scaled = kScaledWidth * kScaledHeight * kBpp; - uint8* rgb_scaled_bytes = new uint8[size_of_rgb_scaled]; - - media::ScaleYV16ToRGB32(yuv_bytes, // Y plane - yuv_bytes + kWidth * kHeight, // U plane - yuv_bytes + kWidth * kHeight * 3 / 2, // V plane - rgb_scaled_bytes, // Rgb output - kWidth, kHeight, // Dimensions - kScaledWidth, kScaledHeight, // Dimensions - kWidth, // YStride - kWidth / 2, // UvStride - kScaledWidth * kBpp, // RgbStride - media::ROTATE_0); - - unsigned int rgb_hash = hash(rgb_scaled_bytes, size_of_rgb_scaled); - - // To get this hash value, run once and examine the following EXPECT_EQ. - // Then plug new hash value into EXPECT_EQ statements. - - // TODO(fbarchard): Make reference code mimic MMX exactly -#if USE_MMX - EXPECT_EQ(2317989539u, rgb_hash); -#else - EXPECT_EQ(2946450771u, rgb_hash); -#endif - return; -} - |