Handle a case of colorspace mismatch with JPEG2000 images.

Some PDFs generated on iOS have a mismatch between the PDF image
metadata and the JPEG2000 image metadata. Detect this case and do the
necessary image conversion.

Unsuppress a pixel test now that it renders correctly.

Bug: chromium:1012369
Change-Id: I36322a81ec70182d89f69b1f89d1af2bb4f2b106
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/65753
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 6976a0d..9e73072 100644
--- a/core/fpdfapi/page/cpdf_dib.cpp
+++ b/core/fpdfapi/page/cpdf_dib.cpp
@@ -103,15 +103,24 @@
   kDoNothing,
   kUseRgb,
   kUseCmyk,
+  kConvertArgbToRgb,
 };
 
-JpxDecodeAction GetJpxDecodeAction(uint32_t jpx_components,
+JpxDecodeAction GetJpxDecodeAction(const CJPX_Decoder::JpxImageInfo& jpx_info,
                                    const CPDF_ColorSpace* pdf_colorspace) {
   if (pdf_colorspace) {
     // Make sure the JPX image and the PDF colorspace agree on the number of
-    // components.
-    if (jpx_components != pdf_colorspace->CountComponents())
+    // components. In case of a mismatch, try to handle the discrepancy.
+    if (jpx_info.components != pdf_colorspace->CountComponents()) {
+      // Many PDFs generated by iOS meets this condition. See
+      // https://crbug.com/1012369 for example.
+      if (pdf_colorspace->CountComponents() == 3 && jpx_info.components == 4 &&
+          jpx_info.colorspace == OPJ_CLRSPC_SRGB) {
+        return JpxDecodeAction::kConvertArgbToRgb;
+      }
+
       return JpxDecodeAction::kFail;
+    }
 
     if (pdf_colorspace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB))
       return JpxDecodeAction::kUseRgb;
@@ -121,7 +130,7 @@
 
   // Cases where the PDF did not provide a colorspace.
   // Choose how to decode based on the number of components in the JPX image.
-  switch (jpx_components) {
+  switch (jpx_info.components) {
     case 3:
       return JpxDecodeAction::kUseRgb;
 
@@ -632,7 +641,8 @@
 
   RetainPtr<CPDF_ColorSpace> original_colorspace = m_pColorSpace;
   bool swap_rgb = false;
-  switch (GetJpxDecodeAction(image_info.components, m_pColorSpace.Get())) {
+  bool convert_argb_to_rgb = false;
+  switch (GetJpxDecodeAction(image_info, m_pColorSpace.Get())) {
     case JpxDecodeAction::kFail:
       return nullptr;
 
@@ -648,6 +658,11 @@
     case JpxDecodeAction::kUseCmyk:
       m_pColorSpace = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
       break;
+
+    case JpxDecodeAction::kConvertArgbToRgb:
+      swap_rgb = true;
+      convert_argb_to_rgb = true;
+      m_pColorSpace.Reset();
   }
 
   // If |original_colorspace| exists, then LoadColorInfo() already set
@@ -681,8 +696,26 @@
     return nullptr;
   }
 
-  if (m_pColorSpace && m_pColorSpace->GetFamily() == PDFCS_INDEXED &&
-      m_bpc < 8) {
+  if (convert_argb_to_rgb) {
+    DCHECK_EQ(3, m_nComponents);
+    auto rgb_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+    if (!rgb_bitmap->Create(image_info.width, image_info.height, FXDIB_Rgb))
+      return nullptr;
+
+    // TODO(thestig): Is there existing code that does this already?
+    // TODO(crbug.com/pdfium/1469): Handle alpha channel.
+    for (uint32_t row = 0; row < image_info.height; ++row) {
+      const uint8_t* src = result_bitmap->GetScanline(row);
+      uint8_t* dest = rgb_bitmap->GetWritableScanline(row);
+      for (uint32_t col = 0; col < image_info.width; ++col) {
+        memcpy(dest, src, 3);
+        src += 4;
+        dest += 3;
+      }
+    }
+    result_bitmap = std::move(rgb_bitmap);
+  } else if (m_pColorSpace && m_pColorSpace->GetFamily() == PDFCS_INDEXED &&
+             m_bpc < 8) {
     int scale = 8 - m_bpc;
     for (uint32_t row = 0; row < image_info.height; ++row) {
       uint8_t* scanline = result_bitmap->GetWritableScanline(row);
diff --git a/core/fxcodec/jpx/cjpx_decoder.cpp b/core/fxcodec/jpx/cjpx_decoder.cpp
index 95f76ff..29fb849 100644
--- a/core/fxcodec/jpx/cjpx_decoder.cpp
+++ b/core/fxcodec/jpx/cjpx_decoder.cpp
@@ -489,7 +489,7 @@
 }
 
 CJPX_Decoder::JpxImageInfo CJPX_Decoder::GetInfo() const {
-  return {m_Image->x1, m_Image->y1, m_Image->numcomps};
+  return {m_Image->x1, m_Image->y1, m_Image->numcomps, m_Image->color_space};
 }
 
 bool CJPX_Decoder::Decode(uint8_t* dest_buf, uint32_t pitch, bool swap_rgb) {
diff --git a/core/fxcodec/jpx/cjpx_decoder.h b/core/fxcodec/jpx/cjpx_decoder.h
index 4d9883c..f0869c1 100644
--- a/core/fxcodec/jpx/cjpx_decoder.h
+++ b/core/fxcodec/jpx/cjpx_decoder.h
@@ -35,6 +35,7 @@
     uint32_t width;
     uint32_t height;
     uint32_t components;
+    COLOR_SPACE colorspace;
   };
 
   static void Sycc420ToRgbForTesting(opj_image_t* img);
diff --git a/testing/SUPPRESSIONS b/testing/SUPPRESSIONS
index b3059e9..d8e0a2b 100644
--- a/testing/SUPPRESSIONS
+++ b/testing/SUPPRESSIONS
@@ -340,9 +340,6 @@
 # TODO(chromium:451366): Remove after associated bug is fixed
 bug_451366.in * * *
 
-# TODO(chromium:1012369): Remove after associated bug is fixed
-bug_1012369.in * * *
-
 # xfa_specific
 
 # TODO(pdfium:1107): Remove after associated bug is fixed