Add option to skip rects to drawImageLattice()

HWUI skips transparent rects when drawing.

When skia draws using bilerp, we will blend
transparent rects with neighboring rects and might
draw a bit of a smudge.

This CL adds the option to skip rects, allowing us
to have compatible behavior with the framework.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2305433002

Review-Url: https://codereview.chromium.org/2305433002
diff --git a/src/core/SkLatticeIter.cpp b/src/core/SkLatticeIter.cpp
index ba6ac97..38ef282 100644
--- a/src/core/SkLatticeIter.cpp
+++ b/src/core/SkLatticeIter.cpp
@@ -12,13 +12,9 @@
  *  Divs must be in increasing order with no duplicates.
  */
 static bool valid_divs(const int* divs, int count, int len) {
-    if (count <= 0) {
-        return false;
-    }
-
     int prev = -1;
     for (int i = 0; i < count; i++) {
-        if (prev >= divs[i] || divs[i] > len) {
+        if (prev >= divs[i] || divs[i] >= len) {
             return false;
         }
     }
@@ -27,6 +23,12 @@
 }
 
 bool SkLatticeIter::Valid(int width, int height, const SkCanvas::Lattice& lattice) {
+    bool zeroXDivs = lattice.fXCount <= 0 || (1 == lattice.fXCount && 0 == lattice.fXDivs[0]);
+    bool zeroYDivs = lattice.fYCount <= 0 || (1 == lattice.fYCount && 0 == lattice.fYDivs[0]);
+    if (zeroXDivs && zeroYDivs) {
+        return false;
+    }
+
     return valid_divs(lattice.fXDivs, lattice.fXCount, width) &&
            valid_divs(lattice.fYDivs, lattice.fYCount, height);
 }
@@ -104,9 +106,9 @@
                              const SkRect& dst)
 {
     const int* xDivs = lattice.fXDivs;
-    int xCount = lattice.fXCount;
+    const int origXCount = lattice.fXCount;
     const int* yDivs = lattice.fYDivs;
-    int yCount = lattice.fYCount;
+    const int origYCount = lattice.fYCount;
 
     // In the x-dimension, the first rectangle always starts at x = 0 and is "scalable".
     // If xDiv[0] is 0, it indicates that the first rectangle is degenerate, so the
@@ -117,15 +119,16 @@
     // As we move left to right across the image, alternating patches will be "fixed" or
     // "scalable" in the x-direction.  Similarly, as move top to bottom, alternating
     // patches will be "fixed" or "scalable" in the y-direction.
-    SkASSERT(xCount > 0 && yCount > 0);
-    bool xIsScalable = (0 == xDivs[0]);
+    int xCount = origXCount;
+    int yCount = origYCount;
+    bool xIsScalable = (xCount > 0 && 0 == xDivs[0]);
     if (xIsScalable) {
         // Once we've decided that the first patch is "scalable", we don't need the
         // xDiv.  It is always implied that we start at zero.
         xDivs++;
         xCount--;
     }
-    bool yIsScalable = (0 == yDivs[0]);
+    bool yIsScalable = (yCount > 0 && 0 == yDivs[0]);
     if (yIsScalable) {
         // Once we've decided that the first patch is "scalable", we don't need the
         // yDiv.  It is always implied that we start at zero.
@@ -133,14 +136,6 @@
         yCount--;
     }
 
-    // We never need the final xDiv/yDiv if it is equal to the width/height.  This is implied.
-    if (xCount > 0 && srcWidth == xDivs[xCount - 1]) {
-        xCount--;
-    }
-    if (yCount > 0 && srcHeight == yDivs[yCount - 1]) {
-        yCount--;
-    }
-
     // Count "scalable" and "fixed" pixels in each dimension.
     int xCountScalable = count_scalable_pixels(xDivs, xCount, xIsScalable, srcWidth);
     int xCountFixed = srcWidth - xCountScalable;
@@ -158,8 +153,42 @@
                dst.fTop, dst.fBottom, yIsScalable);
 
     fCurrX = fCurrY = 0;
-    fDone = false;
-    fNumRects = (xCount + 1) * (yCount + 1);
+    fNumRectsInLattice = (xCount + 1) * (yCount + 1);
+    fNumRectsToDraw = fNumRectsInLattice;
+
+    if (lattice.fFlags) {
+        fFlags.push_back_n(fNumRectsInLattice);
+
+        const SkCanvas::Lattice::Flags* flags = lattice.fFlags;
+
+        bool hasPadRow = (yCount != origYCount);
+        bool hasPadCol = (xCount != origXCount);
+        if (hasPadRow) {
+            // The first row of rects are all empty, skip the first row of flags.
+            flags += origXCount + 1;
+        }
+
+        int i = 0;
+        for (int y = 0; y < yCount + 1; y++) {
+            for (int x = 0; x < origXCount + 1; x++) {
+                if (0 == x && hasPadCol) {
+                    // The first column of rects are all empty.  Skip a rect.
+                    flags++;
+                    continue;
+                }
+
+                fFlags[i] = *flags;
+                flags++;
+                i++;
+            }
+        }
+
+        for (int j = 0; j < fFlags.count(); j++) {
+            if (SkCanvas::Lattice::kTransparent_Flags == fFlags[j]) {
+                fNumRectsToDraw--;
+            }
+        }
+    }
 }
 
 bool SkLatticeIter::Valid(int width, int height, const SkIRect& center) {
@@ -205,12 +234,13 @@
     }
 
     fCurrX = fCurrY = 0;
