Simplify TIFF decoding

- Change ProgressiveDecoder to always use `FXDIB_Format::kArgb`, since
  libtiff can decode images into "kAbgr" format. Stop ProgressiveDecoder
  from doing ARGB to RGB conversions.
- Change TiffDecoder to also only use `FXDIB_Format::kArgb`. Also don't
  try to fall back to scanline decoding if (whole) image decoding fails.
  Then a bunch of scanline decoding code can be removed.
- Pass bitmaps between ProgressiveDecoder and TiffDecoder using move
  semantics.
- Replace the remaining TiffBGRA2RGBA() usage with a FX_BGRA_STRUCT
  component swap.

Change-Id: I16f7da722720916f15a98f2a352f471332bca690
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/122774
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Tom Sepez <tsepez@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcodec/progressive_decoder.cpp b/core/fxcodec/progressive_decoder.cpp
index c0db252..50299f3 100644
--- a/core/fxcodec/progressive_decoder.cpp
+++ b/core/fxcodec/progressive_decoder.cpp
@@ -727,127 +727,14 @@
 }
 
 FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() {
-  bool ret = false;
-  if (m_pDeviceBitmap->GetBPP() == 32) {
-    ret = TiffDecoder::Decode(m_pTiffContext.get(), m_pDeviceBitmap);
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    if (!ret) {
-      m_status = FXCODEC_STATUS::kError;
-      return m_status;
-    }
-    m_status = FXCODEC_STATUS::kDecodeFinished;
-    return m_status;
-  }
-
   // TODO(crbug.com/355630556): Consider adding support for
   // `FXDIB_Format::kArgbPremul`
-  auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Format::kArgb)) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS::kError;
-    return m_status;
-  }
-  ret = TiffDecoder::Decode(m_pTiffContext.get(), pDIBitmap);
-  if (!ret) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS::kError;
-    return m_status;
-  }
-  RetainPtr<CFX_DIBitmap> pFormatBitmap;
-  bool created_format_bitmap = false;
-  switch (m_pDeviceBitmap->GetFormat()) {
-    case FXDIB_Format::kInvalid:
-    case FXDIB_Format::k1bppRgb:
-    case FXDIB_Format::k1bppMask:
-    case FXDIB_Format::k8bppRgb:
-    case FXDIB_Format::k8bppMask:
-      NOTREACHED_NORETURN();
-    case FXDIB_Format::kRgb:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      created_format_bitmap = pFormatBitmap->Create(
-          pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), FXDIB_Format::kRgb);
-      break;
-    case FXDIB_Format::kRgb32:
-      pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-      created_format_bitmap = pFormatBitmap->Create(
-          pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), FXDIB_Format::kRgb32);
-      break;
-    case FXDIB_Format::kArgb:
-      pFormatBitmap = pDIBitmap;
-      created_format_bitmap = true;
-      break;
-#if defined(PDF_USE_SKIA)
-    case FXDIB_Format::kArgbPremul:
-      // TODO(crbug.com/355630556): Consider adding support for
-      // `FXDIB_Format::kArgbPremul`
-      NOTREACHED_NORETURN();
-#endif
-  }
-  if (!created_format_bitmap) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS::kError;
-    return m_status;
-  }
-
-  UNSAFE_TODO({
-    switch (m_pDeviceBitmap->GetFormat()) {
-      case FXDIB_Format::kInvalid:
-      case FXDIB_Format::k1bppRgb:
-      case FXDIB_Format::k1bppMask:
-      case FXDIB_Format::k8bppRgb:
-      case FXDIB_Format::k8bppMask:
-        NOTREACHED_NORETURN();
-      case FXDIB_Format::kRgb:
-      case FXDIB_Format::kRgb32: {
-        int32_t desBpp =
-            (m_pDeviceBitmap->GetFormat() == FXDIB_Format::kRgb) ? 3 : 4;
-        for (int32_t row = 0; row < pDIBitmap->GetHeight(); row++) {
-          const uint8_t* src_line = pDIBitmap->GetScanline(row).data();
-          uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row).data();
-          for (int32_t col = 0; col < pDIBitmap->GetWidth(); col++) {
-            uint8_t _a = 255 - src_line[3];
-            uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
-            uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
-            uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
-            *dest_line++ = b;
-            *dest_line++ = g;
-            *dest_line++ = r;
-            dest_line += desBpp - 3;
-            src_line += 4;
-          }
-        }
-        break;
-      }
-      case FXDIB_Format::kArgb:
-        break;
-#if defined(PDF_USE_SKIA)
-      case FXDIB_Format::kArgbPremul:
-        // TODO(crbug.com/355630556): Consider adding support for
-        // `FXDIB_Format::kArgbPremul`
-        NOTREACHED_NORETURN();
-#endif
-    }
-  });
-  FXDIB_ResampleOptions options;
-  options.bInterpolateBilinear = true;
-  RetainPtr<CFX_DIBitmap> pStrechBitmap =
-      pFormatBitmap->StretchTo(m_SrcWidth, m_SrcHeight, options, nullptr);
-  pFormatBitmap = nullptr;
-  if (!pStrechBitmap) {
-    m_pDeviceBitmap = nullptr;
-    m_pFile = nullptr;
-    m_status = FXCODEC_STATUS::kError;
-    return m_status;
-  }
-  m_pDeviceBitmap->TransferBitmap(m_SrcWidth, m_SrcHeight,
-                                  std::move(pStrechBitmap), 0, 0);
-  m_pDeviceBitmap = nullptr;
+  CHECK_EQ(m_pDeviceBitmap->GetFormat(), FXDIB_Format::kArgb);
+  m_status =
+      TiffDecoder::Decode(m_pTiffContext.get(), std::move(m_pDeviceBitmap))
+          ? FXCODEC_STATUS::kDecodeFinished
+          : FXCODEC_STATUS::kError;
   m_pFile = nullptr;
