From 44da5ae9ca8155c4080028aa9e22cd596cbd273c Mon Sep 17 00:00:00 2001 From: Frank Barchard Date: Tue, 21 Jul 2026 18:14:59 -0700 Subject: [PATCH] [libyuv] Fix color matrix constants in RAWToI420 RAW format in libyuv is Red-first ([R, G, B] in memory), whereas RGB24 is Blue-first ([B, G, R] in memory). RGBToYMatrixRow and RGBToUVMatrixRow internally expand 3-byte input into 4-byte [byte0, byte1, byte2, 255]. For RAW, this buffer becomes [R, G, B, 255] (matching ABGR byte order). RAWToI420 previously passed &kArgbI601Constants (which expects [B, G, R, 255]) instead of &kAbgrI601Constants, causing Red and Blue channels to be swapped. Pass &kAbgrI601Constants in RAWToI420, fix parameter names in source/convert.cc, and add a unit test. Test: libyuv_unittest --gtest_filter=*RAWToI420* Bug: None TAG=agy CONV=2c54c4b5-f515-421b-80b3-079caa9f948b Change-Id: Ic72470813eaf15e80046c2c01cf804cb827039ab --- source/convert.cc | 26 ++++++++++++------------- unit_test/convert_test.cc | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/source/convert.cc b/source/convert.cc index 0caec6ef5..f341c7b85 100644 --- a/source/convert.cc +++ b/source/convert.cc @@ -2862,8 +2862,8 @@ int RGB24ToJ420(const uint8_t* src_rgb24, // Convert RAW to I420. LIBYUV_API -int RAWToI420(const uint8_t* src_rgb24, - int src_stride_rgb24, +int RAWToI420(const uint8_t* src_raw, + int src_stride_raw, uint8_t* dst_y, int dst_stride_y, uint8_t* dst_u, @@ -2925,31 +2925,31 @@ int RAWToI420(const uint8_t* src_rgb24, } #endif - if (!src_rgb24 || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0 || + if (!src_raw || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0 || height == INT_MIN) { return -1; } // Negative height means invert the image. if (height < 0) { height = -height; - src_rgb24 = src_rgb24 + (ptrdiff_t)(height - 1) * src_stride_rgb24; - src_stride_rgb24 = -src_stride_rgb24; + src_raw = src_raw + (ptrdiff_t)(height - 1) * src_stride_raw; + src_stride_raw = -src_stride_raw; } for (y = 0; y < height - 1; y += 2) { - RGBToUVMatrixRow(src_rgb24, src_stride_rgb24, dst_u, dst_v, width, - &kArgbI601Constants); - RGBToYMatrixRow(src_rgb24, dst_y, width, &kArgbI601Constants); - RGBToYMatrixRow(src_rgb24 + src_stride_rgb24, dst_y + dst_stride_y, width, - &kArgbI601Constants); - src_rgb24 += src_stride_rgb24 * 2; + RGBToUVMatrixRow(src_raw, src_stride_raw, dst_u, dst_v, width, + &kAbgrI601Constants); + RGBToYMatrixRow(src_raw, dst_y, width, &kAbgrI601Constants); + RGBToYMatrixRow(src_raw + src_stride_raw, dst_y + dst_stride_y, width, + &kAbgrI601Constants); + src_raw += src_stride_raw * 2; dst_y += dst_stride_y * 2; dst_u += dst_stride_u; dst_v += dst_stride_v; } if (height & 1) { - RGBToYMatrixRow(src_rgb24, dst_y, width, &kArgbI601Constants); - RGBToUVMatrixRow(src_rgb24, 0, dst_u, dst_v, width, &kArgbI601Constants); + RGBToYMatrixRow(src_raw, dst_y, width, &kAbgrI601Constants); + RGBToUVMatrixRow(src_raw, 0, dst_u, dst_v, width, &kAbgrI601Constants); } return 0; } diff --git a/unit_test/convert_test.cc b/unit_test/convert_test.cc index a38e7fdf9..12398fc08 100644 --- a/unit_test/convert_test.cc +++ b/unit_test/convert_test.cc @@ -2176,6 +2176,46 @@ TEST_F(LibYUVConvertTest, TestABGRToI420Matrix) { free_aligned_buffer_page_end(ref_v); } +TEST_F(LibYUVConvertTest, TestRAWToI420) { + const int kWidth = 16; + const int kHeight = 16; + align_buffer_page_end(src_raw, kWidth * kHeight * 3); + align_buffer_page_end(dst_y, kWidth * kHeight); + align_buffer_page_end(dst_u, kWidth / 2 * kHeight / 2); + align_buffer_page_end(dst_v, kWidth / 2 * kHeight / 2); + align_buffer_page_end(src_argb, kWidth * kHeight * 4); + align_buffer_page_end(ref_y, kWidth * kHeight); + align_buffer_page_end(ref_u, kWidth / 2 * kHeight / 2); + align_buffer_page_end(ref_v, kWidth / 2 * kHeight / 2); + + MemRandomize(src_raw, kWidth * kHeight * 3); + + RAWToI420(src_raw, kWidth * 3, dst_y, kWidth, dst_u, kWidth / 2, dst_v, + kWidth / 2, kWidth, kHeight); + + // Verify against RAWToARGB + ARGBToI420 + RAWToARGB(src_raw, kWidth * 3, src_argb, kWidth * 4, kWidth, kHeight); + ARGBToI420(src_argb, kWidth * 4, ref_y, kWidth, ref_u, kWidth / 2, ref_v, + kWidth / 2, kWidth, kHeight); + + for (int i = 0; i < kWidth * kHeight; ++i) { + ASSERT_EQ(dst_y[i], ref_y[i]); + } + for (int i = 0; i < kWidth / 2 * kHeight / 2; ++i) { + ASSERT_EQ(dst_u[i], ref_u[i]); + ASSERT_EQ(dst_v[i], ref_v[i]); + } + + free_aligned_buffer_page_end(src_raw); + free_aligned_buffer_page_end(dst_y); + free_aligned_buffer_page_end(dst_u); + free_aligned_buffer_page_end(dst_v); + free_aligned_buffer_page_end(src_argb); + free_aligned_buffer_page_end(ref_y); + free_aligned_buffer_page_end(ref_u); + free_aligned_buffer_page_end(ref_v); +} + TEST_F(LibYUVConvertTest, TestARGBToNV12Matrix) { const int kWidth = 16; const int kHeight = 16;