Get rid of UNSAFE_TODOs in CompositeRow_Argb2Rgb* functions

Use Zip() and color structs instead. CompositeRow_Foo_RgbByteOrder() can
be combined with CompositeRow_Foo(), as the functions are now templated
and can take both RGB and BGR color struct types in the output param.

Bug: 42271176
Change-Id: I67086142a1078287739aa3456f184e87a11a6cf5
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/123073
Reviewed-by: Tom Sepez <tsepez@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxge/dib/cfx_scanlinecompositor.cpp b/core/fxge/dib/cfx_scanlinecompositor.cpp
index a12aba0..6ee6d70 100644
--- a/core/fxge/dib/cfx_scanlinecompositor.cpp
+++ b/core/fxge/dib/cfx_scanlinecompositor.cpp
@@ -13,7 +13,9 @@
 #include "core/fxcrt/compiler_specific.h"
 #include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/notreached.h"
+#include "core/fxcrt/span_util.h"
 #include "core/fxcrt/stl_util.h"
+#include "core/fxcrt/zip.h"
 #include "core/fxge/dib/blend.h"
 #include "core/fxge/dib/fx_dib.h"
 
@@ -133,6 +135,13 @@
   return result / 255;
 }
 
+template <typename T, typename U>
+void AlphaMerge(const T& input, U& output, uint8_t alpha) {
+  output.blue = FXDIB_ALPHA_MERGE(output.blue, input.blue, alpha);
+  output.green = FXDIB_ALPHA_MERGE(output.green, input.green, alpha);
+  output.red = FXDIB_ALPHA_MERGE(output.red, input.red, alpha);
+}
+
 void CompositeRow_AlphaToMask(pdfium::span<uint8_t> dest_span,
                               pdfium::span<const uint8_t> src_span,
                               int pixel_count,
@@ -459,86 +468,106 @@
   });
 }
 
-void CompositeRow_Argb2Rgb_Blend(pdfium::span<uint8_t> dest_span,
-                                 pdfium::span<const uint8_t> src_span,
-                                 int width,
-                                 BlendMode blend_type,
-                                 int dest_Bpp,
-                                 pdfium::span<const uint8_t> clip_span) {
-  uint8_t* dest_scan = dest_span.data();
-  const uint8_t* src_scan = src_span.data();
-  const uint8_t* clip_scan = clip_span.data();
-  int blended_colors[3];
-  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
-  int dest_gap = dest_Bpp - 3;
-  UNSAFE_TODO({
-    for (int col = 0; col < width; col++) {
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = src_scan[3] * (*clip_scan++) / 255;
-      } else {
-        src_alpha = src_scan[3];
-      }
-      if (src_alpha == 0) {
-        dest_scan += dest_Bpp;
-        src_scan += 4;
-        continue;
-      }
-      if (bNonseparableBlend) {
-        RGB_Blend(blend_type, src_scan, dest_scan, blended_colors);
-      }
-      for (int color = 0; color < 3; color++) {
-        int back_color = *dest_scan;
-        int blended = bNonseparableBlend
-                          ? blended_colors[color]
-                          : Blend(blend_type, back_color, *src_scan);
-        *dest_scan = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha);
-        dest_scan++;
-        src_scan++;
-      }
-      dest_scan += dest_gap;
-      src_scan++;
-    }
-  });
+template <typename DestPixelStruct>
+void CompositePixelArgb2RgbNonSeparableBlend(
+    const FX_BGRA_STRUCT<uint8_t>& input,
+    uint8_t clip,
+    DestPixelStruct& output,
+    BlendMode blend_type) {
+  const uint8_t src_alpha = input.alpha * clip / 255;
+  if (src_alpha == 0) {
+    return;
+  }
+
+  FX_RGB_STRUCT<int> blended_color = RgbBlend(blend_type, input, output);
+  AlphaMerge(blended_color, output, src_alpha);
 }
 
