Remove out parameters from CPDF_ColorSpace::GetRGB().

Use std::optional<> to convey failure. This may avoid potentially
uninit variables.

-- introduce GetRGBOrZerosOnError() helper method.

Change-Id: I9aaa33fe0945ac0d08baaa5bd8c461852dd61b4c
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/120497
Reviewed-by: Thomas Sepez <tsepez@google.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_colorspace.cpp b/core/fpdfapi/page/cpdf_colorspace.cpp
index fab3fab..d9eb5ab 100644
--- a/core/fpdfapi/page/cpdf_colorspace.cpp
+++ b/core/fpdfapi/page/cpdf_colorspace.cpp
@@ -125,10 +125,8 @@
   ~CPDF_CalGray() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   uint32_t v_Load(CPDF_Document* pDoc,
                   const CPDF_Array* pArray,
                   std::set<const CPDF_Object*>* pVisited) override;
@@ -155,10 +153,8 @@
   ~CPDF_CalRGB() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   void TranslateImageLine(pdfium::span<uint8_t> dest_span,
                           pdfium::span<const uint8_t> src_span,
                           int pixels,
@@ -189,10 +185,8 @@
   ~CPDF_LabCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
@@ -223,10 +217,8 @@
   ~CPDF_ICCBasedCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   void TranslateImageLine(pdfium::span<uint8_t> dest_span,
                           pdfium::span<const uint8_t> src_span,
                           int pixels,
@@ -262,10 +254,8 @@
   ~CPDF_SeparationCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
@@ -287,10 +277,8 @@
   ~CPDF_DeviceNCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   void GetDefaultValue(int iComponent,
                        float* value,
                        float* min,
@@ -384,25 +372,19 @@
   return kSRGBSamples2[scale / 4 - 48] / 255.0f;
 }
 
-void XYZ_to_sRGB(float X, float Y, float Z, float* R, float* G, float* B) {
-  float R1 = 3.2410f * X - 1.5374f * Y - 0.4986f * Z;
-  float G1 = -0.9692f * X + 1.8760f * Y + 0.0416f * Z;
-  float B1 = 0.0556f * X - 0.2040f * Y + 1.0570f * Z;
-
-  *R = RGB_Conversion(R1);
-  *G = RGB_Conversion(G1);
-  *B = RGB_Conversion(B1);
+FX_RGB_STRUCT<float> XYZ_to_sRGB(float X, float Y, float Z) {
+  const float R1 = 3.2410f * X - 1.5374f * Y - 0.4986f * Z;
+  const float G1 = -0.9692f * X + 1.8760f * Y + 0.0416f * Z;
+  const float B1 = 0.0556f * X - 0.2040f * Y + 1.0570f * Z;
+  return {RGB_Conversion(R1), RGB_Conversion(G1), RGB_Conversion(B1)};
 }
 
-void XYZ_to_sRGB_WhitePoint(float X,
-                            float Y,
-                            float Z,
-                            float Xw,
-                            float Yw,
-                            float Zw,
-                            float* R,
-                            float* G,
-                            float* B) {
+FX_RGB_STRUCT<float> XYZ_to_sRGB_WhitePoint(float X,
+                                            float Y,
+                                            float Z,
+                                            float Xw,
+                                            float Yw,
+                                            float Zw) {
   // The following RGB_xyz is based on
   // sRGB value {Rx,Ry}={0.64, 0.33}, {Gx,Gy}={0.30, 0.60}, {Bx,By}={0.15, 0.06}
 
@@ -423,9 +405,7 @@
   Matrix_3by3 M = RGB_xyz.Multiply(RGB_SUM_XYZ_DIAG);
   Vector_3by1 RGB = M.Inverse().TransformVector(XYZ);
 
-  *R = RGB_Conversion(RGB.a);
-  *G = RGB_Conversion(RGB.b);
-  *B = RGB_Conversion(RGB.c);
+  return {RGB_Conversion(RGB.a), RGB_Conversion(RGB.b), RGB_Conversion(RGB.c)};
 }
 
 class StockColorSpaces {
@@ -626,16 +606,13 @@
 
 std::optional<FX_COLORREF> CPDF_ColorSpace::GetColorRef(
     pdfium::span<const float> buffer) {
-  float r;
-  float g;
-  float b;
-  if (!GetRGB(buffer, &r, &g, &b)) {
+  auto maybe_rgb = GetRGB(buffer);
+  if (!maybe_rgb.has_value()) {
     return std::nullopt;
   }
-
-  r = std::clamp(r, 0.0f, 1.0f);
-  g = std::clamp(g, 0.0f, 1.0f);
-  b = std::clamp(b, 0.0f, 1.0f);
+  const float r = std::clamp(maybe_rgb.value().red, 0.0f, 1.0f);
+  const float g = std::clamp(maybe_rgb.value().green, 0.0f, 1.0f);
+  const float b = std::clamp(maybe_rgb.value().blue, 0.0f, 1.0f);
   return FXSYS_BGR(FXSYS_roundf(b * 255), FXSYS_roundf(g * 255),
                    FXSYS_roundf(r * 255));
 }
@@ -662,19 +639,16 @@
   uint8_t* dest_buf = dest_span.data();
   const uint8_t* src_buf = src_span.data();
   std::vector<float> src(m_nComponents);
-  float R;
-  float G;
-  float B;
   const int divisor = m_Family != Family::kIndexed ? 255 : 1;
   UNSAFE_TODO({
     for (int i = 0; i < pixels; i++) {
       for (uint32_t j = 0; j < m_nComponents; j++) {
         src[j] = static_cast<float>(*src_buf++) / divisor;
       }
-      GetRGB(src, &R, &G, &B);
-      *dest_buf++ = static_cast<int32_t>(B * 255);
-      *dest_buf++ = static_cast<int32_t>(G * 255);
-      *dest_buf++ = static_cast<int32_t>(R * 255);
+      auto rgb = GetRGBOrZerosOnError(src);
+      *dest_buf++ = static_cast<int32_t>(rgb.blue * 255);
+      *dest_buf++ = static_cast<int32_t>(rgb.green * 255);
+      *dest_buf++ = static_cast<int32_t>(rgb.red * 255);
     }
   });
 }
@@ -731,14 +705,10 @@
   return 1;
 }
 
-bool CPDF_CalGray::GetRGB(pdfium::span<const float> pBuf,
-                          float* R,
-                          float* G,
-                          float* B) const {
-  *R = pBuf[0];
-  *G = pBuf[0];
-  *B = pBuf[0];
-  return true;
+std::optional<FX_RGB_STRUCT<float>> CPDF_CalGray::GetRGB(
+    pdfium::span<const float> pBuf) const {
+  const float gray = pBuf[0];
+  return FX_RGB_STRUCT<float>{gray, gray, gray};
 }
 
 void CPDF_CalGray::TranslateImageLine(pdfium::span<uint8_t> dest_span,
@@ -796,10 +766,8 @@
   return 3;
 }
 
-bool CPDF_CalRGB::GetRGB(pdfium::span<const float> pBuf,
-                         float* R,
-                         float* G,
-                         float* B) const {
+std::optional<FX_RGB_STRUCT<float>> CPDF_CalRGB::GetRGB(
+    pdfium::span<const float> pBuf) const {
   UNSAFE_TODO({
     float A_ = pBuf[0];
     float B_ = pBuf[1];
@@ -822,10 +790,9 @@
       Y = B_;
       Z = C_;
     }
-    XYZ_to_sRGB_WhitePoint(X, Y, Z, m_WhitePoint[0], m_WhitePoint[1],
-                           m_WhitePoint[2], R, G, B);
+    return XYZ_to_sRGB_WhitePoint(X, Y, Z, m_WhitePoint[0], m_WhitePoint[1],
+                                  m_WhitePoint[2]);
   });
-  return true;
 }
 
 void CPDF_CalRGB::TranslateImageLine(pdfium::span<uint8_t> dest_span,
@@ -885,10 +852,8 @@
   return 3;
 }
 
-bool CPDF_LabCS::GetRGB(pdfium::span<const float> pBuf,
-                        float* R,
-                        float* G,
-                        float* B) const {
+std::optional<FX_RGB_STRUCT<float>> CPDF_LabCS::GetRGB(
+    pdfium::span<const float> pBuf) const {
   float Lstar = pBuf[0];
   float astar = pBuf[1];
   float bstar = pBuf[2];
@@ -913,8 +878,7 @@
   else
     Z = 1.0889f * N * N * N;
 
-  XYZ_to_sRGB(X, Y, Z, R, G, B);
-  return true;
+  return XYZ_to_sRGB(X, Y, Z);
 }
 
 void CPDF_LabCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
@@ -929,18 +893,15 @@
   const uint8_t* pSrcBuf = src_span.data();
   UNSAFE_TODO({
     for (int i = 0; i < pixels; i++) {
-      float lab[3];
-      lab[0] = pSrcBuf[0] * 100 / 255.0f;
-      lab[1] = pSrcBuf[1] - 128;
-      lab[2] = pSrcBuf[2] - 128;
-
-      float R;
-      float G;
-      float B;
-      GetRGB(lab, &R, &G, &B);
-      pDestBuf[0] = static_cast<int32_t>(B * 255);
-      pDestBuf[1] = static_cast<int32_t>(G * 255);
-      pDestBuf[2] = static_cast<int32_t>(R * 255);
+      const float lab[3] = {
+          static_cast<float>(pSrcBuf[0] * 100) / 255.0f,
+          static_cast<float>(pSrcBuf[1] - 128),
+          static_cast<float>(pSrcBuf[2] - 128),
+      };
+      auto rgb = GetRGBOrZerosOnError(lab);
+      pDestBuf[0] = static_cast<int32_t>(rgb.blue * 255);
+      pDestBuf[1] = static_cast<int32_t>(rgb.green * 255);
+      pDestBuf[2] = static_cast<int32_t>(rgb.red * 255);
       pDestBuf += 3;
       pSrcBuf += 3;
     }
@@ -990,32 +951,20 @@
   return nComponents;
 }
 
-bool CPDF_ICCBasedCS::GetRGB(pdfium::span<const float> pBuf,
-                             float* R,
-                             float* G,
-                             float* B) const {
-  DCHECK(profile_);
+std::optional<FX_RGB_STRUCT<float>> CPDF_ICCBasedCS::GetRGB(
+    pdfium::span<const float> pBuf) const {
   if (profile_->IsSRGB()) {
-    *R = pBuf[0];
-    *G = pBuf[1];
-    *B = pBuf[2];
-    return true;
+    return FX_RGB_STRUCT<float>{pBuf[0], pBuf[1], pBuf[2]};
   }
   if (profile_->IsSupported()) {
     float rgb[3];
     profile_->Translate(pBuf.first(ComponentCount()), rgb);
-    *R = rgb[0];
-    *G = rgb[1];
-    *B = rgb[2];
-    return true;
+    return FX_RGB_STRUCT<float>{rgb[0], rgb[1], rgb[2]};
   }
-  if (m_pBaseCS)
-    return m_pBaseCS->GetRGB(pBuf, R, G, B);
-
-  *R = 0.0f;
-  *G = 0.0f;
-  *B = 0.0f;
-  return true;
+  if (m_pBaseCS) {
+    return m_pBaseCS->GetRGB(pBuf);
+  }
+  return FX_RGB_STRUCT<float>{};
 }
 
 void CPDF_ICCBasedCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
@@ -1197,37 +1146,29 @@
   return 1;
 }
 
-bool CPDF_SeparationCS::GetRGB(pdfium::span<const float> pBuf,
-                               float* R,
-                               float* G,
-                               float* B) const {
-  if (m_IsNoneType)
-    return false;
-
+std::optional<FX_RGB_STRUCT<float>> CPDF_SeparationCS::GetRGB(
+    pdfium::span<const float> pBuf) const {
+  if (m_IsNoneType) {
+    return std::nullopt;
+  }
   if (!m_pFunc) {
-    if (!m_pBaseCS)
-      return false;
-
-    int nComps = m_pBaseCS->ComponentCount();
-    std::vector<float> results(nComps);
-    for (int i = 0; i < nComps; i++)
-      results[i] = pBuf[0];
-    return m_pBaseCS->GetRGB(results, R, G, B);
+    if (!m_pBaseCS) {
+      return std::nullopt;
+    }
+    std::vector<float> results(m_pBaseCS->ComponentCount(), pBuf[0]);
+    return m_pBaseCS->GetRGB(results);
   }
 
   // Using at least 16 elements due to the call m_pAltCS->GetRGB() below.
   std::vector<float> results(std::max(m_pFunc->OutputCount(), 16u));
   uint32_t nresults = m_pFunc->Call(pBuf.first(1), results).value_or(0);
-  if (nresults == 0)
-    return false;
-
-  if (m_pBaseCS)
-    return m_pBaseCS->GetRGB(results, R, G, B);
-
-  *R = 0.0f;
-  *G = 0.0f;
-  *B = 0.0f;
-  return false;
+  if (nresults == 0) {
+    return std::nullopt;
+  }
+  if (m_pBaseCS) {
+    return m_pBaseCS->GetRGB(results);
+  }
+  return std::nullopt;
 }
 
 CPDF_DeviceNCS::CPDF_DeviceNCS() : CPDF_BasedCS(Family::kDeviceN) {}
@@ -1269,21 +1210,19 @@
   return fxcrt::CollectionSize<uint32_t>(*pObj);
 }
 
-bool CPDF_DeviceNCS::GetRGB(pdfium::span<const float> pBuf,
-                            float* R,
-                            float* G,
-                            float* B) const {
-  if (!m_pFunc)
-    return false;
-
+std::optional<FX_RGB_STRUCT<float>> CPDF_DeviceNCS::GetRGB(
+    pdfium::span<const float> pBuf) const {
+  if (!m_pFunc) {
+    return std::nullopt;
+  }
   // Using at least 16 elements due to the call m_pAltCS->GetRGB() below.
   std::vector<float> results(std::max(m_pFunc->OutputCount(), 16u));
   uint32_t nresults =
       m_pFunc->Call(pBuf.first(ComponentCount()), pdfium::make_span(results))
           .value_or(0);
 
-  if (nresults == 0)
-    return false;
-
-  return m_pBaseCS->GetRGB(results, R, G, B);
+  if (nresults == 0) {
+    return std::nullopt;
+  }
+  return m_pBaseCS->GetRGB(results);
 }
diff --git a/core/fpdfapi/page/cpdf_colorspace.h b/core/fpdfapi/page/cpdf_colorspace.h
index c747524..769eb60 100644
--- a/core/fpdfapi/page/cpdf_colorspace.h
+++ b/core/fpdfapi/page/cpdf_colorspace.h
@@ -104,11 +104,16 @@
   // components are integers.
   std::optional<FX_COLORREF> GetColorRef(pdfium::span<const float> buffer);
 
+  // Wrapper around GetRGB() that returns black (0, 0, 0) when an actual value
+  // can not be determined.
+  FX_RGB_STRUCT<float> GetRGBOrZerosOnError(
+      pdfium::span<const float> pBuf) const {
+    return GetRGB(pBuf).value_or(FX_RGB_STRUCT<float>{});
+  }
+
   // Use CPDF_Pattern::GetPatternColorRef() instead of GetRGB() for patterns.
-  virtual bool GetRGB(pdfium::span<const float> pBuf,
-                      float* R,
-                      float* G,
-                      float* B) const = 0;
+  virtual std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const = 0;
 
   virtual void GetDefaultValue(int iComponent,
                                float* value,
diff --git a/core/fpdfapi/page/cpdf_devicecs.cpp b/core/fpdfapi/page/cpdf_devicecs.cpp
index c3b25e7..5887dd3 100644
--- a/core/fpdfapi/page/cpdf_devicecs.cpp
+++ b/core/fpdfapi/page/cpdf_devicecs.cpp
@@ -43,45 +43,37 @@
   NOTREACHED_NORETURN();
 }
 
-bool CPDF_DeviceCS::GetRGB(pdfium::span<const float> pBuf,
-                           float* R,
-                           float* G,
-                           float* B) const {
+std::optional<FX_RGB_STRUCT<float>> CPDF_DeviceCS::GetRGB(
+    pdfium::span<const float> pBuf) const {
   switch (GetFamily()) {
     case Family::kDeviceGray: {
       const float pix = NormalizeChannel(pBuf.front());
-      *R = pix;
-      *G = pix;
-      *B = pix;
-      return true;
+      return FX_RGB_STRUCT<float>{pix, pix, pix};
     }
     case Family::kDeviceRGB: {
-      auto rgb =
-          fxcrt::truncating_reinterpret_span<const FX_RGB_STRUCT<float>>(pBuf);
-      *R = NormalizeChannel(rgb.front().red);
-      *G = NormalizeChannel(rgb.front().green);
-      *B = NormalizeChannel(rgb.front().blue);
-      return true;
+      const auto& rgb =
+          fxcrt::truncating_reinterpret_span<const FX_RGB_STRUCT<float>>(pBuf)
+              .front();
+      return FX_RGB_STRUCT<float>{
+          NormalizeChannel(rgb.red),
+          NormalizeChannel(rgb.green),
+          NormalizeChannel(rgb.blue),
+      };
     }
     case Family::kDeviceCMYK: {
-      auto cmyk =
-          fxcrt::truncating_reinterpret_span<const FX_CMYK_STRUCT<float>>(pBuf);
+      const auto& cmyk =
+          fxcrt::truncating_reinterpret_span<const FX_CMYK_STRUCT<float>>(pBuf)
+              .front();
       if (IsStdConversionEnabled()) {
-        float k = cmyk.front().key;
-        *R = 1.0f - std::min(1.0f, cmyk.front().cyan + k);
-        *G = 1.0f - std::min(1.0f, cmyk.front().magenta + k);
-        *B = 1.0f - std::min(1.0f, cmyk.front().yellow + k);
-        return true;
+        return FX_RGB_STRUCT<float>{
+            1.0f - std::min(1.0f, cmyk.cyan + cmyk.key),
+            1.0f - std::min(1.0f, cmyk.magenta + cmyk.key),
+            1.0f - std::min(1.0f, cmyk.yellow + cmyk.key),
+        };
       }
-      FX_RGB_STRUCT<float> rgb =
-          AdobeCMYK_to_sRGB(NormalizeChannel(cmyk.front().cyan),
-                            NormalizeChannel(cmyk.front().magenta),
-                            NormalizeChannel(cmyk.front().yellow),
-                            NormalizeChannel(cmyk.front().key));
-      *R = rgb.red;
-      *G = rgb.green;
-      *B = rgb.blue;
-      return true;
+      return AdobeCMYK_to_sRGB(
+          NormalizeChannel(cmyk.cyan), NormalizeChannel(cmyk.magenta),
+          NormalizeChannel(cmyk.yellow), NormalizeChannel(cmyk.key));
     }
     default:
       NOTREACHED_NORETURN();
diff --git a/core/fpdfapi/page/cpdf_devicecs.h b/core/fpdfapi/page/cpdf_devicecs.h
index 8e8ca66..788c36e 100644
--- a/core/fpdfapi/page/cpdf_devicecs.h
+++ b/core/fpdfapi/page/cpdf_devicecs.h
@@ -18,10 +18,8 @@
   ~CPDF_DeviceCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   void TranslateImageLine(pdfium::span<uint8_t> dest_span,
                           pdfium::span<const uint8_t> src_span,
                           int pixels,
diff --git a/core/fpdfapi/page/cpdf_devicecs_unittest.cpp b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
index 4c3c348..a50d19a 100644
--- a/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
@@ -10,110 +10,120 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(CPDF_DeviceCSTest, GetRGBFromGray) {
-  float R;
-  float G;
-  float B;
   auto device_gray =
       pdfium::MakeRetain<CPDF_DeviceCS>(CPDF_ColorSpace::Family::kDeviceGray);
 
   // Test normal values. For gray, only first value from buf should be used.
   float buf[3] = {0.43f, 0.11f, 0.34f};
-  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.43f, R);
-  EXPECT_FLOAT_EQ(0.43f, G);
-  EXPECT_FLOAT_EQ(0.43f, B);
+  auto maybe_rgb = device_gray->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.43f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.43f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.43f, maybe_rgb.value().blue);
+
   buf[0] = 0.872f;
-  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.872f, R);
-  EXPECT_FLOAT_EQ(0.872f, G);
-  EXPECT_FLOAT_EQ(0.872f, B);
+  maybe_rgb = device_gray->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.872f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.872f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.872f, maybe_rgb.value().blue);
 
   // Test boundary values
   buf[0] = {0.0f};
-  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.0f, R);
-  EXPECT_FLOAT_EQ(0.0f, G);
-  EXPECT_FLOAT_EQ(0.0f, B);
+  maybe_rgb = device_gray->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.0f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.0f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.0f, maybe_rgb.value().blue);
+
   buf[0] = 1.0f;
-  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(1.0f, R);
-  EXPECT_FLOAT_EQ(1.0f, G);
-  EXPECT_FLOAT_EQ(1.0f, B);
+  maybe_rgb = device_gray->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(1.0f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(1.0f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(1.0f, maybe_rgb.value().blue);
 
   // Test out of range values
   buf[0] = -0.01f;
-  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.0f, R);
-  EXPECT_FLOAT_EQ(0.0f, G);
-  EXPECT_FLOAT_EQ(0.0f, B);
+  maybe_rgb = device_gray->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.0f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.0f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.0f, maybe_rgb.value().blue);
+
   buf[0] = 12.5f;
-  ASSERT_TRUE(device_gray->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(1.0f, R);
-  EXPECT_FLOAT_EQ(1.0f, G);
-  EXPECT_FLOAT_EQ(1.0f, B);
+  maybe_rgb = device_gray->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(1.0f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(1.0f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(1.0f, maybe_rgb.value().blue);
 }
 
 TEST(CPDF_DeviceCSTest, GetRGBFromRGB) {
-  float R;
-  float G;
-  float B;
   auto device_rgb =
       pdfium::MakeRetain<CPDF_DeviceCS>(CPDF_ColorSpace::Family::kDeviceRGB);
 
   // Test normal values
   float buf[3] = {0.13f, 1.0f, 0.652f};
-  ASSERT_TRUE(device_rgb->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.13f, R);
-  EXPECT_FLOAT_EQ(1.0f, G);
-  EXPECT_FLOAT_EQ(0.652f, B);
+  auto maybe_rgb = device_rgb->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.13f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(1.0f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.652f, maybe_rgb.value().blue);
+
   buf[0] = 0.0f;
   buf[1] = 0.52f;
   buf[2] = 0.78f;
-  ASSERT_TRUE(device_rgb->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.0f, R);
-  EXPECT_FLOAT_EQ(0.52f, G);
-  EXPECT_FLOAT_EQ(0.78f, B);
+  maybe_rgb = device_rgb->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.0f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.52f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.78f, maybe_rgb.value().blue);
 
   // Test out of range values
   buf[0] = -10.5f;
   buf[1] = 100.0f;
-  ASSERT_TRUE(device_rgb->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.0f, R);
-  EXPECT_FLOAT_EQ(1.0f, G);
-  EXPECT_FLOAT_EQ(0.78f, B);
+  maybe_rgb = device_rgb->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.0f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(1.0f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.78f, maybe_rgb.value().blue);
 }
 
 TEST(CPDF_DeviceCSTest, GetRGBFromCMYK) {
-  float R;
-  float G;
-  float B;
   auto device_cmyk =
       pdfium::MakeRetain<CPDF_DeviceCS>(CPDF_ColorSpace::Family::kDeviceCMYK);
 
   // Test normal values
   float buf[4] = {0.6f, 0.5f, 0.3f, 0.9f};
-  ASSERT_TRUE(device_cmyk->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.0627451f, R);
-  EXPECT_FLOAT_EQ(0.0627451f, G);
-  EXPECT_FLOAT_EQ(0.10588236f, B);
+  auto maybe_rgb = device_cmyk->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.0627451f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.0627451f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.10588236f, maybe_rgb.value().blue);
+
   buf[0] = 0.15f;
   buf[2] = 0.0f;
-  ASSERT_TRUE(device_cmyk->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.2f, R);
-  EXPECT_FLOAT_EQ(0.0862745f, G);
-  EXPECT_FLOAT_EQ(0.16470589f, B);
+  maybe_rgb = device_cmyk->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.2f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.0862745f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.16470589f, maybe_rgb.value().blue);
+
   buf[2] = 1.0f;
   buf[3] = 0.0f;
-  ASSERT_TRUE(device_cmyk->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.85098046f, R);
-  EXPECT_FLOAT_EQ(0.552941f, G);
-  EXPECT_FLOAT_EQ(0.15686275f, B);
+  maybe_rgb = device_cmyk->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.85098046f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.552941f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.15686275f, maybe_rgb.value().blue);
 
   // Test out of range values
   buf[2] = 1.5f;
   buf[3] = -0.6f;
-  ASSERT_TRUE(device_cmyk->GetRGB(buf, &R, &G, &B));
-  EXPECT_FLOAT_EQ(0.85098046f, R);
-  EXPECT_FLOAT_EQ(0.552941f, G);
-  EXPECT_FLOAT_EQ(0.15686275f, B);
+  maybe_rgb = device_cmyk->GetRGB(buf);
+  ASSERT_TRUE(maybe_rgb.has_value());
+  EXPECT_FLOAT_EQ(0.85098046f, maybe_rgb.value().red);
+  EXPECT_FLOAT_EQ(0.552941f, maybe_rgb.value().green);
+  EXPECT_FLOAT_EQ(0.15686275f, maybe_rgb.value().blue);
 }
diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp
index 8bb6e2a..b33f04f 100644
--- a/core/fpdfapi/page/cpdf_dib.cpp
+++ b/core/fpdfapi/page/cpdf_dib.cpp
@@ -876,12 +876,10 @@
     std::vector<float> colors =
         ReadArrayElementsToVector(pMatte.Get(), m_nComponents);
 
-    float R;
-    float G;
-    float B;
-    m_pColorSpace->GetRGB(colors, &R, &G, &B);
-    m_MatteColor = ArgbEncode(0, FXSYS_roundf(R * 255), FXSYS_roundf(G * 255),
-                              FXSYS_roundf(B * 255));
+    auto rgb = m_pColorSpace->GetRGBOrZerosOnError(colors);
+    m_MatteColor =
+        ArgbEncode(0, FXSYS_roundf(rgb.red * 255),
+                   FXSYS_roundf(rgb.green * 255), FXSYS_roundf(rgb.blue * 255));
   }
   return StartLoadMaskDIB(std::move(mask));
 }
@@ -955,13 +953,10 @@
     std::fill(std::begin(color_values), std::end(color_values),
               m_CompData[0].m_DecodeMin);
 
-    float R = 0.0f;
-    float G = 0.0f;
-    float B = 0.0f;
-    m_pColorSpace->GetRGB(color_values, &R, &G, &B);
-
-    FX_ARGB argb0 = ArgbEncode(255, FXSYS_roundf(R * 255),
-                               FXSYS_roundf(G * 255), FXSYS_roundf(B * 255));
+    auto rgb = m_pColorSpace->GetRGBOrZerosOnError(color_values);
+    FX_ARGB argb0 =
+        ArgbEncode(255, FXSYS_roundf(rgb.red * 255),
+                   FXSYS_roundf(rgb.green * 255), FXSYS_roundf(rgb.blue * 255));
     FX_ARGB argb1;
     const CPDF_IndexedCS* indexed_cs = m_pColorSpace->AsIndexedCS();
     if (indexed_cs && indexed_cs->GetMaxIndex() == 0) {
@@ -973,9 +968,10 @@
       color_values[0] += m_CompData[0].m_DecodeStep;
       color_values[1] += m_CompData[0].m_DecodeStep;
       color_values[2] += m_CompData[0].m_DecodeStep;
-      m_pColorSpace->GetRGB(color_values, &R, &G, &B);
-      argb1 = ArgbEncode(255, FXSYS_roundf(R * 255), FXSYS_roundf(G * 255),
-                         FXSYS_roundf(B * 255));
+      auto result = m_pColorSpace->GetRGBOrZerosOnError(color_values);
+      argb1 = ArgbEncode(255, FXSYS_roundf(result.red * 255),
+                         FXSYS_roundf(result.green * 255),
+                         FXSYS_roundf(result.blue * 255));
     }
 
     if (argb0 != 0xFF000000 || argb1 != 0xFFFFFFFF) {
@@ -1001,21 +997,18 @@
       color_values[j] = m_CompData[j].m_DecodeMin +
                         m_CompData[j].m_DecodeStep * encoded_component;
     }
-    float R = 0;
-    float G = 0;
-    float B = 0;
+    FX_RGB_STRUCT<float> rgb;
     if (m_nComponents == 1 && m_Family == CPDF_ColorSpace::Family::kICCBased &&
         m_pColorSpace->ComponentCount() > 1) {
-      int nComponents = m_pColorSpace->ComponentCount();
-      std::vector<float> temp_buf(nComponents);
-      for (int k = 0; k < nComponents; ++k)
-        temp_buf[k] = color_values[0];
-      m_pColorSpace->GetRGB(temp_buf, &R, &G, &B);
+      const size_t nComponents = m_pColorSpace->ComponentCount();
+      std::vector<float> temp_buf(nComponents, color_values[0]);
+      rgb = m_pColorSpace->GetRGBOrZerosOnError(temp_buf);
     } else {
-      m_pColorSpace->GetRGB(color_values, &R, &G, &B);
+      rgb = m_pColorSpace->GetRGBOrZerosOnError(color_values);
     }
-    SetPaletteArgb(i, ArgbEncode(255, FXSYS_roundf(R * 255),
-                                 FXSYS_roundf(G * 255), FXSYS_roundf(B * 255)));
+    SetPaletteArgb(i, ArgbEncode(255, FXSYS_roundf(rgb.red * 255),
+                                 FXSYS_roundf(rgb.green * 255),
+                                 FXSYS_roundf(rgb.blue * 255)));
   }
 }
 
@@ -1055,9 +1048,7 @@
 
   // Using at least 16 elements due to the call m_pColorSpace->GetRGB().
   std::vector<float> color_values(std::max(m_nComponents, 16u));
-  float R = 0.0f;
-  float G = 0.0f;
-  float B = 0.0f;
+  FX_RGB_STRUCT<float> rgb = {};
   uint64_t src_bit_pos = 0;
   uint64_t src_byte_pos = 0;
   size_t dest_byte_pos = 0;
@@ -1075,18 +1066,17 @@
         src_bit_pos += m_bpc;
       }
     }
