enable skia gradients, fix bugs

Allow shading to use Skia for some gradients;
linear, radial, and coons patch.

The corpus uncovered a few gradient-related bugs
in the Skia driver which are fixed as well.

Fix corpus 11.pdf by ignoring zero area
filled paths. PDFium uses CFX_PathData::GetZeroAreaPath()
to determine if a path that doesn't enclosing anything
should be drawn anyway, and CFX_AggDeviceDriver::GetDriverType()
returns 1 so that the empty path is tweaked so that
it doesn't draw too often.

There are no tests in the corpus that show
the utility of this approach; 11.pdf draws correctly
if the zero area paths are ignored.

Standardize debugging output so that
a separate tool can visualize it.

R=dsinclair@chromium.org, thestig@chromium.org

Review-Url: https://codereview.chromium.org/2530343004
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index cf4f428..84db838 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -6,14 +6,14 @@
 #include <utility>
 #include <vector>
 
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcrt/fx_memory.h"
-
+#include "core/fpdfapi/page/cpdf_meshstream.h"
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
 #include "core/fpdfapi/page/pageint.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fxcodec/fx_codec.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxge/cfx_fxgedevice.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_graphstatedata.h"
@@ -161,7 +161,7 @@
   sk_bzero(buffer, sizeof(buffer));
   SkMemoryWStream stream(buffer, sizeof(buffer));
   path.dump(&stream, false, false);
-  printf("%s\n", buffer);
+  printf("%s", buffer);
 #endif  // SHOW_SKIA_PATH
 }
 
@@ -170,13 +170,13 @@
   SkMatrix matrix = canvas->getTotalMatrix();
   SkScalar m[9];
   matrix.get9(m);
-  printf("(%g,%g,%g) (%g,%g,%g) (%g,%g,%g)\n", m[0], m[1], m[2], m[3], m[4],
-         m[5], m[6], m[7], m[8]);
+  printf("matrix (%g,%g,%g) (%g,%g,%g) (%g,%g,%g)\n", m[0], m[1], m[2], m[3],
+         m[4], m[5], m[6], m[7], m[8]);
 #endif  // SHOW_SKIA_PATH
 }
 
-void DebugShowCanvasClip(const SkCanvas* canvas) {
 #if SHOW_SKIA_PATH
+void DebugShowCanvasClip(const SkCanvas* canvas) {
   SkRect local;
   SkIRect device;
   canvas->getClipBounds(&local);
@@ -187,6 +187,39 @@
          device.fRight, device.fBottom);
   const SkClipStack* clipStack = canvas->getClipStack();
   clipStack->dump();
+}
+
+void DebugShowSkiaPaint(const SkPaint& paint) {
+  if (SkPaint::kFill_Style == paint.getStyle()) {
+    printf("fill 0x%08x\n", paint.getColor());
+  } else {
+    printf("stroke 0x%08x width %g\n", paint.getColor(),
+           paint.getStrokeWidth());
+  }
+}
+#endif  // SHOW_SKIA_PATH
+
+void DebugShowSkiaDrawPath(const SkCanvas* canvas,
+                           const SkPaint& paint,
+                           const SkPath& path) {
+#if SHOW_SKIA_PATH
+  DebugShowSkiaPaint(paint);
+  DebugShowCanvasMatrix(canvas);
+  DebugShowCanvasClip(canvas);
+  DebugShowSkiaPath(path);
+  printf("\n");
+#endif  // SHOW_SKIA_PATH
+}
+
+void DebugShowSkiaDrawRect(const SkCanvas* canvas,
+                           const SkPaint& paint,
+                           const SkRect& rect) {
+#if SHOW_SKIA_PATH
+  DebugShowSkiaPaint(paint);
+  DebugShowCanvasMatrix(canvas);
+  DebugShowCanvasClip(canvas);
+  printf("rect %g %g %g %g\n", rect.fLeft, rect.fTop, rect.fRight,
+         rect.fBottom);
 #endif  // SHOW_SKIA_PATH
 }
 
