Encapsulate more JPEG2000 decoding logic

Even though previous CLs moved some code out to helper functions,
CPDF_DIB::LoadJpxBitmap() is still over 150 lines long. Do another round
of refactoring to move JPEG2000 decoding logic into a separate
JpxDecodeConversion class.

Change-Id: I64d72dcd618210f022f545efee844a46f3f08589
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/134212
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp
index ed153fe..ede4844 100644
--- a/core/fpdfapi/page/cpdf_dib.cpp
+++ b/core/fpdfapi/page/cpdf_dib.cpp
@@ -201,17 +201,6 @@
   NOTREACHED();
 }
 
-std::optional<JpxDecodeAction> GetJpxDecodeAction(
-    const CJPX_Decoder::JpxImageInfo& jpx_info,
-    const CPDF_ColorSpace* pdf_colorspace) {
-  if (pdf_colorspace) {
-    return GetJpxDecodeActionFromColorSpaces(jpx_info, pdf_colorspace);
-  }
-
-  // When PDF does not provide a color space, check the image color space.
-  return GetJpxDecodeActionFromImageColorSpace(jpx_info);
-}
-
 int GetComponentCountFromOpjColorSpace(OPJ_COLOR_SPACE colorspace) {
   switch (colorspace) {
     case OPJ_CLRSPC_UNKNOWN:
@@ -232,6 +221,89 @@
   NOTREACHED();
 }
 
+class JpxDecodeConversion {
+ public:
+  static std::optional<JpxDecodeConversion> Create(
+      const CJPX_Decoder::JpxImageInfo& jpx_info,
+      const CPDF_ColorSpace* pdf_colorspace) {
+    // When the PDF does not provide a color space, check the image color space.
+    std::optional<JpxDecodeAction> maybe_action =
+        pdf_colorspace
+            ? GetJpxDecodeActionFromColorSpaces(jpx_info, pdf_colorspace)
+            : GetJpxDecodeActionFromImageColorSpace(jpx_info);
+    if (!maybe_action.has_value()) {
+      return std::nullopt;
+    }
+
+    JpxDecodeConversion conversion;
+    conversion.action_ = maybe_action.value();
+    switch (conversion.action_) {
+      case JpxDecodeAction::kDoNothing:
+        break;
+
+      case JpxDecodeAction::kUseGray:
+        conversion.override_colorspace_ =
+            CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray);
+        break;
+
+      case JpxDecodeAction::kUseIndexed:
+        break;
+
+      case JpxDecodeAction::kUseRgb:
+        DCHECK_GE(jpx_info.channels, 3);
+        conversion.override_colorspace_ = nullptr;
+        break;
+
+      case JpxDecodeAction::kUseCmyk:
+        conversion.override_colorspace_ =
+            CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK);
+        break;
+
+      case JpxDecodeAction::kConvertArgbToRgb:
+        conversion.override_colorspace_ = nullptr;
+        break;
+    }
+
+    // If there exists a PDF colorspace, then CPDF_DIB already has the
+    // components count.
+    if (!pdf_colorspace) {
+      conversion.jpx_components_count_ =
+          GetComponentCountFromOpjColorSpace(jpx_info.colorspace);
+    }
+    return conversion;
+  }
+
+  JpxDecodeAction action() const { return action_; }
+
+  const std::optional<RetainPtr<CPDF_ColorSpace>>& override_colorspace() const {
+    return override_colorspace_;
+  }
+
+  const std::optional<int>& jpx_components_count() const {
+    return jpx_components_count_;
+  }
+
+  bool swap_rgb() const {
+    return action_ == JpxDecodeAction::kUseRgb ||
+           action_ == JpxDecodeAction::kConvertArgbToRgb;
+  }
+
+ private:
+  JpxDecodeAction action_;
+
+  // The colorspace to override the existing colorspace.
+  //
+  // std::nullopt means no override colorspace.
+  // nullptr means reset the colorspace.
+  std::optional<RetainPtr<CPDF_ColorSpace>> override_colorspace_;
+
+  // The components count from the JPEG2000 image.
+  //
+  // std::nullopt means no new components count.
+  // Value <= 0 means failure.
+  std::optional<int> jpx_components_count_;
+};
+
 }  // namespace
 
 CPDF_DIB::CPDF_DIB(CPDF_Document* pDoc, RetainPtr<const CPDF_Stream> pStream)