-
     if (TransMask()) {
       float k = 1.0f - color_values[3];
-      R = (1.0f - color_values[0]) * k;
-      G = (1.0f - color_values[1]) * k;
-      B = (1.0f - color_values[2]) * k;
+      rgb.red = (1.0f - color_values[0]) * k;
+      rgb.green = (1.0f - color_values[1]) * k;
+      rgb.blue = (1.0f - color_values[2]) * k;
     } else if (m_Family != CPDF_ColorSpace::Family::kPattern) {
-      m_pColorSpace->GetRGB(color_values, &R, &G, &B);
+      rgb = m_pColorSpace->GetRGBOrZerosOnError(color_values);
     }
-    R = std::clamp(R, 0.0f, 1.0f);
-    G = std::clamp(G, 0.0f, 1.0f);
-    B = std::clamp(B, 0.0f, 1.0f);
+    const float R = std::clamp(rgb.red, 0.0f, 1.0f);
+    const float G = std::clamp(rgb.green, 0.0f, 1.0f);
+    const float B = std::clamp(rgb.blue, 0.0f, 1.0f);
     dest_scan[dest_byte_pos] = static_cast<uint8_t>(B * 255);
     dest_scan[dest_byte_pos + 1] = static_cast<uint8_t>(G * 255);
     dest_scan[dest_byte_pos + 2] = static_cast<uint8_t>(R * 255);
