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