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 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 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 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 35 static 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 66 static 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 109 static 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 148 static 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) 196 static 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) 242 static 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) 257 static 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) 271 static 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 319 template <int distanceMagnitude> 320 static unsigned char pack_distance_field_val(float dist) { 321 // The distance field is constructed as unsigned char values, so that the zero value is at 128, 322 // Beside 128, we have 128 values in range [0, 128), but only 127 values in range (128, 255]. 323 // So we multiply distanceMagnitude by 127/128 at the latter range to avoid overflow. 324 dist = SkScalarPin(-dist, -distanceMagnitude, distanceMagnitude * 127.0f / 128.0f); 325 326 // Scale into the positive range for unsigned distance. 327 dist += distanceMagnitude; 328 329 // Scale into unsigned char range. 330 // Round to place negative and positive values as equally as possible around 128 331 // (which represents zero). 332 return (unsigned char)SkScalarRoundToInt(dist / (2 * distanceMagnitude) * 256.0f); 333 } 334 #endif 335 336 // assumes a padded 8-bit image and distance field 337 // width and height are the original width and height of the image 338 static bool generate_distance_field_from_image(unsigned char* distanceField, 339 const unsigned char* copyPtr, 340 int width, int height) { 341 SkASSERT(distanceField); 342 SkASSERT(copyPtr); 343 344 // we expand our temp data by one more on each side to simplify 345 // the scanning code -- will always be treated as infinitely far away 346 int pad = SK_DistanceFieldPad + 1; 347 348 // set params for distance field data 349 int dataWidth = width + 2*pad; 350 int dataHeight = height + 2*pad; 351 352 // create zeroed temp DFData+edge storage 353 SkAutoFree storage(sk_calloc_throw(dataWidth*dataHeight*(sizeof(DFData) + 1))); 354 DFData* dataPtr = (DFData*)storage.get(); 355 unsigned char* edgePtr = (unsigned char*)storage.get() + dataWidth*dataHeight*sizeof(DFData); 356 357 // copy glyph into distance field storage 358 init_glyph_data(dataPtr, edgePtr, copyPtr, 359 dataWidth, dataHeight, 360 width+2, height+2, SK_DistanceFieldPad); 361 362 // create initial distance data, particularly at edges 363 init_distances(dataPtr, edgePtr, dataWidth, dataHeight); 364 365 // now perform Euclidean distance transform to propagate distances 366 367 // forwards in y 368 DFData* currData = dataPtr+dataWidth+1; // skip outer buffer 369 unsigned char* currEdge = edgePtr+dataWidth+1; 370 for (int j = 1; j < dataHeight-1; ++j) { 371 // forwards in x 372 for (int i = 1; i < dataWidth-1; ++i) { 373 // don't need to calculate distance for edge pixels 374 if (!*currEdge) { 375 F1(currData, dataWidth); 376 } 377 ++currData; 378 ++currEdge; 379 } 380 381 // backwards in x 382 --currData; // reset to end 383 --currEdge; 384 for (int i = 1; i < dataWidth-1; ++i) { 385 // don't need to calculate distance for edge pixels 386 if (!*currEdge) { 387 F2(currData, dataWidth); 388 } 389 --currData; 390 --currEdge; 391 } 392 393 currData += dataWidth+1; 394 currEdge += dataWidth+1; 395 } 396 397 // backwards in y 398 currData = dataPtr+dataWidth*(dataHeight-2) - 1; // skip outer buffer 399 currEdge = edgePtr+dataWidth*(dataHeight-2) - 1; 400 for (int j = 1; j < dataHeight-1; ++j) { 401 // forwards in x 402 for (int i = 1; i < dataWidth-1; ++i) { 403 // don't need to calculate distance for edge pixels 404 if (!*currEdge) { 405 B1(currData, dataWidth); 406 } 407 ++currData; 408 ++currEdge; 409 } 410 411 // backwards in x 412 --currData; // reset to end 413 --currEdge; 414 for (int i = 1; i < dataWidth-1; ++i) { 415 // don't need to calculate distance for edge pixels 416 if (!*currEdge) { 417 B2(currData, dataWidth); 418 } 419 --currData; 420 --currEdge; 421 } 422 423 currData -= dataWidth-1; 424 currEdge -= dataWidth-1; 425 } 426 427 // copy results to final distance field data 428 currData = dataPtr + dataWidth+1; 429 currEdge = edgePtr + dataWidth+1; 430 unsigned char *dfPtr = distanceField; 431 for (int j = 1; j < dataHeight-1; ++j) { 432 for (int i = 1; i < dataWidth-1; ++i) { 433 #if DUMP_EDGE 434 float alpha = currData->fAlpha; 435 float edge = 0.0f; 436 if (*currEdge) { 437 edge = 0.25f; 438 } 439 // blend with original image 440 float result = alpha + (1.0f-alpha)*edge; 441 unsigned char val = sk_float_round2int(255*result); 442 *dfPtr++ = val; 443 #else 444 float dist; 445 if (currData->fAlpha > 0.5f) { 446 dist = -SkScalarSqrt(currData->fDistSq); 447 } else { 448 dist = SkScalarSqrt(currData->fDistSq); 449 } 450 *dfPtr++ = pack_distance_field_val<SK_DistanceFieldMagnitude>(dist); 451 #endif 452 ++currData; 453 ++currEdge; 454 } 455 currData += 2; 456 currEdge += 2; 457 } 458 459 return true; 460 } 461 462 // assumes an 8-bit image and distance field 463 bool SkGenerateDistanceFieldFromA8Image(unsigned char* distanceField, 464 const unsigned char* image, 465 int width, int height, size_t rowBytes) { 466 SkASSERT(distanceField); 467 SkASSERT(image); 468 469 // create temp data 470 SkAutoSMalloc<1024> copyStorage((width+2)*(height+2)*sizeof(char)); 471 unsigned char* copyPtr = (unsigned char*) copyStorage.get(); 472 473 // we copy our source image into a padded copy to ensure we catch edge transitions 474 // around the outside 475 const unsigned char* currSrcScanLine = image; 476 sk_bzero(copyPtr, (width+2)*sizeof(char)); 477 unsigned char* currDestPtr = copyPtr + width + 2; 478 for (int i = 0; i < height; ++i) { 479 *currDestPtr++ = 0; 480 memcpy(currDestPtr, currSrcScanLine, rowBytes); 481 currSrcScanLine += rowBytes; 482 currDestPtr += width; 483 *currDestPtr++ = 0; 484 } 485 sk_bzero(currDestPtr, (width+2)*sizeof(char)); 486 487 return generate_distance_field_from_image(distanceField, copyPtr, width, height); 488 } 489 490 // assumes a 1-bit image and 8-bit distance field 491 bool SkGenerateDistanceFieldFromBWImage(unsigned char* distanceField, 492 const unsigned char* image, 493 int width, int height, size_t rowBytes) { 494 SkASSERT(distanceField); 495 SkASSERT(image); 496 497 // create temp data 498 SkAutoSMalloc<1024> copyStorage((width+2)*(height+2)*sizeof(char)); 499 unsigned char* copyPtr = (unsigned char*) copyStorage.get(); 500 501 // we copy our source image into a padded copy to ensure we catch edge transitions 502 // around the outside 503 const unsigned char* currSrcScanLine = image; 504 sk_bzero(copyPtr, (width+2)*sizeof(char)); 505 unsigned char* currDestPtr = copyPtr + width + 2; 506 for (int i = 0; i < height; ++i) { 507 *currDestPtr++ = 0; 508 int rowWritesLeft = width; 509 const unsigned char *maskPtr = currSrcScanLine; 510 while (rowWritesLeft > 0) { 511 unsigned mask = *maskPtr++; 512 for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) { 513 *currDestPtr++ = (mask & (1 << i)) ? 0xff : 0; 514 } 515 } 516 currSrcScanLine += rowBytes; 517 *currDestPtr++ = 0; 518 } 519 sk_bzero(currDestPtr, (width+2)*sizeof(char)); 520 521 return generate_distance_field_from_image(distanceField, copyPtr, width, height); 522 } 523