diff --git a/core/fpdfapi/page/cpdf_indexedcs.cpp b/core/fpdfapi/page/cpdf_indexedcs.cpp
index 7ec1925..7f3a259 100644
--- a/core/fpdfapi/page/cpdf_indexedcs.cpp
+++ b/core/fpdfapi/page/cpdf_indexedcs.cpp
@@ -89,13 +89,11 @@
   return 1;
 }
 
-bool CPDF_IndexedCS::GetRGB(pdfium::span<const float> pBuf,
-                            float* R,
-                            float* G,
-                            float* B) const {
+std::optional<FX_RGB_STRUCT<float>> CPDF_IndexedCS::GetRGB(
+    pdfium::span<const float> pBuf) const {
   int32_t index = static_cast<int32_t>(pBuf[0]);
   if (index < 0 || index > max_index_) {
-    return false;
+    return std::nullopt;
   }
 
   DCHECK(!component_min_max_.empty());
@@ -105,10 +103,7 @@
   length += 1;
   length *= component_min_max_.size();
   if (!length.IsValid() || length.ValueOrDie() > lookup_table_.size()) {
-    *R = 0;
-    *G = 0;
-    *B = 0;
-    return false;
+    return std::nullopt;
   }
 
   DataVector<float> comps(component_min_max_.size());
@@ -118,5 +113,5 @@
         comp.min +
         comp.max * lookup_table_[index * component_min_max_.size() + i] / 255;
   }
-  return m_pBaseCS->GetRGB(comps, R, G, B);
+  return m_pBaseCS->GetRGB(comps);
 }
