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 template <int distanceMagnitude>
    320 static unsigned char pack_distance_field_val(float dist) {
    321     // The distance field is constructed as unsigned char values, so that the zero value is at 128,
    322     // Beside 128, we have 128 values in range [0, 128), but only 127 values in range (128, 255].
    323     // So we multiply distanceMagnitude by 127/128 at the latter range to avoid overflow.
    324     dist = SkScalarPin(-dist, -distanceMagnitude, distanceMagnitude * 127.0f / 128.0f);
    325 
    326     // Scale into the positive range for unsigned distance.
    327     dist += distanceMagnitude;
    328 
    329     // Scale into unsigned char range.
    330     // Round to place negative and positive values as equally as possible around 128
    331     // (which represents zero).
    332     return (unsigned char)SkScalarRoundToInt(dist / (2 * distanceMagnitude) * 256.0f);
    333 }
    334 #endif
    335 
    336 // assumes a padded 8-bit image and distance field
    337 // width and height are the original width and height of the image
    338 static bool generate_distance_field_from_image(unsigned char* distanceField,
    339                                                const unsigned char* copyPtr,
    340                                                int width, int height) {
    341     SkASSERT(distanceField);
    342     SkASSERT(copyPtr);
    343 
    344     // we expand our temp data by one more on each side to simplify
    345     // the scanning code -- will always be treated as infinitely far away
    346     int pad = SK_DistanceFieldPad + 1;
    347 
    348     // set params for distance field data
    349     int dataWidth = width + 2*pad;
    350     int dataHeight = height + 2*pad;
    351 
    352     // create zeroed temp DFData+edge storage
    353     SkAutoFree storage(sk_calloc_throw(dataWidth*dataHeight*(sizeof(DFData) + 1)));
    354     DFData*        dataPtr = (DFData*)storage.get();
    355     unsigned char* edgePtr = (unsigned char*)storage.get() + dataWidth*dataHeight*sizeof(DFData);
    356 
    357     // copy glyph into distance field storage
    358     init_glyph_data(dataPtr, edgePtr, copyPtr,
    359                     dataWidth, dataHeight,
    360                     width+2, height+2, SK_DistanceFieldPad);
    361 
    362     // create initial distance data, particularly at edges
    363     init_distances(dataPtr, edgePtr, dataWidth, dataHeight);
    364 
    365     // now perform Euclidean distance transform to propagate distances
    366 
    367     // forwards in y
    368     DFData* currData = dataPtr+dataWidth+1; // skip outer buffer
    369     unsigned char* currEdge = edgePtr+dataWidth+1;
    370     for (int j = 1; j < dataHeight-1; ++j) {
    371         // forwards in x
    372         for (int i = 1; i < dataWidth-1; ++i) {
    373             // don't need to calculate distance for edge pixels
    374             if (!*currEdge) {
    375                 F1(currData, dataWidth);
    376             }
    377             ++currData;
    378             ++currEdge;
    379         }
    380 
    381         // backwards in x
    382         --currData; // reset to end
    383         --currEdge;
    384         for (int i = 1; i < dataWidth-1; ++i) {
    385             // don't need to calculate distance for edge pixels
    386             if (!*currEdge) {
    387                 F2(currData, dataWidth);
    388             }
    389             --currData;
    390             --currEdge;
    391         }
    392 
    393         currData += dataWidth+1;
    394         currEdge += dataWidth+1;
    395     }
    396 
    397     // backwards in y
    398     currData = dataPtr+dataWidth*(dataHeight-2) - 1; // skip outer buffer
    399     currEdge = edgePtr+dataWidth*(dataHeight-2) - 1;
    400     for (int j = 1; j < dataHeight-1; ++j) {
    401         // forwards in x
    402         for (int i = 1; i < dataWidth-1; ++i) {
    403             // don't need to calculate distance for edge pixels
    404             if (!*currEdge) {
    405                 B1(currData, dataWidth);
    406             }
    407             ++currData;
    408             ++currEdge;
    409         }
    410 
    411         // backwards in x
    412         --currData; // reset to end
    413         --currEdge;
    414         for (int i = 1; i < dataWidth-1; ++i) {
    415             // don't need to calculate distance for edge pixels
    416             if (!*currEdge) {
    417                 B2(currData, dataWidth);
    418             }
    419             --currData;
    420             --currEdge;
    421         }
    422 
    423         currData -= dataWidth-1;
    424         currEdge -= dataWidth-1;
    425     }
    426 
    427     // copy results to final distance field data
    428     currData = dataPtr + dataWidth+1;
    429     currEdge = edgePtr + dataWidth+1;
    430     unsigned char *dfPtr = distanceField;
    431     for (int j = 1; j < dataHeight-1; ++j) {
    432         for (int i = 1; i < dataWidth-1; ++i) {
    433 #if DUMP_EDGE
    434             float alpha = currData->fAlpha;
    435             float edge = 0.0f;
    436             if (*currEdge) {
    437                 edge = 0.25f;
    438             }
    439             // blend with original image
    440             float result = alpha + (1.0f-alpha)*edge;
    441             unsigned char val = sk_float_round2int(255*result);
    442             *dfPtr++ = val;
    443 #else
    444             float dist;
    445             if (currData->fAlpha > 0.5f) {
    446                 dist = -SkScalarSqrt(currData->fDistSq);
    447             } else {
    448                 dist = SkScalarSqrt(currData->fDistSq);
    449             }
    450             *dfPtr++ = pack_distance_field_val<SK_DistanceFieldMagnitude>(dist);
    451 #endif
    452             ++currData;
    453             ++currEdge;
    454         }
    455         currData += 2;
    456         currEdge += 2;
    457     }
    458 
    459     return true;
    460 }
    461 
    462 // assumes an 8-bit image and distance field
    463 bool SkGenerateDistanceFieldFromA8Image(unsigned char* distanceField,
    464                                         const unsigned char* image,
    465                                         int width, int height, size_t rowBytes) {
    466     SkASSERT(distanceField);
    467     SkASSERT(image);
    468 
    469     // create temp data
    470     SkAutoSMalloc<1024> copyStorage((width+2)*(height+2)*sizeof(char));
    471     unsigned char* copyPtr = (unsigned char*) copyStorage.get();
    472 
    473     // we copy our source image into a padded copy to ensure we catch edge transitions
    474     // around the outside
    475     const unsigned char* currSrcScanLine = image;
    476     sk_bzero(copyPtr, (width+2)*sizeof(char));
    477     unsigned char* currDestPtr = copyPtr + width + 2;
    478     for (int i = 0; i < height; ++i) {
    479         *currDestPtr++ = 0;
    480         memcpy(currDestPtr, currSrcScanLine, rowBytes);
    481         currSrcScanLine += rowBytes;
    482         currDestPtr += width;
    483         *currDestPtr++ = 0;
    484     }
    485     sk_bzero(currDestPtr, (width+2)*sizeof(char));
    486 
    487     return generate_distance_field_from_image(distanceField, copyPtr, width, height);
    488 }
    489 
    490 // assumes a 1-bit image and 8-bit distance field
    491 bool SkGenerateDistanceFieldFromBWImage(unsigned char* distanceField,
    492                                         const unsigned char* image,
    493                                         int width, int height, size_t rowBytes) {
    494     SkASSERT(distanceField);
    495     SkASSERT(image);
    496 
    497     // create temp data
    498     SkAutoSMalloc<1024> copyStorage((width+2)*(height+2)*sizeof(char));
    499     unsigned char* copyPtr = (unsigned char*) copyStorage.get();
    500 
    501     // we copy our source image into a padded copy to ensure we catch edge transitions
    502     // around the outside
    503     const unsigned char* currSrcScanLine = image;
    504     sk_bzero(copyPtr, (width+2)*sizeof(char));
    505     unsigned char* currDestPtr = copyPtr + width + 2;
    506     for (int i = 0; i < height; ++i) {
    507         *currDestPtr++ = 0;
    508         int rowWritesLeft = width;
    509         const unsigned char *maskPtr = currSrcScanLine;
    510         while (rowWritesLeft > 0) {
    511             unsigned mask = *maskPtr++;
    512             for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) {
    513                 *currDestPtr++ = (mask & (1 << i)) ? 0xff : 0;
    514             }
    515         }
    516         currSrcScanLine += rowBytes;
    517         *currDestPtr++ = 0;
    518     }
    519     sk_bzero(currDestPtr, (width+2)*sizeof(char));
    520 
    521     return generate_distance_field_from_image(distanceField, copyPtr, width, height);
    522 }
    523