-    fDone = false;
-    fNumRects = 9;
+    fNumRectsInLattice = 9;
+    fNumRectsToDraw = 9;
 }
 
 bool SkLatticeIter::next(SkRect* src, SkRect* dst) {
-    if (fDone) {
+    int currRect = fCurrX + fCurrY * (fSrcX.count() - 1);
+    if (currRect == fNumRectsInLattice) {
         return false;
     }
 
@@ -219,15 +249,17 @@
     SkASSERT(x >= 0 && x < fSrcX.count() - 1);
     SkASSERT(y >= 0 && y < fSrcY.count() - 1);
 
-    src->set(fSrcX[x], fSrcY[y], fSrcX[x + 1], fSrcY[y + 1]);
-    dst->set(fDstX[x], fDstY[y], fDstX[x + 1], fDstY[y + 1]);
     if (fSrcX.count() - 1 == ++fCurrX) {
         fCurrX = 0;
         fCurrY += 1;
-        if (fCurrY >= fSrcY.count() - 1) {
-            fDone = true;
-        }
     }
+
+    if (fFlags.count() > 0 && SkToBool(SkCanvas::Lattice::kTransparent_Flags & fFlags[currRect])) {
+        return this->next(src, dst);
+    }
+
+    src->set(fSrcX[x], fSrcY[y], fSrcX[x + 1], fSrcY[y + 1]);
+    dst->set(fDstX[x], fDstY[y], fDstX[x + 1], fDstY[y + 1]);
     return true;
 }
 
diff --git a/src/core/SkLatticeIter.h b/src/core/SkLatticeIter.h
index 192b6c5..deb2fe9 100644
--- a/src/core/SkLatticeIter.h
+++ b/src/core/SkLatticeIter.h
@@ -41,10 +41,10 @@
     void mapDstScaleTranslate(const SkMatrix& matrix);
 
     /**
-     *  Returns the total number of rects that will be drawn.
+     *  Returns the number of rects that will actually be drawn.
      */
-    int numRects() const {
-        return fNumRects;
+    int numRectsToDraw() const {
+        return fNumRectsToDraw;
     }
 
 private:
@@ -52,11 +52,12 @@
     SkTArray<SkScalar> fSrcY;
     SkTArray<SkScalar> fDstX;
     SkTArray<SkScalar> fDstY;
+    SkTArray<SkCanvas::Lattice::Flags> fFlags;
 
     int  fCurrX;
     int  fCurrY;
-    bool fDone;
-    int  fNumRects;
+    int  fNumRectsInLattice;
+    int  fNumRectsToDraw;
 };
 
 #endif
diff --git a/src/core/SkLiteDL.cpp b/src/core/SkLiteDL.cpp
index eaa5a79..8c8abb1 100644
--- a/src/core/SkLiteDL.cpp
+++ b/src/core/SkLiteDL.cpp
@@ -352,19 +352,21 @@
     };
     struct DrawImageLattice final : Op {
         static const auto kType = Type::DrawImageLattice;
-        DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys,
+        DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys, int fs,
                          const SkRect& dst, const SkPaint* paint)
-            : image(std::move(image)), xs(xs), ys(ys), dst(dst) {
+            : image(std::move(image)), xs(xs), ys(ys), fs(fs), dst(dst) {
             if (paint) { this->paint = *paint; }
         }
         sk_sp<const SkImage> image;
-        int                  xs, ys;
+        int                  xs, ys, fs;
         SkRect               dst;
         SkPaint              paint;
         void draw(SkCanvas* c, const SkMatrix&) {
             auto xdivs = pod<int>(this, 0),
                  ydivs = pod<int>(this, xs*sizeof(int));
-            c->drawImageLattice(image.get(), {xdivs, xs, ydivs, ys}, dst, &paint);
+            auto flags = (0 == fs) ? nullptr :
+                                     pod<SkCanvas::Lattice::Flags>(this, (xs+ys)*sizeof(int));
+            c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys}, dst, &paint);
         }
     };
 