-  m_status = FXCODEC_STATUS::kDecodeFinished;
   return m_status;
 }
 #endif  // PDF_ENABLE_XFA_TIFF
@@ -1250,14 +1137,14 @@
 #ifdef PDF_ENABLE_XFA_BMP
     case FXCODEC_IMAGE_BMP:
 #endif  // PDF_ENABLE_XFA_BMP
-#ifdef PDF_ENABLE_XFA_TIFF
-    case FXCODEC_IMAGE_TIFF:
-#endif  // PDF_ENABLE_XFA_TIFF
       return GetBitsPerPixel() <= 24 ? FXDIB_Format::kRgb
                                      : FXDIB_Format::kRgb32;
 #ifdef PDF_ENABLE_XFA_PNG
     case FXCODEC_IMAGE_PNG:
 #endif  // PDF_ENABLE_XFA_PNG
+#ifdef PDF_ENABLE_XFA_TIFF
+    case FXCODEC_IMAGE_TIFF:
+#endif  // PDF_ENABLE_XFA_TIFF
     default:
       // TODO(crbug.com/355630556): Consider adding support for
       // `FXDIB_Format::kArgbPremul`
diff --git a/core/fxcodec/tiff/tiff_decoder.cpp b/core/fxcodec/tiff/tiff_decoder.cpp
index c9ff433..c9468e8 100644
--- a/core/fxcodec/tiff/tiff_decoder.cpp
+++ b/core/fxcodec/tiff/tiff_decoder.cpp
@@ -6,8 +6,8 @@
 
 #include "core/fxcodec/tiff/tiff_decoder.h"
 
-#include <limits>
 #include <memory>
+#include <utility>
 
 #include "core/fxcodec/cfx_codec_memory.h"
 #include "core/fxcodec/fx_codec.h"
@@ -55,31 +55,13 @@
                      int32_t* comps,
                      int32_t* bpc,
                      CFX_DIBAttribute* pAttribute);
-  bool Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+  bool Decode(RetainPtr<CFX_DIBitmap> bitmap);
 
   RetainPtr<IFX_SeekableReadStream> io_in() const { return m_io_in; }
   uint32_t offset() const { return m_offset; }
   void set_offset(uint32_t offset) { m_offset = offset; }
 
  private:
-  bool IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const;
-  void SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap, uint16_t bps);
-  bool Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                     int32_t height,
-                     int32_t width,
-                     uint16_t bps,
-                     uint16_t spp);
-  bool Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                     int32_t height,
-                     int32_t width,
-                     uint16_t bps,
-                     uint16_t spp);
-  bool Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                      int32_t height,
-                      int32_t width,
-                      uint16_t bps,
-                      uint16_t spp);
-
   RetainPtr<IFX_SeekableReadStream> m_io_in;
   uint32_t m_offset = 0;
   std::unique_ptr<TIFF, TiffDeleter> m_tif_ctx;
@@ -192,17 +174,6 @@
 
 void tiff_unmap(thandle_t context, tdata_t, toff_t) {}
 
-void TiffBGRA2RGBA(uint8_t* pBuf, int32_t pixel, int32_t spp) {
-  UNSAFE_TODO({
-    for (int32_t n = 0; n < pixel; n++) {
-      uint8_t tmp = pBuf[0];
-      pBuf[0] = pBuf[2];
-      pBuf[2] = tmp;
-      pBuf += spp;
-    }
-  });
-}
-
 }  // namespace
 
 bool CTiffContext::InitDecoder(
@@ -268,199 +239,36 @@
   return true;
 }
 