-void CompositeRow_Argb2Rgb_NoBlend(pdfium::span<uint8_t> dest_span,
-                                   pdfium::span<const uint8_t> src_span,
-                                   int width,
-                                   int dest_Bpp,
-                                   pdfium::span<const uint8_t> clip_span) {
-  uint8_t* dest_scan = dest_span.data();
-  const uint8_t* src_scan = src_span.data();
-  const uint8_t* clip_scan = clip_span.data();
-  int dest_gap = dest_Bpp - 3;
-  UNSAFE_TODO({
-    for (int col = 0; col < width; col++) {
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = src_scan[3] * (*clip_scan++) / 255;
-      } else {
-        src_alpha = src_scan[3];
+template <typename DestPixelStruct>
+void CompositePixelArgb2RgbBlend(const FX_BGRA_STRUCT<uint8_t>& input,
+                                 uint8_t clip,
+                                 DestPixelStruct& output,
+                                 BlendMode blend_type) {
+  const uint8_t src_alpha = input.alpha * clip / 255;
+  if (src_alpha == 0) {
+    return;
+  }
+
+  FX_RGB_STRUCT<int> blended_color = {
+      .red = Blend(blend_type, output.red, input.red),
+      .green = Blend(blend_type, output.green, input.green),
+      .blue = Blend(blend_type, output.blue, input.blue),
+  };
+  AlphaMerge(blended_color, output, src_alpha);
+}
+
+template <typename DestPixelStruct>
+void CompositePixelArgb2RgbNoBlend(const FX_BGRA_STRUCT<uint8_t>& input,
+                                   uint8_t clip,
+                                   DestPixelStruct& output) {
+  const uint8_t src_alpha = input.alpha * clip / 255;
+  if (src_alpha == 255) {
+    output.blue = input.blue;
+    output.green = input.green;
+    output.red = input.red;
+    return;
+  }
+  if (src_alpha == 0) {
+    return;
+  }
+  AlphaMerge(input, output, src_alpha);
+}
+
+template <typename DestPixelStruct>
+void CompositeRowArgb2RgbBlend(
+    pdfium::span<const FX_BGRA_STRUCT<uint8_t>> src_span,
+    pdfium::span<const uint8_t> clip_span,
+    pdfium::span<DestPixelStruct> dest_span,
+    BlendMode blend_type) {
+  const bool non_separable_blend = IsNonSeparableBlendMode(blend_type);
+  if (clip_span.empty()) {
+    if (non_separable_blend) {
+      for (auto [input, output] : fxcrt::Zip(src_span, dest_span)) {
+        CompositePixelArgb2RgbNonSeparableBlend(input, /*clip=*/255, output,
+                                                blend_type);
       }
-      if (src_alpha == 255) {
-        FXSYS_memcpy(dest_scan, src_scan, 3);
-        dest_scan += dest_Bpp;
-        src_scan += 4;
-        continue;
-      }
-      if (src_alpha == 0) {
-        dest_scan += dest_Bpp;
-        src_scan += 4;
-        continue;
-      }
-      for (int color = 0; color < 3; color++) {
-        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha);
-        dest_scan++;
-        src_scan++;
-      }
-      dest_scan += dest_gap;
-      src_scan++;
+      return;
     }
-  });
+    for (auto [input, output] : fxcrt::Zip(src_span, dest_span)) {
+      CompositePixelArgb2RgbBlend(input, /*clip=*/255, output, blend_type);
+    }
+    return;
+  }
+
+  if (non_separable_blend) {
+    for (auto [input, clip, output] :
+         fxcrt::Zip(src_span, clip_span, dest_span)) {
+      CompositePixelArgb2RgbNonSeparableBlend(input, clip, output, blend_type);
+    }
+    return;
+  }
+  for (auto [input, clip, output] :
+       fxcrt::Zip(src_span, clip_span, dest_span)) {
+    CompositePixelArgb2RgbBlend(input, clip, output, blend_type);
+  }
+}
+
+template <typename DestPixelStruct>
+void CompositeRowArgb2RgbNoBlend(
+    pdfium::span<const FX_BGRA_STRUCT<uint8_t>> src_span,
+    pdfium::span<const uint8_t> clip_span,
+    pdfium::span<DestPixelStruct> dest_span) {
+  if (clip_span.empty()) {
+    for (auto [input, output] : fxcrt::Zip(src_span, dest_span)) {
+      CompositePixelArgb2RgbNoBlend(input, /*clip=*/255, output);
+    }
+    return;
+  }
+
+  for (auto [input, clip, output] :
+       fxcrt::Zip(src_span, clip_span, dest_span)) {
+    CompositePixelArgb2RgbNoBlend(input, clip, output);
+  }
 }
 
 void CompositeRow_Rgb2Rgb_Blend_NoClip(pdfium::span<uint8_t> dest_span,
@@ -1417,51 +1446,6 @@
   });
 }
 
-void CompositeRow_Argb2Rgb_Blend_RgbByteOrder(
-    pdfium::span<uint8_t> dest_span,
-    pdfium::span<const uint8_t> src_span,
-    int width,
-    BlendMode blend_type,
-    int dest_Bpp,
-    pdfium::span<const uint8_t> clip_span) {
-  int blended_colors[3];
-  bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type);
-  uint8_t* dest_scan = dest_span.data();
-  const uint8_t* src_scan = src_span.data();
-  const uint8_t* clip_scan = clip_span.data();
-  UNSAFE_TODO({
-    for (int col = 0; col < width; col++) {
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = src_scan[3] * (*clip_scan++) / 255;
-      } else {
-        src_alpha = src_scan[3];
-      }
-      if (src_alpha == 0) {
-        dest_scan += dest_Bpp;
-        src_scan += 4;
-        continue;
-      }
-      if (bNonseparableBlend) {
-        uint8_t dest_scan_o[3];
-        ReverseCopy3Bytes(dest_scan_o, dest_scan);
-        RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors);
-      }
-      for (int color = 0; color < 3; color++) {
-        int index = 2 - color;
-        int back_color = dest_scan[index];
-        int blended = bNonseparableBlend
-                          ? blended_colors[color]
-                          : Blend(blend_type, back_color, *src_scan);
-        dest_scan[index] = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha);
-        src_scan++;
-      }
-      dest_scan += dest_Bpp;
-      src_scan++;
-    }
-  });
-}
-
 void CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(
     pdfium::span<uint8_t> dest_span,
     pdfium::span<const uint8_t> src_span,
@@ -1518,46 +1502,6 @@
   });
 }
 