diff --git a/core/fpdfapi/page/cpdf_indexedcs.h b/core/fpdfapi/page/cpdf_indexedcs.h
index b3d774f..99a80e8 100644
--- a/core/fpdfapi/page/cpdf_indexedcs.h
+++ b/core/fpdfapi/page/cpdf_indexedcs.h
@@ -28,10 +28,8 @@
   ~CPDF_IndexedCS() override;
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   const CPDF_IndexedCS* AsIndexedCS() const override;
   uint32_t v_Load(CPDF_Document* pDoc,
                   const CPDF_Array* pArray,
diff --git a/core/fpdfapi/page/cpdf_meshstream.cpp b/core/fpdfapi/page/cpdf_meshstream.cpp
index 4cb16c3..51208d1 100644
--- a/core/fpdfapi/page/cpdf_meshstream.cpp
+++ b/core/fpdfapi/page/cpdf_meshstream.cpp
@@ -209,22 +209,16 @@
                                            (m_ColorMax[i] - m_ColorMin[i]) /
                                            m_ComponentMax;
   }
-
-  FX_RGB_STRUCT<float> rgb = {};
   if (m_funcs.empty()) {
-    m_pCS->GetRGB(color_value, &rgb.red, &rgb.green, &rgb.blue);
-    return rgb;
+    return m_pCS->GetRGBOrZerosOnError(color_value);
   }
