summaryrefslogtreecommitdiffstats
path: root/media/base/yuv_convert.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/base/yuv_convert.cc')
-rw-r--r--media/base/yuv_convert.cc155
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);
}
}