@@ -729,66 +801,39 @@
     return nullptr;
   }
 
-  RetainPtr<CPDF_ColorSpace> original_colorspace = color_space_;
-  bool swap_rgb = false;
-  bool convert_argb_to_rgb = false;
-  auto maybe_action = GetJpxDecodeAction(image_info, color_space_.Get());
-  if (!maybe_action.has_value()) {
+  auto maybe_conversion =
+      JpxDecodeConversion::Create(image_info, color_space_.Get());
+  if (!maybe_conversion.has_value()) {
     return nullptr;
   }
 
-  const auto& action = maybe_action.value();
-  switch (action) {
-    case JpxDecodeAction::kDoNothing:
-      break;
-
-    case JpxDecodeAction::kUseGray:
-      color_space_ =
-          CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray);
-      break;
-
-    case JpxDecodeAction::kUseIndexed:
-      break;
-
-    case JpxDecodeAction::kUseRgb:
-      DCHECK(image_info.channels >= 3);
-      swap_rgb = true;
-      color_space_ = nullptr;
-      break;
-
-    case JpxDecodeAction::kUseCmyk:
-      color_space_ =
-          CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK);
-      break;
-
-    case JpxDecodeAction::kConvertArgbToRgb:
-      swap_rgb = true;
-      convert_argb_to_rgb = true;
-      color_space_.Reset();
-      break;
+  const auto& conversion = maybe_conversion.value();
+  if (conversion.override_colorspace().has_value()) {
+    color_space_ = conversion.override_colorspace().value();
   }
 
-  // If |original_colorspace| exists, then LoadColorInfo() already set
-  // |components_|.
-  if (original_colorspace) {
-    DCHECK_NE(0u, components_);
-  } else {
+  if (conversion.jpx_components_count().has_value()) {
     DCHECK_EQ(0u, components_);
-    components_ = GetComponentCountFromOpjColorSpace(image_info.colorspace);
-    if (components_ == 0) {
+    components_ = conversion.jpx_components_count().value();
+    if (components_ <= 0) {
       return nullptr;
     }
+  } else {
+    // LoadColorInfo() already set `components_`.
+    DCHECK_NE(0u, components_);
   }
 
   FXDIB_Format format;
-  if (action == JpxDecodeAction::kUseGray ||
-      action == JpxDecodeAction::kUseIndexed) {
+  if (conversion.action() == JpxDecodeAction::kUseGray ||
+      conversion.action() == JpxDecodeAction::kUseIndexed) {
     format = FXDIB_Format::k8bppRgb;
-  } else if (action == JpxDecodeAction::kUseRgb && image_info.channels == 3) {
+  } else if (conversion.action() == JpxDecodeAction::kUseRgb &&
+             image_info.channels == 3) {
     format = FXDIB_Format::kBgr;
-  } else if (action == JpxDecodeAction::kUseRgb && image_info.channels == 4) {
+  } else if (conversion.action() == JpxDecodeAction::kUseRgb &&
+             image_info.channels == 4) {
     format = FXDIB_Format::kBgrx;
-  } else if (action == JpxDecodeAction::kConvertArgbToRgb) {
+  } else if (conversion.action() == JpxDecodeAction::kConvertArgbToRgb) {
     CHECK_GE(image_info.channels, 4);
     format = FXDIB_Format::kBgrx;
   } else {
@@ -803,11 +848,12 @@
 
   result_bitmap->Clear(0xFFFFFFFF);
   if (!decoder->Decode(result_bitmap->GetWritableBuffer(),
-                       result_bitmap->GetPitch(), swap_rgb, components_)) {
+                       result_bitmap->GetPitch(), conversion.swap_rgb(),
+                       components_)) {
     return nullptr;
   }
 
-  if (convert_argb_to_rgb) {
+  if (conversion.action() == JpxDecodeAction::kConvertArgbToRgb) {
     DCHECK_EQ(3u, components_);
     auto rgb_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
     if (!rgb_bitmap->Create(image_info.width, image_info.height,