Correctly handle image transparency in path objects

When displaying an embedded image in a path object, the image's alpha
value needs to be multiplied with the alpha value defined in the path
object to ensure transparency is rendered correctly.

Bug: pdfium:2106
Change-Id: Ibaa8786b8a4e522c258590aed26a6779712d921d
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/118830
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
Commit-Queue: Thomas Sepez <tsepez@google.com>
diff --git a/AUTHORS b/AUTHORS
index 6e4b6e4..47c7538 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -38,6 +38,7 @@
 Stefan Ziegler <sz5000@gmx.de>
 Stephan Hartmann <stha09@googlemail.com>
 Tibor Dusnoki <tdusnoki@inf.u-szeged.hu>
+Wang Chuan <jdyaomo@gmail.com>
 Wang Qing <wangqing-hf@loongson.cn>
 Zhuo Qingliang <zhuo.dev@gmail.com>
 # END individuals section.
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 9c2dd94..8e4d72f 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -584,6 +584,7 @@
   }
   RetainPtr<const CPDF_Dictionary> pFormResource;
   float group_alpha = 1.0f;
+  float initial_alpha = 1.0f;
   CPDF_Transparency transparency = m_Transparency;
   bool bGroupTransparent = false;
   const CPDF_FormObject* pFormObj = pPageObj->AsForm();
@@ -592,13 +593,14 @@
     transparency = pFormObj->form()->GetTransparency();
     bGroupTransparent = transparency.IsIsolated();
     pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
+    initial_alpha = m_InitialStates.general_state().GetFillAlpha();
   }
   bool bTextClip =
       (pPageObj->clip_path().HasRef() &&
        pPageObj->clip_path().GetTextCount() > 0 && !m_bPrint &&
        !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP));
   if (!pSMaskDict && group_alpha == 1.0f && blend_type == BlendMode::kNormal &&
-      !bTextClip && !bGroupTransparent) {
+      !bTextClip && !bGroupTransparent && initial_alpha == 1.0f) {
     return false;
   }
   if (m_bPrint) {
@@ -693,6 +695,9 @@
     bitmap_device.MultiplyAlpha(group_alpha);
   }
   transparency = m_Transparency;
+  if (initial_alpha != 1.0f) {
+    bitmap_device.MultiplyAlpha(initial_alpha);
+  }
   if (pPageObj->IsForm()) {
     transparency.SetGroup();
   }
diff --git a/core/fpdfapi/render/cpdf_rendertiling.cpp b/core/fpdfapi/render/cpdf_rendertiling.cpp
index 90a963d..6b50893 100644
--- a/core/fpdfapi/render/cpdf_rendertiling.cpp
+++ b/core/fpdfapi/render/cpdf_rendertiling.cpp
@@ -122,9 +122,15 @@
   if (width > clip_box.Width() || height > clip_box.Height() ||
       width * height > clip_box.Width() * clip_box.Height()) {
     std::unique_ptr<CPDF_GraphicStates> pStates;
-    if (!pPattern->colored())
+    if (!pPattern->colored()) {
       pStates = CPDF_RenderStatus::CloneObjStates(&pPageObj->graphic_states(),
                                                   bStroke);
+    } else if (pPageObj->AsPath()) {
+      pStates = std::make_unique<CPDF_GraphicStates>();
+      pStates->SetDefaultStates();
+      pStates->mutable_general_state().SetFillAlpha(
+          pPageObj->general_state().GetFillAlpha());
+    }
 
     RetainPtr<const CPDF_Dictionary> pFormResource =
         pPatternForm->GetDict()->GetDictFor("Resources");
diff --git a/testing/resources/pixel/bug_2106.in b/testing/resources/pixel/bug_2106.in
new file mode 100644
index 0000000..ad9bc35
--- /dev/null
+++ b/testing/resources/pixel/bug_2106.in
@@ -0,0 +1,110 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ExtGState <<
+      /GS0 6 0 R
+    >>
+    /Pattern <<
+      /Pa0 7 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q /Pattern cs /Pa0 scn /GS0 gs 0 0 20 20 re f* Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /ExtGState
+  /ca 1
+>>
+endobj
+{{object 6 0}} <<
+  /Type /ExtGState
+  /ca 0.5
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Pattern
+  /Subtype /Form
+  /BBox [0 0 30 30]
+  {{streamlen}}
+  /PaintType 1
+  /PatternType 1
+  /Resources <<
+    /ExtGState <<
+      /GS0 5 0 R
+    >>
+    /XObject <<
+      /Fm0 8 0 R
+    >>
+  >>
+  /TilingType 3
+  /XStep 30
+  /YStep 30
+>>
+stream
+q 1 0 0 1 0 0 cm /GS0 gs /Fm0 Do Q
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /Pattern
+  /Subtype /Form
+  /BBox [0 0 30 30]
+  {{streamlen}}
+  /PaintType 1
+  /PatternType 1
+  /Resources <<
+    /ExtGState <<
+      /GS0 5 0 R
+    >>
+    /XObject <<
+      /Fm0 9 0 R
+    >>
+  >>
+  /TilingType 3
+  /XStep 30
+  /YStep 30
+>>
+stream
+q 20 0 0 20 0 0 cm /GS0 gs /Fm0 Do Q
+endstream
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /ColorSpace /DeviceGray
+  /Width 20 /BitsPerComponent 8
+  {{streamlen}}
+  /Height 20
+  /Filter [/ASCII85Decode /DCTDecode]
+  /DecodeParms <</ColorTransform 0>>
+>>
+stream
+s4IA0!"_al8O`[\!<<*#!!*'"s4[N@!!<9(!WiE*!WiE*!s8W."p>&3"9\u7"pG2;#RUnF#RLeE$kEaR
+$P!ON#n7IU%M'*^&J,9X&eblh'+YWc&HBNG$O?_H!#,G7&HMjL!#5J7!<<*"zzz$3.pD'G(9Fzzz!!!$
+!g&MBZ!<<*a!0oeL&-(;~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_2106_expected.pdf.0.png b/testing/resources/pixel/bug_2106_expected.pdf.0.png
new file mode 100644
index 0000000..9117219
--- /dev/null
+++ b/testing/resources/pixel/bug_2106_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_2106_expected_gdi_skia.pdf.0.png b/testing/resources/pixel/bug_2106_expected_gdi_skia.pdf.0.png
new file mode 100644
index 0000000..3d2e154
--- /dev/null
+++ b/testing/resources/pixel/bug_2106_expected_gdi_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_2106_expected_skia.pdf.0.png b/testing/resources/pixel/bug_2106_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3d2e154
--- /dev/null
+++ b/testing/resources/pixel/bug_2106_expected_skia.pdf.0.png
Binary files differ