-bool CTiffContext::IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const {
-  if (TIFFIsTiled(m_tif_ctx.get()))
-    return false;
-
-  uint16_t photometric = 0;
-  if (!TIFFGetField(m_tif_ctx.get(), TIFFTAG_PHOTOMETRIC, &photometric))
-    return false;
-
-  switch (pDIBitmap->GetBPP()) {
-    case 1:
-    case 8:
-      if (photometric != PHOTOMETRIC_PALETTE) {
-        return false;
-      }
-      break;
-    case 24:
-      if (photometric != PHOTOMETRIC_RGB) {
-        return false;
-      }
-      break;
-    default:
-      return false;
-  }
-  uint16_t planarconfig = 0;
-  if (!TIFFGetFieldDefaulted(m_tif_ctx.get(), TIFFTAG_PLANARCONFIG,
-                             &planarconfig))
-    return false;
-
-  return planarconfig != PLANARCONFIG_SEPARATE;
-}
-
-void CTiffContext::SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                              uint16_t bps) {
-  uint16_t* red_orig = nullptr;
-  uint16_t* green_orig = nullptr;
-  uint16_t* blue_orig = nullptr;
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_COLORMAP, &red_orig, &green_orig,
-               &blue_orig);
-#define CVT(x) ((uint16_t)((x) >> 8))
-  UNSAFE_TODO({
-    for (int32_t i = pdfium::checked_cast<int32_t>((1L << bps) - 1); i >= 0;
-         i--) {
-      red_orig[i] = CVT(red_orig[i]);
-      green_orig[i] = CVT(green_orig[i]);
-      blue_orig[i] = CVT(blue_orig[i]);
-    }
-    int32_t len = 1 << bps;
-    for (int32_t index = 0; index < len; index++) {
-      uint32_t r = red_orig[index] & 0xFF;
-      uint32_t g = green_orig[index] & 0xFF;
-      uint32_t b = blue_orig[index] & 0xFF;
-      uint32_t color = (uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16) |
-                       (((uint32_t)0xffL) << 24);
-      pDIBitmap->SetPaletteArgb(index, color);
-    }
-  });
-#undef CVT
-}
-
-bool CTiffContext::Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                 int32_t height,
-                                 int32_t width,
-                                 uint16_t bps,
-                                 uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 1 || spp != 1 || bps != 1 ||
-      !IsSupport(pDIBitmap)) {
-    return false;
-  }
-  SetPalette(pDIBitmap, bps);
-  int32_t size = static_cast<int32_t>(TIFFScanlineSize(m_tif_ctx.get()));
-  uint8_t* buf = reinterpret_cast<uint8_t*>(_TIFFmalloc(size));
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
-    return false;
-  }
-  for (int32_t row = 0; row < height; row++) {
-    uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data();
-    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
-    for (int32_t j = 0; j < size; j++) {
-      UNSAFE_TODO(bitMapbuffer[j] = buf[j]);
-    }
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                 int32_t height,
-                                 int32_t width,
-                                 uint16_t bps,
-                                 uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 8 || spp != 1 || (bps != 4 && bps != 8) ||
-      !IsSupport(pDIBitmap)) {
-    return false;
-  }
-  SetPalette(pDIBitmap, bps);
-  int32_t size = static_cast<int32_t>(TIFFScanlineSize(m_tif_ctx.get()));
-  uint8_t* buf = reinterpret_cast<uint8_t*>(_TIFFmalloc(size));
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
-    return false;
-  }
-  for (int32_t row = 0; row < height; row++) {
-    uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data();
-    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
-    UNSAFE_TODO({
-      for (int32_t j = 0; j < size; j++) {
-        switch (bps) {
-          case 4:
-            bitMapbuffer[2 * j + 0] = (buf[j] & 0xF0) >> 4;
-            bitMapbuffer[2 * j + 1] = (buf[j] & 0x0F) >> 0;
-            break;
-          case 8:
-            bitMapbuffer[j] = buf[j];
-            break;
-        }
-      }
-    });
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
-                                  int32_t height,
-                                  int32_t width,
-                                  uint16_t bps,
-                                  uint16_t spp) {
-  if (pDIBitmap->GetBPP() != 24 || !IsSupport(pDIBitmap))
-    return false;
-
-  int32_t size = static_cast<int32_t>(TIFFScanlineSize(m_tif_ctx.get()));
-  uint8_t* buf = reinterpret_cast<uint8_t*>(_TIFFmalloc(size));
-  if (!buf) {
-    TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer");
-    return false;
-  }
-  for (int32_t row = 0; row < height; row++) {
-    uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data();
-    TIFFReadScanline(m_tif_ctx.get(), buf, row, 0);
-    UNSAFE_TODO({
-      for (int32_t j = 0; j < size - 2; j += 3) {
-        bitMapbuffer[j + 0] = buf[j + 2];
-        bitMapbuffer[j + 1] = buf[j + 1];
-        bitMapbuffer[j + 2] = buf[j + 0];
-      }
-    });
-  }
-  _TIFFfree(buf);
-  return true;
-}
-
-bool CTiffContext::Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
-  uint32_t img_width = pDIBitmap->GetWidth();
-  uint32_t img_height = pDIBitmap->GetHeight();
+bool CTiffContext::Decode(RetainPtr<CFX_DIBitmap> bitmap) {
+  // TODO(crbug.com/355630556): Consider adding support for
+  // `FXDIB_Format::kArgbPremul`
+  CHECK_EQ(bitmap->GetFormat(), FXDIB_Format::kArgb);
+  const uint32_t img_width = bitmap->GetWidth();
+  const uint32_t img_height = bitmap->GetHeight();
   uint32_t width = 0;
   uint32_t height = 0;
   TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGEWIDTH, &width);
   TIFFGetField(m_tif_ctx.get(), TIFFTAG_IMAGELENGTH, &height);
