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