diff --git a/README.chromium b/README.chromium index ed4395f77..117680bd1 100644 --- a/README.chromium +++ b/README.chromium @@ -1,6 +1,6 @@ Name: libyuv URL: http://code.google.com/p/libyuv/ -Version: 293 +Version: 294 License: BSD License File: LICENSE diff --git a/include/libyuv/rotate.h b/include/libyuv/rotate.h index 773290701..3a8c42dc0 100644 --- a/include/libyuv/rotate.h +++ b/include/libyuv/rotate.h @@ -38,8 +38,7 @@ int I420Rotate(const uint8* src_y, int src_stride_y, uint8* dst_y, int dst_stride_y, uint8* dst_u, int dst_stride_u, uint8* dst_v, int dst_stride_v, - int width, int height, - RotationMode mode); + int src_width, int src_height, RotationMode mode); // Rotate NV12 input and store in I420 int NV12ToI420Rotate(const uint8* src_y, int src_stride_y, @@ -47,8 +46,7 @@ int NV12ToI420Rotate(const uint8* src_y, int src_stride_y, uint8* dst_y, int dst_stride_y, uint8* dst_u, int dst_stride_u, uint8* dst_v, int dst_stride_v, - int width, int height, - RotationMode mode); + int src_width, int src_height, RotationMode mode); #ifdef __cplusplus } // extern "C" diff --git a/include/libyuv/rotate_argb.h b/include/libyuv/rotate_argb.h new file mode 100644 index 000000000..8d6d759b3 --- /dev/null +++ b/include/libyuv/rotate_argb.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 The LibYuv project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef INCLUDE_LIBYUV_ROTATE_ARGB_H_ +#define INCLUDE_LIBYUV_ROTATE_ARGB_H_ + +#include "libyuv/basic_types.h" +#include "libyuv/rotate.h" // For RotationMode. + +#ifdef __cplusplus +namespace libyuv { +extern "C" { +#endif + +// Rotate ARGB frame +int ARGBRotate(const uint8* src_argb, int src_stride_argb, + uint8* dst_argb, int dst_stride_argb, + int src_width, int src_height, RotationMode mode); + +#ifdef __cplusplus +} // extern "C" +} // namespace libyuv +#endif + +#endif // INCLUDE_LIBYUV_ROTATE_ARGB_H_ diff --git a/include/libyuv/version.h b/include/libyuv/version.h index 34bc1da8c..19e04a3b0 100644 --- a/include/libyuv/version.h +++ b/include/libyuv/version.h @@ -11,7 +11,7 @@ #ifndef INCLUDE_LIBYUV_VERSION_H_ #define INCLUDE_LIBYUV_VERSION_H_ -#define LIBYUV_VERSION 293 +#define LIBYUV_VERSION 294 #endif // INCLUDE_LIBYUV_VERSION_H_ diff --git a/libyuv.gyp b/libyuv.gyp index 6a89e5c98..ae556030b 100644 --- a/libyuv.gyp +++ b/libyuv.gyp @@ -28,20 +28,28 @@ ], }, 'sources': [ - # includes - 'include/libyuv/basic_types.h', - 'include/libyuv/convert.h', - 'include/libyuv/convert_from.h', - 'include/libyuv/planar_functions.h', - 'include/libyuv/scale.h', - 'include/libyuv/version.h', - 'include/libyuv/video_common.h', + # includes. + 'include/libyuv.h', + 'include/libyuv/basic_types.h', + 'include/libyuv/compare.h', + 'include/libyuv/convert.h', + 'include/libyuv/convert_from.h', + 'include/libyuv/cpu_id.h', + 'include/libyuv/format_conversion.h', + 'include/libyuv/mjpeg_decoder.h', + 'include/libyuv/planar_functions.h', + 'include/libyuv/rotate.h', + 'include/libyuv/rotate_argb.h', + 'include/libyuv/scale.h', + 'include/libyuv/scale_argb.h', + 'include/libyuv/version.h', + 'include/libyuv/video_common.h', - # headers + # private includes. 'source/rotate_priv.h', 'source/row.h', - # sources + # sources. 'source/compare.cc', 'source/convert.cc', 'source/convert_from.cc', @@ -50,6 +58,7 @@ 'source/mjpeg_decoder.cc', 'source/planar_functions.cc', 'source/rotate.cc', + 'source/rotate_argb.cc', 'source/rotate_neon.cc', 'source/row_common.cc', 'source/row_neon.cc', @@ -60,7 +69,7 @@ 'source/video_common.cc', ], }, - ], # targets + ], # targets. } # Local Variables: diff --git a/libyuv_test.gyp b/libyuv_test.gyp index 5b1764c68..a9bfbbd9f 100644 --- a/libyuv_test.gyp +++ b/libyuv_test.gyp @@ -12,27 +12,28 @@ 'target_name': 'libyuv_unittest', 'type': 'executable', 'dependencies': [ - 'libyuv.gyp:libyuv', - # The tests are based on gtest - 'testing/gtest.gyp:gtest', - 'testing/gtest.gyp:gtest_main', + 'libyuv.gyp:libyuv', + # The tests are based on gtest + 'testing/gtest.gyp:gtest', + 'testing/gtest.gyp:gtest_main', ], 'defines': [ 'LIBYUV_SVNREVISION=" kMaxStride) { @@ -1034,6 +1037,10 @@ int I420Rotate(const uint8* src_y, int src_stride_y, uint8* dst_v, int dst_stride_v, int width, int height, RotationMode mode) { + if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || + !dst_y || !dst_u || !dst_v) { + return -1; + } int halfwidth = (width + 1) >> 1; int halfheight = (height + 1) >> 1; @@ -1105,6 +1112,10 @@ int NV12ToI420Rotate(const uint8* src_y, int src_stride_y, uint8* dst_v, int dst_stride_v, int width, int height, RotationMode mode) { + if (!src_y || !src_uv || width <= 0 || height == 0 || + !dst_y || !dst_u || !dst_v) { + return -1; + } int halfwidth = (width + 1) >> 1; int halfheight = (height + 1) >> 1; diff --git a/source/rotate_argb.cc b/source/rotate_argb.cc new file mode 100644 index 000000000..a45672bdf --- /dev/null +++ b/source/rotate_argb.cc @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2012 The LibYuv project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "libyuv/rotate.h" + +#include "libyuv/cpu_id.h" +#include "libyuv/convert.h" +#include "libyuv/planar_functions.h" +#include "source/row.h" + +#ifdef __cplusplus +namespace libyuv { +extern "C" { +#endif + +// ARGBScale has a function to copy pixels to a row, striding each source +// pixel by a constant. +#if !defined(YUV_DISABLE_ASM) && (defined(_M_IX86) || \ + defined(__x86_64__) || defined(__i386__)) +#define HAS_SCALEARGBROWDOWNEVEN_SSE2 +void ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, int src_stride, + int src_stepx, + uint8* dst_ptr, int dst_width); +#endif +void ScaleARGBRowDownEven_C(const uint8* src_ptr, int, + int src_stepx, + uint8* dst_ptr, int dst_width); + +static void ARGBTranspose(const uint8* src, int src_stride, + uint8* dst, int dst_stride, + int width, int height) { + void (*ScaleARGBRowDownEven)(const uint8* src_ptr, int src_stride, + int src_step, uint8* dst_ptr, int dst_width) = ScaleARGBRowDownEven_C; +#if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2) + if (TestCpuFlag(kCpuHasSSE2) && + IS_ALIGNED(height, 4) && // width of dest. + IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { + ScaleARGBRowDownEven = ScaleARGBRowDownEven_SSE2; + } +#endif + + int src_pixel_step = src_stride / 4; + for (int i = 0; i < width; ++i) { // column of source to row of dest. + ScaleARGBRowDownEven(src, 0, src_pixel_step, dst, height); + dst += dst_stride; + src += 4; + } +} + +void ARGBRotate90(const uint8* src, int src_stride, + uint8* dst, int dst_stride, + int width, int height) { + // Rotate by 90 is a ARGBTranspose with the source read + // from bottom to top. So set the source pointer to the end + // of the buffer and flip the sign of the source stride. + src += src_stride * (height - 1); + src_stride = -src_stride; + ARGBTranspose(src, src_stride, dst, dst_stride, width, height); +} + +void ARGBRotate270(const uint8* src, int src_stride, + uint8* dst, int dst_stride, + int width, int height) { + // Rotate by 270 is a ARGBTranspose with the destination written + // from bottom to top. So set the destination pointer to the end + // of the buffer and flip the sign of the destination stride. + dst += dst_stride * (width - 1); + dst_stride = -dst_stride; + ARGBTranspose(src, src_stride, dst, dst_stride, width, height); +} + +void ARGBRotate180(const uint8* src, int src_stride, + uint8* dst, int dst_stride, + int width, int height) { + void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) = + ARGBMirrorRow_C; +#if defined(HAS_ARGBMIRRORROW_SSSE3) + if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4) && + IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) && + IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { + ARGBMirrorRow = ARGBMirrorRow_SSSE3; + } +#endif + void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C; +#if defined(HAS_COPYROW_NEON) + if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width * 4, 64)) { + CopyRow = CopyRow_NEON; + } +#endif +#if defined(HAS_COPYROW_X86) + if (TestCpuFlag(kCpuHasX86)) { + CopyRow = CopyRow_X86; + } +#endif +#if defined(HAS_COPYROW_SSE2) + if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width * 4, 32) && + IS_ALIGNED(src, 16) && IS_ALIGNED(src_stride, 16) && + IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) { + CopyRow = CopyRow_SSE2; + } +#endif + if (width * 4 > kMaxStride) { + return; + } + // Swap first and last row and mirror the content. Uses a temporary row. + SIMD_ALIGNED(uint8 row[kMaxStride]); + const uint8* src_bot = src + src_stride * (height - 1); + uint8* dst_bot = dst + dst_stride * (height - 1); + int half_height = (height + 1) >> 1; + // Odd height will harmlessly mirror the middle row twice. + for (int y = 0; y < half_height; ++y) { + ARGBMirrorRow(src, row, width); // Mirror first row into a buffer + src += src_stride; + ARGBMirrorRow(src_bot, dst, width); // Mirror last row into first row + dst += dst_stride; + CopyRow(row, dst_bot, width * 4); // Copy first mirrored row into last + src_bot -= src_stride; + dst_bot -= dst_stride; + } +} + +int ARGBRotate(const uint8* src_argb, int src_stride_argb, + uint8* dst_argb, int dst_stride_argb, + int width, int height, + RotationMode mode) { + if (!src_argb || width <= 0 || height == 0 || !dst_argb) { + return -1; + } + + // Negative height means invert the image. + if (height < 0) { + height = -height; + src_argb = src_argb + (height - 1) * src_stride_argb; + src_stride_argb = -src_stride_argb; + } + + switch (mode) { + case kRotate0: + // copy frame + return ARGBCopy(src_argb, src_stride_argb, + dst_argb, dst_stride_argb, + width, height); + case kRotate90: + ARGBRotate90(src_argb, src_stride_argb, + dst_argb, dst_stride_argb, + width, height); + return 0; + case kRotate270: + ARGBRotate270(src_argb, src_stride_argb, + dst_argb, dst_stride_argb, + width, height); + return 0; + case kRotate180: + ARGBRotate180(src_argb, src_stride_argb, + dst_argb, dst_stride_argb, + width, height); + return 0; + default: + break; + } + return -1; +} + +#ifdef __cplusplus +} // extern "C" +} // namespace libyuv +#endif diff --git a/unit_test/rotate_argb_test.cc b/unit_test/rotate_argb_test.cc new file mode 100644 index 000000000..fe0d19c97 --- /dev/null +++ b/unit_test/rotate_argb_test.cc @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2012 The LibYuv project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "libyuv/cpu_id.h" +#include "libyuv/rotate_argb.h" +#include "unit_test/unit_test.h" + +namespace libyuv { + +static int ARGBTestRotate(int src_width, int src_height, + int dst_width, int dst_height, + libyuv::RotationMode mode) { + const int b = 128; + int src_argb_plane_size = (src_width + b * 2) * (src_height + b * 2) * 4; + int src_stride_argb = (b * 2 + src_width) * 4; + + align_buffer_16(src_argb, src_argb_plane_size) + memset(src_argb, 1, src_argb_plane_size); + + int dst_argb_plane_size = (dst_width + b * 2) * (dst_height + b * 2) * 4; + int dst_stride_argb = (b * 2 + dst_width) * 4; + + srandom(time(NULL)); + + int i, j; + for (i = b; i < (src_height + b); ++i) { + for (j = b; j < (src_width + b) * 4; ++j) { + src_argb[(i * src_stride_argb) + j] = (random() & 0xff); + } + } + + const int runs = 1000; + align_buffer_16(dst_argb_c, dst_argb_plane_size) + align_buffer_16(dst_argb_opt, dst_argb_plane_size) + memset(dst_argb_c, 2, dst_argb_plane_size); + memset(dst_argb_opt, 3, dst_argb_plane_size); + + // Warm up both versions for consistent benchmarks. + MaskCpuFlags(0); // Disable all CPU optimization. + ARGBRotate(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + dst_argb_c + (dst_stride_argb * b) + b * 4, dst_stride_argb, + src_width, src_height, mode); + MaskCpuFlags(-1); // Enable all CPU optimization. + ARGBRotate(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + dst_argb_opt + (dst_stride_argb * b) + b * 4, dst_stride_argb, + src_width, src_height, mode); + + MaskCpuFlags(0); // Disable all CPU optimization. + double c_time = get_time(); + for (i = 0; i < runs; ++i) { + ARGBRotate(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + dst_argb_c + (dst_stride_argb * b) + b * 4, dst_stride_argb, + src_width, src_height, mode); + } + c_time = (get_time() - c_time) / runs; + + MaskCpuFlags(-1); // Enable all CPU optimization. + double opt_time = get_time(); + for (i = 0; i < runs; ++i) { + ARGBRotate(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + dst_argb_opt + (dst_stride_argb * b) + b * 4, dst_stride_argb, + src_width, src_height, mode); + } + opt_time = (get_time() - opt_time) / runs; + + // Report performance of C vs OPT + printf("filter %d - %8d us C - %8d us OPT\n", + mode, static_cast(c_time*1e6), static_cast(opt_time*1e6)); + + // C version may be a little off from the optimized. Order of + // operations may introduce rounding somewhere. So do a difference + // of the buffers and look to see that the max difference isn't + // over 2. + int max_diff = 0; + for (i = b; i < (dst_height + b); ++i) { + for (j = b * 4; j < (dst_width + b) * 4; ++j) { + int abs_diff = abs(dst_argb_c[(i * dst_stride_argb) + j] - + dst_argb_opt[(i * dst_stride_argb) + j]); + if (abs_diff > max_diff) + max_diff = abs_diff; + } + } + + free_aligned_buffer_16(dst_argb_c) + free_aligned_buffer_16(dst_argb_opt) + free_aligned_buffer_16(src_argb) + return max_diff; +} + +TEST_F(libyuvTest, ARGBRotate0) { + const int src_width = 1280; + const int src_height = 720; + const int dst_width = 1280; + const int dst_height = 720; + + int err = ARGBTestRotate(src_width, src_height, + dst_width, dst_height, kRotate0); + EXPECT_GE(1, err); +} + +TEST_F(libyuvTest, ARGBRotate90) { + const int src_width = 1280; + const int src_height = 720; + const int dst_width = 720; + const int dst_height = 1280; + + int err = ARGBTestRotate(src_width, src_height, + dst_width, dst_height, kRotate90); + EXPECT_GE(1, err); +} + +TEST_F(libyuvTest, ARGBRotate180) { + const int src_width = 1280; + const int src_height = 720; + const int dst_width = 1280; + const int dst_height = 720; + + int err = ARGBTestRotate(src_width, src_height, + dst_width, dst_height, kRotate180); + EXPECT_GE(1, err); +} + +TEST_F(libyuvTest, ARGBRotate270) { + const int src_width = 1280; + const int src_height = 720; + const int dst_width = 720; + const int dst_height = 1280; + + int err = ARGBTestRotate(src_width, src_height, + dst_width, dst_height, kRotate270); + EXPECT_GE(1, err); +} + +TEST_F(libyuvTest, ARGBRotate0_Odd) { + const int src_width = 1277; + const int src_height = 719; + const int dst_width = 1277; + const int dst_height = 719; + + int err = ARGBTestRotate(src_width, src_height, + dst_width, dst_height, kRotate0); + EXPECT_GE(1, err); +} + +TEST_F(libyuvTest, ARGBRotate90_Odd) { + const int src_width = 1277; + const int src_height = 719; + const int dst_width = 719; + const int dst_height = 1277; + + int err = ARGBTestRotate(src_width, src_height, + dst_width, dst_height, kRotate90); + EXPECT_GE(1, err); +} + +TEST_F(libyuvTest, ARGBRotate180_Odd) { + const int src_width = 1277; + const int src_height = 719; + const int dst_width = 1277; + const int dst_height = 719; + + int err = ARGBTestRotate(src_width, src_height, + dst_width, dst_height, kRotate180); + EXPECT_GE(1, err); +} + +TEST_F(libyuvTest, ARGBRotate270_Odd) { + const int src_width = 1277; + const int src_height = 719; + const int dst_width = 719; + const int dst_height = 1277; + + int err = ARGBTestRotate(src_width, src_height, + dst_width, dst_height, kRotate270); + EXPECT_GE(1, err); +} + +} // namespace libyuv diff --git a/unit_test/scale_argb_test.cc b/unit_test/scale_argb_test.cc index 29e1c4cb9..45a726daa 100644 --- a/unit_test/scale_argb_test.cc +++ b/unit_test/scale_argb_test.cc @@ -213,6 +213,21 @@ TEST_F(libyuvTest, ARGBScaleTo1366) { } } +TEST_F(libyuvTest, ARGBScaleTo4074) { + int src_width = 2880 * 2; + int src_height = 1800; + int dst_width = 4074; + int dst_height = 1272; + + for (int f = 0; f < 2; ++f) { + int err = ARGBTestFilter(src_width, src_height, + dst_width, dst_height, + static_cast(f)); + EXPECT_GE(1, err); + } +} + + TEST_F(libyuvTest, ARGBScaleTo853) { int src_width = 1280; int src_height = 720; diff --git a/unit_test/scale_test.cc b/unit_test/scale_test.cc index 0f9c98101..082ab9a67 100644 --- a/unit_test/scale_test.cc +++ b/unit_test/scale_test.cc @@ -275,6 +275,20 @@ TEST_F(libyuvTest, ScaleTo1366) { } } +TEST_F(libyuvTest, ScaleTo4074) { + int src_width = 2880 * 2; + int src_height = 1800; + int dst_width = 4074; + int dst_height = 1272; + + for (int f = 0; f < 3; ++f) { + int err = TestFilter(src_width, src_height, + dst_width, dst_height, + static_cast(f), 1); + EXPECT_GE(1, err); + } +} + TEST_F(libyuvTest, ScaleTo853) { int src_width = 1280; int src_height = 720;