Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2014 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkDistanceFieldGen.h"
      9 #include "SkPoint.h"
     10 
     11 struct DFData {
     12     float   fAlpha;      // alpha value of source texel
     13     float   fDistSq;     // distance squared to nearest (so far) edge texel
     14     SkPoint fDistVector; // distance vector to nearest (so far) edge texel
     15 };
     16 
     17 enum NeighborFlags {
     18     kLeft_NeighborFlag        = 0x01,
     19     kRight_NeighborFlag       = 0x02,
     20     kTopLeft_NeighborFlag     = 0x04,
     21     kTop_NeighborFlag         = 0x08,
     22     kTopRight_NeighborFlag    = 0x10,
     23     kBottomLeft_NeighborFlag  = 0x20,
     24     kBottom_NeighborFlag      = 0x40,
     25     kBottomRight_NeighborFlag = 0x80,
     26     kAll_NeighborFlags        = 0xff,
     27 
     28     kNeighborFlagCount        = 8
     29 };
     30 
     31 // We treat an "edge" as a place where we cross from >=128 to <128, or vice versa, or
     32 // where we have two non-zero pixels that are <128.
     33 // 'neighborFlags' is used to limit the directions in which we test to avoid indexing
     34 // outside of the image
     35 static bool found_edge(const unsigned char* imagePtr, int width, int neighborFlags) {
     36     // the order of these should match the neighbor flags above
     37     const int kNum8ConnectedNeighbors = 8;
     38     const int offsets[8] = {-1, 1, -width-1, -width, -width+1, width-1, width, width+1 };
     39     SkASSERT(kNum8ConnectedNeighbors == kNeighborFlagCount);
     40 
     41     // search for an edge
     42     unsigned char currVal = *imagePtr;
     43     unsigned char currCheck = (currVal >> 7);
     44     for (int i = 0; i < kNum8ConnectedNeighbors; ++i) {
     45         unsigned char neighborVal;
     46         if ((1 << i) & neighborFlags) {
     47             const unsigned char* checkPtr = imagePtr + offsets[i];
     48             neighborVal = *checkPtr;
     49         } else {
     50             neighborVal = 0;
     51         }
     52         unsigned char neighborCheck = (neighborVal >> 7);
     53         SkASSERT(currCheck == 0 || currCheck == 1);
     54         SkASSERT(neighborCheck == 0 || neighborCheck == 1);
     55         // if sharp transition
     56         if (currCheck != neighborCheck ||
     57             // or both <128 and >0
     58             (!currCheck && !neighborCheck && currVal && neighborVal)) {
     59             return true;
     60         }
     61     }
     62 
     63     return false;
     64 }
     65 
     66 static void init_glyph_data(DFData* data, unsigned char* edges, const unsigned char* image,
     67                             int dataWidth, int dataHeight,
     68                             int imageWidth, int imageHeight,
     69                             int pad) {
     70     data += pad*dataWidth;
     71     data += pad;
     72     edges += (pad*dataWidth + pad);
     73 
     74     for (int j = 0; j < imageHeight; ++j) {
     75         for (int i = 0; i < imageWidth; ++i) {
     76             if (255 == *image) {
     77                 data->fAlpha = 1.0f;
     78             } else {
     79                 data->fAlpha = (*image)*0.00392156862f;  // 1/255
     80             }
     81             int checkMask = kAll_NeighborFlags;
     82             if (i == 0) {
     83                 checkMask &= ~(kLeft_NeighborFlag|kTopLeft_NeighborFlag|kBottomLeft_NeighborFlag);
     84             }
     85             if (i == imageWidth-1) {
     86                 checkMask &= ~(kRight_NeighborFlag|kTopRight_NeighborFlag|kBottomRight_NeighborFlag);
     87             }
     88             if (j == 0) {
     89                 checkMask &= ~(kTopLeft_NeighborFlag|kTop_NeighborFlag|kTopRight_NeighborFlag);
     90             }
     91             if (j == imageHeight-1) {
     92                 checkMask &= ~(kBottomLeft_NeighborFlag|kBottom_NeighborFlag|kBottomRight_NeighborFlag);
     93             }
     94             if (found_edge(image, imageWidth, checkMask)) {
     95                 *edges = 255;  // using 255 makes for convenient debug rendering
     96             }
     97             ++data;
     98             ++image;
     99             ++edges;
    100         }
    101         data += 2*pad;
    102         edges += 2*pad;
    103     }
    104 }
    105 
    106 // from Gustavson (2011)
    107 // computes the distance to an edge given an edge normal vector and a pixel's alpha value
    108 // assumes that direction has been pre-normalized
    109 static float edge_distance(const SkPoint& direction, float alpha) {
    110     float dx = direction.fX;
    111     float dy = direction.fY;
    112     float distance;
    113     if (SkScalarNearlyZero(dx) || SkScalarNearlyZero(dy)) {
    114         distance = 0.5f - alpha;
    115     } else {
    116         // this is easier if we treat the direction as being in the first octant
    117         // (other octants are symmetrical)
    118         dx = SkScalarAbs(dx);
    119         dy = SkScalarAbs(dy);
    120         if (dx < dy) {
    121             SkTSwap(dx, dy);
    122         }
    123 
    124         // a1 = 0.5*dy/dx is the smaller fractional area chopped off by the edge
    125         // to avoid the divide, we just consider the numerator
    126         float a1num = 0.5f*dy;
    127 
    128         // we now compute the approximate distance, depending where the alpha falls
    129         // relative to the edge fractional area
    130 
    131         // if 0 <= alpha < a1
    132         if (alpha*dx < a1num) {
    133             // TODO: find a way to do this without square roots?
    134             distance = 0.5f*(dx + dy) - SkScalarSqrt(2.0f*dx*dy*alpha);
    135         // if a1 <= alpha <= 1 - a1
    136         } else if (alpha*dx < (dx - a1num)) {
    137             distance = (0.5f - alpha)*dx;
    138         // if 1 - a1 < alpha <= 1
    139         } else {
    140             // TODO: find a way to do this without square roots?
    141             distance = -0.5f*(dx + dy) + SkScalarSqrt(2.0f*dx*dy*(1.0f - alpha));
    142         }
    143     }
    144 
    145     return distance;
    146 }
    147 
    148 static void init_distances(DFData* data, unsigned char* edges, int width, int height) {
    149     // skip one pixel border
    150     DFData* currData = data;
    151     DFData* prevData = data - width;
    152     DFData* nextData = data + width;
    153 
    154     for (int j = 0; j < height; ++j) {
    155         for (int i = 0; i < width; ++i) {
    156             if (*edges) {
    157                 // we should not be in the one-pixel outside band
    158                 SkASSERT(i > 0 && i < width-1 && j > 0 && j < height-1);
    159                 // gradient will point from low to high
    160                 // +y is down in this case
    161                 // i.e., if you're outside, gradient points towards edge
    162                 // if you're inside, gradient points away from edge
    163                 SkPoint currGrad;
    164                 currGrad.fX = (prevData+1)->fAlpha - (prevData-1)->fAlpha
    165                              + SK_ScalarSqrt2*(currData+1)->fAlpha
    166                              - SK_ScalarSqrt2*(currData-1)->fAlpha
    167                              + (nextData+1)->fAlpha - (nextData-1)->fAlpha;
    168                 currGrad.fY = (nextData-1)->fAlpha - (prevData-1)->fAlpha
    169                              + SK_ScalarSqrt2*nextData->fAlpha
    170                              - SK_ScalarSqrt2*prevData->fAlpha
    171                              + (nextData+1)->fAlpha - (prevData+1)->fAlpha;
    172                 currGrad.setLengthFast(1.0f);
    173 
    174                 // init squared distance to edge and distance vector
    175                 float dist = edge_distance(currGrad, currData->fAlpha);
    176                 currGrad.scale(dist, &currData->fDistVector);
    177                 currData->fDistSq = dist*dist;
    178             } else {
    179                 // init distance to "far away"
    180                 currData->fDistSq = 2000000.f;
    181                 currData->fDistVector.fX = 1000.f;
    182                 currData->fDistVector.fY = 1000.f;
    183             }
    184             ++currData;
    185             ++prevData;
    186             ++nextData;
    187             ++edges;
    188         }
    189     }
    190 }
    191 
    192 // Danielsson's 8SSEDT
    193 
    194 // first stage forward pass
    195 // (forward in Y, forward in X)
    196 static void F1(DFData* curr, int width) {
    197     // upper left
    198     DFData* check = curr - width-1;
    199     SkPoint distVec = check->fDistVector;
    200     float distSq = check->fDistSq - 2.0f*(distVec.fX + distVec.fY - 1.0f);
    201     if (distSq < curr->fDistSq) {
    202         distVec.fX -= 1.0f;
    203         distVec.fY -= 1.0f;
    204         curr->fDistSq = distSq;
    205         curr->fDistVector = distVec;
    206     }
    207 
    208     // up
    209     check = curr - width;
    210     distVec = check->fDistVector;
    211     distSq = check->fDistSq - 2.0f*distVec.fY + 1.0f;
    212     if (distSq < curr->fDistSq) {
    213         distVec.fY -= 1.0f;
    214         curr->fDistSq = distSq;
    215         curr->fDistVector = distVec;
    216     }
    217 
    218     // upper right
    219     check = curr - width+1;
    220     distVec = check->fDistVector;
    221     distSq = check->fDistSq + 2.0f*(distVec.fX - distVec.fY + 1.0f);
    222     if (distSq < curr->fDistSq) {
    223         distVec.fX += 1.0f;
    224         distVec.fY -= 1.0f;
    225         curr->fDistSq = distSq;
    226         curr->fDistVector = distVec;
    227     }
    228 
    229     // left
    230     check = curr - 1;
    231     distVec = check->fDistVector;
    232     distSq = check->fDistSq - 2.0f*distVec.fX + 1.0f;
    233     if (distSq < curr->fDistSq) {
    234         distVec.fX -= 1.0f;
    235         curr->fDistSq = distSq;
    236         curr->fDistVector = distVec;
    237     }
    238 }
    239 
    240 // second stage forward pass
    241 // (forward in Y, backward in X)
    242 static void F2(DFData* curr, int width) {
    243     // right
    244     DFData* check = curr + 1;
    245     float distSq = check->fDistSq;
    246     SkPoint distVec = check->fDistVector;
    247     distSq = check->fDistSq + 2.0f*distVec.fX + 1.0f;
    248     if (distSq < curr->fDistSq) {
    249         distVec.fX += 1.0f;
    250         curr->fDistSq = distSq;
    251         curr->fDistVector = distVec;
    252     }
    253 }
    254 
    255 // first stage backward pass
    256 // (backward in Y, forward in X)
    257 static void B1(DFData* curr, int width) {
    258     // left
    259     DFData* check = curr - 1;
    260     SkPoint distVec = check->fDistVector;
    261     float distSq = check->fDistSq - 2.0f*distVec.fX + 1.0f;
    262     if (distSq < curr->fDistSq) {
    263         distVec.fX -= 1.0f;
    264         curr->fDistSq = distSq;
    265         curr->fDistVector = distVec;
    266     }
    267 }
    268 
    269 // second stage backward pass
    270 // (backward in Y, backwards in X)
    271 static void B2(DFData* curr, int width) {
    272     // right
    273     DFData* check = curr + 1;
    274     SkPoint distVec = check->fDistVector;
    275     float distSq = check->fDistSq + 2.0f*distVec.fX + 1.0f;
    276     if (distSq < curr->fDistSq) {
    277         distVec.fX += 1.0f;
    278         curr->fDistSq = distSq;
    279         curr->fDistVector = distVec;
    280     }
    281 
    282     // bottom left
    283     check = curr + width-1;
    284     distVec = check->fDistVector;
    285     distSq = check->fDistSq - 2.0f*(distVec.fX - distVec.fY - 1.0f);
    286     if (distSq < curr->fDistSq) {
    287         distVec.fX -= 1.0f;
    288         distVec.fY += 1.0f;
    289         curr->fDistSq = distSq;
    290         curr->fDistVector = distVec;
    291     }
    292 
    293     // bottom
    294     check = curr + width;
    295     distVec = check->fDistVector;
    296     distSq = check->fDistSq + 2.0f*distVec.fY + 1.0f;
    297     if (distSq < curr->fDistSq) {
    298         distVec.fY += 1.0f;
    299         curr->fDistSq = distSq;
    300         curr->fDistVector = distVec;
    301     }
    302 
    303     // bottom right
    304     check = curr + width+1;
    305     distVec = check->fDistVector;
    306     distSq = check->fDistSq + 2.0f*(distVec.fX + distVec.fY + 1.0f);
    307     if (distSq < curr->fDistSq) {
    308         distVec.fX += 1.0f;
    309         distVec.fY += 1.0f;
    310         curr->fDistSq = distSq;
    311         curr->fDistVector = distVec;
    312     }
    313 }
    314 
    315 // enable this to output edge data rather than the distance field
    316 #define DUMP_EDGE 0
    317 
    318 #if !DUMP_EDGE
    319 static unsigned char pack_distance_field_val(float dist, float distanceMagnitude) {
    320     if (dist <= -distanceMagnitude) {
    321         return 255;
    322     } else if (dist > distanceMagnitude) {
    323         return 0;
    324     } else {
    325         return (unsigned char)((distanceMagnitude-dist)*128.0f/distanceMagnitude);
    326     }
    327 }
    328 #endif
    329 
    330 // assumes a padded 8-bit image and distance field
    331 // width and height are the original width and height of the image
    332 static bool generate_distance_field_from_image(unsigned char* distanceField,
    333                                                const unsigned char* copyPtr,
    334                                                int width, int height) {
    335     SkASSERT(distanceField);
    336     SkASSERT(copyPtr);
    337 
    338     // we expand our temp data by one more on each side to simplify
    339     // the scanning code -- will always be treated as infinitely far away
    340     int pad = SK_DistanceFieldPad + 1;
    341 
    342     // set params for distance field data
    343     int dataWidth = width + 2*pad;
    344     int dataHeight = height + 2*pad;
    345 
    346     // create temp data
    347     size_t dataSize = dataWidth*dataHeight*sizeof(DFData);
    348     SkAutoSMalloc<1024> dfStorage(dataSize);
    349     DFData* dataPtr = (DFData*) dfStorage.get();
    350     sk_bzero(dataPtr, dataSize);
    351 
    352     SkAutoSMalloc<1024> edgeStorage(dataWidth*dataHeight*sizeof(char));
    353     unsigned char* edgePtr = (unsigned char*) edgeStorage.get();
    354     sk_bzero(edgePtr, dataWidth*dataHeight*sizeof(char));
    355 
    356     // copy glyph into distance field storage
    357     init_glyph_data(dataPtr, edgePtr, copyPtr,
    358                     dataWidth, dataHeight,
    359                     width+2, height+2, SK_DistanceFieldPad);
    360 
    361     // create initial distance data, particularly at edges
    362     init_distances(dataPtr, edgePtr, dataWidth, dataHeight);
    363 
    364     // now perform Euclidean distance transform to propagate distances
    365 
    366     // forwards in y
    367     DFData* currData = dataPtr+dataWidth+1; // skip outer buffer
    368     unsigned char* currEdge = edgePtr+dataWidth+1;
    369     for (int j = 1; j < dataHeight-1; ++j) {
    370         // forwards in x
    371         for (int i = 1; i < dataWidth-1; ++i) {
    372             // don't need to calculate distance for edge pixels
    373             if (!*currEdge) {
    374                 F1(currData, dataWidth);
    375             }
    376             ++currData;
    377             ++currEdge;
    378         }
    379 
    380         // backwards in x
    381         --currData; // reset to end
    382         --currEdge;
    383         for (int i = 1; i < dataWidth-1; ++i) {
    384             // don't need to calculate distance for edge pixels
    385             if (!*currEdge) {
    386                 F2(currData, dataWidth);
    387             }
    388             --currData;
    389             --currEdge;
    390         }
    391 
    392         currData += dataWidth+1;
    393         currEdge += dataWidth+1;
    394     }
    395 
    396     // backwards in y
    397     currData = dataPtr+dataWidth*(dataHeight-2) - 1; // skip outer buffer
    398     currEdge = edgePtr+dataWidth*(dataHeight-2) - 1;
    399     for (int j = 1; j < dataHeight-1; ++j) {
    400         // forwards in x
    401         for (int i = 1; i < dataWidth-1; ++i) {
    402             // don't need to calculate distance for edge pixels
    403             if (!*currEdge) {
    404                 B1(currData, dataWidth);
    405             }
    406             ++currData;
    407             ++currEdge;
    408         }
    409 
    410         // backwards in x
    411         --currData; // reset to end
    412         --currEdge;
    413         for (int i = 1; i < dataWidth-1; ++i) {
    414             // don't need to calculate distance for edge pixels
    415             if (!*currEdge) {
    416                 B2(currData, dataWidth);
    417             }
    418             --currData;
    419             --currEdge;
    420         }
    421 
    422         currData -= dataWidth-1;
    423         currEdge -= dataWidth-1;
    424     }
    425 
    426     // copy results to final distance field data
    427     currData = dataPtr + dataWidth+1;
    428     currEdge = edgePtr + dataWidth+1;
    429     unsigned char *dfPtr = distanceField;
    430     for (int j = 1; j < dataHeight-1; ++j) {
    431         for (int i = 1; i < dataWidth-1; ++i) {
    432 #if DUMP_EDGE
    433             float alpha = currData->fAlpha;
    434             float edge = 0.0f;
    435             if (*currEdge) {
    436                 edge = 0.25f;
    437             }
    438             // blend with original image
    439             float result = alpha + (1.0f-alpha)*edge;
    440             unsigned char val = sk_float_round2int(255*result);
    441             *dfPtr++ = val;
    442 #else
    443             float dist;
    444             if (currData->fAlpha > 0.5f) {
    445                 dist = -SkScalarSqrt(currData->fDistSq);
    446             } else {
    447                 dist = SkScalarSqrt(currData->fDistSq);
    448             }
    449             *dfPtr++ = pack_distance_field_val(dist, (float)SK_DistanceFieldMagnitude);
    450 #endif
    451             ++currData;
    452             ++currEdge;
    453         }
    454         currData += 2;
    455         currEdge += 2;
    456     }
    457 
    458     return true;
    459 }
    460 
    461 // assumes an 8-bit image and distance field
    462 bool SkGenerateDistanceFieldFromA8Image(unsigned char* distanceField,
    463                                         const unsigned char* image,
    464                                         int width, int height, size_t rowBytes) {
    465     SkASSERT(distanceField);
    466     SkASSERT(image);
    467 
    468     // create temp data
    469     SkAutoSMalloc<1024> copyStorage((width+2)*(height+2)*sizeof(char));
    470     unsigned char* copyPtr = (unsigned char*) copyStorage.get();
    471 
    472     // we copy our source image into a padded copy to ensure we catch edge transitions
    473     // around the outside
    474     const unsigned char* currSrcScanLine = image;
    475     sk_bzero(copyPtr, (width+2)*sizeof(char));
    476     unsigned char* currDestPtr = copyPtr + width + 2;
    477     for (int i = 0; i < height; ++i) {
    478         *currDestPtr++ = 0;
    479         memcpy(currDestPtr, currSrcScanLine, rowBytes);
    480         currSrcScanLine += rowBytes;
    481         currDestPtr += width;
    482         *currDestPtr++ = 0;
    483     }
    484     sk_bzero(currDestPtr, (width+2)*sizeof(char));
    485 
    486     return generate_distance_field_from_image(distanceField, copyPtr, width, height);
    487 }
    488 
    489 // assumes a 1-bit image and 8-bit distance field
    490 bool SkGenerateDistanceFieldFromBWImage(unsigned char* distanceField,
    491                                         const unsigned char* image,
    492                                         int width, int height, size_t rowBytes) {
    493     SkASSERT(distanceField);
    494     SkASSERT(image);
    495 
    496     // create temp data
    497     SkAutoSMalloc<1024> copyStorage((width+2)*(height+2)*sizeof(char));
    498     unsigned char* copyPtr = (unsigned char*) copyStorage.get();
    499 
    500     // we copy our source image into a padded copy to ensure we catch edge transitions
    501     // around the outside
    502     const unsigned char* currSrcScanLine = image;
    503     sk_bzero(copyPtr, (width+2)*sizeof(char));
    504     unsigned char* currDestPtr = copyPtr + width + 2;
    505     for (int i = 0; i < height; ++i) {
    506         *currDestPtr++ = 0;
    507         int rowWritesLeft = width;
    508         const unsigned char *maskPtr = currSrcScanLine;
    509         while (rowWritesLeft > 0) {
    510             unsigned mask = *maskPtr++;
    511             for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) {
    512                 *currDestPtr++ = (mask & (1 << i)) ? 0xff : 0;
    513             }
    514         }
    515         currSrcScanLine += rowBytes;
    516         *currDestPtr++ = 0;
    517     }
    518     sk_bzero(currDestPtr, (width+2)*sizeof(char));
    519 
    520     return generate_distance_field_from_image(distanceField, copyPtr, width, height);
    521 }
    522