@@ -384,18 +417,18 @@
 bool AddStitching(const CPDF_StitchFunc* pFunc,
                   SkTDArray<SkColor>* skColors,
                   SkTDArray<SkScalar>* skPos) {
-  int inputs = pFunc->CountInputs();
   FX_FLOAT boundsStart = pFunc->GetDomain(0);
 
   const auto& subFunctions = pFunc->GetSubFunctions();
-  for (int i = 0; i < inputs; ++i) {
+  int subFunctionCount = subFunctions.size();
+  for (int i = 0; i < subFunctionCount; ++i) {
     const CPDF_ExpIntFunc* pSubFunc = subFunctions[i]->ToExpIntFunc();
     if (!pSubFunc)
       return false;
     if (!AddColors(pSubFunc, skColors))
       return false;
     FX_FLOAT boundsEnd =
-        i < inputs - 1 ? pFunc->GetBound(i) : pFunc->GetDomain(1);
+        i < subFunctionCount - 1 ? pFunc->GetBound(i + 1) : pFunc->GetDomain(1);
     skPos->push(boundsStart);
     skPos->push(boundsEnd);
     boundsStart = boundsEnd;
@@ -1215,7 +1248,7 @@
       return 0;
     case FXDC_RENDER_CAPS: {
       int flags = FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE |
-                  FXRC_BLEND_MODE | FXRC_SOFT_CLIP;
+                  FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_SHADING;
       if (m_pBitmap->HasAlpha()) {
         flags |= FXRC_ALPHA_OUTPUT;
       } else if (m_pBitmap->IsAlphaMask()) {
@@ -1406,6 +1439,8 @@
     uint32_t stroke_color,                  // stroke color
     int fill_mode,  // fill mode, WINDING or ALTERNATE. 0 for not filled
     int blend_type) {
+  if (fill_mode & FX_ZEROAREA_FILL)
+    return true;
 #ifdef _SKIA_SUPPORT_
   if (m_pCache->DrawPath(pPathData, pObject2Device, pGraphState, fill_color,
                          stroke_color, fill_mode, blend_type, this)) {
@@ -1447,23 +1482,19 @@
     }
     skPaint.setStyle(SkPaint::kFill_Style);
     skPaint.setColor(fill_color);
-    DebugShowSkiaPath(*fillPath);
-    DebugShowCanvasMatrix(m_pCanvas);
-    DebugShowCanvasClip(m_pCanvas);
 #ifdef _SKIA_SUPPORT_PATHS_
     m_pBitmap->PreMultiply();
 #endif  // _SKIA_SUPPORT_PATHS_
+    DebugShowSkiaDrawPath(m_pCanvas, skPaint, *fillPath);
     m_pCanvas->drawPath(*fillPath, skPaint);
   }
   if (pGraphState && stroke_alpha) {
-    DebugShowSkiaPath(skPath);
-    DebugShowCanvasMatrix(m_pCanvas);
-    DebugShowCanvasClip(m_pCanvas);
     skPaint.setStyle(SkPaint::kStroke_Style);
     skPaint.setColor(stroke_color);
 #ifdef _SKIA_SUPPORT_PATHS_
     m_pBitmap->PreMultiply();
 #endif  // _SKIA_SUPPORT_PATHS_
+    DebugShowSkiaDrawPath(m_pCanvas, skPaint, skPath);
     m_pCanvas->drawPath(skPath, skPaint);
   }
   m_pCanvas->restore();
@@ -1486,15 +1517,10 @@
   spaint.setAntiAlias(true);
   spaint.setColor(fill_color);
   spaint.setBlendMode(GetSkiaBlendMode(blend_type));
-  DebugShowCanvasClip(m_pCanvas);
-  DebugShowCanvasMatrix(m_pCanvas);
   SkRect rect =
       SkRect::MakeLTRB(pRect->left, SkTMin(pRect->top, pRect->bottom),
                        pRect->right, SkTMax(pRect->bottom, pRect->top));
-#if SHOW_SKIA_PATH
-  printf("fill rect = %g %g %g %g\n\n", rect.fLeft, rect.fTop, rect.fRight,
-         rect.fBottom);
-#endif  // SHOW_SKIA_PATH
+  DebugShowSkiaDrawRect(m_pCanvas, spaint, rect);
   m_pCanvas->drawRect(rect, spaint);
   return true;
 }
@@ -1504,20 +1530,24 @@
                                        const FX_RECT& clip_rect,
                                        int alpha,
                                        bool bAlphaMode) {
-  if (kAxialShading != pPattern->GetShadingType() &&
-      kRadialShading != pPattern->GetShadingType()) {
+  ShadingType shadingType = pPattern->GetShadingType();
+  if (kAxialShading != shadingType && kRadialShading != shadingType &&
+      kCoonsPatchMeshShading != shadingType) {
     // TODO(caryclark) more types
     return false;
   }
+  int csFamily = pPattern->GetCS()->GetFamily();
+  if (PDFCS_DEVICERGB != csFamily && PDFCS_DEVICEGRAY != csFamily)
+    return false;
   const std::vector<std::unique_ptr<CPDF_Function>>& pFuncs =
       pPattern->GetFuncs();
   int nFuncs = pFuncs.size();
-  if (nFuncs != 1)  // TODO(caryclark) remove this restriction
+  if (nFuncs > 1)  // TODO(caryclark) remove this restriction
     return false;
   CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict();
   CPDF_Array* pCoords = pDict->GetArrayFor("Coords");
-  if (!pCoords)
-    return true;
+  if (!pCoords && kCoonsPatchMeshShading != shadingType)
+    return false;
   // TODO(caryclark) Respect Domain[0], Domain[1]. (Don't know what they do
   // yet.)
   SkTDArray<SkColor> skColors;
@@ -1556,7 +1586,7 @@
                                    clip_rect.right, clip_rect.bottom);
   SkPath skClip;
   SkPath skPath;
-  if (kAxialShading == pPattern->GetShadingType()) {
+  if (kAxialShading == shadingType) {
     FX_FLOAT start_x = pCoords->GetNumberAt(0);
     FX_FLOAT start_y = pCoords->GetNumberAt(1);
     FX_FLOAT end_x = pCoords->GetNumberAt(2);
@@ -1596,8 +1626,7 @@
     }
     skPath.addRect(skRect);
     skMatrix.setIdentity();
-  } else {
-    ASSERT(kRadialShading == pPattern->GetShadingType());
+  } else if (kRadialShading == shadingType) {
     FX_FLOAT start_x = pCoords->GetNumberAt(0);
     FX_FLOAT start_y = pCoords->GetNumberAt(1);
     FX_FLOAT start_r = pCoords->GetNumberAt(2);
@@ -1623,6 +1652,47 @@
       return false;
     skPath.addRect(skRect);
     skPath.transform(inverse);
+  } else {
+    ASSERT(kCoonsPatchMeshShading == shadingType);
+    CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject());
+    if (!pStream)
+      return false;
+    CPDF_MeshStream stream(shadingType, pPattern->GetFuncs(), pStream,
+                           pPattern->GetCS());
+    if (!stream.Load())
+      return false;
+    SkPoint cubics[12];
+    SkColor colors[4];
+    m_pCanvas->save();
+    if (!skClip.isEmpty())
+      m_pCanvas->clipPath(skClip, SkCanvas::kIntersect_Op, true);
+    m_pCanvas->concat(skMatrix);
+    while (!stream.BitStream()->IsEOF()) {
+      uint32_t flag = stream.GetFlag();
+      int iStartPoint = flag ? 4 : 0;
+      int iStartColor = flag ? 2 : 0;
+      if (flag) {
+        SkPoint tempCubics[4];
+        for (int i = 0; i < (int)SK_ARRAY_COUNT(tempCubics); i++)
+          tempCubics[i] = cubics[(flag * 3 + i) % 12];
+        FXSYS_memcpy(cubics, tempCubics, sizeof(tempCubics));
+        SkColor tempColors[2];
+        tempColors[0] = colors[flag];
+        tempColors[1] = colors[(flag + 1) % 4];
+        FXSYS_memcpy(colors, tempColors, sizeof(tempColors));
+      }
+      for (int i = iStartPoint; i < (int)SK_ARRAY_COUNT(cubics); i++)
+        stream.GetCoords(cubics[i].fX, cubics[i].fY);
+      for (int i = iStartColor; i < (int)SK_ARRAY_COUNT(colors); i++) {
+        FX_FLOAT r = 0.0f, g = 0.0f, b = 0.0f;
+        stream.GetColor(r, g, b);
+        colors[i] = SkColorSetARGBInline(0xFF, (U8CPU)(r * 255),
+                                         (U8CPU)(g * 255), (U8CPU)(b * 255));
+      }
+      m_pCanvas->drawPatch(cubics, colors, nullptr, paint);
+    }
+    m_pCanvas->restore();
+    return true;
   }
   m_pCanvas->save();
   if (!skClip.isEmpty())