Move GetZeroAreaPath() to core/fxge/cfx_renderdevice.cpp.

In CFX_RenderDevice::DrawPathWithBlend(), to process each sub path from
`pPathData`, we will need to form every sub path in that same function
and use GetZeroAreaPath() to calculate its zero areas before drawing.
This requires GetZeroAreaPath() taking any vector of FX_PATHPOINT as a
path and processing it, instead of processing all the points of
`pPathData`. Therefore GetZeroAreaPath() no longer needs to be a member
of CFX_PathDataon.

This CL makes GetZeroAreaPath() take a pdfium::span<const FX_PATHPOINT>
as an input parameter, and moves it and its helper functions to the
anonymous namespace in core/fxge/cfx_renderdevice.cpp. Also changes the
helpers take pdfium::span<> instead of std::vector as inputs.

Bug: pdfium:1638
Change-Id: Ia07cbb261a09bd002ed56b976d450ed2951c9cc9
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/77970
Commit-Queue: Hui Yingst <nigi@chromium.org>
Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxge/cfx_pathdata.cpp b/core/fxge/cfx_pathdata.cpp
index 682fc56..29cce9b 100644
--- a/core/fxge/cfx_pathdata.cpp
+++ b/core/fxge/cfx_pathdata.cpp
@@ -12,96 +12,6 @@
 
 namespace {
 
-// Returns true if the simple path contains 3 points which draw a line from
-// A->B->A and form a zero area.
-bool CheckSimpleLinePath(const std::vector<FX_PATHPOINT>& points,
-                         const CFX_Matrix* matrix,
-                         bool adjust,
-                         CFX_PathData* new_path,
-                         bool* thin,
-                         bool* set_identity) {
-  if (points.size() != 3)
-    return false;
-
-  if (points[0].m_Type != FXPT_TYPE::MoveTo ||
-      points[1].m_Type != FXPT_TYPE::LineTo ||
-      points[2].m_Type != FXPT_TYPE::LineTo ||
-      points[0].m_Point != points[2].m_Point) {
-    return false;
-  }
-
-  for (size_t i = 0; i < 2; i++) {
-    CFX_PointF point = points[i].m_Point;
-    if (adjust) {
-      if (matrix)
-        point = matrix->Transform(point);
-
-      point = CFX_PointF(static_cast<int>(point.x) + 0.5f,
-                         static_cast<int>(point.y) + 0.5f);
-    }
-    new_path->AppendPoint(point,
-                          i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::LineTo);
-  }
-  if (adjust && matrix)
-    *set_identity = true;
-
-  // Note, both x and y coordinates of the end points need to be different.
-  if (points[0].m_Point.x != points[1].m_Point.x &&
-      points[0].m_Point.y != points[1].m_Point.y) {
-    *thin = true;
-  }
-  return true;
-}
-
-// Returns true if `points` is palindromic and forms zero area. Otherwise,
-// returns false.
-bool CheckPalindromicPath(const std::vector<FX_PATHPOINT>& points,
-                          CFX_PathData* new_path,
-                          bool* thin) {
-  if (points.size() <= 3 || !(points.size() % 2))
-    return false;
-
-  const int mid = points.size() / 2;
-  bool zero_area = true;
-  CFX_PathData temp_path;
-  for (int i = 0; i < mid; i++) {
-    if (!(points[mid - i - 1].m_Point == points[mid + i + 1].m_Point &&
-          points[mid - i - 1].m_Type != FXPT_TYPE::BezierTo &&
-          points[mid + i + 1].m_Type != FXPT_TYPE::BezierTo)) {
-      zero_area = false;
-      break;
-    }
-
-    temp_path.AppendPoint(points[mid - i].m_Point, FXPT_TYPE::MoveTo);
-    temp_path.AppendPoint(points[mid - i - 1].m_Point, FXPT_TYPE::LineTo);
-  }
-  if (!zero_area)
-    return false;
-
-  new_path->Append(&temp_path, nullptr);
-  *thin = true;
-  return true;
-}
-
-bool IsFoldingVerticalLine(const CFX_PointF& a,
-                           const CFX_PointF& b,
-                           const CFX_PointF& c) {
-  return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0;
-}
-
-bool IsFoldingHorizontalLine(const CFX_PointF& a,
-                             const CFX_PointF& b,
-                             const CFX_PointF& c) {
-  return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0;
-}
-
-bool IsFoldingDiagonalLine(const CFX_PointF& a,
-                           const CFX_PointF& b,
-                           const CFX_PointF& c) {
-  return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y &&
-         (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x);
-}
-
 void UpdateLineEndPoints(CFX_FloatRect* rect,
                          const CFX_PointF& start_pos,
                          const CFX_PointF& end_pos,
@@ -392,76 +302,6 @@
     point.m_Point = matrix.Transform(point.m_Point);
 }
 
-bool CFX_PathData::GetZeroAreaPath(const CFX_Matrix* matrix,
-                                   bool adjust,
-                                   CFX_PathData* new_path,
-                                   bool* thin,
-                                   bool* set_identity) const {
-  *set_identity = false;
-
-  // TODO(crbug.com/pdfium/1639): Need to handle the case when there are
-  // only 2 points in the path that forms a zero area.
-  if (m_Points.size() < 3)
-    return false;
-
-  if (CheckSimpleLinePath(m_Points, matrix, adjust, new_path, thin,
-                          set_identity)) {
-    return true;
-  }
-
-  if (CheckPalindromicPath(m_Points, new_path, thin))
-    return true;
-
-  int start_point = 0;
-  for (size_t i = 0; i < m_Points.size(); i++) {
-    FXPT_TYPE point_type = m_Points[i].m_Type;
-    if (point_type == FXPT_TYPE::MoveTo) {
-      start_point = i;
-      continue;
-    }
-
-    if (point_type == FXPT_TYPE::BezierTo) {
-      i += 2;
-      continue;
-    }
-
-    DCHECK(point_type == FXPT_TYPE::LineTo);
-    int next_index =
-        (i + 1 - start_point) % (m_Points.size() - start_point) + start_point;
-    const FX_PATHPOINT& next = m_Points[next_index];
-    if (next.m_Type == FXPT_TYPE::BezierTo || next.m_Type == FXPT_TYPE::MoveTo)
-      continue;
-
-    const FX_PATHPOINT& prev = m_Points[i - 1];
-    const FX_PATHPOINT& cur = m_Points[i];
-    if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
-      bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) <
-                      fabs(cur.m_Point.y - next.m_Point.y);
-      const FX_PATHPOINT& start = use_prev ? prev : cur;
-      const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next;
-      new_path->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo);
-      new_path->AppendPoint(end.m_Point, FXPT_TYPE::LineTo);
-      continue;
-    }
-
-    if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) ||
-        IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
-      bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) <
-                      fabs(cur.m_Point.x - next.m_Point.x);
-      const FX_PATHPOINT& start = use_prev ? prev : cur;
-      const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next;
-      new_path->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo);
-      new_path->AppendPoint(end.m_Point, FXPT_TYPE::LineTo);
-      continue;
-    }
-  }
-
-  size_t new_path_size = new_path->GetPoints().size();
-  if (m_Points.size() > 3 && new_path_size > 0)
-    *thin = true;
-  return new_path_size != 0;
-}
-
 bool CFX_PathData::IsRect() const {
   if (m_Points.size() != 5 && m_Points.size() != 4)
     return false;
diff --git a/core/fxge/cfx_pathdata.h b/core/fxge/cfx_pathdata.h
index 9af026a..860807e7 100644
--- a/core/fxge/cfx_pathdata.h
+++ b/core/fxge/cfx_pathdata.h
@@ -55,11 +55,6 @@
 
   void Transform(const CFX_Matrix& matrix);
   bool IsRect() const;
-  bool GetZeroAreaPath(const CFX_Matrix* matrix,
-                       bool adjust,
-                       CFX_PathData* new_path,
-                       bool* thin,
-                       bool* set_identity) const;
   Optional<CFX_FloatRect> GetRect(const CFX_Matrix* matrix) const;
 
   void Append(const CFX_PathData* src, const CFX_Matrix* matrix);
diff --git a/core/fxge/cfx_renderdevice.cpp b/core/fxge/cfx_renderdevice.cpp
index 5738027..ae9c723 100644
--- a/core/fxge/cfx_renderdevice.cpp
+++ b/core/fxge/cfx_renderdevice.cpp
@@ -314,6 +314,167 @@
   return true;
 }
 
