1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2014 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/private/SkColorData.h" 9cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 10cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h" 11cb93a386Sopenharmony_ci#include "src/core/SkAutoMalloc.h" 12cb93a386Sopenharmony_ci#include "src/core/SkDistanceFieldGen.h" 13cb93a386Sopenharmony_ci#include "src/core/SkMask.h" 14cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h" 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ci#include <utility> 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_cistruct DFData { 19cb93a386Sopenharmony_ci float fAlpha; // alpha value of source texel 20cb93a386Sopenharmony_ci float fDistSq; // distance squared to nearest (so far) edge texel 21cb93a386Sopenharmony_ci SkPoint fDistVector; // distance vector to nearest (so far) edge texel 22cb93a386Sopenharmony_ci}; 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_cienum NeighborFlags { 25cb93a386Sopenharmony_ci kLeft_NeighborFlag = 0x01, 26cb93a386Sopenharmony_ci kRight_NeighborFlag = 0x02, 27cb93a386Sopenharmony_ci kTopLeft_NeighborFlag = 0x04, 28cb93a386Sopenharmony_ci kTop_NeighborFlag = 0x08, 29cb93a386Sopenharmony_ci kTopRight_NeighborFlag = 0x10, 30cb93a386Sopenharmony_ci kBottomLeft_NeighborFlag = 0x20, 31cb93a386Sopenharmony_ci kBottom_NeighborFlag = 0x40, 32cb93a386Sopenharmony_ci kBottomRight_NeighborFlag = 0x80, 33cb93a386Sopenharmony_ci kAll_NeighborFlags = 0xff, 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci kNeighborFlagCount = 8 36cb93a386Sopenharmony_ci}; 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_ci// We treat an "edge" as a place where we cross from >=128 to <128, or vice versa, or 39cb93a386Sopenharmony_ci// where we have two non-zero pixels that are <128. 40cb93a386Sopenharmony_ci// 'neighborFlags' is used to limit the directions in which we test to avoid indexing 41cb93a386Sopenharmony_ci// outside of the image 42cb93a386Sopenharmony_cistatic bool found_edge(const unsigned char* imagePtr, int width, int neighborFlags) { 43cb93a386Sopenharmony_ci // the order of these should match the neighbor flags above 44cb93a386Sopenharmony_ci const int kNum8ConnectedNeighbors = 8; 45cb93a386Sopenharmony_ci const int offsets[8] = {-1, 1, -width-1, -width, -width+1, width-1, width, width+1 }; 46cb93a386Sopenharmony_ci SkASSERT(kNum8ConnectedNeighbors == kNeighborFlagCount); 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci // search for an edge 49cb93a386Sopenharmony_ci unsigned char currVal = *imagePtr; 50cb93a386Sopenharmony_ci unsigned char currCheck = (currVal >> 7); 51cb93a386Sopenharmony_ci for (int i = 0; i < kNum8ConnectedNeighbors; ++i) { 52cb93a386Sopenharmony_ci unsigned char neighborVal; 53cb93a386Sopenharmony_ci if ((1 << i) & neighborFlags) { 54cb93a386Sopenharmony_ci const unsigned char* checkPtr = imagePtr + offsets[i]; 55cb93a386Sopenharmony_ci neighborVal = *checkPtr; 56cb93a386Sopenharmony_ci } else { 57cb93a386Sopenharmony_ci neighborVal = 0; 58cb93a386Sopenharmony_ci } 59cb93a386Sopenharmony_ci unsigned char neighborCheck = (neighborVal >> 7); 60cb93a386Sopenharmony_ci SkASSERT(currCheck == 0 || currCheck == 1); 61cb93a386Sopenharmony_ci SkASSERT(neighborCheck == 0 || neighborCheck == 1); 62cb93a386Sopenharmony_ci // if sharp transition 63cb93a386Sopenharmony_ci if (currCheck != neighborCheck || 64cb93a386Sopenharmony_ci // or both <128 and >0 65cb93a386Sopenharmony_ci (!currCheck && !neighborCheck && currVal && neighborVal)) { 66cb93a386Sopenharmony_ci return true; 67cb93a386Sopenharmony_ci } 68cb93a386Sopenharmony_ci } 69cb93a386Sopenharmony_ci 70cb93a386Sopenharmony_ci return false; 71cb93a386Sopenharmony_ci} 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_cistatic void init_glyph_data(DFData* data, unsigned char* edges, const unsigned char* image, 74cb93a386Sopenharmony_ci int dataWidth, int dataHeight, 75cb93a386Sopenharmony_ci int imageWidth, int imageHeight, 76cb93a386Sopenharmony_ci int pad) { 77cb93a386Sopenharmony_ci data += pad*dataWidth; 78cb93a386Sopenharmony_ci data += pad; 79cb93a386Sopenharmony_ci edges += (pad*dataWidth + pad); 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ci for (int j = 0; j < imageHeight; ++j) { 82cb93a386Sopenharmony_ci for (int i = 0; i < imageWidth; ++i) { 83cb93a386Sopenharmony_ci if (255 == *image) { 84cb93a386Sopenharmony_ci data->fAlpha = 1.0f; 85cb93a386Sopenharmony_ci } else { 86cb93a386Sopenharmony_ci data->fAlpha = (*image)*0.00392156862f; // 1/255 87cb93a386Sopenharmony_ci } 88cb93a386Sopenharmony_ci int checkMask = kAll_NeighborFlags; 89cb93a386Sopenharmony_ci if (i == 0) { 90cb93a386Sopenharmony_ci checkMask &= ~(kLeft_NeighborFlag|kTopLeft_NeighborFlag|kBottomLeft_NeighborFlag); 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci if (i == imageWidth-1) { 93cb93a386Sopenharmony_ci checkMask &= ~(kRight_NeighborFlag|kTopRight_NeighborFlag|kBottomRight_NeighborFlag); 94cb93a386Sopenharmony_ci } 95cb93a386Sopenharmony_ci if (j == 0) { 96cb93a386Sopenharmony_ci checkMask &= ~(kTopLeft_NeighborFlag|kTop_NeighborFlag|kTopRight_NeighborFlag); 97cb93a386Sopenharmony_ci } 98cb93a386Sopenharmony_ci if (j == imageHeight-1) { 99cb93a386Sopenharmony_ci checkMask &= ~(kBottomLeft_NeighborFlag|kBottom_NeighborFlag|kBottomRight_NeighborFlag); 100cb93a386Sopenharmony_ci } 101cb93a386Sopenharmony_ci if (found_edge(image, imageWidth, checkMask)) { 102cb93a386Sopenharmony_ci *edges = 255; // using 255 makes for convenient debug rendering 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci ++data; 105cb93a386Sopenharmony_ci ++image; 106cb93a386Sopenharmony_ci ++edges; 107cb93a386Sopenharmony_ci } 108cb93a386Sopenharmony_ci data += 2*pad; 109cb93a386Sopenharmony_ci edges += 2*pad; 110cb93a386Sopenharmony_ci } 111cb93a386Sopenharmony_ci} 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci// from Gustavson (2011) 114cb93a386Sopenharmony_ci// computes the distance to an edge given an edge normal vector and a pixel's alpha value 115cb93a386Sopenharmony_ci// assumes that direction has been pre-normalized 116cb93a386Sopenharmony_cistatic float edge_distance(const SkPoint& direction, float alpha) { 117cb93a386Sopenharmony_ci float dx = direction.fX; 118cb93a386Sopenharmony_ci float dy = direction.fY; 119cb93a386Sopenharmony_ci float distance; 120cb93a386Sopenharmony_ci if (SkScalarNearlyZero(dx) || SkScalarNearlyZero(dy)) { 121cb93a386Sopenharmony_ci distance = 0.5f - alpha; 122cb93a386Sopenharmony_ci } else { 123cb93a386Sopenharmony_ci // this is easier if we treat the direction as being in the first octant 124cb93a386Sopenharmony_ci // (other octants are symmetrical) 125cb93a386Sopenharmony_ci dx = SkScalarAbs(dx); 126cb93a386Sopenharmony_ci dy = SkScalarAbs(dy); 127cb93a386Sopenharmony_ci if (dx < dy) { 128cb93a386Sopenharmony_ci using std::swap; 129cb93a386Sopenharmony_ci swap(dx, dy); 130cb93a386Sopenharmony_ci } 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci // a1 = 0.5*dy/dx is the smaller fractional area chopped off by the edge 133cb93a386Sopenharmony_ci // to avoid the divide, we just consider the numerator 134cb93a386Sopenharmony_ci float a1num = 0.5f*dy; 135cb93a386Sopenharmony_ci 136cb93a386Sopenharmony_ci // we now compute the approximate distance, depending where the alpha falls 137cb93a386Sopenharmony_ci // relative to the edge fractional area 138cb93a386Sopenharmony_ci 139cb93a386Sopenharmony_ci // if 0 <= alpha < a1 140cb93a386Sopenharmony_ci if (alpha*dx < a1num) { 141cb93a386Sopenharmony_ci // TODO: find a way to do this without square roots? 142cb93a386Sopenharmony_ci distance = 0.5f*(dx + dy) - SkScalarSqrt(2.0f*dx*dy*alpha); 143cb93a386Sopenharmony_ci // if a1 <= alpha <= 1 - a1 144cb93a386Sopenharmony_ci } else if (alpha*dx < (dx - a1num)) { 145cb93a386Sopenharmony_ci distance = (0.5f - alpha)*dx; 146cb93a386Sopenharmony_ci // if 1 - a1 < alpha <= 1 147cb93a386Sopenharmony_ci } else { 148cb93a386Sopenharmony_ci // TODO: find a way to do this without square roots? 149cb93a386Sopenharmony_ci distance = -0.5f*(dx + dy) + SkScalarSqrt(2.0f*dx*dy*(1.0f - alpha)); 150cb93a386Sopenharmony_ci } 151cb93a386Sopenharmony_ci } 152cb93a386Sopenharmony_ci 153cb93a386Sopenharmony_ci return distance; 154cb93a386Sopenharmony_ci} 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_cistatic void init_distances(DFData* data, unsigned char* edges, int width, int height) { 157cb93a386Sopenharmony_ci // skip one pixel border 158cb93a386Sopenharmony_ci DFData* currData = data; 159cb93a386Sopenharmony_ci DFData* prevData = data - width; 160cb93a386Sopenharmony_ci DFData* nextData = data + width; 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci for (int j = 0; j < height; ++j) { 163cb93a386Sopenharmony_ci for (int i = 0; i < width; ++i) { 164cb93a386Sopenharmony_ci if (*edges) { 165cb93a386Sopenharmony_ci // we should not be in the one-pixel outside band 166cb93a386Sopenharmony_ci SkASSERT(i > 0 && i < width-1 && j > 0 && j < height-1); 167cb93a386Sopenharmony_ci // gradient will point from low to high 168cb93a386Sopenharmony_ci // +y is down in this case 169cb93a386Sopenharmony_ci // i.e., if you're outside, gradient points towards edge 170cb93a386Sopenharmony_ci // if you're inside, gradient points away from edge 171cb93a386Sopenharmony_ci SkPoint currGrad; 172cb93a386Sopenharmony_ci currGrad.fX = (prevData+1)->fAlpha - (prevData-1)->fAlpha 173cb93a386Sopenharmony_ci + SK_ScalarSqrt2*(currData+1)->fAlpha 174cb93a386Sopenharmony_ci - SK_ScalarSqrt2*(currData-1)->fAlpha 175cb93a386Sopenharmony_ci + (nextData+1)->fAlpha - (nextData-1)->fAlpha; 176cb93a386Sopenharmony_ci currGrad.fY = (nextData-1)->fAlpha - (prevData-1)->fAlpha 177cb93a386Sopenharmony_ci + SK_ScalarSqrt2*nextData->fAlpha 178cb93a386Sopenharmony_ci - SK_ScalarSqrt2*prevData->fAlpha 179cb93a386Sopenharmony_ci + (nextData+1)->fAlpha - (prevData+1)->fAlpha; 180cb93a386Sopenharmony_ci SkPointPriv::SetLengthFast(&currGrad, 1.0f); 181cb93a386Sopenharmony_ci 182cb93a386Sopenharmony_ci // init squared distance to edge and distance vector 183cb93a386Sopenharmony_ci float dist = edge_distance(currGrad, currData->fAlpha); 184cb93a386Sopenharmony_ci currGrad.scale(dist, &currData->fDistVector); 185cb93a386Sopenharmony_ci currData->fDistSq = dist*dist; 186cb93a386Sopenharmony_ci } else { 187cb93a386Sopenharmony_ci // init distance to "far away" 188cb93a386Sopenharmony_ci currData->fDistSq = 2000000.f; 189cb93a386Sopenharmony_ci currData->fDistVector.fX = 1000.f; 190cb93a386Sopenharmony_ci currData->fDistVector.fY = 1000.f; 191cb93a386Sopenharmony_ci } 192cb93a386Sopenharmony_ci ++currData; 193cb93a386Sopenharmony_ci ++prevData; 194cb93a386Sopenharmony_ci ++nextData; 195cb93a386Sopenharmony_ci ++edges; 196cb93a386Sopenharmony_ci } 197cb93a386Sopenharmony_ci } 198cb93a386Sopenharmony_ci} 199cb93a386Sopenharmony_ci 200cb93a386Sopenharmony_ci// Danielsson's 8SSEDT 201cb93a386Sopenharmony_ci 202cb93a386Sopenharmony_ci// first stage forward pass 203cb93a386Sopenharmony_ci// (forward in Y, forward in X) 204cb93a386Sopenharmony_cistatic void F1(DFData* curr, int width) { 205cb93a386Sopenharmony_ci // upper left 206cb93a386Sopenharmony_ci DFData* check = curr - width-1; 207cb93a386Sopenharmony_ci SkPoint distVec = check->fDistVector; 208cb93a386Sopenharmony_ci float distSq = check->fDistSq - 2.0f*(distVec.fX + distVec.fY - 1.0f); 209cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 210cb93a386Sopenharmony_ci distVec.fX -= 1.0f; 211cb93a386Sopenharmony_ci distVec.fY -= 1.0f; 212cb93a386Sopenharmony_ci curr->fDistSq = distSq; 213cb93a386Sopenharmony_ci curr->fDistVector = distVec; 214cb93a386Sopenharmony_ci } 215cb93a386Sopenharmony_ci 216cb93a386Sopenharmony_ci // up 217cb93a386Sopenharmony_ci check = curr - width; 218cb93a386Sopenharmony_ci distVec = check->fDistVector; 219cb93a386Sopenharmony_ci distSq = check->fDistSq - 2.0f*distVec.fY + 1.0f; 220cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 221cb93a386Sopenharmony_ci distVec.fY -= 1.0f; 222cb93a386Sopenharmony_ci curr->fDistSq = distSq; 223cb93a386Sopenharmony_ci curr->fDistVector = distVec; 224cb93a386Sopenharmony_ci } 225cb93a386Sopenharmony_ci 226cb93a386Sopenharmony_ci // upper right 227cb93a386Sopenharmony_ci check = curr - width+1; 228cb93a386Sopenharmony_ci distVec = check->fDistVector; 229cb93a386Sopenharmony_ci distSq = check->fDistSq + 2.0f*(distVec.fX - distVec.fY + 1.0f); 230cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 231cb93a386Sopenharmony_ci distVec.fX += 1.0f; 232cb93a386Sopenharmony_ci distVec.fY -= 1.0f; 233cb93a386Sopenharmony_ci curr->fDistSq = distSq; 234cb93a386Sopenharmony_ci curr->fDistVector = distVec; 235cb93a386Sopenharmony_ci } 236cb93a386Sopenharmony_ci 237cb93a386Sopenharmony_ci // left 238cb93a386Sopenharmony_ci check = curr - 1; 239cb93a386Sopenharmony_ci distVec = check->fDistVector; 240cb93a386Sopenharmony_ci distSq = check->fDistSq - 2.0f*distVec.fX + 1.0f; 241cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 242cb93a386Sopenharmony_ci distVec.fX -= 1.0f; 243cb93a386Sopenharmony_ci curr->fDistSq = distSq; 244cb93a386Sopenharmony_ci curr->fDistVector = distVec; 245cb93a386Sopenharmony_ci } 246cb93a386Sopenharmony_ci} 247cb93a386Sopenharmony_ci 248cb93a386Sopenharmony_ci// second stage forward pass 249cb93a386Sopenharmony_ci// (forward in Y, backward in X) 250cb93a386Sopenharmony_cistatic void F2(DFData* curr, int width) { 251cb93a386Sopenharmony_ci // right 252cb93a386Sopenharmony_ci DFData* check = curr + 1; 253cb93a386Sopenharmony_ci SkPoint distVec = check->fDistVector; 254cb93a386Sopenharmony_ci float distSq = check->fDistSq + 2.0f*distVec.fX + 1.0f; 255cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 256cb93a386Sopenharmony_ci distVec.fX += 1.0f; 257cb93a386Sopenharmony_ci curr->fDistSq = distSq; 258cb93a386Sopenharmony_ci curr->fDistVector = distVec; 259cb93a386Sopenharmony_ci } 260cb93a386Sopenharmony_ci} 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ci// first stage backward pass 263cb93a386Sopenharmony_ci// (backward in Y, forward in X) 264cb93a386Sopenharmony_cistatic void B1(DFData* curr, int width) { 265cb93a386Sopenharmony_ci // left 266cb93a386Sopenharmony_ci DFData* check = curr - 1; 267cb93a386Sopenharmony_ci SkPoint distVec = check->fDistVector; 268cb93a386Sopenharmony_ci float distSq = check->fDistSq - 2.0f*distVec.fX + 1.0f; 269cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 270cb93a386Sopenharmony_ci distVec.fX -= 1.0f; 271cb93a386Sopenharmony_ci curr->fDistSq = distSq; 272cb93a386Sopenharmony_ci curr->fDistVector = distVec; 273cb93a386Sopenharmony_ci } 274cb93a386Sopenharmony_ci} 275cb93a386Sopenharmony_ci 276cb93a386Sopenharmony_ci// second stage backward pass 277cb93a386Sopenharmony_ci// (backward in Y, backwards in X) 278cb93a386Sopenharmony_cistatic void B2(DFData* curr, int width) { 279cb93a386Sopenharmony_ci // right 280cb93a386Sopenharmony_ci DFData* check = curr + 1; 281cb93a386Sopenharmony_ci SkPoint distVec = check->fDistVector; 282cb93a386Sopenharmony_ci float distSq = check->fDistSq + 2.0f*distVec.fX + 1.0f; 283cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 284cb93a386Sopenharmony_ci distVec.fX += 1.0f; 285cb93a386Sopenharmony_ci curr->fDistSq = distSq; 286cb93a386Sopenharmony_ci curr->fDistVector = distVec; 287cb93a386Sopenharmony_ci } 288cb93a386Sopenharmony_ci 289cb93a386Sopenharmony_ci // bottom left 290cb93a386Sopenharmony_ci check = curr + width-1; 291cb93a386Sopenharmony_ci distVec = check->fDistVector; 292cb93a386Sopenharmony_ci distSq = check->fDistSq - 2.0f*(distVec.fX - distVec.fY - 1.0f); 293cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 294cb93a386Sopenharmony_ci distVec.fX -= 1.0f; 295cb93a386Sopenharmony_ci distVec.fY += 1.0f; 296cb93a386Sopenharmony_ci curr->fDistSq = distSq; 297cb93a386Sopenharmony_ci curr->fDistVector = distVec; 298cb93a386Sopenharmony_ci } 299cb93a386Sopenharmony_ci 300cb93a386Sopenharmony_ci // bottom 301cb93a386Sopenharmony_ci check = curr + width; 302cb93a386Sopenharmony_ci distVec = check->fDistVector; 303cb93a386Sopenharmony_ci distSq = check->fDistSq + 2.0f*distVec.fY + 1.0f; 304cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 305cb93a386Sopenharmony_ci distVec.fY += 1.0f; 306cb93a386Sopenharmony_ci curr->fDistSq = distSq; 307cb93a386Sopenharmony_ci curr->fDistVector = distVec; 308cb93a386Sopenharmony_ci } 309cb93a386Sopenharmony_ci 310cb93a386Sopenharmony_ci // bottom right 311cb93a386Sopenharmony_ci check = curr + width+1; 312cb93a386Sopenharmony_ci distVec = check->fDistVector; 313cb93a386Sopenharmony_ci distSq = check->fDistSq + 2.0f*(distVec.fX + distVec.fY + 1.0f); 314cb93a386Sopenharmony_ci if (distSq < curr->fDistSq) { 315cb93a386Sopenharmony_ci distVec.fX += 1.0f; 316cb93a386Sopenharmony_ci distVec.fY += 1.0f; 317cb93a386Sopenharmony_ci curr->fDistSq = distSq; 318cb93a386Sopenharmony_ci curr->fDistVector = distVec; 319cb93a386Sopenharmony_ci } 320cb93a386Sopenharmony_ci} 321cb93a386Sopenharmony_ci 322cb93a386Sopenharmony_ci// enable this to output edge data rather than the distance field 323cb93a386Sopenharmony_ci#define DUMP_EDGE 0 324cb93a386Sopenharmony_ci 325cb93a386Sopenharmony_ci#if !DUMP_EDGE 326cb93a386Sopenharmony_citemplate <int distanceMagnitude> 327cb93a386Sopenharmony_cistatic unsigned char pack_distance_field_val(float dist) { 328cb93a386Sopenharmony_ci // The distance field is constructed as unsigned char values, so that the zero value is at 128, 329cb93a386Sopenharmony_ci // Beside 128, we have 128 values in range [0, 128), but only 127 values in range (128, 255]. 330cb93a386Sopenharmony_ci // So we multiply distanceMagnitude by 127/128 at the latter range to avoid overflow. 331cb93a386Sopenharmony_ci dist = SkTPin<float>(-dist, -distanceMagnitude, distanceMagnitude * 127.0f / 128.0f); 332cb93a386Sopenharmony_ci 333cb93a386Sopenharmony_ci // Scale into the positive range for unsigned distance. 334cb93a386Sopenharmony_ci dist += distanceMagnitude; 335cb93a386Sopenharmony_ci 336cb93a386Sopenharmony_ci // Scale into unsigned char range. 337cb93a386Sopenharmony_ci // Round to place negative and positive values as equally as possible around 128 338cb93a386Sopenharmony_ci // (which represents zero). 339cb93a386Sopenharmony_ci return (unsigned char)SkScalarRoundToInt(dist / (2 * distanceMagnitude) * 256.0f); 340cb93a386Sopenharmony_ci} 341cb93a386Sopenharmony_ci#endif 342cb93a386Sopenharmony_ci 343cb93a386Sopenharmony_ci// assumes a padded 8-bit image and distance field 344cb93a386Sopenharmony_ci// width and height are the original width and height of the image 345cb93a386Sopenharmony_cistatic bool generate_distance_field_from_image(unsigned char* distanceField, 346cb93a386Sopenharmony_ci const unsigned char* copyPtr, 347cb93a386Sopenharmony_ci int width, int height) { 348cb93a386Sopenharmony_ci SkASSERT(distanceField); 349cb93a386Sopenharmony_ci SkASSERT(copyPtr); 350cb93a386Sopenharmony_ci 351cb93a386Sopenharmony_ci // we expand our temp data by one more on each side to simplify 352cb93a386Sopenharmony_ci // the scanning code -- will always be treated as infinitely far away 353cb93a386Sopenharmony_ci int pad = SK_DistanceFieldPad + 1; 354cb93a386Sopenharmony_ci 355cb93a386Sopenharmony_ci // set params for distance field data 356cb93a386Sopenharmony_ci int dataWidth = width + 2*pad; 357cb93a386Sopenharmony_ci int dataHeight = height + 2*pad; 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_ci // create zeroed temp DFData+edge storage 360cb93a386Sopenharmony_ci SkAutoFree storage(sk_calloc_throw(dataWidth*dataHeight*(sizeof(DFData) + 1))); 361cb93a386Sopenharmony_ci DFData* dataPtr = (DFData*)storage.get(); 362cb93a386Sopenharmony_ci unsigned char* edgePtr = (unsigned char*)storage.get() + dataWidth*dataHeight*sizeof(DFData); 363cb93a386Sopenharmony_ci 364cb93a386Sopenharmony_ci // copy glyph into distance field storage 365cb93a386Sopenharmony_ci init_glyph_data(dataPtr, edgePtr, copyPtr, 366cb93a386Sopenharmony_ci dataWidth, dataHeight, 367cb93a386Sopenharmony_ci width+2, height+2, SK_DistanceFieldPad); 368cb93a386Sopenharmony_ci 369cb93a386Sopenharmony_ci // create initial distance data, particularly at edges 370cb93a386Sopenharmony_ci init_distances(dataPtr, edgePtr, dataWidth, dataHeight); 371cb93a386Sopenharmony_ci 372cb93a386Sopenharmony_ci // now perform Euclidean distance transform to propagate distances 373cb93a386Sopenharmony_ci 374cb93a386Sopenharmony_ci // forwards in y 375cb93a386Sopenharmony_ci DFData* currData = dataPtr+dataWidth+1; // skip outer buffer 376cb93a386Sopenharmony_ci unsigned char* currEdge = edgePtr+dataWidth+1; 377cb93a386Sopenharmony_ci for (int j = 1; j < dataHeight-1; ++j) { 378cb93a386Sopenharmony_ci // forwards in x 379cb93a386Sopenharmony_ci for (int i = 1; i < dataWidth-1; ++i) { 380cb93a386Sopenharmony_ci // don't need to calculate distance for edge pixels 381cb93a386Sopenharmony_ci if (!*currEdge) { 382cb93a386Sopenharmony_ci F1(currData, dataWidth); 383cb93a386Sopenharmony_ci } 384cb93a386Sopenharmony_ci ++currData; 385cb93a386Sopenharmony_ci ++currEdge; 386cb93a386Sopenharmony_ci } 387cb93a386Sopenharmony_ci 388cb93a386Sopenharmony_ci // backwards in x 389cb93a386Sopenharmony_ci --currData; // reset to end 390cb93a386Sopenharmony_ci --currEdge; 391cb93a386Sopenharmony_ci for (int i = 1; i < dataWidth-1; ++i) { 392cb93a386Sopenharmony_ci // don't need to calculate distance for edge pixels 393cb93a386Sopenharmony_ci if (!*currEdge) { 394cb93a386Sopenharmony_ci F2(currData, dataWidth); 395cb93a386Sopenharmony_ci } 396cb93a386Sopenharmony_ci --currData; 397cb93a386Sopenharmony_ci --currEdge; 398cb93a386Sopenharmony_ci } 399cb93a386Sopenharmony_ci 400cb93a386Sopenharmony_ci currData += dataWidth+1; 401cb93a386Sopenharmony_ci currEdge += dataWidth+1; 402cb93a386Sopenharmony_ci } 403cb93a386Sopenharmony_ci 404cb93a386Sopenharmony_ci // backwards in y 405cb93a386Sopenharmony_ci currData = dataPtr+dataWidth*(dataHeight-2) - 1; // skip outer buffer 406cb93a386Sopenharmony_ci currEdge = edgePtr+dataWidth*(dataHeight-2) - 1; 407cb93a386Sopenharmony_ci for (int j = 1; j < dataHeight-1; ++j) { 408cb93a386Sopenharmony_ci // forwards in x 409cb93a386Sopenharmony_ci for (int i = 1; i < dataWidth-1; ++i) { 410cb93a386Sopenharmony_ci // don't need to calculate distance for edge pixels 411cb93a386Sopenharmony_ci if (!*currEdge) { 412cb93a386Sopenharmony_ci B1(currData, dataWidth); 413cb93a386Sopenharmony_ci } 414cb93a386Sopenharmony_ci ++currData; 415cb93a386Sopenharmony_ci ++currEdge; 416cb93a386Sopenharmony_ci } 417cb93a386Sopenharmony_ci 418cb93a386Sopenharmony_ci // backwards in x 419cb93a386Sopenharmony_ci --currData; // reset to end 420cb93a386Sopenharmony_ci --currEdge; 421cb93a386Sopenharmony_ci for (int i = 1; i < dataWidth-1; ++i) { 422cb93a386Sopenharmony_ci // don't need to calculate distance for edge pixels 423cb93a386Sopenharmony_ci if (!*currEdge) { 424cb93a386Sopenharmony_ci B2(currData, dataWidth); 425cb93a386Sopenharmony_ci } 426cb93a386Sopenharmony_ci --currData; 427cb93a386Sopenharmony_ci --currEdge; 428cb93a386Sopenharmony_ci } 429cb93a386Sopenharmony_ci 430cb93a386Sopenharmony_ci currData -= dataWidth-1; 431cb93a386Sopenharmony_ci currEdge -= dataWidth-1; 432cb93a386Sopenharmony_ci } 433cb93a386Sopenharmony_ci 434cb93a386Sopenharmony_ci // copy results to final distance field data 435cb93a386Sopenharmony_ci currData = dataPtr + dataWidth+1; 436cb93a386Sopenharmony_ci currEdge = edgePtr + dataWidth+1; 437cb93a386Sopenharmony_ci unsigned char *dfPtr = distanceField; 438cb93a386Sopenharmony_ci for (int j = 1; j < dataHeight-1; ++j) { 439cb93a386Sopenharmony_ci for (int i = 1; i < dataWidth-1; ++i) { 440cb93a386Sopenharmony_ci#if DUMP_EDGE 441cb93a386Sopenharmony_ci float alpha = currData->fAlpha; 442cb93a386Sopenharmony_ci float edge = 0.0f; 443cb93a386Sopenharmony_ci if (*currEdge) { 444cb93a386Sopenharmony_ci edge = 0.25f; 445cb93a386Sopenharmony_ci } 446cb93a386Sopenharmony_ci // blend with original image 447cb93a386Sopenharmony_ci float result = alpha + (1.0f-alpha)*edge; 448cb93a386Sopenharmony_ci unsigned char val = sk_float_round2int(255*result); 449cb93a386Sopenharmony_ci *dfPtr++ = val; 450cb93a386Sopenharmony_ci#else 451cb93a386Sopenharmony_ci float dist; 452cb93a386Sopenharmony_ci if (currData->fAlpha > 0.5f) { 453cb93a386Sopenharmony_ci dist = -SkScalarSqrt(currData->fDistSq); 454cb93a386Sopenharmony_ci } else { 455cb93a386Sopenharmony_ci dist = SkScalarSqrt(currData->fDistSq); 456cb93a386Sopenharmony_ci } 457cb93a386Sopenharmony_ci *dfPtr++ = pack_distance_field_val<SK_DistanceFieldMagnitude>(dist); 458cb93a386Sopenharmony_ci#endif 459cb93a386Sopenharmony_ci ++currData; 460cb93a386Sopenharmony_ci ++currEdge; 461cb93a386Sopenharmony_ci } 462cb93a386Sopenharmony_ci currData += 2; 463cb93a386Sopenharmony_ci currEdge += 2; 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci 466cb93a386Sopenharmony_ci return true; 467cb93a386Sopenharmony_ci} 468cb93a386Sopenharmony_ci 469cb93a386Sopenharmony_ci// assumes an 8-bit image and distance field 470cb93a386Sopenharmony_cibool SkGenerateDistanceFieldFromA8Image(unsigned char* distanceField, 471cb93a386Sopenharmony_ci const unsigned char* image, 472cb93a386Sopenharmony_ci int width, int height, size_t rowBytes) { 473cb93a386Sopenharmony_ci SkASSERT(distanceField); 474cb93a386Sopenharmony_ci SkASSERT(image); 475cb93a386Sopenharmony_ci 476cb93a386Sopenharmony_ci // create temp data 477cb93a386Sopenharmony_ci SkAutoSMalloc<1024> copyStorage((width+2)*(height+2)*sizeof(char)); 478cb93a386Sopenharmony_ci unsigned char* copyPtr = (unsigned char*) copyStorage.get(); 479cb93a386Sopenharmony_ci 480cb93a386Sopenharmony_ci // we copy our source image into a padded copy to ensure we catch edge transitions 481cb93a386Sopenharmony_ci // around the outside 482cb93a386Sopenharmony_ci const unsigned char* currSrcScanLine = image; 483cb93a386Sopenharmony_ci sk_bzero(copyPtr, (width+2)*sizeof(char)); 484cb93a386Sopenharmony_ci unsigned char* currDestPtr = copyPtr + width + 2; 485cb93a386Sopenharmony_ci for (int i = 0; i < height; ++i) { 486cb93a386Sopenharmony_ci *currDestPtr++ = 0; 487cb93a386Sopenharmony_ci memcpy(currDestPtr, currSrcScanLine, width); 488cb93a386Sopenharmony_ci currSrcScanLine += rowBytes; 489cb93a386Sopenharmony_ci currDestPtr += width; 490cb93a386Sopenharmony_ci *currDestPtr++ = 0; 491cb93a386Sopenharmony_ci } 492cb93a386Sopenharmony_ci sk_bzero(currDestPtr, (width+2)*sizeof(char)); 493cb93a386Sopenharmony_ci 494cb93a386Sopenharmony_ci return generate_distance_field_from_image(distanceField, copyPtr, width, height); 495cb93a386Sopenharmony_ci} 496cb93a386Sopenharmony_ci 497cb93a386Sopenharmony_ci// assumes a 16-bit lcd mask and 8-bit distance field 498cb93a386Sopenharmony_cibool SkGenerateDistanceFieldFromLCD16Mask(unsigned char* distanceField, 499cb93a386Sopenharmony_ci const unsigned char* image, 500cb93a386Sopenharmony_ci int w, int h, size_t rowBytes) { 501cb93a386Sopenharmony_ci SkASSERT(distanceField); 502cb93a386Sopenharmony_ci SkASSERT(image); 503cb93a386Sopenharmony_ci 504cb93a386Sopenharmony_ci // create temp data 505cb93a386Sopenharmony_ci SkAutoSMalloc<1024> copyStorage((w+2)*(h+2)*sizeof(char)); 506cb93a386Sopenharmony_ci unsigned char* copyPtr = (unsigned char*) copyStorage.get(); 507cb93a386Sopenharmony_ci 508cb93a386Sopenharmony_ci // we copy our source image into a padded copy to ensure we catch edge transitions 509cb93a386Sopenharmony_ci // around the outside 510cb93a386Sopenharmony_ci const uint16_t* start = reinterpret_cast<const uint16_t*>(image); 511cb93a386Sopenharmony_ci auto currSrcScanline = SkMask::AlphaIter<SkMask::kLCD16_Format>(start); 512cb93a386Sopenharmony_ci auto endSrcScanline = SkMask::AlphaIter<SkMask::kLCD16_Format>(start + w); 513cb93a386Sopenharmony_ci sk_bzero(copyPtr, (w+2)*sizeof(char)); 514cb93a386Sopenharmony_ci unsigned char* currDestPtr = copyPtr + w + 2; 515cb93a386Sopenharmony_ci for (int i = 0; i < h; ++i, currSrcScanline >>= rowBytes, endSrcScanline >>= rowBytes) { 516cb93a386Sopenharmony_ci *currDestPtr++ = 0; 517cb93a386Sopenharmony_ci for (auto src = currSrcScanline; src < endSrcScanline; ++src) { 518cb93a386Sopenharmony_ci *currDestPtr++ = *src; 519cb93a386Sopenharmony_ci } 520cb93a386Sopenharmony_ci *currDestPtr++ = 0; 521cb93a386Sopenharmony_ci } 522cb93a386Sopenharmony_ci sk_bzero(currDestPtr, (w+2)*sizeof(char)); 523cb93a386Sopenharmony_ci 524cb93a386Sopenharmony_ci return generate_distance_field_from_image(distanceField, copyPtr, w, h); 525cb93a386Sopenharmony_ci} 526cb93a386Sopenharmony_ci 527cb93a386Sopenharmony_ci// assumes a 1-bit image and 8-bit distance field 528cb93a386Sopenharmony_cibool SkGenerateDistanceFieldFromBWImage(unsigned char* distanceField, 529cb93a386Sopenharmony_ci const unsigned char* image, 530cb93a386Sopenharmony_ci int width, int height, size_t rowBytes) { 531cb93a386Sopenharmony_ci SkASSERT(distanceField); 532cb93a386Sopenharmony_ci SkASSERT(image); 533cb93a386Sopenharmony_ci 534cb93a386Sopenharmony_ci // create temp data 535cb93a386Sopenharmony_ci SkAutoSMalloc<1024> copyStorage((width+2)*(height+2)*sizeof(char)); 536cb93a386Sopenharmony_ci unsigned char* copyPtr = (unsigned char*) copyStorage.get(); 537cb93a386Sopenharmony_ci 538cb93a386Sopenharmony_ci // we copy our source image into a padded copy to ensure we catch edge transitions 539cb93a386Sopenharmony_ci // around the outside 540cb93a386Sopenharmony_ci const unsigned char* currSrcScanLine = image; 541cb93a386Sopenharmony_ci sk_bzero(copyPtr, (width+2)*sizeof(char)); 542cb93a386Sopenharmony_ci unsigned char* currDestPtr = copyPtr + width + 2; 543cb93a386Sopenharmony_ci for (int i = 0; i < height; ++i) { 544cb93a386Sopenharmony_ci *currDestPtr++ = 0; 545cb93a386Sopenharmony_ci 546cb93a386Sopenharmony_ci int rowWritesLeft = width; 547cb93a386Sopenharmony_ci const unsigned char *maskPtr = currSrcScanLine; 548cb93a386Sopenharmony_ci while (rowWritesLeft > 0) { 549cb93a386Sopenharmony_ci unsigned mask = *maskPtr++; 550cb93a386Sopenharmony_ci for (int j = 7; j >= 0 && rowWritesLeft; --j, --rowWritesLeft) { 551cb93a386Sopenharmony_ci *currDestPtr++ = (mask & (1 << j)) ? 0xff : 0; 552cb93a386Sopenharmony_ci } 553cb93a386Sopenharmony_ci } 554cb93a386Sopenharmony_ci currSrcScanLine += rowBytes; 555cb93a386Sopenharmony_ci 556cb93a386Sopenharmony_ci *currDestPtr++ = 0; 557cb93a386Sopenharmony_ci } 558cb93a386Sopenharmony_ci sk_bzero(currDestPtr, (width+2)*sizeof(char)); 559cb93a386Sopenharmony_ci 560cb93a386Sopenharmony_ci return generate_distance_field_from_image(distanceField, copyPtr, width, height); 561cb93a386Sopenharmony_ci} 562