Split a path into sub paths for drawing zero area.

Currently GetZeroAreaPath() is used to calculate the zero areas formed
by a whole path. However, its output parameter `thin` `set_identity` can
be set due to a sub path (a subset of the whole path that started with
"move to" action), while affecting the rendering results of all the zero
areas formed by the whole path.

This CL adds the process to split the whole path into sub paths, and
calls DrawZeroAreaPath() to process each sub path individually. Also add
a pixel test to make sure that `set_identity` is being set for each sub
path separately.

Bug: pdfium:1638
Change-Id: I5492870e5c7e9254de346cc7d7c9255661e3abd2
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/78030
Commit-Queue: Hui Yingst <nigi@chromium.org>
Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
diff --git a/DEPS b/DEPS
index f265f12..eebc457 100644
--- a/DEPS
+++ b/DEPS
@@ -89,7 +89,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling pdfium_tests
   # and whatever else without interference from each other.
-  'pdfium_tests_revision': 'db1faf0ee5ab4e8be140dc1c0fc0031ab70a817c',
+  'pdfium_tests_revision': '7adedee8c61163fb68005fdbd968f16532bb04d4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling skia
   # and whatever else without interference from each other.
diff --git a/core/fxge/cfx_renderdevice.cpp b/core/fxge/cfx_renderdevice.cpp
index 7130649..dcba015 100644
--- a/core/fxge/cfx_renderdevice.cpp
+++ b/core/fxge/cfx_renderdevice.cpp
@@ -30,6 +30,7 @@
 #include "core/fxge/text_char_pos.h"
 #include "core/fxge/text_glyph_pos.h"
 #include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 #include "third_party/base/notreached.h"
 #include "third_party/base/span.h"
 
@@ -332,6 +333,11 @@
     return false;
   }
 
+  // A special case that all points are identical, zero area is formed and no
+  // thin line needs to be drawn.
+  if (points[0].m_Point == points[1].m_Point)
+    return true;
+
   for (size_t i = 0; i < 2; i++) {
     CFX_PointF point = points[i].m_Point;
     if (adjust) {
@@ -425,22 +431,21 @@
   if (CheckPalindromicPath(points, new_path, thin))
     return true;
 
-  int start_point = 0;
   for (size_t i = 0; i < points.size(); i++) {
     FXPT_TYPE point_type = points[i].m_Type;
     if (point_type == FXPT_TYPE::MoveTo) {
-      start_point = i;
+      DCHECK_EQ(0, i);
       continue;
     }
 
     if (point_type == FXPT_TYPE::BezierTo) {
       i += 2;
+      DCHECK(i < points.size());
       continue;
     }
 
     DCHECK(point_type == FXPT_TYPE::LineTo);
-    int next_index =
-        (i + 1 - start_point) % (points.size() - start_point) + start_point;
+    int next_index = (i + 1) % (points.size());
     const FX_PATHPOINT& next = points[next_index];
     if (next.m_Type == FXPT_TYPE::BezierTo || next.m_Type == FXPT_TYPE::MoveTo)
       continue;
@@ -691,12 +696,39 @@
         return true;
     }
   }
+
   if (fill && stroke_alpha == 0 && !fill_options.stroke &&
       !fill_options.text_mode) {
-    // TODO(crbug.com/pdfium/1638): Make DrawZeroAreaPath() only process a sub
-    // path instead the whole path.
-    DrawZeroAreaPath(pPathData->GetPoints(), pObject2Device,
-                     !!m_pDeviceDriver->GetDriverType(),
+    bool adjust = !!m_pDeviceDriver->GetDriverType();
+    std::vector<FX_PATHPOINT> sub_path;
+    for (size_t i = 0; i < points.size(); i++) {
+      FXPT_TYPE point_type = points[i].m_Type;
+      if (point_type == FXPT_TYPE::MoveTo) {
+        // Process the exisitng sub path.
+        DrawZeroAreaPath(sub_path, pObject2Device, adjust,
+                         fill_options.aliased_path, fill_color, fill_alpha,
+                         blend_type);
+        sub_path.clear();
+
+        // Start forming the next sub path.
+        sub_path.push_back(points[i]);
+        continue;
+      }
+
+      if (point_type == FXPT_TYPE::BezierTo) {
+        sub_path.push_back(points[i]);
+        sub_path.push_back(points[i + 1]);
+        sub_path.push_back(points[i + 2]);
+        i += 2;
+        continue;
+      }
+
+      DCHECK(point_type == FXPT_TYPE::LineTo);
+      sub_path.push_back(points[i]);
+      continue;
+    }
+    // Process the last sub paths.
+    DrawZeroAreaPath(sub_path, pObject2Device, adjust,
                      fill_options.aliased_path, fill_color, fill_alpha,
                      blend_type);
   }
diff --git a/testing/resources/pixel/bug_1638.in b/testing/resources/pixel/bug_1638.in
new file mode 100644
index 0000000..f6dea38
--- /dev/null
+++ b/testing/resources/pixel/bug_1638.in
@@ -0,0 +1,57 @@
+{{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 400 400]
+  /Resources <<
+    /ExtGState <<
+      /GS1 <<
+        /ca 1.0
+      >>
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/GS1 gs
+0 0 1 rg
+% The 1st sub path.
+50 350 m
+200 320 l
+50 350 l
+% The 2nd sub path.
+100 300 m
+50 300 l
+200 300 l
+300 200 l
+270 230 l
+% The 3rd sub path.
+200 50 m
+350 50 l
+200 50 l
+200 100 l
+200 50 l
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1638_expected.pdf.0.png b/testing/resources/pixel/bug_1638_expected.pdf.0.png
new file mode 100644
index 0000000..23a39d5
--- /dev/null
+++ b/testing/resources/pixel/bug_1638_expected.pdf.0.png
Binary files differ