Add RGB, BGR to WP2ConversionFunction()

Change-Id: I9a0d08ff081c58cd7438864e29bfff914ce2d37d
Reviewed-on: https://chromium-review.googlesource.com/c/codecs/libwebp2/+/5756494
Reviewed-by: Maryla Ustarroz-Calonge <maryla@google.com>
Tested-by: WebM Builds <builds@webmproject.org>
diff --git a/src/dsp/argb_converter.cc b/src/dsp/argb_converter.cc
index 48df546..d48cbd2 100644
--- a/src/dsp/argb_converter.cc
+++ b/src/dsp/argb_converter.cc
@@ -413,14 +413,18 @@
 }
 
 //------------------------------------------------------------------------------
-// Combinations (32-bit to 32-bit only)
+// Combinations
 
 namespace {
 
 template <int from, int to>
 void WP2ConvertFromTo(const void* src, uint32_t width, void* dst) {
-  static_assert(from > WP2_ARGB_32 && from <= WP2_BGRX_32, "Unimplemented");
-  static_assert(to > WP2_ARGB_32 && to <= WP2_BGRX_32, "Unimplemented");
+  static_assert(from > WP2_ARGB_32 && from <= WP2_BGR_24, "Unimplemented");
+  static_assert(to > WP2_ARGB_32 && to <= WP2_BGR_24, "Unimplemented");
+  const size_t src_bytes_per_pixel =
+      WP2FormatBpp(static_cast<WP2SampleFormat>(from));
+  const size_t dst_bytes_per_pixel =
+      WP2FormatBpp(static_cast<WP2SampleFormat>(to));
 
   // Use a temporary layout Argb or ARGB to go from SRC to DST.
   // Do not premultiply or unmultiply unless necessary.
@@ -435,8 +439,9 @@
       WP2ARGBConvertFrom[from](src, num_pixels, tmp);
       WP2ARGBConvertTo[to](tmp, num_pixels, dst);
     }
-    src = reinterpret_cast<const uint8_t*>(src) + num_pixels * 4;
-    dst = reinterpret_cast<uint8_t*>(dst) + num_pixels * 4;
+    src = reinterpret_cast<const uint8_t*>(src) +
+          num_pixels * src_bytes_per_pixel;
+    dst = reinterpret_cast<uint8_t*>(dst) + num_pixels * dst_bytes_per_pixel;
     width -= num_pixels;
   }
 }
@@ -450,6 +455,8 @@
   if (to == WP2_bgrA_32) return WP2ConvertFromTo<from, WP2_bgrA_32>;
   if (to == WP2_BGRA_32) return WP2ConvertFromTo<from, WP2_BGRA_32>;
   if (to == WP2_BGRX_32) return WP2ConvertFromTo<from, WP2_BGRX_32>;
+  if (to == WP2_RGB_24) return WP2ConvertFromTo<from, WP2_RGB_24>;
+  if (to == WP2_BGR_24) return WP2ConvertFromTo<from, WP2_BGR_24>;
   return nullptr;
 }
 
@@ -476,6 +483,8 @@
   if (from == WP2_bgrA_32) return WP2ConvertFromToFunc<WP2_bgrA_32>(to);
   if (from == WP2_BGRA_32) return WP2ConvertFromToFunc<WP2_BGRA_32>(to);
   if (from == WP2_BGRX_32) return WP2ConvertFromToFunc<WP2_BGRX_32>(to);
+  if (from == WP2_RGB_24) return WP2ConvertFromToFunc<WP2_RGB_24>(to);
+  if (from == WP2_BGR_24) return WP2ConvertFromToFunc<WP2_BGR_24>(to);
   return nullptr;
 }
 
diff --git a/tests/test_conversion.cc b/tests/test_conversion.cc
index 5c9a88d..8aa55e3 100644
--- a/tests/test_conversion.cc
+++ b/tests/test_conversion.cc
@@ -28,7 +28,8 @@
 
   for (WP2SampleFormat from :
        {WP2_Argb_32, WP2_ARGB_32, WP2_XRGB_32, WP2_rgbA_32, WP2_RGBA_32,
-        WP2_RGBX_32, WP2_bgrA_32, WP2_BGRA_32, WP2_BGRX_32}) {
+        WP2_RGBX_32, WP2_bgrA_32, WP2_BGRA_32, WP2_BGRX_32, WP2_RGB_24,
+        WP2_BGR_24}) {
     ArgbBuffer src(from);
     ASSERT_WP2_OK(src.Resize(1, 1));
     ArgbBuffer roundtrip(from);
@@ -36,7 +37,8 @@
 
     for (WP2SampleFormat to :
          {WP2_Argb_32, WP2_ARGB_32, WP2_XRGB_32, WP2_rgbA_32, WP2_RGBA_32,
-          WP2_RGBX_32, WP2_bgrA_32, WP2_BGRA_32, WP2_BGRX_32}) {
+          WP2_RGBX_32, WP2_bgrA_32, WP2_BGRA_32, WP2_BGRX_32, WP2_RGB_24,
+          WP2_BGR_24}) {
       uint8_t values[] = {123, 61, 200, 211};  // Arbitrary.
 
       uint32_t alpha_channel_index;
@@ -50,11 +52,11 @@
           uint8_t* max_element = std::max_element(values, values + 4);
           std::swap(*max_element, values[alpha_channel_index]);
         }
-      } else {
+      } else if (alpha_channel_index < WP2FormatBpp(from)) {
         values[alpha_channel_index] = 255;
       }
 
