OLD | NEW |
---|---|
(Empty) | |
1 /* | |
robertphillips
2014/03/10 15:48:11
2014?
jvanverth1
2014/03/10 18:42:06
Done.
| |
2 * Copyright 2013 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 // We treat an "edge" as a place where we cross from a texel >= 128 to a texel < 128, | |
18 // or vice versa. This means we just need to check if the MSBs are different. | |
19 static bool found_edge(unsigned char* imagePtr, int width) { | |
20 const int offsets[8] = {-1, 1, -width-1, -width, -width+1, width-1, width, w idth+1 }; | |
21 | |
22 // search for an edge | |
23 int checkVal = *imagePtr >> 7; | |
24 for (int i = 0; i < 8; ++i) { | |
25 unsigned char* checkPtr = imagePtr + offsets[i]; | |
26 if (checkVal ^ (*checkPtr >> 7)) { | |
27 return true; | |
28 } | |
29 } | |
30 | |
31 return false; | |
32 } | |
33 | |
34 static void init_glyph_data(DFData* data, char* edges, unsigned char* image, | |
35 int dataWidth, int dataHeight, | |
36 int imageWidth, int imageHeight, | |
37 int pad) { | |
38 data += pad*dataWidth; | |
39 data += pad; | |
40 edges += (pad*dataWidth + pad); | |
41 | |
42 for (int j = 0; j < imageHeight; ++j) { | |
43 for (int i = 0; i < imageWidth; ++i) { | |
44 if (255 == *image) { | |
45 data->fAlpha = 1.0f; | |
46 } else { | |
47 data->fAlpha = (*image)*0.00392156862f; // 1/255 | |
48 } | |
49 if (i > 0 && i < imageWidth-1 && j > 0 && j < imageHeight-1 && | |
50 found_edge(image, imageWidth)) { | |
51 *edges = 255; // using 255 makes for convenient debug rendering | |
52 } | |
53 ++data; | |
54 ++image; | |
55 ++edges; | |
56 } | |
57 data += 2*pad; | |
58 edges += 2*pad; | |
59 } | |
60 } | |
61 | |
62 // from Gustavson (2011) | |
63 // computes the distance to an edge given an edge normal vector and a pixel's al pha value | |
64 // assumes that direction has been pre-normalized | |
65 static float edge_distance(const SkPoint& direction, float alpha) { | |
66 float dx = direction.fX; | |
67 float dy = direction.fY; | |
68 float distance; | |
69 if (SkScalarNearlyZero(dx) || SkScalarNearlyZero(dy)) { | |
70 distance = 0.5f - alpha; | |
71 } else { | |
72 // this is easier if we treat the direction as being in the first octant | |
73 // (other octants are symmetrical) | |
74 dx = fabs(dx); | |
75 dy = fabs(dy); | |
76 if (dx < dy) { | |
77 float temp = dx; | |
78 dx = dy; | |
79 dy = temp; | |
80 } | |
81 | |
82 // a1 = 0.5*dy/dx is the smaller fractional area chopped off by the edge | |
83 // to avoid the divide, we just consider the numerator | |
84 float a1num = 0.5f*dy; | |
85 | |
86 // we now compute the approximate distance, depending where the alpha fa lls | |
87 // relative to the edge fractional area | |
88 | |
89 // if 0 <= alpha < a1 | |
90 if (alpha*dx < a1num) { | |
91 // TODO: find a way to do this without square roots? | |
92 distance = 0.5f*(dx + dy) - SkScalarSqrt(2.0f*dx*dy*alpha); | |
93 // if a1 <= alpha <= 1 - a1 | |
94 } else if (alpha*dx < (dx - a1num)) { | |
95 distance = (0.5f - alpha)*dx; | |
96 // if 1 - a1 < alpha <= 1 | |
97 } else { | |
98 // TODO: find a way to do this without square roots? | |
99 distance = -0.5f*(dx + dy) + SkScalarSqrt(2.0f*dx*dy*(1.0f - alpha)) ; | |
100 } | |
101 } | |
102 | |
103 return distance; | |
104 } | |
105 | |
106 static void init_distances(DFData* data, char* edges, int width, int height) { | |
107 // skip one pixel border | |
108 DFData* currData = data; | |
109 DFData* prevData = data - width; | |
110 DFData* nextData = data + width; | |
111 | |
112 for (int j = 0; j < height; ++j) { | |
113 for (int i = 0; i < width; ++i) { | |
114 if (*edges) { | |
115 // we should not be in the one-pixel outside band | |
116 SkASSERT(i > 0 && i < width-1 && j > 0 && j < height-1); | |
117 // gradient will point from low to high | |
118 // +y is down in this case | |
119 // i.e., if you're outside, gradient points towards edge | |
120 // if you're inside, gradient points away from edge | |
121 SkPoint currGrad; | |
122 currGrad.fX = (prevData+1)->fAlpha - (prevData-1)->fAlpha | |
123 + SK_ScalarSqrt2*(currData+1)->fAlpha | |
124 - SK_ScalarSqrt2*(currData-1)->fAlpha | |
125 + (nextData+1)->fAlpha - (nextData-1)->fAlpha; | |
126 currGrad.fY = (nextData-1)->fAlpha - (prevData-1)->fAlpha | |
127 + SK_ScalarSqrt2*nextData->fAlpha | |
128 - SK_ScalarSqrt2*prevData->fAlpha | |
129 + (nextData+1)->fAlpha - (prevData+1)->fAlpha; | |
130 currGrad.setLengthFast(1.0f); | |
131 | |
132 // init squared distance to edge and distance vector | |
133 float dist = edge_distance(currGrad, currData->fAlpha); | |
134 currGrad.scale(dist, &currData->fDistVector); | |
135 currData->fDistSq = dist*dist; | |
136 } else { | |
137 // init distance to "far away" | |
138 currData->fDistSq = 2000000.f; | |
139 currData->fDistVector.fX = 1000.f; | |
140 currData->fDistVector.fY = 1000.f; | |
141 } | |
142 ++currData; | |
143 ++prevData; | |
144 ++nextData; | |
145 ++edges; | |
146 } | |
147 } | |
148 } | |
149 | |
150 // Danielsson's 8SSEDT | |
151 | |
152 // first stage forward pass | |
153 // (forward in Y, forward in X) | |
154 static void F1(DFData* curr, int width) { | |
155 // upper left | |
156 DFData* check = curr - width-1; | |
157 SkPoint distVec = check->fDistVector; | |
158 float distSq = check->fDistSq - 2.0f*(distVec.fX + distVec.fY - 1.0f); | |
159 if (distSq < curr->fDistSq) { | |
160 distVec.fX -= 1.0f; | |
161 distVec.fY -= 1.0f; | |
162 curr->fDistSq = distSq; | |
163 curr->fDistVector = distVec; | |
164 } | |
165 | |
166 // up | |
167 check = curr - width; | |
168 distVec = check->fDistVector; | |
169 distSq = check->fDistSq - 2.0f*distVec.fY + 1.0f; | |
170 if (distSq < curr->fDistSq) { | |
171 distVec.fY -= 1.0f; | |
172 curr->fDistSq = distSq; | |
173 curr->fDistVector = distVec; | |
174 } | |
175 | |
176 // upper right | |
177 check = curr - width+1; | |
178 distVec = check->fDistVector; | |
179 distSq = check->fDistSq + 2.0f*(distVec.fX - distVec.fY + 1.0f); | |
180 if (distSq < curr->fDistSq) { | |
181 distVec.fX += 1.0f; | |
182 distVec.fY -= 1.0f; | |
183 curr->fDistSq = distSq; | |
184 curr->fDistVector = distVec; | |
185 } | |
186 | |
187 // left | |
188 check = curr - 1; | |
189 distVec = check->fDistVector; | |
190 distSq = check->fDistSq - 2.0f*distVec.fX + 1.0f; | |
191 if (distSq < curr->fDistSq) { | |
192 distVec.fX -= 1.0f; | |
193 curr->fDistSq = distSq; | |
194 curr->fDistVector = distVec; | |
195 } | |
196 } | |
197 | |
198 // second stage forward pass | |
199 // (forward in Y, backward in X) | |
200 static void F2(DFData* curr, int width) { | |
201 // right | |
202 DFData* check = curr + 1; | |
203 float distSq = check->fDistSq; | |
204 SkPoint distVec = check->fDistVector; | |
205 distSq = check->fDistSq + 2.0f*distVec.fX + 1.0f; | |
206 if (distSq < curr->fDistSq) { | |
207 distVec.fX += 1.0f; | |
208 curr->fDistSq = distSq; | |
209 curr->fDistVector = distVec; | |
210 } | |
211 } | |
212 | |
213 // first stage backward pass | |
214 // (backward in Y, forward in X) | |
215 static void B1(DFData* curr, int width) { | |
216 // left | |
217 DFData* check = curr - 1; | |
218 SkPoint distVec = check->fDistVector; | |
219 float distSq = check->fDistSq - 2.0f*distVec.fX + 1.0f; | |
220 if (distSq < curr->fDistSq) { | |
221 distVec.fX -= 1.0f; | |
222 curr->fDistSq = distSq; | |
223 curr->fDistVector = distVec; | |
224 } | |
225 } | |
226 | |
227 // second stage backward pass | |
228 // (backward in Y, backwards in X) | |
229 static void B2(DFData* curr, int width) { | |
230 // right | |
231 DFData* check = curr + 1; | |
232 SkPoint distVec = check->fDistVector; | |
233 float distSq = check->fDistSq + 2.0f*distVec.fX + 1.0f; | |
234 if (distSq < curr->fDistSq) { | |
235 distVec.fX += 1.0f; | |
236 curr->fDistSq = distSq; | |
237 curr->fDistVector = distVec; | |
238 } | |
239 | |
240 // bottom left | |
241 check = curr + width-1; | |
242 distVec = check->fDistVector; | |
243 distSq = check->fDistSq - 2.0f*(distVec.fX - distVec.fY - 1.0f); | |
244 if (distSq < curr->fDistSq) { | |
245 distVec.fX -= 1.0f; | |
246 distVec.fY += 1.0f; | |
247 curr->fDistSq = distSq; | |
248 curr->fDistVector = distVec; | |
249 } | |
250 | |
251 // bottom | |
252 check = curr + width; | |
253 distVec = check->fDistVector; | |
254 distSq = check->fDistSq + 2.0f*distVec.fY + 1.0f; | |
255 if (distSq < curr->fDistSq) { | |
256 distVec.fY += 1.0f; | |
257 curr->fDistSq = distSq; | |
258 curr->fDistVector = distVec; | |
259 } | |
260 | |
261 // bottom right | |
262 check = curr + width+1; | |
263 distVec = check->fDistVector; | |
264 distSq = check->fDistSq + 2.0f*(distVec.fX + distVec.fY + 1.0f); | |
265 if (distSq < curr->fDistSq) { | |
266 distVec.fX += 1.0f; | |
267 distVec.fY += 1.0f; | |
268 curr->fDistSq = distSq; | |
269 curr->fDistVector = distVec; | |
270 } | |
271 } | |
272 | |
273 static unsigned char pack_distance_field_val(float dist, float distanceMagnitude ) { | |
274 if (dist <= -distanceMagnitude) { | |
275 return 255; | |
276 } else if (dist > distanceMagnitude) { | |
277 return 0; | |
278 } else { | |
279 return (unsigned char)((distanceMagnitude-dist)*128.0f/distanceMagnitude ); | |
280 } | |
281 } | |
282 | |
283 // assumes an 8-bit image and distance field | |
284 bool SkGenerateDistanceFieldFromImage(unsigned char* distanceField, | |
285 unsigned char* image, | |
286 int width, int height, | |
287 int distanceMagnitude) { | |
288 SkASSERT(NULL != distanceField); | |
289 SkASSERT(NULL != image); | |
290 | |
291 // the final distance field will have additional texels on each side to hand le | |
292 // the maximum distance | |
293 // we expand our temp data by one more on each side to simplify | |
294 // the scanning code -- will always be treated as infinitely far away | |
295 int pad = distanceMagnitude+1; | |
296 | |
297 // set params for distance field data | |
298 int dataWidth = width + 2*pad; | |
299 int dataHeight = height + 2*pad; | |
300 | |
301 // create temp data | |
302 size_t dataSize = dataWidth*dataHeight*sizeof(DFData); | |
303 SkAutoSMalloc<1024> dfStorage(dataSize); | |
304 DFData* dataPtr = (DFData*) dfStorage.get(); | |
305 sk_bzero(dataPtr, dataSize); | |
306 | |
307 SkAutoSMalloc<1024> edgeStorage(dataWidth*dataHeight*sizeof(char)); | |
308 char* edgePtr = (char*) edgeStorage.get(); | |
309 sk_bzero(edgePtr, dataWidth*dataHeight*sizeof(char)); | |
310 | |
311 // copy glyph into distance field storage | |
312 init_glyph_data(dataPtr, edgePtr, image, | |
313 dataWidth, dataHeight, | |
314 width, height, pad); | |
315 | |
316 // create initial distance data, particularly at edges | |
317 init_distances(dataPtr, edgePtr, dataWidth, dataHeight); | |
318 | |
319 // now perform Euclidean distance transform to propagate distances | |
320 | |
321 // forwards in y | |
322 DFData* currData = dataPtr+dataWidth+1; // skip outer buffer | |
323 char* currEdge = edgePtr+dataWidth+1; | |
324 for (int j = 1; j < dataHeight-1; ++j) { | |
325 // forwards in x | |
326 for (int i = 1; i < dataWidth-1; ++i) { | |
327 // don't need to calculate distance for edge pixels | |
328 if (!*currEdge) { | |
329 F1(currData, dataWidth); | |
330 } | |
331 ++currData; | |
332 ++currEdge; | |
333 } | |
334 | |
335 // backwards in x | |
336 --currData; // reset to end | |
337 --currEdge; | |
338 for (int i = 1; i < dataWidth-1; ++i) { | |
339 // don't need to calculate distance for edge pixels | |
340 if (!*currEdge) { | |
341 F2(currData, dataWidth); | |
342 } | |
343 --currData; | |
344 --currEdge; | |
345 } | |
346 | |
347 currData += dataWidth+1; | |
348 currEdge += dataWidth+1; | |
349 } | |
350 | |
351 // backwards in y | |
352 currData = dataPtr+dataWidth*(dataHeight-2) - 1; // skip outer buffer | |
353 currEdge = edgePtr+dataWidth*(dataHeight-2) - 1; | |
354 for (int j = 1; j < dataHeight-1; ++j) { | |
355 // forwards in x | |
356 for (int i = 1; i < dataWidth-1; ++i) { | |
357 // don't need to calculate distance for edge pixels | |
358 if (!*currEdge) { | |
359 B1(currData, dataWidth); | |
360 } | |
361 ++currData; | |
362 ++currEdge; | |
363 } | |
364 | |
365 // backwards in x | |
366 --currData; // reset to end | |
367 --currEdge; | |
368 for (int i = 1; i < dataWidth-1; ++i) { | |
369 // don't need to calculate distance for edge pixels | |
370 if (!*currEdge) { | |
371 B2(currData, dataWidth); | |
372 } | |
373 --currData; | |
374 --currEdge; | |
375 } | |
376 | |
377 currData -= dataWidth-1; | |
378 currEdge -= dataWidth-1; | |
379 } | |
380 | |
381 // copy results to final distance field data | |
382 currData = dataPtr + dataWidth+1; | |
383 currEdge = edgePtr + dataWidth+1; | |
384 unsigned char *dfPtr = distanceField; | |
385 for (int j = 1; j < dataHeight-1; ++j) { | |
386 for (int i = 1; i < dataWidth-1; ++i) { | |
387 float dist; | |
388 if (currData->fAlpha > 0.5f) { | |
389 dist = -SkScalarSqrt(currData->fDistSq); | |
390 } else { | |
391 dist = SkScalarSqrt(currData->fDistSq); | |
392 } | |
393 | |
394 *dfPtr++ = pack_distance_field_val(dist, (float)distanceMagnitude); | |
395 ++currData; | |
396 ++currEdge; | |
397 } | |
398 currData += 2; | |
399 currEdge += 2; | |
400 } | |
401 | |
402 return true; | |
403 } | |
OLD | NEW |