Chromium Code Reviews| 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 |