-      std::copy(values, values + 4, src.GetRow8(0));
+      std::copy(values, values + WP2FormatBpp(from), src.GetRow8(0));
 
       ArgbBuffer dst(to);
       ASSERT_WP2_OK(dst.ConvertFrom(src));
@@ -62,7 +64,7 @@
 
       const int tolerance =
           WP2IsPremultiplied(from) == WP2IsPremultiplied(to) ? 0 : 1;
-      for (int i = 0; i < 4; ++i) {
+      for (uint32_t i = 0; i < WP2FormatBpp(from); ++i) {
         ASSERT_NEAR(roundtrip.GetRow8(0)[i], values[i], tolerance)
             << " (channel " << i << ") from " << from << " to " << to << " ("
             << static_cast<int>(dst.GetRow8(0)[i]) << ")";
diff --git a/tests/test_formats.cc b/tests/test_formats.cc
index fd52008..0105c44 100644
--- a/tests/test_formats.cc
+++ b/tests/test_formats.cc
@@ -389,6 +389,8 @@
     {WP2_bgrA_32, "WP2_bgrA_32", 274353, true},
     {WP2_BGRA_32, "WP2_BGRA_32", 897534, true},
     {WP2_BGRX_32, "WP2_BGRX_32", 414125, false},
+    {WP2_RGB_24, "WP2_RGB_24", 414125, false},
+    {WP2_BGR_24, "WP2_BGR_24", 414125, false},
 };
 
 typedef std::tuple<FmtTestCase, testutil::WP2CPUInfoStruct> SpeedTestParam;
diff --git a/tests/test_imageio_conversion.cc b/tests/test_imageio_conversion.cc
index 18b1f04..d7a5320 100644
--- a/tests/test_imageio_conversion.cc
+++ b/tests/test_imageio_conversion.cc
@@ -79,7 +79,7 @@
         Param{"source3.jpg", "ccsp/source3_C444p8.y4m", 50.f},
         Param{"source3.jpg", "ccsp/source3_C444p10.y4m", 55.f},
         Param{"source3.jpg", "ccsp/source3_C444p12.y4m", 59.f},
-        // Downsampling loss outweights more precision bits.
+        // Downsampling loss outweighs more precision bits.
         Param{"source3.jpg", "ccsp/source3_C420p8.y4m", 35.f},
         Param{"source3.jpg", "ccsp/source3_C420p10.y4m", 35.f},
         Param{"source3.jpg", "ccsp/source3_C420p12.y4m", 35.f}));
@@ -269,14 +269,13 @@
 }
 
 TEST(ReadImageTest, ChannelOrder) {
-  // WP2ConversionFunction() only returns conversion functions for all formats
-  // from and to WP2_ARGB_32 or WP2_Argb_32 and between 32-bit formats. It also
-  // supports plain copy, and ImageReaderPNG reads PNG samples in RGB(A) order.
+  // ImageReaderPNG reads PNG samples in RGB(A) order.
 
   // Opaque
   EXPECT_WP2_OK(TestChannelOrder("taiwan.png", WP2_ARGB_32));
   EXPECT_WP2_OK(TestChannelOrder("taiwan.png", WP2_Argb_32));
   EXPECT_WP2_OK(TestChannelOrder("taiwan.png", WP2_RGB_24));
+  EXPECT_WP2_OK(TestChannelOrder("taiwan.png", WP2_BGR_24));
 
   // Alpha
   EXPECT_WP2_OK(TestChannelOrder("source1.png", WP2_ARGB_32));
@@ -284,25 +283,16 @@
   EXPECT_WP2_OK(TestChannelOrder("source1.png", WP2_BGRA_32));
 
   // RGBA -> premultiplied/opaque Argb -> ARGB implies quality loss.
-  for (WP2SampleFormat format : {WP2_Argb_32, WP2_rgbA_32, WP2_bgrA_32,
-                                 WP2_XRGB_32, WP2_RGBX_32, WP2_BGRX_32}) {
+  for (WP2SampleFormat format :
+       {WP2_Argb_32, WP2_rgbA_32, WP2_bgrA_32, WP2_XRGB_32, WP2_RGBX_32,
+        WP2_BGRX_32, WP2_RGB_24, WP2_BGR_24}) {
     EXPECT_EQ(TestChannelOrder("source1.png", format),
               WP2_STATUS_INVALID_PARAMETER);
   }
 
-  // 24-bit to something else except WP2_Argb_32 and WP2_ARGB_32 is unsupported.
-  for (WP2SampleFormat format :
-       {WP2_RGBA_32, WP2_RGBX_32, WP2_bgrA_32, WP2_BGRA_32, WP2_BGRX_32,
-        WP2_BGR_24, WP2_Argb_38}) {
-    EXPECT_EQ(TestChannelOrder("taiwan.png", format),
-              WP2_STATUS_INVALID_COLORSPACE);
-  }
-
-  // Expect all other combinations to fail.
-  for (WP2SampleFormat format : {WP2_RGB_24, WP2_BGR_24, WP2_Argb_38}) {
-    EXPECT_EQ(TestChannelOrder("source1.png", format),
-              WP2_STATUS_INVALID_COLORSPACE);
-  }
+  // 10-bit is unsupported.
+  EXPECT_EQ(TestChannelOrder("source1.png", WP2_Argb_38),
+            WP2_STATUS_INVALID_COLORSPACE);
 }
 
 //------------------------------------------------------------------------------