Add new module for distance field generation.
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 10 Mar 2014 19:38:51 +0000 (19:38 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 10 Mar 2014 19:38:51 +0000 (19:38 +0000)
This improves the speed over the previous method by 10x+, and makes using distance fields practical.

BUG=skia:2173
R=bsalomon@google.com, robertphillips@google.com

Author: jvanverth@google.com

Review URL: https://codereview.chromium.org/178543007

git-svn-id: http://skia.googlecode.com/svn/trunk@13729 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/gmmain.cpp
gyp/core.gypi
gyp/edtaa.gyp [deleted file]
gyp/gpu.gyp
src/core/SkDistanceFieldGen.cpp [new file with mode: 0755]
src/core/SkDistanceFieldGen.h [new file with mode: 0755]
src/gpu/GrTextStrike.cpp
third_party/edtaa/LICENSE [deleted file]
third_party/edtaa/edtaa3.h [deleted file]
third_party/edtaa/edtaa3func.cpp [deleted file]

index 165c5affb7278556732e91080e5931c2f20b7231..de9e1a15a0566934abfcfc21be23863916b0a93c 100644 (file)
@@ -46,6 +46,7 @@
 
 #ifdef SK_DEBUG
 static const bool kDebugOnly = true;
+#define GR_DUMP_FONT_CACHE 0
 #else
 static const bool kDebugOnly = false;
 #endif
@@ -2408,6 +2409,18 @@ int tool_main(int argc, char** argv) {
     }
 #endif
 
+#if GR_DUMP_FONT_CACHE
+    for (int i = 0; i < configs.count(); i++) {
+        ConfigData config = gRec[configs[i]];
+
+        if (kGPU_Backend == config.fBackend) {
+            GrContext* gr = grFactory->get(config.fGLContextType);
+
+           gr->dumpFontCache();
+        }
+    }
+#endif
+
     delete grFactory;
 #endif
     SkGraphics::Term();
index a1eed0a72989e03042aebd6a15adb897aa00b45b..cdb0bab1d7db7839ac6202dbc8ecbf6afe74537a 100644 (file)
@@ -70,6 +70,8 @@
         '<(skia_src_path)/core/SkDeviceProfile.cpp',
         '<(skia_src_path)/lazy/SkDiscardableMemoryPool.cpp',
         '<(skia_src_path)/lazy/SkDiscardablePixelRef.cpp',
+        '<(skia_src_path)/core/SkDistanceFieldGen.cpp',
+        '<(skia_src_path)/core/SkDistanceFieldGen.h',
         '<(skia_src_path)/core/SkDither.cpp',
         '<(skia_src_path)/core/SkDraw.cpp',
         '<(skia_src_path)/core/SkDrawLooper.cpp',
diff --git a/gyp/edtaa.gyp b/gyp/edtaa.gyp
deleted file mode 100755 (executable)
index 4f7272e..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Copyright 2013 Google Inc.
-#
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-
-{
-  'targets': [
-    {
-      'target_name': 'edtaa',
-      'type': 'none',
-      'conditions': [
-        [ 'skia_distancefield_fonts', {
-          'type': 'static_library',
-          'sources': [
-            '../third_party/edtaa/edtaa3func.cpp',
-          ],
-          'include_dirs': [
-            '../third_party/edtaa/',
-          ],
-          'all_dependent_settings': {
-            'include_dirs': [
-              '../third_party/edtaa/',
-            ],
-          },
-        }],
-      ],
-    },
-  ],
-}
index 471004cbe443fd588ede184cf7d0960e30e091dc..f303eb50d89c59f4dd228f266354912d20f3ddab 100644 (file)
@@ -83,7 +83,6 @@
       'standalone_static_library': 1,
       'dependencies': [
         'core.gyp:*',
-        'edtaa.gyp:*',
         'utils.gyp:*',
       ],
       'includes': [
diff --git a/src/core/SkDistanceFieldGen.cpp b/src/core/SkDistanceFieldGen.cpp
new file mode 100755 (executable)
index 0000000..fb7131e
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDistanceFieldGen.h"
+#include "SkPoint.h"
+
+struct DFData {
+    float   fAlpha;      // alpha value of source texel
+    float   fDistSq;     // distance squared to nearest (so far) edge texel
+    SkPoint fDistVector; // distance vector to nearest (so far) edge texel
+};
+
+// We treat an "edge" as a place where we cross from a texel >= 128 to a texel < 128,
+// or vice versa. This means we just need to check if the MSBs are different.
+static bool found_edge(const unsigned char* imagePtr, int width) {
+    const int offsets[8] = {-1, 1, -width-1, -width, -width+1, width-1, width, width+1 };
+
+    // search for an edge
+    int checkVal = *imagePtr >> 7;
+    for (int i = 0; i < 8; ++i) {
+        const unsigned char* checkPtr = imagePtr + offsets[i];
+        if (checkVal ^ (*checkPtr >> 7)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static void init_glyph_data(DFData* data, char* edges, const unsigned char* image,
+                            int dataWidth, int dataHeight, 
+                            int imageWidth, int imageHeight,
+                            int pad) {
+    data += pad*dataWidth;
+    data += pad;
+    edges += (pad*dataWidth + pad);
+
+    for (int j = 0; j < imageHeight; ++j) {
+        for (int i = 0; i < imageWidth; ++i) {
+            if (255 == *image) {
+                data->fAlpha = 1.0f;
+            } else {
+                data->fAlpha = (*image)*0.00392156862f;  // 1/255
+            }
+            if (i > 0 && i < imageWidth-1 && j > 0 && j < imageHeight-1 &&
+                found_edge(image, imageWidth)) {
+                *edges = 255;  // using 255 makes for convenient debug rendering
+            }
+            ++data;
+            ++image;
+            ++edges;
+        }
+        data += 2*pad;
+        edges += 2*pad;
+    }
+}
+
+// from Gustavson (2011)
+// computes the distance to an edge given an edge normal vector and a pixel's alpha value
+// assumes that direction has been pre-normalized
+static float edge_distance(const SkPoint& direction, float alpha) {
+    float dx = direction.fX;
+    float dy = direction.fY;
+    float distance;
+    if (SkScalarNearlyZero(dx) || SkScalarNearlyZero(dy)) {
+        distance = 0.5f - alpha;
+    } else {
+        // this is easier if we treat the direction as being in the first octant
+        // (other octants are symmetrical)
+        dx = SkScalarAbs(dx);
+        dy = SkScalarAbs(dy);
+        if (dx < dy) {
+            SkTSwap(dx, dy);
+        }
+
+        // a1 = 0.5*dy/dx is the smaller fractional area chopped off by the edge
+        // to avoid the divide, we just consider the numerator
+        float a1num = 0.5f*dy;
+
+        // we now compute the approximate distance, depending where the alpha falls
+        // relative to the edge fractional area
+
+        // if 0 <= alpha < a1
+        if (alpha*dx < a1num) {
+            // TODO: find a way to do this without square roots?
+            distance = 0.5f*(dx + dy) - SkScalarSqrt(2.0f*dx*dy*alpha);
+        // if a1 <= alpha <= 1 - a1
+        } else if (alpha*dx < (dx - a1num)) {
+            distance = (0.5f - alpha)*dx;
+        // if 1 - a1 < alpha <= 1
+        } else {
+            // TODO: find a way to do this without square roots?
+            distance = -0.5f*(dx + dy) + SkScalarSqrt(2.0f*dx*dy*(1.0f - alpha));
+        }
+    }
+
+    return distance;
+}
+
+static void init_distances(DFData* data, char* edges, int width, int height) {
+    // skip one pixel border
+    DFData* currData = data;
+    DFData* prevData = data - width;
+    DFData* nextData = data + width;
+
+    for (int j = 0; j < height; ++j) {
+        for (int i = 0; i < width; ++i) {
+            if (*edges) {
+                // we should not be in the one-pixel outside band
+                SkASSERT(i > 0 && i < width-1 && j > 0 && j < height-1);
+                // gradient will point from low to high
+                // +y is down in this case
+                // i.e., if you're outside, gradient points towards edge
+                // if you're inside, gradient points away from edge
+                SkPoint currGrad;
+                currGrad.fX = (prevData+1)->fAlpha - (prevData-1)->fAlpha
+                             + SK_ScalarSqrt2*(currData+1)->fAlpha 
+                             - SK_ScalarSqrt2*(currData-1)->fAlpha
+                             + (nextData+1)->fAlpha - (nextData-1)->fAlpha;
+                currGrad.fY = (nextData-1)->fAlpha - (prevData-1)->fAlpha 
+                             + SK_ScalarSqrt2*nextData->fAlpha 
+                             - SK_ScalarSqrt2*prevData->fAlpha
+                             + (nextData+1)->fAlpha - (prevData+1)->fAlpha;
+                currGrad.setLengthFast(1.0f);
+
+                // init squared distance to edge and distance vector
+                float dist = edge_distance(currGrad, currData->fAlpha);
+                currGrad.scale(dist, &currData->fDistVector);
+                currData->fDistSq = dist*dist;
+            } else {
+                // init distance to "far away"
+                currData->fDistSq = 2000000.f;
+                currData->fDistVector.fX = 1000.f;
+                currData->fDistVector.fY = 1000.f;
+            }
+            ++currData;
+            ++prevData;
+            ++nextData;
+            ++edges;
+        }
+    }
+}
+
+// Danielsson's 8SSEDT
+
+// first stage forward pass
+// (forward in Y, forward in X)
+static void F1(DFData* curr, int width) {
+    // upper left
+    DFData* check = curr - width-1;
+    SkPoint distVec = check->fDistVector;
+    float distSq = check->fDistSq - 2.0f*(distVec.fX + distVec.fY - 1.0f);
+    if (distSq < curr->fDistSq) {
+        distVec.fX -= 1.0f;
+        distVec.fY -= 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+
+    // up
+    check = curr - width;
+    distVec = check->fDistVector;
+    distSq = check->fDistSq - 2.0f*distVec.fY + 1.0f;
+    if (distSq < curr->fDistSq) {
+        distVec.fY -= 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+
+    // upper right
+    check = curr - width+1;
+    distVec = check->fDistVector;
+    distSq = check->fDistSq + 2.0f*(distVec.fX - distVec.fY + 1.0f);
+    if (distSq < curr->fDistSq) {
+        distVec.fX += 1.0f;
+        distVec.fY -= 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+
+    // left
+    check = curr - 1;
+    distVec = check->fDistVector;
+    distSq = check->fDistSq - 2.0f*distVec.fX + 1.0f;
+    if (distSq < curr->fDistSq) {
+        distVec.fX -= 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+}
+
+// second stage forward pass
+// (forward in Y, backward in X)
+static void F2(DFData* curr, int width) {
+    // right
+    DFData* check = curr + 1;
+    float distSq = check->fDistSq;
+    SkPoint distVec = check->fDistVector;
+    distSq = check->fDistSq + 2.0f*distVec.fX + 1.0f;
+    if (distSq < curr->fDistSq) {
+        distVec.fX += 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+}
+
+// first stage backward pass
+// (backward in Y, forward in X)
+static void B1(DFData* curr, int width) {
+    // left
+    DFData* check = curr - 1;
+    SkPoint distVec = check->fDistVector;
+    float distSq = check->fDistSq - 2.0f*distVec.fX + 1.0f;
+    if (distSq < curr->fDistSq) {
+        distVec.fX -= 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+}
+
+// second stage backward pass
+// (backward in Y, backwards in X)
+static void B2(DFData* curr, int width) {
+    // right
+    DFData* check = curr + 1;
+    SkPoint distVec = check->fDistVector;
+    float distSq = check->fDistSq + 2.0f*distVec.fX + 1.0f;
+    if (distSq < curr->fDistSq) {
+        distVec.fX += 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+
+    // bottom left
+    check = curr + width-1;
+    distVec = check->fDistVector;
+    distSq = check->fDistSq - 2.0f*(distVec.fX - distVec.fY - 1.0f);
+    if (distSq < curr->fDistSq) {
+        distVec.fX -= 1.0f;
+        distVec.fY += 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+
+    // bottom
+    check = curr + width;
+    distVec = check->fDistVector;
+    distSq = check->fDistSq + 2.0f*distVec.fY + 1.0f;
+    if (distSq < curr->fDistSq) {
+        distVec.fY += 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+
+    // bottom right
+    check = curr + width+1;
+    distVec = check->fDistVector;
+    distSq = check->fDistSq + 2.0f*(distVec.fX + distVec.fY + 1.0f);
+    if (distSq < curr->fDistSq) {
+        distVec.fX += 1.0f;
+        distVec.fY += 1.0f;
+        curr->fDistSq = distSq;
+        curr->fDistVector = distVec;
+    }
+}
+
+static unsigned char pack_distance_field_val(float dist, float distanceMagnitude) {
+    if (dist <= -distanceMagnitude) {
+        return 255;
+    } else if (dist > distanceMagnitude) {
+        return 0;
+    } else {
+        return (unsigned char)((distanceMagnitude-dist)*128.0f/distanceMagnitude);
+    }
+}
+
+// assumes an 8-bit image and distance field
+bool SkGenerateDistanceFieldFromImage(unsigned char* distanceField,
+                                      const unsigned char* image,
+                                      int width, int height,
+                                      int distanceMagnitude) {
+    SkASSERT(NULL != distanceField);
+    SkASSERT(NULL != image);
+
+    // the final distance field will have additional texels on each side to handle
+    // the maximum distance 
+    // we expand our temp data by one more on each side to simplify
+    // the scanning code -- will always be treated as infinitely far away
+    int pad = distanceMagnitude+1;
+
+    // set params for distance field data
+    int dataWidth = width + 2*pad;
+    int dataHeight = height + 2*pad;
+
+    // create temp data
+    size_t dataSize = dataWidth*dataHeight*sizeof(DFData);
+    SkAutoSMalloc<1024> dfStorage(dataSize);
+    DFData* dataPtr = (DFData*) dfStorage.get();
+    sk_bzero(dataPtr, dataSize);
+
+    SkAutoSMalloc<1024> edgeStorage(dataWidth*dataHeight*sizeof(char));
+    char* edgePtr = (char*) edgeStorage.get();
+    sk_bzero(edgePtr, dataWidth*dataHeight*sizeof(char));
+
+    // copy glyph into distance field storage
+    init_glyph_data(dataPtr, edgePtr, image,
+                    dataWidth, dataHeight,
+                    width, height, pad);
+
+    // create initial distance data, particularly at edges
+    init_distances(dataPtr, edgePtr, dataWidth, dataHeight);
+
+    // now perform Euclidean distance transform to propagate distances
+
+    // forwards in y
+    DFData* currData = dataPtr+dataWidth+1; // skip outer buffer
+    char* currEdge = edgePtr+dataWidth+1;
+    for (int j = 1; j < dataHeight-1; ++j) {
+        // forwards in x
+        for (int i = 1; i < dataWidth-1; ++i) {
+            // don't need to calculate distance for edge pixels
+            if (!*currEdge) {
+                F1(currData, dataWidth);
+            }
+            ++currData;
+            ++currEdge;
+        }
+
+        // backwards in x
+        --currData; // reset to end
+        --currEdge;
+        for (int i = 1; i < dataWidth-1; ++i) {
+            // don't need to calculate distance for edge pixels
+            if (!*currEdge) {
+                F2(currData, dataWidth);
+            }
+            --currData;
+            --currEdge;
+        }
+
+        currData += dataWidth+1;
+        currEdge += dataWidth+1;
+    }
+
+    // backwards in y
+    currData = dataPtr+dataWidth*(dataHeight-2) - 1; // skip outer buffer
+    currEdge = edgePtr+dataWidth*(dataHeight-2) - 1;
+    for (int j = 1; j < dataHeight-1; ++j) {
+        // forwards in x
+        for (int i = 1; i < dataWidth-1; ++i) {
+            // don't need to calculate distance for edge pixels
+            if (!*currEdge) {
+                B1(currData, dataWidth);
+            }
+            ++currData;
+            ++currEdge;
+        }
+
+        // backwards in x
+        --currData; // reset to end
+        --currEdge;
+        for (int i = 1; i < dataWidth-1; ++i) {
+            // don't need to calculate distance for edge pixels
+            if (!*currEdge) {
+                B2(currData, dataWidth);
+            }
+            --currData;
+            --currEdge;
+        }
+
+        currData -= dataWidth-1;
+        currEdge -= dataWidth-1;
+    }
+
+    // copy results to final distance field data
+    currData = dataPtr + dataWidth+1;
+    currEdge = edgePtr + dataWidth+1;
+    unsigned char *dfPtr = distanceField;
+    for (int j = 1; j < dataHeight-1; ++j) {
+        for (int i = 1; i < dataWidth-1; ++i) {
+            float dist;
+            if (currData->fAlpha > 0.5f) {
+                dist = -SkScalarSqrt(currData->fDistSq);
+            } else {
+                dist = SkScalarSqrt(currData->fDistSq);
+            }
+
+            *dfPtr++ = pack_distance_field_val(dist, (float)distanceMagnitude);
+            ++currData;
+            ++currEdge;
+        }
+        currData += 2;
+        currEdge += 2;
+    }
+
+    return true;
+}
diff --git a/src/core/SkDistanceFieldGen.h b/src/core/SkDistanceFieldGen.h
new file mode 100755 (executable)
index 0000000..3d2ec23
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkDistanceFieldGen_DEFINED
+#define SkDistanceFieldGen_DEFINED
+
+/** Given 8-bit mask data, generate the associated distance field
+
+ *  @param distanceField     The distance field to be generated. Should already be allocated
+ *                           by the client with the padding below.
+ *  @param image             8-bit mask we're using to generate the distance field.
+ *  @param w                 Width of the image.
+ *  @param h                 Height of the image.
+ *  @param distanceMagnitude Largest possible absolute value for the distance. The distance field
+ *                           will be padded to w + 2*distanceMagnitude, h + 2*distanceMagnitude.
+ */
+bool SkGenerateDistanceFieldFromImage(unsigned char* distanceField,
+                                      const unsigned char* image,
+                                      int w, int h,
+                                      int distanceMagnitude);
+
+#endif
index e303a23df8d2b851800d4a2c25b40f84e540894b..0d7fcafe678ebb9cc1fb96f0e32d4c5cea17dc0a 100644 (file)
@@ -13,7 +13,7 @@
 #include "SkString.h"
 
 #if SK_DISTANCEFIELD_FONTS
-#include "edtaa3.h"
+#include "SkDistanceFieldGen.h"
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -199,8 +199,9 @@ void GrFontCache::dump() const {
 #endif
 
 #if SK_DISTANCEFIELD_FONTS
-#define DISTANCE_FIELD_PAD   4
-#define DISTANCE_FIELD_RANGE (4.0)
+// this acts as the max magnitude for the distance field,
+// as well as the pad we need around the glyph
+#define DISTANCE_FIELD_RANGE   4
 #endif
 
 /*
@@ -253,10 +254,10 @@ GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
 #if SK_DISTANCEFIELD_FONTS
     // expand bounds to hold full distance field data
     if (fUseDistanceField) {
-        bounds.fLeft   -= DISTANCE_FIELD_PAD;
-        bounds.fRight  += DISTANCE_FIELD_PAD;
-        bounds.fTop    -= DISTANCE_FIELD_PAD;
-        bounds.fBottom += DISTANCE_FIELD_PAD;
+        bounds.fLeft   -= DISTANCE_FIELD_RANGE;
+        bounds.fRight  += DISTANCE_FIELD_RANGE;
+        bounds.fTop    -= DISTANCE_FIELD_RANGE;
+        bounds.fBottom += DISTANCE_FIELD_RANGE;
     }
 #endif
     glyph->init(packed, bounds);
@@ -294,15 +295,13 @@ bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
     GrPlot* plot;
 #if SK_DISTANCEFIELD_FONTS
     if (fUseDistanceField) {
-        SkASSERT(1 == bytesPerPixel);
-
         // we've already expanded the glyph dimensions to match the final size
         // but must shrink back down to get the packed glyph data
         int dfWidth = glyph->width();
         int dfHeight = glyph->height();
-        int width = dfWidth - 2*DISTANCE_FIELD_PAD;
-        int height = dfHeight - 2*DISTANCE_FIELD_PAD;
-        size_t stride = width*bytesPerPixel;
+        int width = dfWidth - 2*DISTANCE_FIELD_RANGE;
+        int height = dfHeight - 2*DISTANCE_FIELD_RANGE;
+        int stride = width*bytesPerPixel;
 
         size_t size = width * height * bytesPerPixel;
         SkAutoSMalloc<1024> storage(size);
@@ -313,71 +312,28 @@ bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
         // alloc storage for distance field glyph
         size_t dfSize = dfWidth * dfHeight * bytesPerPixel;
         SkAutoSMalloc<1024> dfStorage(dfSize);
-
-        // copy glyph into distance field storage
-        sk_bzero(dfStorage.get(), dfSize);
-
-        unsigned char* ptr = (unsigned char*) storage.get();
-        unsigned char* dfPtr = (unsigned char*) dfStorage.get();
-        size_t dfStride = dfWidth*bytesPerPixel;
-        dfPtr += DISTANCE_FIELD_PAD*dfStride;
-        dfPtr += DISTANCE_FIELD_PAD*bytesPerPixel;
-
-        for (int i = 0; i < height; ++i) {
-            memcpy(dfPtr, ptr, stride);
-
-            dfPtr += dfStride;
-            ptr += stride;
-        }
-
-        // generate distance field data
-        SkAutoSMalloc<1024> distXStorage(dfWidth*dfHeight*sizeof(short));
-        SkAutoSMalloc<1024> distYStorage(dfWidth*dfHeight*sizeof(short));
-        SkAutoSMalloc<1024> outerDistStorage(dfWidth*dfHeight*sizeof(double));
-        SkAutoSMalloc<1024> innerDistStorage(dfWidth*dfHeight*sizeof(double));
-        SkAutoSMalloc<1024> gxStorage(dfWidth*dfHeight*sizeof(double));
-        SkAutoSMalloc<1024> gyStorage(dfWidth*dfHeight*sizeof(double));
-
-        short* distX = (short*) distXStorage.get();
-        short* distY = (short*) distYStorage.get();
-        double* outerDist = (double*) outerDistStorage.get();
-        double* innerDist = (double*) innerDistStorage.get();
-        double* gx = (double*) gxStorage.get();
-        double* gy = (double*) gyStorage.get();
-
-        dfPtr = (unsigned char*) dfStorage.get();
-        EDTAA::computegradient(dfPtr, dfWidth, dfHeight, gx, gy);
-        EDTAA::edtaa3(dfPtr, gx, gy, dfWidth, dfHeight, distX, distY, outerDist);
-
-        for (int i = 0; i < dfWidth*dfHeight; ++i) {
-            *dfPtr = 255 - *dfPtr;
-            dfPtr++;
-        }
-        dfPtr = (unsigned char*) dfStorage.get();
-        sk_bzero(gx, sizeof(double)*dfWidth*dfHeight);
-        sk_bzero(gy, sizeof(double)*dfWidth*dfHeight);
-        EDTAA::computegradient(dfPtr, dfWidth, dfHeight, gx, gy);
-        EDTAA::edtaa3(dfPtr, gx, gy, dfWidth, dfHeight, distX, distY, innerDist);
-
-        for (int i = 0; i < dfWidth*dfHeight; ++i) {
-            unsigned char val;
-            double outerval = outerDist[i];
-            if (outerval < 0.0) {
-                outerval = 0.0;
-            }
-            double innerval = innerDist[i];
-            if (innerval < 0.0) {
-                innerval = 0.0;
-            }
-            double dist = outerval - innerval;
-            if (dist <= -DISTANCE_FIELD_RANGE) {
-                val = 255;
-            } else if (dist > DISTANCE_FIELD_RANGE) {
-                val = 0;
-            } else {
-                val = (unsigned char)((DISTANCE_FIELD_RANGE-dist)*128.0/DISTANCE_FIELD_RANGE);
+        
+        if (1 == bytesPerPixel) {
+            (void) SkGenerateDistanceFieldFromImage((unsigned char*)dfStorage.get(),
+                                                    (unsigned char*)storage.get(),
+                                                    width, height, DISTANCE_FIELD_RANGE);
+        } else {
+            // TODO: Fix color emoji
+            // for now, copy glyph into distance field storage
+            // this is not correct, but it won't crash
+            sk_bzero(dfStorage.get(), dfSize);
+            unsigned char* ptr = (unsigned char*) storage.get();
+            unsigned char* dfPtr = (unsigned char*) dfStorage.get();
+            size_t dfStride = dfWidth*bytesPerPixel;
+            dfPtr += DISTANCE_FIELD_RANGE*dfStride;
+            dfPtr += DISTANCE_FIELD_RANGE*bytesPerPixel;
+
+            for (int i = 0; i < height; ++i) {
+                memcpy(dfPtr, ptr, stride);
+
+                dfPtr += dfStride;
+                ptr += stride;
             }
-            *dfPtr++ = val;
         }
 
         // copy to atlas
diff --git a/third_party/edtaa/LICENSE b/third_party/edtaa/LICENSE
deleted file mode 100755 (executable)
index a558805..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)\r
-\r
-Copyright (c) 2009-2012 Stefan Gustavson (stefan.gustavson@gmail.com)\r
-\r
-Permission is hereby granted, free of charge, to any person obtaining a copy\r
-of this software and associated documentation files (the "Software"), to deal\r
-in the Software without restriction, including without limitation the rights\r
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
-copies of the Software, and to permit persons to whom the Software is\r
-furnished to do so, subject to the following conditions:\r
-\r
-The above copyright notice and this permission notice shall be included in\r
-all copies or substantial portions of the Software.\r
-\r
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
-THE SOFTWARE.\r
diff --git a/third_party/edtaa/edtaa3.h b/third_party/edtaa/edtaa3.h
deleted file mode 100755 (executable)
index 4e30677..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/* \r
- Permission is hereby granted, free of charge, to any person obtaining a copy\r
- of this software and associated documentation files (the "Software"), to deal\r
- in the Software without restriction, including without limitation the rights\r
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
- copies of the Software, and to permit persons to whom the Software is\r
- furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in\r
- all copies or substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
- THE SOFTWARE.\r
- */\r
-\r
-#define EDTAA_UNSIGNED_CHAR_INPUT 1\r
-\r
-namespace EDTAA {\r
-\r
-#if EDTAA_UNSIGNED_CHAR_INPUT\r
-typedef unsigned char EdtaaImageType;\r
-#else\r
-typedef double EdtaaImageType;\r
-#endif\r
-\r
-void computegradient(EdtaaImageType *img, int w, int h, double *gx, double *gy);\r
-void edtaa3(EdtaaImageType *img, double *gx, double *gy, int w, int h, \r
-                   short *distx, short *disty, double *dist);\r
-\r
-}\r
diff --git a/third_party/edtaa/edtaa3func.cpp b/third_party/edtaa/edtaa3func.cpp
deleted file mode 100755 (executable)
index beed198..0000000
+++ /dev/null
@@ -1,586 +0,0 @@
-/*\r
- * edtaa3()\r
- *\r
- * Sweep-and-update Euclidean distance transform of an\r
- * image. Positive pixels are treated as object pixels,\r
- * zero or negative pixels are treated as background.\r
- * An attempt is made to treat antialiased edges correctly.\r
- * The input image must have pixels in the range [0,1],\r
- * and the antialiased image should be a box-filter\r
- * sampling of the ideal, crisp edge.\r
- * If the antialias region is more than 1 pixel wide,\r
- * the result from this transform will be inaccurate.\r
- *\r
- * By Stefan Gustavson (stefan.gustavson@gmail.com).\r
- *\r
- * Originally written in 1994, based on a verbal\r
- * description of the SSED8 algorithm published in the\r
- * PhD dissertation of Ingemar Ragnemalm. This is his\r
- * algorithm, I only implemented it in C.\r
- *\r
- * Updated in 2004 to treat border pixels correctly,\r
- * and cleaned up the code to improve readability.\r
- *\r
- * Updated in 2009 to handle anti-aliased edges.\r
- *\r
- * Updated in 2011 to avoid a corner case infinite loop.\r
- *\r
- * Updated 2012 to change license from LGPL to MIT.\r
- */\r
-\r
-/*\r
- Copyright (C) 2009-2012 Stefan Gustavson (stefan.gustavson@gmail.com)\r
- The code in this file is distributed under the MIT license:\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy\r
- of this software and associated documentation files (the "Software"), to deal\r
- in the Software without restriction, including without limitation the rights\r
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
- copies of the Software, and to permit persons to whom the Software is\r
- furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in\r
- all copies or substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
- THE SOFTWARE.\r
- */\r
-\r
-#include "edtaa3.h"\r
-\r
-#include <math.h>\r
-\r
-#if EDTAA_UNSIGNED_CHAR_INPUT\r
-#define IMG(i) ((double)(img[i] & 0xff)/256.0)\r
-#else\r
-#define IMG(i) (img[i])\r
-#endif\r
-\r
-namespace EDTAA {\r
-\r
-/*\r
- * Compute the local gradient at edge pixels using convolution filters.\r
- * The gradient is computed only at edge pixels. At other places in the\r
- * image, it is never used, and it's mostly zero anyway.\r
- */\r
-void computegradient(EdtaaImageType *img, int w, int h, double *gx, double *gy)\r
-{\r
-    int i,j,k;\r
-    double glength;\r
-#define SQRT2 1.4142136\r
-    for(i = 1; i < h-1; i++) { // Avoid edges where the kernels would spill over\r
-        for(j = 1; j < w-1; j++) {\r
-            k = i*w + j;\r
-            if((IMG(k)>0.0) && (IMG(k)<1.0)) { // Compute gradient for edge pixels only\r
-                gx[k] = -IMG(k-w-1) - SQRT2*IMG(k-1) - IMG(k+w-1) + IMG(k-w+1) + SQRT2*IMG(k+1) + IMG(k+w+1);\r
-                gy[k] = -IMG(k-w-1) - SQRT2*IMG(k-w) - IMG(k+w-1) + IMG(k-w+1) + SQRT2*IMG(k+w) + IMG(k+w+1);\r
-                glength = gx[k]*gx[k] + gy[k]*gy[k];\r
-                if(glength > 0.0) { // Avoid division by zero\r
-                    glength = sqrt(glength);\r
-                    gx[k]=gx[k]/glength;\r
-                    gy[k]=gy[k]/glength;\r
-                }\r
-            }\r
-        }\r
-    }\r
-    // TODO: Compute reasonable values for gx, gy also around the image edges.\r
-    // (These are zero now, which reduces the accuracy for a 1-pixel wide region\r
-       // around the image edge.) 2x2 kernels would be suitable for this.\r
-}\r
-\r
-/*\r
- * A somewhat tricky function to approximate the distance to an edge in a\r
- * certain pixel, with consideration to either the local gradient (gx,gy)\r
- * or the direction to the pixel (dx,dy) and the pixel greyscale value a.\r
- * The latter alternative, using (dx,dy), is the metric used by edtaa2().\r
- * Using a local estimate of the edge gradient (gx,gy) yields much better\r
- * accuracy at and near edges, and reduces the error even at distant pixels\r
- * provided that the gradient direction is accurately estimated.\r
- */\r
-static double edgedf(double gx, double gy, double a)\r
-{\r
-    double df, glength, temp, a1;\r
-\r
-    if ((gx == 0) || (gy == 0)) { // Either A) gu or gv are zero, or B) both\r
-        df = 0.5-a;  // Linear approximation is A) correct or B) a fair guess\r
-    } else {\r
-        glength = sqrt(gx*gx + gy*gy);\r
-        if(glength>0) {\r
-            gx = gx/glength;\r
-            gy = gy/glength;\r
-        }\r
-        /* Everything is symmetric wrt sign and transposition,\r
-         * so move to first octant (gx>=0, gy>=0, gx>=gy) to\r
-         * avoid handling all possible edge directions.\r
-         */\r
-        gx = fabs(gx);\r
-        gy = fabs(gy);\r
-        if(gx<gy) {\r
-            temp = gx;\r
-            gx = gy;\r
-            gy = temp;\r
-        }\r
-        a1 = 0.5*gy/gx;\r
-        if (a < a1) { // 0 <= a < a1\r
-            df = 0.5*(gx + gy) - sqrt(2.0*gx*gy*a);\r
-        } else if (a < (1.0-a1)) { // a1 <= a <= 1-a1\r
-            df = (0.5-a)*gx;\r
-        } else { // 1-a1 < a <= 1\r
-            df = -0.5*(gx + gy) + sqrt(2.0*gx*gy*(1.0-a));\r
-        }\r
-    }    \r
-    return df;\r
-}\r
-\r
-static double distaa3(EdtaaImageType *img, double *gximg, double *gyimg, int w, int c, int xc, int yc, int xi, int yi)\r
-{\r
-  double di, df, dx, dy, gx, gy, a;\r
-  int closest;\r
-  \r
-  closest = c-xc-yc*w; // Index to the edge pixel pointed to from c\r
-  a = IMG(closest);    // Grayscale value at the edge pixel\r
-  gx = gximg[closest]; // X gradient component at the edge pixel\r
-  gy = gyimg[closest]; // Y gradient component at the edge pixel\r
-  \r
-  if(a > 1.0) a = 1.0;\r
-  if(a < 0.0) a = 0.0; // Clip grayscale values outside the range [0,1]\r
-  if(a == 0.0) return 1000000.0; // Not an object pixel, return "very far" ("don't know yet")\r
-\r
-  dx = (double)xi;\r
-  dy = (double)yi;\r
-  di = sqrt(dx*dx + dy*dy); // Length of integer vector, like a traditional EDT\r
-  if(di==0) { // Use local gradient only at edges\r
-      // Estimate based on local gradient only\r
-      df = edgedf(gx, gy, a);\r
-  } else {\r
-      // Estimate gradient based on direction to edge (accurate for large di)\r
-      df = edgedf(dx, dy, a);\r
-  }\r
-  return di + df; // Same metric as edtaa2, except at edges (where di=0)\r
-}\r
-\r
-// Shorthand macro: add ubiquitous parameters dist, gx, gy, img and w and call distaa3()\r
-#define DISTAA(c,xc,yc,xi,yi) (distaa3(img, gx, gy, w, c, xc, yc, xi, yi))\r
-\r
-void edtaa3(EdtaaImageType *img, double *gx, double *gy, int w, int h, short *distx, short *disty, double *dist)\r
-{\r
-  int x, y, i, c;\r
-  int offset_u, offset_ur, offset_r, offset_rd,\r
-  offset_d, offset_dl, offset_l, offset_lu;\r
-  double olddist, newdist;\r
-  int cdistx, cdisty, newdistx, newdisty;\r
-  int changed;\r
-  double epsilon = 1e-3;\r
-  double a;\r
-\r
-  /* Initialize index offsets for the current image width */\r
-  offset_u = -w;\r
-  offset_ur = -w+1;\r
-  offset_r = 1;\r
-  offset_rd = w+1;\r
-  offset_d = w;\r
-  offset_dl = w-1;\r
-  offset_l = -1;\r
-  offset_lu = -w-1;\r
-\r
-  /* Initialize the distance images */\r
-  for(i=0; i<w*h; i++) {\r
-    distx[i] = 0; // At first, all pixels point to\r
-    disty[i] = 0; // themselves as the closest known.\r
-    a = IMG(i);\r
-    if(a <= 0.0)\r
-      {\r
-       dist[i]= 1000000.0; // Big value, means "not set yet"\r
-      }\r
-    else if (a<1.0) {\r
-      dist[i] = edgedf(gx[i], gy[i], a); // Gradient-assisted estimate\r
-    }\r
-    else {\r
-      dist[i]= 0.0; // Inside the object\r
-    }\r
-  }\r
-\r
-  /* Perform the transformation */\r
-  do\r
-    {\r
-      changed = 0;\r
-\r
-      /* Scan rows, except first row */\r
-      for(y=1; y<h; y++)\r
-        {\r
-\r
-          /* move index to leftmost pixel of current row */\r
-          i = y*w;\r
-\r
-          /* scan right, propagate distances from above & left */\r
-\r
-          /* Leftmost pixel is special, has no left neighbors */\r
-          olddist = dist[i];\r
-          if(olddist > 0) // If non-zero distance or not set yet\r
-            {\r
-             c = i + offset_u; // Index of candidate for testing\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx;\r
-              newdisty = cdisty+1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_ur;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx-1;\r
-              newdisty = cdisty+1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  changed = 1;\r
-                }\r
-            }\r
-          i++;\r
-\r
-          /* Middle pixels have all neighbors */\r
-          for(x=1; x<w-1; x++, i++)\r
-            {\r
-              olddist = dist[i];\r
-              if(olddist <= 0) continue; // No need to update further\r
-\r
-             c = i+offset_l;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx+1;\r
-              newdisty = cdisty;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_lu;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx+1;\r
-              newdisty = cdisty+1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_u;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx;\r
-              newdisty = cdisty+1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_ur;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx-1;\r
-              newdisty = cdisty+1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  changed = 1;\r
-                }\r
-            }\r
-\r
-          /* Rightmost pixel of row is special, has no right neighbors */\r
-          olddist = dist[i];\r
-          if(olddist > 0) // If not already zero distance\r
-            {\r
-             c = i+offset_l;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx+1;\r
-              newdisty = cdisty;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_lu;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx+1;\r
-              newdisty = cdisty+1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_u;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx;\r
-              newdisty = cdisty+1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  changed = 1;\r
-                }\r
-            }\r
-\r
-          /* Move index to second rightmost pixel of current row. */\r
-          /* Rightmost pixel is skipped, it has no right neighbor. */\r
-          i = y*w + w-2;\r
-\r
-          /* scan left, propagate distance from right */\r
-          for(x=w-2; x>=0; x--, i--)\r
-            {\r
-              olddist = dist[i];\r
-              if(olddist <= 0) continue; // Already zero distance\r
-\r
-             c = i+offset_r;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx-1;\r
-              newdisty = cdisty;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  changed = 1;\r
-                }\r
-            }\r
-        }\r
-      \r
-      /* Scan rows in reverse order, except last row */\r
-      for(y=h-2; y>=0; y--)\r
-        {\r
-          /* move index to rightmost pixel of current row */\r
-          i = y*w + w-1;\r
-\r
-          /* Scan left, propagate distances from below & right */\r
-\r
-          /* Rightmost pixel is special, has no right neighbors */\r
-          olddist = dist[i];\r
-          if(olddist > 0) // If not already zero distance\r
-            {\r
-             c = i+offset_d;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx;\r
-              newdisty = cdisty-1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_dl;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx+1;\r
-              newdisty = cdisty-1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  changed = 1;\r
-                }\r
-            }\r
-          i--;\r
-\r
-          /* Middle pixels have all neighbors */\r
-          for(x=w-2; x>0; x--, i--)\r
-            {\r
-              olddist = dist[i];\r
-              if(olddist <= 0) continue; // Already zero distance\r
-\r
-             c = i+offset_r;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx-1;\r
-              newdisty = cdisty;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_rd;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx-1;\r
-              newdisty = cdisty-1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_d;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx;\r
-              newdisty = cdisty-1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                  dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_dl;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx+1;\r
-              newdisty = cdisty-1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                  dist[i]=newdist;\r
-                  changed = 1;\r
-                }\r
-            }\r
-          /* Leftmost pixel is special, has no left neighbors */\r
-          olddist = dist[i];\r
-          if(olddist > 0) // If not already zero distance\r
-            {\r
-             c = i+offset_r;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx-1;\r
-              newdisty = cdisty;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                  dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_rd;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx-1;\r
-              newdisty = cdisty-1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                 dist[i]=newdist;\r
-                  olddist=newdist;\r
-                  changed = 1;\r
-                }\r
-\r
-             c = i+offset_d;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx;\r
-              newdisty = cdisty-1;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                  dist[i]=newdist;\r
-                  changed = 1;\r
-                }\r
-            }\r
-\r
-          /* Move index to second leftmost pixel of current row. */\r
-          /* Leftmost pixel is skipped, it has no left neighbor. */\r
-          i = y*w + 1;\r
-          for(x=1; x<w; x++, i++)\r
-            {\r
-              /* scan right, propagate distance from left */\r
-              olddist = dist[i];\r
-              if(olddist <= 0) continue; // Already zero distance\r
-\r
-             c = i+offset_l;\r
-             cdistx = distx[c];\r
-             cdisty = disty[c];\r
-              newdistx = cdistx+1;\r
-              newdisty = cdisty;\r
-              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
-              if(newdist < olddist-epsilon)\r
-                {\r
-                  distx[i]=newdistx;\r
-                  disty[i]=newdisty;\r
-                  dist[i]=newdist;\r
-                  changed = 1;\r
-                }\r
-            }\r
-        }\r
-    }\r
-  while(changed); // Sweep until no more updates are made\r
-\r
-  /* The transformation is completed. */\r
-\r
-}\r
-\r
-} // namespace EDTAA\r