@@ -665,11 +667,13 @@
 void SkLiteDL::drawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice,
                                  const SkRect& dst, const SkPaint* paint) {
     int xs = lattice.fXCount, ys = lattice.fYCount;
-    size_t bytes = (xs + ys) * sizeof(int);
-    void* pod = this->push<DrawImageLattice>(bytes, SkImage::MakeFromBitmap(bm), xs, ys, dst,
+    int fs = lattice.fFlags ? (xs + 1) * (ys + 1) : 0;
+    size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::Flags);
+    void* pod = this->push<DrawImageLattice>(bytes, SkImage::MakeFromBitmap(bm), xs, ys, fs, dst,
                                              paint);
     copy_v(pod, lattice.fXDivs, xs,
-                lattice.fYDivs, ys);
+                lattice.fYDivs, ys,
+                lattice.fFlags, fs);
 }
 
 void SkLiteDL::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
@@ -686,10 +690,12 @@
 void SkLiteDL::drawImageLattice(const SkImage* image, const SkCanvas::Lattice& lattice,
                                 const SkRect& dst, const SkPaint* paint) {
     int xs = lattice.fXCount, ys = lattice.fYCount;
-    size_t bytes = (xs + ys) * sizeof(int);
-    void* pod = this->push<DrawImageLattice>(bytes, sk_ref_sp(image), xs, ys, dst, paint);
+    int fs = lattice.fFlags ? (xs + 1) * (ys + 1) : 0;
+    size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::Flags);
+    void* pod = this->push<DrawImageLattice>(bytes, sk_ref_sp(image), xs, ys, fs, dst, paint);
     copy_v(pod, lattice.fXDivs, xs,
-                lattice.fYDivs, ys);
+                lattice.fYDivs, ys,
+                lattice.fFlags, fs);
 }
 
 void SkLiteDL::drawText(const void* text, size_t bytes,
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 3df6422..4520b6d 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -316,6 +316,9 @@
             lattice.fXDivs = (const int*) reader->skip(lattice.fXCount * sizeof(int32_t));
             lattice.fYCount = reader->readInt();
             lattice.fYDivs = (const int*) reader->skip(lattice.fYCount * sizeof(int32_t));
+            int flagCount = reader->readInt();
+            lattice.fFlags = (0 == flagCount) ? nullptr : (const SkCanvas::Lattice::Flags*)
+                    reader->skip(SkAlign4(flagCount * sizeof(SkCanvas::Lattice::Flags)));
             SkRect dst;
             reader->readRect(&dst);
             canvas->drawImageLattice(image, lattice, dst, paint);
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 8eab630..5ee2e9f 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -538,7 +538,9 @@
 void SkPictureRecord::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
                                          const SkRect& dst, const SkPaint* paint) {
     // xCount + xDivs + yCount+ yDivs
-    size_t latticeSize = (1 + lattice.fXCount + 1 + lattice.fYCount) * kUInt32Size;
+    int flagCount = (nullptr == lattice.fFlags) ? 0 : (lattice.fXCount + 1) * (lattice.fYCount + 1);
+    size_t latticeSize = (1 + lattice.fXCount + 1 + lattice.fYCount + 1) * kUInt32Size +
+                         SkAlign4(flagCount * sizeof(SkCanvas::Lattice::Flags));
 
     // op + paint index + image index + lattice + dst rect
     size_t size = 3 * kUInt32Size + latticeSize + sizeof(dst);
@@ -549,6 +551,8 @@
     fWriter.writePad(lattice.fXDivs, lattice.fXCount * kUInt32Size);
     this->addInt(lattice.fYCount);
     fWriter.writePad(lattice.fYDivs, lattice.fYCount * kUInt32Size);
+    this->addInt(flagCount);
+    fWriter.writePad(lattice.fFlags, flagCount * sizeof(SkCanvas::Lattice::Flags));
     this->addRect(dst);
     this->validate(initialOffset, size);
 }
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index 6545586..2952591 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -106,6 +106,7 @@
     lattice.fXDivs = r.xDivs;
     lattice.fYCount = r.yCount;
     lattice.fYDivs = r.yDivs;
+    lattice.fFlags = (0 == r.flagCount) ? nullptr : r.flags;
     fCanvas->drawImageLattice(r.image.get(), lattice, r.dst, r.paint);
 }
 
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index 840e19b..4c09e51 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -238,9 +238,11 @@
 
 void SkRecorder::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
                                     const SkPaint* paint) {
+    int flagCount = lattice.fFlags ? (lattice.fXCount + 1) * (lattice.fYCount + 1) : 0;
     APPEND(DrawImageLattice, this->copy(paint), sk_ref_sp(image),
            lattice.fXCount, this->copy(lattice.fXDivs, lattice.fXCount),
-           lattice.fYCount, this->copy(lattice.fYDivs, lattice.fYCount), dst);
+           lattice.fYCount, this->copy(lattice.fYDivs, lattice.fYCount),
+           flagCount, this->copy(lattice.fFlags, flagCount), dst);
 }
 
 void SkRecorder::onDrawText(const void* text, size_t byteLength,