-  if (img_width != width || img_height != height)
+  if (img_width != width || img_height != height) {
     return false;
+  }
 
-  if (pDIBitmap->GetBPP() == 32) {
-    uint16_t rotation = ORIENTATION_TOPLEFT;
-    TIFFGetField(m_tif_ctx.get(), TIFFTAG_ORIENTATION, &rotation);
-    uint32_t* data =
-        fxcrt::reinterpret_span<uint32_t>(pDIBitmap->GetWritableBuffer())
-            .data();
-    if (TIFFReadRGBAImageOriented(m_tif_ctx.get(), img_width, img_height, data,
-                                  rotation, 1)) {
-      for (uint32_t row = 0; row < img_height; row++) {
-        uint8_t* row_buf = pDIBitmap->GetWritableScanline(row).data();
-        TiffBGRA2RGBA(row_buf, img_width, 4);
-      }
-      return true;
+  uint16_t rotation = ORIENTATION_TOPLEFT;
+  TIFFGetField(m_tif_ctx.get(), TIFFTAG_ORIENTATION, &rotation);
+  uint32_t* data =
+      fxcrt::reinterpret_span<uint32_t>(bitmap->GetWritableBuffer()).data();
+  if (!TIFFReadRGBAImageOriented(m_tif_ctx.get(), img_width, img_height, data,
+                                 rotation, 1)) {
+    return false;
+  }
+
+  for (uint32_t row = 0; row < img_height; row++) {
+    auto row_span = bitmap->GetWritableScanlineAs<FX_BGRA_STRUCT<uint8_t>>(row);
+    for (auto& pixel : row_span) {
+      std::swap(pixel.blue, pixel.red);
     }
   }
-  uint16_t spp = 0;
-  uint16_t bps = 0;
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_SAMPLESPERPIXEL, &spp);
-  TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &bps);
-  FX_SAFE_UINT32 safe_bpp = bps;
-  safe_bpp *= spp;
-  if (!safe_bpp.IsValid())
-    return false;
-  uint32_t bpp = safe_bpp.ValueOrDie();
-  if (bpp == 1)
-    return Decode1bppRGB(pDIBitmap, height, width, bps, spp);
-  if (bpp <= 8)
-    return Decode8bppRGB(pDIBitmap, height, width, bps, spp);
-  if (bpp <= 24)
-    return Decode24bppRGB(pDIBitmap, height, width, bps, spp);
-  return false;
+  return true;
 }
 
 namespace fxcodec {
@@ -491,9 +299,9 @@
 
 // static
 bool TiffDecoder::Decode(ProgressiveDecoderIface::Context* pContext,
-                         const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
+                         RetainPtr<CFX_DIBitmap> bitmap) {
   auto* ctx = static_cast<CTiffContext*>(pContext);
-  return ctx->Decode(pDIBitmap);
+  return ctx->Decode(std::move(bitmap));
 }
 
 }  // namespace fxcodec
diff --git a/core/fxcodec/tiff/tiff_decoder.h b/core/fxcodec/tiff/tiff_decoder.h
index 8804095..d8e63be 100644
--- a/core/fxcodec/tiff/tiff_decoder.h
+++ b/core/fxcodec/tiff/tiff_decoder.h
@@ -35,8 +35,9 @@
                             int32_t* comps,
                             int32_t* bpc,
                             CFX_DIBAttribute* pAttribute);
+  // `bitmap` must be `FXDIB_Format::kArgb`.
   static bool Decode(ProgressiveDecoderIface::Context* ctx,
-                     const RetainPtr<CFX_DIBitmap>& pDIBitmap);
+                     RetainPtr<CFX_DIBitmap> bitmap);
 
   TiffDecoder() = delete;
   TiffDecoder(const TiffDecoder&) = delete;