-
   float result[kMaxComponents] = {};
   for (const auto& func : m_funcs) {
     if (func && func->OutputCount() <= kMaxComponents) {
       func->Call(pdfium::make_span(color_value).first(1u), result);
     }
   }
-
-  m_pCS->GetRGB(result, &rgb.red, &rgb.green, &rgb.blue);
-  return rgb;
+  return m_pCS->GetRGBOrZerosOnError(result);
 }
 
 bool CPDF_MeshStream::ReadVertex(const CFX_Matrix& pObject2Bitmap,
diff --git a/core/fpdfapi/page/cpdf_patterncs.cpp b/core/fpdfapi/page/cpdf_patterncs.cpp
index fea5416..5c37dbd 100644
--- a/core/fpdfapi/page/cpdf_patterncs.cpp
+++ b/core/fpdfapi/page/cpdf_patterncs.cpp
@@ -44,10 +44,8 @@
   return m_pBaseCS->ComponentCount() + 1;
 }
 
-bool CPDF_PatternCS::GetRGB(pdfium::span<const float> pBuf,
-                            float* R,
-                            float* G,
-                            float* B) const {
+std::optional<FX_RGB_STRUCT<float>> CPDF_PatternCS::GetRGB(
+    pdfium::span<const float> pBuf) const {
   NOTREACHED_NORETURN();
 }
 
diff --git a/core/fpdfapi/page/cpdf_patterncs.h b/core/fpdfapi/page/cpdf_patterncs.h
index 07d6f54..d55b200 100644
--- a/core/fpdfapi/page/cpdf_patterncs.h
+++ b/core/fpdfapi/page/cpdf_patterncs.h
@@ -26,10 +26,8 @@
   void InitializeStockPattern();
 
   // CPDF_ColorSpace:
-  bool GetRGB(pdfium::span<const float> pBuf,
-              float* R,
-              float* G,
-              float* B) const override;
+  std::optional<FX_RGB_STRUCT<float>> GetRGB(
+      pdfium::span<const float> pBuf) const override;
   const CPDF_PatternCS* AsPatternCS() const override;
   uint32_t v_Load(CPDF_Document* pDoc,
                   const CPDF_Array* pArray,
diff --git a/core/fpdfapi/render/cpdf_rendershading.cpp b/core/fpdfapi/render/cpdf_rendershading.cpp
index 325cee1..4601d39 100644
--- a/core/fpdfapi/render/cpdf_rendershading.cpp
+++ b/core/fpdfapi/render/cpdf_rendershading.cpp
@@ -83,12 +83,10 @@
       if (nresults.has_value())
         result_span = result_span.subspan(nresults.value());
     }
-    float R = 0.0f;
-    float G = 0.0f;
-    float B = 0.0f;
-    pCS->GetRGB(result_array, &R, &G, &B);
-    shading_steps[i] = ArgbEncode(alpha, FXSYS_roundf(R * 255),
-                                  FXSYS_roundf(G * 255), FXSYS_roundf(B * 255));
+    auto rgb = pCS->GetRGBOrZerosOnError(result_array);
+    shading_steps[i] =
+        ArgbEncode(alpha, FXSYS_roundf(rgb.red * 255),
+                   FXSYS_roundf(rgb.green * 255), FXSYS_roundf(rgb.blue * 255));
   }
   return shading_steps;
 }
