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
11struct 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
17enum 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
35static 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
66static 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
109static 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
148static 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)
196static 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)
242static 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)
257static 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)
271static 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
319static 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
332static 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
462bool SkGenerateDistanceFieldFromA8Image(unsigned char* distanceField,
463                                        const unsigned char* image,
464                                        int width, int height, int 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
490bool SkGenerateDistanceFieldFromBWImage(unsigned char* distanceField,
491                                        const unsigned char* image,
492                                        int width, int height, int 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