diff options
Diffstat (limited to 'media/base/yuv_convert.cc')
-rw-r--r-- | media/base/yuv_convert.cc | 155 |
1 files changed, 122 insertions, 33 deletions
diff --git a/media/base/yuv_convert.cc b/media/base/yuv_convert.cc index c73dfe4..257d7c8 100644 --- a/media/base/yuv_convert.cc +++ b/media/base/yuv_convert.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -20,8 +20,20 @@ // Header for low level row functions. #include "media/base/yuv_row.h" +#if USE_MMX +#if defined(_MSC_VER) +#include <intrin.h> +#else +#include <emmintrin.h> +#endif +#endif + namespace media { +// 16.16 fixed point arithmetic. +const int kFractionBits = 16; +const int kFractionMax = 1 << kFractionBits; + // Convert a frame of YUV to 32 bit ARGB. void ConvertYUVToRGB32(const uint8* y_buf, const uint8* u_buf, @@ -51,6 +63,60 @@ void ConvertYUVToRGB32(const uint8* y_buf, EMMS(); } +// FilterRows combines two rows of the image using linear interpolation. +// 4 pixels are blended at a time. +static void FilterRows(uint8* ybuf, const uint8* y0_ptr, const uint8* y1_ptr, + int width, int scaled_y_fraction) { +#if USE_MMX + __m128i zero = _mm_setzero_si128(); + __m128i y1_fraction = _mm_set1_epi16( + static_cast<unsigned short>(scaled_y_fraction >> 8)); + __m128i y0_fraction = _mm_set1_epi16( + static_cast<unsigned short>((scaled_y_fraction >> 8) ^ 255)); + + uint8* end = ybuf + width; + if (ybuf < end) { + do { + __m128i y0 = _mm_loadl_epi64(reinterpret_cast<__m128i const*>(y0_ptr)); + __m128i y1 = _mm_loadl_epi64(reinterpret_cast<__m128i const*>(y1_ptr)); + y0 = _mm_unpacklo_epi8 (y0, zero); + y1 = _mm_unpacklo_epi8 (y1, zero); + y0 = _mm_mullo_epi16(y0, y0_fraction); + y1 = _mm_mullo_epi16(y1, y1_fraction); + y0 = _mm_add_epi16(y0, y1); // 8.8 fixed point result + y0 = _mm_srli_epi16(y0, 8); + y0 = _mm_packus_epi16(y0, y0); + _mm_storel_epi64(reinterpret_cast<__m128i *>(ybuf), y0); + y0_ptr += 8; + y1_ptr += 8; + ybuf += 8; + } while (ybuf < end); + } +#else + int y0_fraction = kFractionMax - 1 - scaled_y_fraction; + int y1_fraction = scaled_y_fraction; + uint8* end = ybuf + width; + while (ybuf < end) { + ybuf[0] = (y0_ptr[0] * (y0_fraction) + + y1_ptr[0] * (y1_fraction)) >> kFractionBits; + ybuf[1] = (y0_ptr[1] * (y0_fraction) + + y1_ptr[1] * (y1_fraction)) >> kFractionBits; + ybuf[2] = (y0_ptr[2] * (y0_fraction) + + y1_ptr[2] * (y1_fraction)) >> kFractionBits; + ybuf[3] = (y0_ptr[3] * (y0_fraction) + + y1_ptr[3] * (y1_fraction)) >> kFractionBits; + y0_ptr += 4; + y1_ptr += 4; + ybuf += 4; + } +#endif + + // Value at |ybuf[width]| must be the same as at |ybuf[width-1]|. + if (width > 1) { + end[0] = end[-1]; + } +} + // Scale a frame of YUV to 32 bit ARGB. void ScaleYUVToRGB32(const uint8* y_buf, const uint8* u_buf, @@ -64,7 +130,15 @@ void ScaleYUVToRGB32(const uint8* y_buf, int uv_pitch, int rgb_pitch, YUVType yuv_type, - Rotate view_rotate) { + Rotate view_rotate, + ScaleFilter filter) { + const int kFilterBufferSize = 8192; + // Disable filtering if the screen is too big (to avoid buffer overflows). + // This should never happen to regular users: they don't have monitors + // wider than 8192 pixels. + if (width > kFilterBufferSize) + filter = FILTER_NONE; + unsigned int y_shift = yuv_type; // Diagram showing origin and direction of source sampling. // ->0 4<- @@ -96,8 +170,8 @@ void ScaleYUVToRGB32(const uint8* y_buf, // 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 = width * kFractionMax / scaled_width; + int scaled_dy = height * kFractionMax / scaled_height; int scaled_dx_uv = scaled_dx; @@ -111,8 +185,8 @@ void ScaleYUVToRGB32(const uint8* y_buf, 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_dx = ((original_dy >> kFractionBits) * y_pitch) << kFractionBits; + scaled_dx_uv = ((original_dy >> kFractionBits) * uv_pitch) << kFractionBits; scaled_dy = original_dx; if (view_rotate == ROTATE_90) { y_pitch = -1; @@ -124,39 +198,54 @@ void ScaleYUVToRGB32(const uint8* y_buf, } } + // Need padding in the end because FilterRows() may override up to 7 + // pixels after the end. + uint8 ybuf[kFilterBufferSize + 16]; + uint8 ubuf[kFilterBufferSize / 2 + 16]; + uint8 vbuf[kFilterBufferSize / 2 + 16]; + int yscale_fixed = (height << kFractionBits) / scaled_height; 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; - -#if USE_MMX && defined(_MSC_VER) - 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); + int scaled_y = (y * yscale_fixed); + + const uint8* y0_ptr = y_buf + (scaled_y >> kFractionBits) * y_pitch; + const uint8* y1_ptr = y0_ptr + y_pitch; + + const uint8* u0_ptr = u_buf + + ((scaled_y >> kFractionBits) >> y_shift) * uv_pitch; + const uint8* u1_ptr = u0_ptr + uv_pitch; + const uint8* v0_ptr = v_buf + + ((scaled_y >> kFractionBits) >> y_shift) * uv_pitch; + const uint8* v1_ptr = v0_ptr + uv_pitch; + + int scaled_y_fraction = scaled_y & (kFractionMax - 1); + int scaled_uv_fraction = (scaled_y >> y_shift) & (kFractionMax - 1); + + const uint8* y_ptr = y0_ptr; + const uint8* u_ptr = u0_ptr; + const uint8* v_ptr = v0_ptr; + // TODO(sergeyu): Avoid filtering when fraction is 0. + if (filter == media::FILTER_BILINEAR && y + 1 < scaled_height) { + FilterRows(ybuf, y0_ptr, y1_ptr, width, scaled_y_fraction); + y_ptr = ybuf; + + if ((y >> y_shift) + 1 < scaled_height >> y_shift) { + FilterRows(ubuf, u0_ptr, u1_ptr, width / 2, scaled_uv_fraction); + u_ptr = ubuf; + FilterRows(vbuf, v0_ptr, v1_ptr, width / 2, scaled_uv_fraction); + v_ptr = vbuf; } -#else - if (scaled_dx == 16) { // Not scaled + } + if (scaled_dx == kFractionMax) { // 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); + if (filter == FILTER_BILINEAR) + LinearScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr, + dest_pixel, scaled_width, scaled_dx); + else + ScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr, + dest_pixel, scaled_width, scaled_dx); } } |