-void CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(
-    pdfium::span<uint8_t> dest_span,
-    pdfium::span<const uint8_t> src_span,
-    int width,
-    int dest_Bpp,
-    pdfium::span<const uint8_t> clip_span) {
-  uint8_t* dest_scan = dest_span.data();
-  const uint8_t* src_scan = src_span.data();
-  const uint8_t* clip_scan = clip_span.data();
-  UNSAFE_TODO({
-    for (int col = 0; col < width; col++) {
-      uint8_t src_alpha;
-      if (clip_scan) {
-        src_alpha = src_scan[3] * (*clip_scan++) / 255;
-      } else {
-        src_alpha = src_scan[3];
-      }
-      if (src_alpha == 255) {
-        ReverseCopy3Bytes(dest_scan, src_scan);
-        dest_scan += dest_Bpp;
-        src_scan += 4;
-        continue;
-      }
-      if (src_alpha == 0) {
-        dest_scan += dest_Bpp;
-        src_scan += 4;
-        continue;
-      }
-      for (int color = 0; color < 3; color++) {
-        int index = 2 - color;
-        dest_scan[index] =
-            FXDIB_ALPHA_MERGE(dest_scan[index], *src_scan, src_alpha);
-        src_scan++;
-      }
-      dest_scan += dest_Bpp;
-      src_scan++;
-    }
-  });
-}
-
 void CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder(
     pdfium::span<uint8_t> dest_span,
     pdfium::span<const uint8_t> src_span,
@@ -2473,27 +2417,52 @@
       CompositeRow_AlphaToMask(dest_scan, src_scan, width, clip_scan, 4);
       return;
     }
-    case FXDIB_Format::kRgb:
-    case FXDIB_Format::kRgb32: {
-      const int dest_Bpp = GetCompsFromFormat(m_DestFormat);
+    case FXDIB_Format::kRgb: {
+      auto src_span =
+          fxcrt::reinterpret_span<const FX_BGRA_STRUCT<uint8_t>>(src_scan)
+              .first(width);
       if (m_bRgbByteOrder) {
+        auto dest_span =
+            fxcrt::reinterpret_span<FX_RGB_STRUCT<uint8_t>>(dest_scan);
         if (m_BlendType == BlendMode::kNormal) {
-          CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(dest_scan, src_scan, width,
-                                                     dest_Bpp, clip_scan);
+          CompositeRowArgb2RgbNoBlend(src_span, clip_scan, dest_span);
           return;
         }
-        CompositeRow_Argb2Rgb_Blend_RgbByteOrder(
-            dest_scan, src_scan, width, m_BlendType, dest_Bpp, clip_scan);
+        CompositeRowArgb2RgbBlend(src_span, clip_scan, dest_span, m_BlendType);
         return;
       }
 
+      auto dest_span =
+          fxcrt::reinterpret_span<FX_BGR_STRUCT<uint8_t>>(dest_scan);
       if (m_BlendType == BlendMode::kNormal) {
-        CompositeRow_Argb2Rgb_NoBlend(dest_scan, src_scan, width, dest_Bpp,
-                                      clip_scan);
+        CompositeRowArgb2RgbNoBlend(src_span, clip_scan, dest_span);
         return;
       }
-      CompositeRow_Argb2Rgb_Blend(dest_scan, src_scan, width, m_BlendType,
-                                  dest_Bpp, clip_scan);
+      CompositeRowArgb2RgbBlend(src_span, clip_scan, dest_span, m_BlendType);
+      return;
+    }
+    case FXDIB_Format::kRgb32: {
+      auto src_span =
+          fxcrt::reinterpret_span<const FX_BGRA_STRUCT<uint8_t>>(src_scan)
+              .first(width);
+      if (m_bRgbByteOrder) {
+        auto dest_span =
+            fxcrt::reinterpret_span<FX_RGBA_STRUCT<uint8_t>>(dest_scan);
+        if (m_BlendType == BlendMode::kNormal) {
+          CompositeRowArgb2RgbNoBlend(src_span, clip_scan, dest_span);
+          return;
+        }
+        CompositeRowArgb2RgbBlend(src_span, clip_scan, dest_span, m_BlendType);
+        return;
+      }
+
+      auto dest_span =
+          fxcrt::reinterpret_span<FX_BGRA_STRUCT<uint8_t>>(dest_scan);
+      if (m_BlendType == BlendMode::kNormal) {
+        CompositeRowArgb2RgbNoBlend(src_span, clip_scan, dest_span);
+        return;
+      }
+      CompositeRowArgb2RgbBlend(src_span, clip_scan, dest_span, m_BlendType);
       return;
     }
     case FXDIB_Format::kArgb: {