Fix SDF generation for pixel-aligned paths
authorJim Van Verth <jvanverth@google.com>
Wed, 7 Dec 2016 15:40:09 +0000 (10:40 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Wed, 7 Dec 2016 16:48:35 +0000 (16:48 +0000)
BUG=668550

Change-Id: Ib496db82c7391aca61b31afaeb5445260170cc49
Reviewed-on: https://skia-review.googlesource.com/5549
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>

gm/pathfill.cpp
src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
src/gpu/batches/GrAADistanceFieldPathRenderer.h

index 3496cfd..ae49a9d 100644 (file)
@@ -50,15 +50,15 @@ static SkScalar make_oval(SkPath* path) {
     return SkIntToScalar(30);
 }
 
-static SkScalar make_sawtooth(SkPath* path) {
+static SkScalar make_sawtooth(SkPath* path, int teeth) {
     SkScalar x = SkIntToScalar(20);
     SkScalar y = SkIntToScalar(20);
     const SkScalar x0 = x;
-    const SkScalar dx = SK_Scalar1 * 5;
-    const SkScalar dy = SK_Scalar1 * 10;
+    const SkScalar dx = SkIntToScalar(5);
+    const SkScalar dy = SkIntToScalar(10);
 
     path->moveTo(x, y);
-    for (int i = 0; i < 32; i++) {
+    for (int i = 0; i < teeth; i++) {
         x += dx;
         path->lineTo(x, y - dy);
         x += dx;
@@ -70,6 +70,33 @@ static SkScalar make_sawtooth(SkPath* path) {
     return SkIntToScalar(30);
 }
 
+static SkScalar make_sawtooth_3(SkPath* path) { return make_sawtooth(path, 3); }
+static SkScalar make_sawtooth_32(SkPath* path) { return make_sawtooth(path, 32); }
+
+static SkScalar make_house(SkPath* path) {
+    path->moveTo(21, 23);
+    path->lineTo(21, 11.534f);
+    path->lineTo(22.327f, 12.741f);
+    path->lineTo(23.673f, 11.261f);
+    path->lineTo(12, 0.648f);
+    path->lineTo(8, 4.285f);
+    path->lineTo(8, 2);
+    path->lineTo(4, 2);
+    path->lineTo(4, 7.921f);
+    path->lineTo(0.327f, 11.26f);
+    path->lineTo(1.673f, 12.74f);
+    path->lineTo(3, 11.534f);
+    path->lineTo(3, 23);
+    path->lineTo(11, 23);
+    path->lineTo(11, 18);
+    path->lineTo(13, 18);
+    path->lineTo(13, 23);
+    path->lineTo(21, 23);
+    path->close();
+    path->offset(20, 0);
+    return SkIntToScalar(30);
+}
+
 static SkScalar make_star(SkPath* path, int n) {
     const SkScalar c = SkIntToScalar(45);
     const SkScalar r = SkIntToScalar(20);
@@ -107,10 +134,12 @@ constexpr MakePathProc gProcs[] = {
     make_triangle,
     make_rect,
     make_oval,
-    make_sawtooth,
+    make_sawtooth_32,
     make_star_5,
     make_star_13,
     make_line,
+    make_house,
+    make_sawtooth_3,
 };
 
 #define N   SK_ARRAY_COUNT(gProcs)
index b06c503..e61b1cb 100644 (file)
@@ -248,9 +248,16 @@ private:
             const SkRect& bounds = args.fShape.bounds();
             SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
             SkScalar size = maxScale * maxDim;
-            uint32_t desiredDimension;
-            if (size <= kSmallMIP) {
+            SkScalar desiredDimension;
+            // For minimizing (or the common case of identity) transforms, we try to
+            // create the DF at the appropriately sized native src-space path resolution.
+            // In the majority of cases this will yield a crisper rendering.
+            if (size <= maxDim && maxDim < kSmallMIP) {
+                desiredDimension = maxDim;
+            } else if (size <= kSmallMIP) {
                 desiredDimension = kSmallMIP;
+            } else if (size <= maxDim) {
+                desiredDimension = maxDim;
             } else if (size <= kMediumMIP) {
                 desiredDimension = kMediumMIP;
             } else {
@@ -258,7 +265,7 @@ private:
             }
 
             // check to see if path is cached
-            ShapeData::Key key(args.fShape, desiredDimension);
+            ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
             ShapeData* shapeData = fShapeCache->find(key);
             if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
                 // Remove the stale cache entry
@@ -268,6 +275,7 @@ private:
                     delete shapeData;
                 }
                 SkScalar scale = desiredDimension/maxDim;
+
                 shapeData = new ShapeData;
                 if (!this->addPathToAtlas(target,
                                           &flushInfo,
@@ -275,7 +283,7 @@ private:
                                           shapeData,
                                           args.fShape,
                                           args.fAntiAlias,
-                                          desiredDimension,
+                                          SkScalarCeilToInt(desiredDimension),
                                           scale)) {
                     delete shapeData;
                     SkDebugf("Can't rasterize path\n");
@@ -311,29 +319,25 @@ private:
         scaledBounds.fTop *= scale;
         scaledBounds.fRight *= scale;
         scaledBounds.fBottom *= scale;
-        // move the origin to an integer boundary (gives better results)
-        SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
-        SkScalar dy = SkScalarFraction(scaledBounds.fTop);
+        // subtract out integer portion of origin
+        // (SDF created will be placed with fractional offset burnt in)
+        SkScalar dx = SkScalarFloorToInt(scaledBounds.fLeft);
+        SkScalar dy = SkScalarFloorToInt(scaledBounds.fTop);
         scaledBounds.offset(-dx, -dy);
         // get integer boundary
         SkIRect devPathBounds;
         scaledBounds.roundOut(&devPathBounds);
         // pad to allow room for antialiasing
         const int intPad = SkScalarCeilToInt(kAntiAliasPad);
-        // pre-move origin (after outset, will be 0,0)
-        int width = devPathBounds.width();
-        int height = devPathBounds.height();
-        devPathBounds.fLeft = intPad;
-        devPathBounds.fTop = intPad;
-        devPathBounds.fRight = intPad + width;
-        devPathBounds.fBottom = intPad + height;
-        devPathBounds.outset(intPad, intPad);
+        // place devBounds at origin
+        int width = devPathBounds.width() + 2*intPad;
+        int height = devPathBounds.height() + 2*intPad;
+        devPathBounds = SkIRect::MakeWH(width, height);
 
         // draw path to bitmap
         SkMatrix drawMatrix;
-        drawMatrix.setTranslate(-bounds.left(), -bounds.top());
-        drawMatrix.postScale(scale, scale);
-        drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
+        drawMatrix.setScale(scale, scale);
+        drawMatrix.postTranslate(intPad - dx, intPad - dy);
 
         // setup bitmap backing
         SkASSERT(devPathBounds.fLeft == 0);
@@ -378,7 +382,7 @@ private:
         // add to atlas
         SkIPoint16 atlasLocation;
         GrBatchAtlas::AtlasID id;
-       if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
+        if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
             this->flush(target, flushInfo);
             if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
                 return false;
@@ -387,22 +391,20 @@ private:
 
         // add to cache
         shapeData->fKey.set(shape, dimension);
-        shapeData->fScale = scale;
         shapeData->fID = id;
-        // change the scaled rect to match the size of the inset distance field
-        scaledBounds.fRight = scaledBounds.fLeft +
-            SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
-        scaledBounds.fBottom = scaledBounds.fTop +
-            SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
-        // shift the origin to the correct place relative to the distance field
-        // need to also restore the fractional translation
-        scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
-                            -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
-        shapeData->fBounds = scaledBounds;
-        // origin we render from is inset from distance field edge
-        atlasLocation.fX += SK_DistanceFieldInset;
-        atlasLocation.fY += SK_DistanceFieldInset;
-        shapeData->fAtlasLocation = atlasLocation;
+
+        // set the bounds rect to match the size and placement of the distance field in the atlas
+        shapeData->fBounds.setXYWH(atlasLocation.fX + SK_DistanceFieldPad,
+                                   atlasLocation.fY + SK_DistanceFieldPad,
+                                   width - 2*SK_DistanceFieldPad,
+                                   height - 2*SK_DistanceFieldPad);
+        // we also need to store the transformation from texture space to the original path's space
+        // which is a simple uniform scale and translate
+        shapeData->fScaleToDev = SkScalarInvert(scale);
+        dx -= SK_DistanceFieldPad + kAntiAliasPad;
+        dy -= SK_DistanceFieldPad + kAntiAliasPad;
+        shapeData->fTranslateToDev = SkPoint::Make((-atlasLocation.fX + dx)*shapeData->fScaleToDev,
+                                                   (-atlasLocation.fY + dy)*shapeData->fScaleToDev);
 
         fShapeCache->add(shapeData);
         fShapeList->addToTail(shapeData);
@@ -426,11 +428,14 @@ private:
         SkScalar width = shapeData->fBounds.width();
         SkScalar height = shapeData->fBounds.height();
 
-        SkScalar invScale = 1.0f / shapeData->fScale;
+        // transform texture bounds to the original path's space
+        SkScalar invScale = shapeData->fScaleToDev;
         dx *= invScale;
         dy *= invScale;
         width *= invScale;
         height *= invScale;
+        dx += shapeData->fTranslateToDev.fX;
+        dy += shapeData->fTranslateToDev.fY;
 
         SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
 
@@ -445,15 +450,12 @@ private:
             *colorPtr = color;
         }
 
-        const SkScalar tx = SkIntToScalar(shapeData->fAtlasLocation.fX);
-        const SkScalar ty = SkIntToScalar(shapeData->fAtlasLocation.fY);
-
         // vertex texture coords
         SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor));
-        textureCoords->setRectFan(tx / texture->width(),
-                                  ty / texture->height(),
-                                  (tx + shapeData->fBounds.width()) / texture->width(),
-                                  (ty + shapeData->fBounds.height())  / texture->height(),
+        textureCoords->setRectFan((shapeData->fBounds.fLeft) / texture->width(),
+                                  (shapeData->fBounds.fTop) / texture->height(),
+                                  (shapeData->fBounds.fRight)/texture->width(),
+                                  (shapeData->fBounds.fBottom)/texture->height(),
                                   vertexStride);
     }
 
index 171108a..7bb9022 100644 (file)
@@ -70,10 +70,10 @@ private:
             SkAutoSTArray<24, uint32_t> fKey;
         };
         Key                   fKey;
-        SkScalar              fScale;
         GrBatchAtlas::AtlasID fID;
         SkRect                fBounds;
-        SkIPoint16            fAtlasLocation;
+        SkScalar              fScaleToDev;
+        SkPoint               fTranslateToDev;
         SK_DECLARE_INTERNAL_LLIST_INTERFACE(ShapeData);
 
         static inline const Key& GetKey(const ShapeData& data) {