+// Returns true if the simple path contains 3 points which draw a line from
+// A->B->A and form a zero area.
+bool CheckSimpleLinePath(pdfium::span<const FX_PATHPOINT> points,
+                         const CFX_Matrix* matrix,
+                         bool adjust,
+                         CFX_PathData* new_path,
+                         bool* thin,
+                         bool* set_identity) {
+  if (points.size() != 3)
+    return false;
+
+  if (points[0].m_Type != FXPT_TYPE::MoveTo ||
+      points[1].m_Type != FXPT_TYPE::LineTo ||
+      points[2].m_Type != FXPT_TYPE::LineTo ||
+      points[0].m_Point != points[2].m_Point) {
+    return false;
+  }
+
+  for (size_t i = 0; i < 2; i++) {
+    CFX_PointF point = points[i].m_Point;
+    if (adjust) {
+      if (matrix)
+        point = matrix->Transform(point);
+
+      point = CFX_PointF(static_cast<int>(point.x) + 0.5f,
+                         static_cast<int>(point.y) + 0.5f);
+    }
+    new_path->AppendPoint(point,
+                          i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::LineTo);
+  }
+  if (adjust && matrix)
+    *set_identity = true;
+
+  // Note, both x and y coordinates of the end points need to be different.
+  if (points[0].m_Point.x != points[1].m_Point.x &&
+      points[0].m_Point.y != points[1].m_Point.y) {
+    *thin = true;
+  }
+  return true;
+}
+
+// Returns true if `points` is palindromic and forms zero area. Otherwise,
+// returns false.
+bool CheckPalindromicPath(pdfium::span<const FX_PATHPOINT> points,
+                          CFX_PathData* new_path,
+                          bool* thin) {
+  if (points.size() <= 3 || !(points.size() % 2))
+    return false;
+
+  const int mid = points.size() / 2;
+  bool zero_area = true;
+  CFX_PathData temp_path;
+  for (int i = 0; i < mid; i++) {
+    if (!(points[mid - i - 1].m_Point == points[mid + i + 1].m_Point &&
+          points[mid - i - 1].m_Type != FXPT_TYPE::BezierTo &&
+          points[mid + i + 1].m_Type != FXPT_TYPE::BezierTo)) {
+      zero_area = false;
+      break;
+    }
+
+    temp_path.AppendPoint(points[mid - i].m_Point, FXPT_TYPE::MoveTo);
+    temp_path.AppendPoint(points[mid - i - 1].m_Point, FXPT_TYPE::LineTo);
+  }
+  if (!zero_area)
+    return false;
+
+  new_path->Append(&temp_path, nullptr);
+  *thin = true;
+  return true;
+}
+
+bool IsFoldingVerticalLine(const CFX_PointF& a,
+                           const CFX_PointF& b,
+                           const CFX_PointF& c) {
+  return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0;
+}
+
+bool IsFoldingHorizontalLine(const CFX_PointF& a,
+                             const CFX_PointF& b,
+                             const CFX_PointF& c) {
+  return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0;
+}
+
+bool IsFoldingDiagonalLine(const CFX_PointF& a,
+                           const CFX_PointF& b,
+                           const CFX_PointF& c) {
+  return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y &&
+         (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x);
+}
+
+bool GetZeroAreaPath(pdfium::span<const FX_PATHPOINT> points,
+                     const CFX_Matrix* matrix,
+                     bool adjust,
+                     CFX_PathData* new_path,
+                     bool* thin,
+                     bool* set_identity) {
+  *set_identity = false;
+
+  // TODO(crbug.com/pdfium/1639): Need to handle the case when there are
+  // only 2 points in the path that forms a zero area.
+  if (points.size() < 3)
+    return false;
+
+  if (CheckSimpleLinePath(points, matrix, adjust, new_path, thin,
+                          set_identity)) {
+    return true;
+  }
+
+  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;
+      continue;
+    }
+
+    if (point_type == FXPT_TYPE::BezierTo) {
+      i += 2;
+      continue;
+    }
+
+    DCHECK(point_type == FXPT_TYPE::LineTo);
+    int next_index =
+        (i + 1 - start_point) % (points.size() - start_point) + start_point;
+    const FX_PATHPOINT& next = points[next_index];
+    if (next.m_Type == FXPT_TYPE::BezierTo || next.m_Type == FXPT_TYPE::MoveTo)
+      continue;
+
+    const FX_PATHPOINT& prev = points[i - 1];
+    const FX_PATHPOINT& cur = points[i];
+    if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
+      bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) <
+                      fabs(cur.m_Point.y - next.m_Point.y);
+      const FX_PATHPOINT& start = use_prev ? prev : cur;
+      const FX_PATHPOINT& end = use_prev ? points[next_index - 1] : next;
+      new_path->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo);
+      new_path->AppendPoint(end.m_Point, FXPT_TYPE::LineTo);
+      continue;
+    }
+
+    if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) ||
+        IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
+      bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) <
+                      fabs(cur.m_Point.x - next.m_Point.x);
+      const FX_PATHPOINT& start = use_prev ? prev : cur;
+      const FX_PATHPOINT& end = use_prev ? points[next_index - 1] : next;
+      new_path->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo);
+      new_path->AppendPoint(end.m_Point, FXPT_TYPE::LineTo);
+      continue;
+    }
+  }
+
+  size_t new_path_size = new_path->GetPoints().size();
+  if (points.size() > 3 && new_path_size > 0)
+    *thin = true;
+  return new_path_size != 0;
+}
+
 }  // namespace
 
 CFX_RenderDevice::CFX_RenderDevice() = default;
@@ -535,9 +696,11 @@
     CFX_PathData newPath;
     bool bThin = false;
     bool setIdentity = false;
-    if (pPathData->GetZeroAreaPath(pObject2Device,
-                                   !!m_pDeviceDriver->GetDriverType(), &newPath,
-                                   &bThin, &setIdentity)) {
+    // TODO(crbug.com/pdfium/1638): GetZeroAreaPath() should only process a sub
+    // path instead the whole path.
+    if (GetZeroAreaPath(pPathData->GetPoints(), pObject2Device,
+                        !!m_pDeviceDriver->GetDriverType(), &newPath, &bThin,
+                        &setIdentity)) {
       CFX_GraphStateData graphState;
       graphState.m_LineWidth = 0.0f;