@@ -314,13 +312,10 @@
         if (nresults.has_value())
           result_span = result_span.subspan(nresults.value());
       }
-      float R = 0.0f;
-      float G = 0.0f;
-      float B = 0.0f;
-      pCS->GetRGB(result_array, &R, &G, &B);
-      dib_buf[column] = ArgbEncode(alpha, static_cast<int32_t>(R * 255),
-                                   static_cast<int32_t>(G * 255),
-                                   static_cast<int32_t>(B * 255));
+      auto rgb = pCS->GetRGBOrZerosOnError(result_array);
+      dib_buf[column] = ArgbEncode(alpha, static_cast<int32_t>(rgb.red * 255),
+                                   static_cast<int32_t>(rgb.green * 255),
+                                   static_cast<int32_t>(rgb.blue * 255));
     }
   }
 }
@@ -911,13 +906,10 @@
       std::vector<float> comps = ReadArrayElementsToVector(
           pBackColor.Get(), pColorSpace->ComponentCount());
 
-      float R = 0.0f;
-      float G = 0.0f;
-      float B = 0.0f;
-      pColorSpace->GetRGB(comps, &R, &G, &B);
-      background = ArgbEncode(255, static_cast<int32_t>(R * 255),
-                              static_cast<int32_t>(G * 255),
-                              static_cast<int32_t>(B * 255));
+      auto rgb = pColorSpace->GetRGBOrZerosOnError(comps);
+      background = ArgbEncode(255, static_cast<int32_t>(rgb.red * 255),
+                              static_cast<int32_t>(rgb.green * 255),
+                              static_cast<int32_t>(rgb.blue * 255));
     }
   }
   FX_RECT clip_rect_bbox = clip_rect;
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 9c2dd94..94ab6c1 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -1457,10 +1457,8 @@
   std::vector<float> floats = ReadArrayElementsToVector(pBC.Get(), count);
   floats.resize(comps);
 
-  float R;
-  float G;
-  float B;
-  pCS->GetRGB(floats, &R, &G, &B);
-  return ArgbEncode(255, static_cast<int>(R * 255), static_cast<int>(G * 255),
-                    static_cast<int>(B * 255));
+  auto rgb = pCS->GetRGBOrZerosOnError(floats);
+  return ArgbEncode(255, static_cast<int>(rgb.red * 255),
+                    static_cast<int>(rgb.green * 255),
+                    static_cast<int>(rgb.blue * 255));
 }