[AGG] Support negative dash phases

Support negative dash phases by following the ISO 32000-2:2020 section
8.4.3.6 specification.

Note that the resulting PDF for the pixel test dashed_lines.in is still
incorrect, since it contains dashed lines for odd sized dash arrays,
which still needs to be fixed.

Bug: 40491355, 370546365
Change-Id: I957f222f2ef09d49fc8b2728f3ba98eedbab77b8
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/127230
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Andy Phan <andyphan@chromium.org>
diff --git a/testing/resources/pixel/bug_660850_expected.pdf.0.png b/testing/resources/pixel/bug_660850_expected.pdf.0.png
index 98281e1..254f45b 100644
--- a/testing/resources/pixel/bug_660850_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_660850_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png b/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png
deleted file mode 100644
index 254f45b..0000000
--- a/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/dashed_lines_expected.pdf.0.png b/testing/resources/pixel/dashed_lines_expected.pdf.0.png
index 81a4356..6b5918b 100644
--- a/testing/resources/pixel/dashed_lines_expected.pdf.0.png
+++ b/testing/resources/pixel/dashed_lines_expected.pdf.0.png
Binary files differ
diff --git a/third_party/agg23/0016-support-negative-dash-phases.patch b/third_party/agg23/0016-support-negative-dash-phases.patch
new file mode 100644
index 0000000..de1bb71
--- /dev/null
+++ b/third_party/agg23/0016-support-negative-dash-phases.patch
@@ -0,0 +1,24 @@
+diff --git a/third_party/agg23/agg_vcgen_dash.cpp b/third_party/agg23/agg_vcgen_dash.cpp
+index a7dc2dfda..47683f9b2 100644
+--- a/third_party/agg23/agg_vcgen_dash.cpp
++++ b/third_party/agg23/agg_vcgen_dash.cpp
+@@ -58,8 +58,17 @@ void vcgen_dash::add_dash(float dash_len, float gap_len)
+ }
+ void vcgen_dash::dash_start(float ds)
+ {
+-    m_dash_start = ds;
+-    calc_dash_start(fabs(ds));
++  CHECK_GT(m_total_dash_len, 0);
++  // According to ISO 32000-2:2020 section 8.4.3.6:
++  // If the dash phase is negative, it shall be incremented by twice the sum of
++  // all lengths in the dash array until it is positive.
++  if (ds < 0.0f) {
++    float dash_len_sum = m_total_dash_len * 2;
++    ds += ceil(-ds / dash_len_sum) * dash_len_sum;
++  }
++  CHECK_GE(ds, 0);
++  m_dash_start = ds;
++  calc_dash_start(ds);
+ }
+ void vcgen_dash::calc_dash_start(float ds)
+ {
diff --git a/third_party/agg23/README.pdfium b/third_party/agg23/README.pdfium
index eaf4610..d32671a 100644
--- a/third_party/agg23/README.pdfium
+++ b/third_party/agg23/README.pdfium
@@ -35,3 +35,5 @@
 0014-ubsan-render-line.patch: Fix some integer overflows in
 outline_aa::render_line().
 0015-include-string-h.patch: IWYU for <string.h>
+0016-support-negative-dash-phases.patch: Support negative dash phases in
+calc_dash_start().
\ No newline at end of file
diff --git a/third_party/agg23/agg_vcgen_dash.cpp b/third_party/agg23/agg_vcgen_dash.cpp
index a7dc2df..47683f9 100644
--- a/third_party/agg23/agg_vcgen_dash.cpp
+++ b/third_party/agg23/agg_vcgen_dash.cpp
@@ -58,8 +58,17 @@
 }
 void vcgen_dash::dash_start(float ds)
 {
-    m_dash_start = ds;
-    calc_dash_start(fabs(ds));
+  CHECK_GT(m_total_dash_len, 0);
+  // According to ISO 32000-2:2020 section 8.4.3.6:
+  // If the dash phase is negative, it shall be incremented by twice the sum of
+  // all lengths in the dash array until it is positive.
+  if (ds < 0.0f) {
+    float dash_len_sum = m_total_dash_len * 2;
+    ds += ceil(-ds / dash_len_sum) * dash_len_sum;
+  }
+  CHECK_GE(ds, 0);
+  m_dash_start = ds;
+  calc_dash_start(ds);
 }
 void vcgen_dash::calc_dash_start(float ds)
 {