diff options
author | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-14 01:21:18 +0000 |
---|---|---|
committer | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-14 01:21:18 +0000 |
commit | 5a7838c5fdba06e93779ee4045189d02af0be0e7 (patch) | |
tree | 4ea275675630c5da305c53911626c698a1dc6796 /media | |
parent | 2afff558196f309cc1f69a2b91b9ff2a200322a6 (diff) | |
download | chromium_src-5a7838c5fdba06e93779ee4045189d02af0be0e7.zip chromium_src-5a7838c5fdba06e93779ee4045189d02af0be0e7.tar.gz chromium_src-5a7838c5fdba06e93779ee4045189d02af0be0e7.tar.bz2 |
Linear sub-rectangle scaler for use in Chromoting.
This implementation re-uses the common row filter procedures, but is currently limited to a C horizontal interpolation procedure.
There's also plenty of scope for optimizing the new sub-rectangle scaler routine.
BUG=93451
TEST=media_unittests, remoting_unittests and manual verification of image quality of Chromoting sessions using fit-to-screen.
Review URL: http://codereview.chromium.org/8954003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@117748 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/simd/convert_yuv_to_rgb.h | 10 | ||||
-rw-r--r-- | media/base/simd/convert_yuv_to_rgb_c.cc | 51 | ||||
-rw-r--r-- | media/base/yuv_convert.cc | 154 | ||||
-rw-r--r-- | media/base/yuv_convert.h | 21 | ||||
-rw-r--r-- | media/base/yuv_convert_unittest.cc | 78 | ||||
-rw-r--r-- | media/tools/scaler_bench/scaler_bench.cc | 45 |
6 files changed, 336 insertions, 23 deletions
diff --git a/media/base/simd/convert_yuv_to_rgb.h b/media/base/simd/convert_yuv_to_rgb.h index ff7d8ec..164ad11 100644 --- a/media/base/simd/convert_yuv_to_rgb.h +++ b/media/base/simd/convert_yuv_to_rgb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -124,6 +124,14 @@ void LinearScaleYUVToRGB32Row_C(const uint8* y_buf, int width, int source_dx); +void LinearScaleYUVToRGB32RowWithRange_C(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int dest_width, + int source_x, + int source_dx); + void LinearScaleYUVToRGB32Row_MMX(const uint8* y_buf, const uint8* u_buf, const uint8* v_buf, diff --git a/media/base/simd/convert_yuv_to_rgb_c.cc b/media/base/simd/convert_yuv_to_rgb_c.cc index c403984..abd7bfc 100644 --- a/media/base/simd/convert_yuv_to_rgb_c.cc +++ b/media/base/simd/convert_yuv_to_rgb_c.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -9,11 +9,10 @@ #define paddsw(x, y) (((x) + (y)) < -32768 ? -32768 : \ (((x) + (y)) > 32767 ? 32767 : ((x) + (y)))) -static inline void YUVPixel(uint8 y, - uint8 u, - uint8 v, - uint8* rgb_buf) { - +static inline void ConvertYUVToRGB32_C(uint8 y, + uint8 u, + uint8 v, + uint8* rgb_buf) { int b = kCoefficientsRgbY[256+u][0]; int g = kCoefficientsRgbY[256+u][1]; int r = kCoefficientsRgbY[256+u][2]; @@ -40,6 +39,11 @@ static inline void YUVPixel(uint8 y, (packuswb(a) << 24); } +// 16.16 fixed point arithmetic +const int kFractionBits = 16; +const int kFractionMax = 1 << kFractionBits; +const int kFractionMask = ((1 << kFractionBits) - 1); + extern "C" { void ConvertYUVToRGB32Row_C(const uint8* y_buf, @@ -51,10 +55,10 @@ void ConvertYUVToRGB32Row_C(const uint8* y_buf, uint8 u = u_buf[x >> 1]; uint8 v = v_buf[x >> 1]; uint8 y0 = y_buf[x]; - YUVPixel(y0, u, v, rgb_buf); + ConvertYUVToRGB32_C(y0, u, v, rgb_buf); if ((x + 1) < width) { uint8 y1 = y_buf[x + 1]; - YUVPixel(y1, u, v, rgb_buf + 4); + ConvertYUVToRGB32_C(y1, u, v, rgb_buf + 4); } rgb_buf += 8; // Advance 2 pixels. } @@ -75,11 +79,11 @@ void ScaleYUVToRGB32Row_C(const uint8* y_buf, int y = y_buf[x >> 16]; int u = u_buf[(x >> 17)]; int v = v_buf[(x >> 17)]; - YUVPixel(y, u, v, rgb_buf); + ConvertYUVToRGB32_C(y, u, v, rgb_buf); x += source_dx; if ((i + 1) < width) { y = y_buf[x >> 16]; - YUVPixel(y, u, v, rgb_buf+4); + ConvertYUVToRGB32_C(y, u, v, rgb_buf+4); x += source_dx; } rgb_buf += 8; @@ -92,11 +96,22 @@ void LinearScaleYUVToRGB32Row_C(const uint8* y_buf, uint8* rgb_buf, int width, int source_dx) { - int x = 0; - if (source_dx >= 0x20000) { - x = 32768; - } - for (int i = 0; i < width; i += 2) { + // Avoid point-sampling for down-scaling by > 2:1. + int source_x = 0; + if (source_dx >= 0x20000) + source_x += 0x8000; + LinearScaleYUVToRGB32RowWithRange_C(y_buf, u_buf, v_buf, rgb_buf, width, + source_x, source_dx); +} + +void LinearScaleYUVToRGB32RowWithRange_C(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int dest_width, + int x, + int source_dx) { + for (int i = 0; i < dest_width; i += 2) { int y0 = y_buf[x >> 16]; int y1 = y_buf[(x >> 16) + 1]; int u0 = u_buf[(x >> 17)]; @@ -108,14 +123,14 @@ void LinearScaleYUVToRGB32Row_C(const uint8* y_buf, int y = (y_frac * y1 + (y_frac ^ 65535) * y0) >> 16; int u = (uv_frac * u1 + (uv_frac ^ 65535) * u0) >> 16; int v = (uv_frac * v1 + (uv_frac ^ 65535) * v0) >> 16; - YUVPixel(y, u, v, rgb_buf); + ConvertYUVToRGB32_C(y, u, v, rgb_buf); x += source_dx; - if ((i + 1) < width) { + if ((i + 1) < dest_width) { y0 = y_buf[x >> 16]; y1 = y_buf[(x >> 16) + 1]; y_frac = (x & 65535); y = (y_frac * y1 + (y_frac ^ 65535) * y0) >> 16; - YUVPixel(y, u, v, rgb_buf+4); + ConvertYUVToRGB32_C(y, u, v, rgb_buf+4); x += source_dx; } rgb_buf += 8; diff --git a/media/base/yuv_convert.cc b/media/base/yuv_convert.cc index 93af853..8d5073b 100644 --- a/media/base/yuv_convert.cc +++ b/media/base/yuv_convert.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -18,6 +18,7 @@ #include "media/base/yuv_convert.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "build/build_config.h" #include "media/base/cpu_features.h" #include "media/base/simd/convert_rgb_to_yuv.h" @@ -277,6 +278,157 @@ void ScaleYUVToRGB32(const uint8* y_buf, EmptyRegisterState(); } +// Scale a frame of YV12 to 32 bit ARGB for a specific rectangle. +void ScaleYUVToRGB32WithRect(const uint8* y_buf, + const uint8* u_buf, + const uint8* v_buf, + uint8* rgb_buf, + int source_width, + int source_height, + int dest_width, + int dest_height, + int dest_rect_left, + int dest_rect_top, + int dest_rect_right, + int dest_rect_bottom, + int y_pitch, + int uv_pitch, + int rgb_pitch) { + static FilterYUVRowsProc filter_proc = NULL; + if (!filter_proc) + filter_proc = ChooseFilterYUVRowsProc(); + + // This routine doesn't currently support up-scaling. + CHECK(dest_width <= source_width && dest_height <= source_height); + + // Sanity-check the destination rectangle. + DCHECK(dest_rect_left >= 0 && dest_rect_right <= dest_width); + DCHECK(dest_rect_top >= 0 && dest_rect_bottom <= dest_height); + DCHECK(dest_rect_right > dest_rect_left); + DCHECK(dest_rect_bottom > dest_rect_top); + + // Fixed-point value of vertical and horizontal scale down factor. + // Values are in the format 16.16. + int y_step = kFractionMax * source_height / dest_height; + int x_step = kFractionMax * source_width / dest_width; + + // Determine the coordinates of the rectangle in 16.16 coords. + // NB: Our origin is the *center* of the top/left pixel, NOT its top/left. + // If we're down-scaling by more than a factor of two, we start with a 50% + // fraction to avoid degenerating to point-sampling - we should really just + // fix the fraction at 50% for all pixels in that case. + int source_left = dest_rect_left * x_step; + int source_right = (dest_rect_right - 1) * x_step; + if (x_step < kFractionMax * 2) { + source_left += ((x_step - kFractionMax) / 2); + source_right += ((x_step - kFractionMax) / 2); + } else { + source_left += kFractionMax / 2; + source_right += kFractionMax / 2; + } + int source_top = dest_rect_top * y_step; + if (y_step < kFractionMax * 2) { + source_top += ((y_step - kFractionMax) / 2); + } else { + source_top += kFractionMax / 2; + } + + // Determine the parts of the Y, U and V buffers to interpolate. + int source_y_left = source_left >> kFractionBits; + int source_y_right = (source_right >> kFractionBits) + 2; + DCHECK(source_y_right <= source_width); + + int source_uv_left = source_y_left / 2; + int source_uv_right = std::min( + (source_right >> (kFractionBits + 1)) + 2, + (source_width + 1) / 2); + + int source_y_width = source_y_right - source_y_left; + int source_uv_width = source_uv_right - source_uv_left; + + // Determine number of pixels in each output row. + int dest_rect_width = dest_rect_right - dest_rect_left; + + // Intermediate buffer for vertical interpolation. + // 4096 bytes allows 3 buffers to fit in 12k, which fits in a 16K L1 cache, + // and is bigger than most users will generally need. + // The buffer is 16-byte aligned and padded with 16 extra bytes; some of the + // FilterYUVRowProcs have alignment requirements, and the SSE version can + // write up to 16 bytes past the end of the buffer. + const int kFilterBufferSize = 4096; + if (source_width > kFilterBufferSize) + filter_proc = NULL; + uint8 yuv_temp[16 + kFilterBufferSize * 3 + 16]; + uint8* y_temp = + reinterpret_cast<uint8*>( + reinterpret_cast<uintptr_t>(yuv_temp + 15) & ~15); + uint8* u_temp = y_temp + kFilterBufferSize; + uint8* v_temp = u_temp + kFilterBufferSize; + + // Move to the top-left pixel of output. + rgb_buf += dest_rect_top * rgb_pitch; + rgb_buf += dest_rect_left * 4; + + // For each destination row perform interpolation and color space + // conversion to produce the output. + for (int row = dest_rect_top; row < dest_rect_bottom; ++row) { + // Round the fixed-point y position to get the current row. + int source_row = source_top >> kFractionBits; + int source_uv_row = source_row / 2; + DCHECK(source_row < source_height); + + // Locate the first row for each plane for interpolation. + const uint8* y0_ptr = y_buf + y_pitch * source_row + source_y_left; + const uint8* u0_ptr = u_buf + uv_pitch * source_uv_row + source_uv_left; + const uint8* v0_ptr = v_buf + uv_pitch * source_uv_row + source_uv_left; + const uint8* y1_ptr = NULL; + const uint8* u1_ptr = NULL; + const uint8* v1_ptr = NULL; + + // Locate the second row for interpolation, being careful not to overrun. + if (source_row + 1 >= source_height) { + y1_ptr = y0_ptr; + } else { + y1_ptr = y0_ptr + y_pitch; + } + if (source_uv_row + 1 >= (source_height + 1) / 2) { + u1_ptr = u0_ptr; + v1_ptr = v0_ptr; + } else { + u1_ptr = u0_ptr + uv_pitch; + v1_ptr = v0_ptr + uv_pitch; + } + + if (filter_proc) { + // Vertical scaler uses 16.8 fixed point. + int fraction = (source_top & kFractionMask) >> 8; + filter_proc(y_temp + source_y_left, y0_ptr, y1_ptr, + source_y_width, fraction); + filter_proc(u_temp + source_uv_left, u0_ptr, u1_ptr, + source_uv_width, fraction); + filter_proc(v_temp + source_uv_left, v0_ptr, v1_ptr, + source_uv_width, fraction); + + // Perform horizontal interpolation and color space conversion. + // TODO(hclam): Use the MMX version after more testing. + LinearScaleYUVToRGB32RowWithRange_C( + y_temp, u_temp, v_temp, rgb_buf, + dest_rect_width, source_left, x_step); + } else { + // If the frame is too large then we linear scale a single row. + LinearScaleYUVToRGB32RowWithRange_C( + y0_ptr, u0_ptr, v0_ptr, rgb_buf, + dest_rect_width, source_left, x_step); + } + + // Advance vertically in the source and destination image. + source_top += y_step; + rgb_buf += rgb_pitch; + } + + EmptyRegisterState(); +} + void ConvertRGB32ToYUV(const uint8* rgbframe, uint8* yplane, uint8* uplane, diff --git a/media/base/yuv_convert.h b/media/base/yuv_convert.h index 95b1780..aa1e3dc 100644 --- a/media/base/yuv_convert.h +++ b/media/base/yuv_convert.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -67,6 +67,25 @@ void ScaleYUVToRGB32(const uint8* yplane, Rotate view_rotate, ScaleFilter filter); +// Biliner Scale a frame of YV12 to 32 bits ARGB on a specified rectangle. +// |yplane|, etc and |rgbframe| should point to the top-left pixels of the +// source and destination buffers. +void ScaleYUVToRGB32WithRect(const uint8* yplane, + const uint8* uplane, + const uint8* vplane, + uint8* rgbframe, + int source_width, + int source_height, + int dest_width, + int dest_height, + int dest_rect_left, + int dest_rect_top, + int dest_rect_right, + int dest_rect_bottom, + int ystride, + int uvstride, + int rgbstride); + void ConvertRGB32ToYUV(const uint8* rgbframe, uint8* yplane, uint8* uplane, diff --git a/media/base/yuv_convert_unittest.cc b/media/base/yuv_convert_unittest.cc index 9db998b..3a74bb2 100644 --- a/media/base/yuv_convert_unittest.cc +++ b/media/base/yuv_convert_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -13,6 +13,7 @@ #include "media/base/simd/filter_yuv.h" #include "media/base/yuv_convert.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/rect.h" // Size of raw image. static const int kSourceWidth = 640; @@ -22,6 +23,8 @@ static const int kSourceUOffset = kSourceYSize; static const int kSourceVOffset = kSourceYSize * 5 / 4; static const int kScaledWidth = 1024; static const int kScaledHeight = 768; +static const int kDownScaledWidth = 512; +static const int kDownScaledHeight = 320; static const int kBpp = 4; // Surface sizes for various test files. @@ -379,6 +382,79 @@ TEST(YUVConvertTest, YUY2ToYUV) { EXPECT_EQ(666823187u, yuy_hash); } +TEST(YUVConvertTest, DownScaleYUVToRGB32WithRect) { + // 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_640x360_P420.yuv")); + const size_t size_of_yuv = kSourceYSize * 12 / 8; // 12 bpp. + scoped_array<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.get()), + static_cast<int>(size_of_yuv))); + + // Scale the full frame of YUV to 32 bit ARGB. + // The API currently only supports down-scaling, so we don't test up-scaling. + const size_t size_of_rgb_scaled = kDownScaledWidth * kDownScaledHeight * kBpp; + scoped_array<uint8> rgb_scaled_bytes(new uint8[size_of_rgb_scaled]); + gfx::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight); + + // We can't compare with the full-frame scaler because it uses slightly + // different sampling coordinates. + media::ScaleYUVToRGB32WithRect( + yuv_bytes.get(), // Y + yuv_bytes.get() + kSourceUOffset, // U + yuv_bytes.get() + kSourceVOffset, // V + rgb_scaled_bytes.get(), // Rgb output + kSourceWidth, kSourceHeight, // Dimensions + kDownScaledWidth, kDownScaledHeight, // Dimensions + sub_rect.x(), sub_rect.y(), // Dest rect + sub_rect.right(), sub_rect.bottom(), // Dest rect + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kDownScaledWidth * kBpp); // RgbStride + + uint32 rgb_hash_full_rect = DJB2Hash(rgb_scaled_bytes.get(), + size_of_rgb_scaled, + kDJB2HashSeed); + + // Re-scale sub-rectangles and verify the results are the same. + int next_sub_rect = 0; + while (!sub_rect.IsEmpty()) { + // Scale a partial rectangle. + media::ScaleYUVToRGB32WithRect( + yuv_bytes.get(), // Y + yuv_bytes.get() + kSourceUOffset, // U + yuv_bytes.get() + kSourceVOffset, // V + rgb_scaled_bytes.get(), // Rgb output + kSourceWidth, kSourceHeight, // Dimensions + kDownScaledWidth, kDownScaledHeight, // Dimensions + sub_rect.x(), sub_rect.y(), // Dest rect + sub_rect.right(), sub_rect.bottom(), // Dest rect + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kDownScaledWidth * kBpp); // RgbStride + uint32 rgb_hash_sub_rect = DJB2Hash(rgb_scaled_bytes.get(), + size_of_rgb_scaled, + kDJB2HashSeed); + + EXPECT_EQ(rgb_hash_full_rect, rgb_hash_sub_rect); + + // Now pick choose a quarter rect of this sub-rect. + if (next_sub_rect & 1) + sub_rect.set_x(sub_rect.x() + sub_rect.width() / 2); + if (next_sub_rect & 2) + sub_rect.set_y(sub_rect.y() + sub_rect.height() / 2); + sub_rect.set_width(sub_rect.width() / 2); + sub_rect.set_height(sub_rect.height() / 2); + next_sub_rect++; + } +} + #if !defined(ARCH_CPU_ARM_FAMILY) TEST(YUVConvertTest, RGB32ToYUV_SSE2_MatchReference) { if (!media::hasSSE2()) { diff --git a/media/tools/scaler_bench/scaler_bench.cc b/media/tools/scaler_bench/scaler_bench.cc index aa8e227..0aa0531 100644 --- a/media/tools/scaler_bench/scaler_bench.cc +++ b/media/tools/scaler_bench/scaler_bench.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -152,6 +152,47 @@ static double BenchmarkFilter(media::ScaleFilter filter) { return static_cast<double>((end - start).InMilliseconds()) / num_frames; } +static double BenchmarkScaleWithRect() { + std::vector<scoped_refptr<VideoFrame> > source_frames; + std::vector<scoped_refptr<VideoFrame> > dest_frames; + + for (int i = 0; i < num_buffers; i++) { + source_frames.push_back( + VideoFrame::CreateBlackFrame(source_width, source_height)); + + dest_frames.push_back( + VideoFrame::CreateFrame(VideoFrame::RGB32, + dest_width, + dest_height, + TimeDelta::FromSeconds(0), + TimeDelta::FromSeconds(0))); + } + + TimeTicks start = TimeTicks::HighResNow(); + for (int i = 0; i < num_frames; i++) { + scoped_refptr<VideoFrame> source_frame = source_frames[i % num_buffers]; + scoped_refptr<VideoFrame> dest_frame = dest_frames[i % num_buffers]; + + media::ScaleYUVToRGB32WithRect( + source_frame->data(VideoFrame::kYPlane), + source_frame->data(VideoFrame::kUPlane), + source_frame->data(VideoFrame::kVPlane), + dest_frame->data(0), + source_width, + source_height, + dest_width, + dest_height, + 0, 0, + dest_width, + dest_height, + source_frame->stride(VideoFrame::kYPlane), + source_frame->stride(VideoFrame::kUPlane), + dest_frame->stride(0)); + } + TimeTicks end = TimeTicks::HighResNow(); + return static_cast<double>((end - start).InMilliseconds()) / num_frames; +} + int main(int argc, const char** argv) { CommandLine::Init(argc, argv); const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); @@ -231,6 +272,8 @@ int main(int argc, const char** argv) { << "ms/frame" << std::endl; std::cout << "Bilinear: " << BenchmarkFilter(media::FILTER_BILINEAR) << "ms/frame" << std::endl; + std::cout << "Bilinear with rect: " << BenchmarkScaleWithRect() + << "ms/frame" << std::endl; return 0; } |