OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkDistanceFieldGen.h" | 8 #include "SkDistanceFieldGen.h" |
9 #include "SkPoint.h" | 9 #include "SkPoint.h" |
10 | 10 |
11 struct DFData { | 11 struct DFData { |
12 float fAlpha; // alpha value of source texel | 12 float fAlpha; // alpha value of source texel |
13 float fDistSq; // distance squared to nearest (so far) edge texel | 13 float fDistSq; // distance squared to nearest (so far) edge texel |
14 SkPoint fDistVector; // distance vector to nearest (so far) edge texel | 14 SkPoint fDistVector; // distance vector to nearest (so far) edge texel |
15 }; | 15 }; |
16 | 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 |
17 // We treat an "edge" as a place where we cross from a texel >= 128 to a texel <
128, | 31 // 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. | 32 // or vice versa. This means we just need to check if the MSBs are different. |
19 static bool found_edge(const unsigned char* imagePtr, int width) { | 33 // 'neighborFlags' is used to limit the directions in which we test to avoid ind
exing |
| 34 // outside of the image |
| 35 static bool found_edge(const unsigned char* imagePtr, int width, int neighborFla
gs) { |
| 36 // the order of these should match the neighbor flags above |
| 37 const int kNum8ConnectedNeighbors = 8; |
20 const int offsets[8] = {-1, 1, -width-1, -width, -width+1, width-1, width, w
idth+1 }; | 38 const int offsets[8] = {-1, 1, -width-1, -width, -width+1, width-1, width, w
idth+1 }; |
| 39 SkASSERT(kNum8ConnectedNeighbors == kNeighborFlagCount); |
21 | 40 |
22 // search for an edge | 41 // search for an edge |
23 int checkVal = *imagePtr >> 7; | 42 unsigned char currVal = *imagePtr >> 7; |
24 for (int i = 0; i < 8; ++i) { | 43 for (int i = 0; i < kNum8ConnectedNeighbors; ++i) { |
25 const unsigned char* checkPtr = imagePtr + offsets[i]; | 44 unsigned char checkVal; |
26 if (checkVal ^ (*checkPtr >> 7)) { | 45 if ((1 << i) & neighborFlags) { |
| 46 const unsigned char* checkPtr = imagePtr + offsets[i]; |
| 47 checkVal = *checkPtr >> 7; |
| 48 } else { |
| 49 checkVal = 0; |
| 50 } |
| 51 SkASSERT(checkVal == 0 || checkVal == 1); |
| 52 SkASSERT(currVal == 0 || currVal == 1); |
| 53 if (checkVal != currVal) { |
27 return true; | 54 return true; |
28 } | 55 } |
29 } | 56 } |
30 | 57 |
31 return false; | 58 return false; |
32 } | 59 } |
33 | 60 |
34 static void init_glyph_data(DFData* data, unsigned char* edges, const unsigned c
har* image, | 61 static void init_glyph_data(DFData* data, unsigned char* edges, const unsigned c
har* image, |
35 int dataWidth, int dataHeight, | 62 int dataWidth, int dataHeight, |
36 int imageWidth, int imageHeight, | 63 int imageWidth, int imageHeight, |
37 int pad) { | 64 int pad) { |
38 data += pad*dataWidth; | 65 data += pad*dataWidth; |
39 data += pad; | 66 data += pad; |
40 edges += (pad*dataWidth + pad); | 67 edges += (pad*dataWidth + pad); |
41 | 68 |
42 for (int j = 0; j < imageHeight; ++j) { | 69 for (int j = 0; j < imageHeight; ++j) { |
43 for (int i = 0; i < imageWidth; ++i) { | 70 for (int i = 0; i < imageWidth; ++i) { |
44 if (255 == *image) { | 71 if (255 == *image) { |
45 data->fAlpha = 1.0f; | 72 data->fAlpha = 1.0f; |
46 } else { | 73 } else { |
47 data->fAlpha = (*image)*0.00392156862f; // 1/255 | 74 data->fAlpha = (*image)*0.00392156862f; // 1/255 |
48 } | 75 } |
49 if (i > 0 && i < imageWidth-1 && j > 0 && j < imageHeight-1 && | 76 int checkMask = kAll_NeighborFlags; |
50 found_edge(image, imageWidth)) { | 77 if (i == 0) { |
| 78 checkMask &= ~(kLeft_NeighborFlag|kTopLeft_NeighborFlag|kBottomL
eft_NeighborFlag); |
| 79 } |
| 80 if (i == imageWidth-1) { |
| 81 checkMask &= ~(kRight_NeighborFlag|kTopRight_NeighborFlag|kBotto
mRight_NeighborFlag); |
| 82 } |
| 83 if (j == 0) { |
| 84 checkMask &= ~(kTopLeft_NeighborFlag|kTop_NeighborFlag|kTopRight
_NeighborFlag); |
| 85 } |
| 86 if (j == imageHeight-1) { |
| 87 checkMask &= ~(kBottomLeft_NeighborFlag|kBottom_NeighborFlag|kBo
ttomRight_NeighborFlag); |
| 88 } |
| 89 if (found_edge(image, imageWidth, checkMask)) { |
51 *edges = 255; // using 255 makes for convenient debug rendering | 90 *edges = 255; // using 255 makes for convenient debug rendering |
52 } | 91 } |
53 ++data; | 92 ++data; |
54 ++image; | 93 ++image; |
55 ++edges; | 94 ++edges; |
56 } | 95 } |
57 data += 2*pad; | 96 data += 2*pad; |
58 edges += 2*pad; | 97 edges += 2*pad; |
59 } | 98 } |
60 } | 99 } |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 distVec = check->fDistVector; | 300 distVec = check->fDistVector; |
262 distSq = check->fDistSq + 2.0f*(distVec.fX + distVec.fY + 1.0f); | 301 distSq = check->fDistSq + 2.0f*(distVec.fX + distVec.fY + 1.0f); |
263 if (distSq < curr->fDistSq) { | 302 if (distSq < curr->fDistSq) { |
264 distVec.fX += 1.0f; | 303 distVec.fX += 1.0f; |
265 distVec.fY += 1.0f; | 304 distVec.fY += 1.0f; |
266 curr->fDistSq = distSq; | 305 curr->fDistSq = distSq; |
267 curr->fDistVector = distVec; | 306 curr->fDistVector = distVec; |
268 } | 307 } |
269 } | 308 } |
270 | 309 |
| 310 // enable this to output edge data rather than the distance field |
| 311 #define DUMP_EDGE 0 |
| 312 |
| 313 #if !DUMP_EDGE |
271 static unsigned char pack_distance_field_val(float dist, float distanceMagnitude
) { | 314 static unsigned char pack_distance_field_val(float dist, float distanceMagnitude
) { |
272 if (dist <= -distanceMagnitude) { | 315 if (dist <= -distanceMagnitude) { |
273 return 255; | 316 return 255; |
274 } else if (dist > distanceMagnitude) { | 317 } else if (dist > distanceMagnitude) { |
275 return 0; | 318 return 0; |
276 } else { | 319 } else { |
277 return (unsigned char)((distanceMagnitude-dist)*128.0f/distanceMagnitude
); | 320 return (unsigned char)((distanceMagnitude-dist)*128.0f/distanceMagnitude
); |
278 } | 321 } |
279 } | 322 } |
| 323 #endif |
280 | 324 |
281 // assumes an 8-bit image and distance field | 325 // assumes an 8-bit image and distance field |
282 bool SkGenerateDistanceFieldFromImage(unsigned char* distanceField, | 326 bool SkGenerateDistanceFieldFromImage(unsigned char* distanceField, |
283 const unsigned char* image, | 327 const unsigned char* image, |
284 int width, int height, | 328 int width, int height, |
285 int distanceMagnitude) { | 329 int distanceMagnitude) { |
286 SkASSERT(NULL != distanceField); | 330 SkASSERT(NULL != distanceField); |
287 SkASSERT(NULL != image); | 331 SkASSERT(NULL != image); |
288 | 332 |
289 // the final distance field will have additional texels on each side to hand
le | 333 // the final distance field will have additional texels on each side to hand
le |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 currData -= dataWidth-1; | 419 currData -= dataWidth-1; |
376 currEdge -= dataWidth-1; | 420 currEdge -= dataWidth-1; |
377 } | 421 } |
378 | 422 |
379 // copy results to final distance field data | 423 // copy results to final distance field data |
380 currData = dataPtr + dataWidth+1; | 424 currData = dataPtr + dataWidth+1; |
381 currEdge = edgePtr + dataWidth+1; | 425 currEdge = edgePtr + dataWidth+1; |
382 unsigned char *dfPtr = distanceField; | 426 unsigned char *dfPtr = distanceField; |
383 for (int j = 1; j < dataHeight-1; ++j) { | 427 for (int j = 1; j < dataHeight-1; ++j) { |
384 for (int i = 1; i < dataWidth-1; ++i) { | 428 for (int i = 1; i < dataWidth-1; ++i) { |
| 429 #if DUMP_EDGE |
| 430 unsigned char val = (currData->fAlpha >= 0.5f) ? 255 : 0; |
| 431 if (*currEdge) { |
| 432 val = 128; |
| 433 } |
| 434 *dfPtr++ = val; |
| 435 #else |
385 float dist; | 436 float dist; |
386 if (currData->fAlpha > 0.5f) { | 437 if (currData->fAlpha > 0.5f) { |
387 dist = -SkScalarSqrt(currData->fDistSq); | 438 dist = -SkScalarSqrt(currData->fDistSq); |
388 } else { | 439 } else { |
389 dist = SkScalarSqrt(currData->fDistSq); | 440 dist = SkScalarSqrt(currData->fDistSq); |
390 } | 441 } |
391 | |
392 *dfPtr++ = pack_distance_field_val(dist, (float)distanceMagnitude); | 442 *dfPtr++ = pack_distance_field_val(dist, (float)distanceMagnitude); |
| 443 #endif |
393 ++currData; | 444 ++currData; |
394 ++currEdge; | 445 ++currEdge; |
395 } | 446 } |
396 currData += 2; | 447 currData += 2; |
397 currEdge += 2; | 448 currEdge += 2; |
398 } | 449 } |
399 | 450 |
400 return true; | 451 return true; |
401 } | 452 } |
OLD | NEW |