Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(260)

Unified Diff: src/gpu/GrTextStrike.cpp

Issue 41213003: Hook in rough distance field support for fonts (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/gpu/GrTextStrike.cpp
diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp
index e21fd482757d6ae446eef1e3b767b0bff6e975c0..2165a70779bc59d870d4464a8c9e649d542d8b11 100644
--- a/src/gpu/GrTextStrike.cpp
+++ b/src/gpu/GrTextStrike.cpp
@@ -22,6 +22,8 @@ SK_DEFINE_INST_COUNT(GrKey)
static int g_PurgeCount = 0;
#endif
+void edtaa2(unsigned char *img, int w, int h, short *distx, short *disty, float *dist);
+
GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
gpu->ref();
for (int i = 0; i < kMaskFormatCount; ++i) {
@@ -194,6 +196,9 @@ void GrFontCache::dump() const {
static int gCounter;
#endif
+#define DISTANCE_FIELD_PAD 4
+#define DISTANCE_FIELD_RANGE (4.f)
+
/*
The text strike is specific to a given font/style/matrix setup, which is
represented by the GrHostFontScaler object we are given in getGlyph().
@@ -247,6 +252,13 @@ GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
}
GrGlyph* glyph = fPool.alloc();
+ // expand bounds to hold full distance field data
+ if (fUseDistanceField) {
+ bounds.fLeft -= DISTANCE_FIELD_PAD;
+ bounds.fRight += DISTANCE_FIELD_PAD;
+ bounds.fTop -= DISTANCE_FIELD_PAD;
+ bounds.fBottom += DISTANCE_FIELD_PAD;
+ }
glyph->init(packed, bounds);
fCache.insert(packed, glyph);
return glyph;
@@ -257,6 +269,7 @@ bool GrTextStrike::removeUnusedPlots() {
return fAtlasMgr->removeUnusedPlots(&fAtlas);
}
+
bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
#if 0 // testing hack to force us to flush our cache often
static int gCounter;
@@ -271,18 +284,99 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
SkAutoRef ar(scaler);
int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
- size_t size = glyph->fBounds.area() * bytesPerPixel;
- SkAutoSMalloc<1024> storage(size);
- if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
- glyph->height(),
- glyph->width() * bytesPerPixel,
- storage.get())) {
- return false;
+
+ GrPlot* plot;
+ if (fUseDistanceField) {
+ SkASSERT(1 == bytesPerPixel);
+
+ // we've already expanded the glyph dimensions to match the final size
+ // but must shrink back down to get the packed glyph data
+ int dfwidth = glyph->width();
+ int dfheight = glyph->height();
+ int width = dfwidth - 2*DISTANCE_FIELD_PAD;
+ int height = dfheight - 2*DISTANCE_FIELD_PAD;
+ size_t stride = width*bytesPerPixel;
+
+ size_t size = width * height * bytesPerPixel;
+ SkAutoSMalloc<1024> storage(size);
+ if (!scaler->getPackedGlyphImage(glyph->fPackedID, width, height, stride, storage.get())) {
+ return false;
+ }
+
+ // alloc storage for distance field glyph
+ size_t dfsize = dfwidth * dfheight * bytesPerPixel;
+ SkAutoSMalloc<1024> dfstorage(dfsize);
+
+ // copy glyph into distance field storage
+ memset(dfstorage.get(), 0, dfsize);
+
+ unsigned char* ptr = (unsigned char*) storage.get();
+ unsigned char* dfptr = (unsigned char*) dfstorage.get();
+ size_t dfstride = dfwidth*bytesPerPixel;
+ dfptr += DISTANCE_FIELD_PAD*dfstride;
+ dfptr += DISTANCE_FIELD_PAD*bytesPerPixel;
+
+ for (int i = 0; i < height; ++i) {
+ memcpy(dfptr, ptr, stride);
+
+ dfptr += dfstride;
+ ptr += stride;
+ }
+
+ // generate distance field data
+ short* distx = new short[dfwidth*dfheight];
+ short* disty = new short[dfwidth*dfheight];
+ float* outerdist = new float[dfwidth*dfheight];
+ dfptr = (unsigned char*) dfstorage.get();
+ edtaa2(dfptr, dfwidth, dfheight, distx, disty, outerdist);
+
+ for (int i = 0; i < dfwidth*dfheight; ++i) {
+ *dfptr++ = 255 - *dfptr;
+ }
+ float* innerdist = new float[dfwidth*dfheight];
+ dfptr = (unsigned char*) dfstorage.get();
+ edtaa2(dfptr, dfwidth, dfheight, distx, disty, innerdist);
+
+ for (int i = 0; i < dfwidth*dfheight; ++i) {
+ unsigned char val;
+ float outerval = outerdist[i];
+ if (outerval < 0.0f) { outerval = 0.0f; }
+ float innerval = innerdist[i];
+ if (innerval < 0.0f) { innerval = 0.0f; }
+ float dist = outerval - innerval;
+ if (dist <= -DISTANCE_FIELD_RANGE) {
+ val = 255;
+ } else if (dist > DISTANCE_FIELD_RANGE) {
+ val = 0;
+ } else {
+ val = (unsigned char)((DISTANCE_FIELD_RANGE-dist)*128.f/DISTANCE_FIELD_RANGE);
+ }
+ *dfptr++ = val;
+ }
+
+ delete [] distx;
+ delete [] disty;
+ delete [] innerdist;
+ delete [] outerdist;
+
+ // copy to atlas
+ plot = fAtlasMgr->addToAtlas(&fAtlas, dfwidth, dfheight, dfstorage.get(),
+ &glyph->fAtlasLocation);
+
+ } else {
+ size_t size = glyph->fBounds.area() * bytesPerPixel;
+ SkAutoSMalloc<1024> storage(size);
+ if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
+ glyph->height(),
+ glyph->width() * bytesPerPixel,
+ storage.get())) {
+ return false;
+ }
+
+ plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), glyph->height(), storage.get(),
+ &glyph->fAtlasLocation);
}
- GrPlot* plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
- glyph->height(), storage.get(),
- &glyph->fAtlasLocation);
if (NULL == plot) {
return false;
}
@@ -290,3 +384,463 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
glyph->fPlot = plot;
return true;
}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * A hairy function to approximate the distance to an edge in a certain pixel,
+ * with consideration to both the direction to the pixel, the local gradient
+ * at that pixel and its greyscale value.
+ * This function is complicated enough to warrant a function call for each
+ * evaluation. Inlining it with a macro would only obfuscate the code.
+ */
+static float distaa2(unsigned char *img, int w, int c, int xc, int yc, int xi, int yi)
+{
+ float di, df, gx, gy, a, a1, temp;
+
+ a = (float)(img[c-xc-yc*w])/255.f; // Grayscale value at the edge pixel pointed to
+ if(a < 0.0f) a = 0.0f; // Clip grayscale values outside the range [0,1]
+ if(a == 0.0f) return 1000000.0f; // Not an object pixel, return "very far" ("don't know yet")
+ if(a > 1.0) a = 1.0f;
+ df = 0.5f-a; // Linear function is correct if gx==0 or gy==0
+
+ gx = (float)abs(xi); // Move to first quadrant gx>=0, gy>=0 (sign symmetry)
+ gy = (float)abs(yi);
+ di = sqrtf(gx*gx+gy*gy); // Length of integer vector, like a traditional EDT
+ if ((gx > 0) && (gy > 0)) { // Neither gx nor gy are zero
+ gx = gx / di;
+ gy = gy / di;
+ if(gx<gy) { // Move to first octant gx >= gy (transposition symmetry)
+ temp = gx;
+ gx = gy;
+ gy = temp;
+ }
+ a1 = 0.5f*gy/gx; // area of corner triangle
+ if (a < a1) // 0 <= a < a1, square root characteristic
+ df = 0.5f*(gx + gy) - sqrtf(2.0f*a*gx*gy);
+ else if (a <= (1.0f-a1)) // a1 <= a <= 1-a1, linear characteristic
+ df = (0.5f-a)*gx;
+ else // a1 < a <= 1, square root characteristic
+ df = -0.5f*(gx + gy) + sqrtf(2.0f*(1.0f-a)*gx*gy);
+ }
+ return di + df; // Negative at edge pixels (di=0) with a>0.5 (df<0)
+}
+
+// Shorthand macro: add ubiquitous parameters dist, img and w and call distaa2()
+#define DISTAA2(c,xc,yc,xi,yi) (distaa2(img, w, c, xc, yc, xi, yi))
+
+static void edtaa2(unsigned char *img, int w, int h, short *distx, short *disty, float *dist)
+{
+ int x, y, i, c;
+ int offset_u, offset_ur, offset_r, offset_rd,
+ offset_d, offset_dl, offset_l, offset_lu;
+ float olddist, newdist;
+ int cdistx, cdisty, newdistx, newdisty;
+ int changed;
+
+ /* Initialize index offsets for the current image width */
+ offset_u = -w;
+ offset_ur = -w+1;
+ offset_r = 1;
+ offset_rd = w+1;
+ offset_d = w;
+ offset_dl = w-1;
+ offset_l = -1;
+ offset_lu = -w-1;
+
+ /* Initialize the distance images */
+ for(i=0; i<w*h; i++) {
+ float val = (float)(img[i])/255.f;
+ distx[i] = 0; // At first, all pixels point to
+ disty[i] = 0; // themselves as the closest known.
+ if(val == 0)
+ {
+ dist[i]= 1000000.0f; // Big value, means "not set yet"
+ }
+ else if (val < 1.0f) {
+ dist[i] = 0.5f-val; // Reasonable local guess
+ }
+ else {
+ dist[i]= 0.0f; // Inside the object
+ }
+ }
+
+ /* Perform the transformation */
+ do
+ {
+ changed = 0;
+
+ /* Scan rows, except first row */
+ for(y=1; y<h; y++)
+ {
+
+ /* move index to leftmost pixel of current row */
+ i = y*w;
+
+ /* scan right, propagate distances from above & left */
+
+ /* Leftmost pixel is special, has no left neighbors */
+ olddist = dist[i];
+ if(olddist > 0) // If non-zero distance or not set yet
+ {
+ c = i + offset_u; // Index of candidate for testing
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx;
+ newdisty = cdisty+1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_ur;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx-1;
+ newdisty = cdisty+1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ changed = 1;
+ }
+ }
+ i++;
+
+ /* Middle pixels have all neighbors */
+ for(x=1; x<w-1; x++, i++)
+ {
+ olddist = dist[i];
+ if(olddist <= 0) continue; // No need to update further
+
+ c = i+offset_l;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx+1;
+ newdisty = cdisty;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_lu;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx+1;
+ newdisty = cdisty+1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_u;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx;
+ newdisty = cdisty+1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_ur;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx-1;
+ newdisty = cdisty+1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ changed = 1;
+ }
+ }
+
+ /* Rightmost pixel of row is special, has no right neighbors */
+ olddist = dist[i];
+ if(olddist > 0) // If not already zero distance
+ {
+ c = i+offset_l;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx+1;
+ newdisty = cdisty;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_lu;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx+1;
+ newdisty = cdisty+1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_u;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx;
+ newdisty = cdisty+1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ changed = 1;
+ }
+ }
+
+ /* Move index to second rightmost pixel of current row. */
+ /* Rightmost pixel is skipped, it has no right neighbor. */
+ i = y*w + w-2;
+
+ /* scan left, propagate distance from right */
+ for(x=w-2; x>=0; x--, i--)
+ {
+ olddist = dist[i];
+ if(olddist <= 0) continue; // Already zero distance
+
+ c = i+offset_r;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx-1;
+ newdisty = cdisty;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ changed = 1;
+ }
+ }
+ }
+
+ /* Scan rows in reverse order, except last row */
+ for(y=h-2; y>=0; y--)
+ {
+ /* move index to rightmost pixel of current row */
+ i = y*w + w-1;
+
+ /* Scan left, propagate distances from below & right */
+
+ /* Rightmost pixel is special, has no right neighbors */
+ olddist = dist[i];
+ if(olddist > 0) // If not already zero distance
+ {
+ c = i+offset_d;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx;
+ newdisty = cdisty-1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_dl;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx+1;
+ newdisty = cdisty-1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ changed = 1;
+ }
+ }
+ i--;
+
+ /* Middle pixels have all neighbors */
+ for(x=w-2; x>0; x--, i--)
+ {
+ olddist = dist[i];
+ if(olddist <= 0) continue; // Already zero distance
+
+ c = i+offset_r;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx-1;
+ newdisty = cdisty;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_rd;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx-1;
+ newdisty = cdisty-1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_d;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx;
+ newdisty = cdisty-1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_dl;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx+1;
+ newdisty = cdisty-1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ changed = 1;
+ }
+ }
+ /* Leftmost pixel is special, has no left neighbors */
+ olddist = dist[i];
+ if(olddist > 0) // If not already zero distance
+ {
+ c = i+offset_r;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx-1;
+ newdisty = cdisty;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_rd;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx-1;
+ newdisty = cdisty-1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ olddist=newdist;
+ changed = 1;
+ }
+
+ c = i+offset_d;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx;
+ newdisty = cdisty-1;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ changed = 1;
+ }
+ }
+
+ /* Move index to second leftmost pixel of current row. */
+ /* Leftmost pixel is skipped, it has no left neighbor. */
+ i = y*w + 1;
+ for(x=1; x<w; x++, i++)
+ {
+ /* scan right, propagate distance from left */
+ olddist = dist[i];
+ if(olddist <= 0) continue; // Already zero distance
+
+ c = i+offset_l;
+ cdistx = distx[c];
+ cdisty = disty[c];
+ newdistx = cdistx+1;
+ newdisty = cdisty;
+ newdist = DISTAA2(c, cdistx, cdisty, newdistx, newdisty);
+ if(newdist < olddist)
+ {
+ distx[i]=newdistx;
+ disty[i]=newdisty;
+ dist[i]=newdist;
+ changed = 1;
+ }
+ }
+ }
+ }
+ while(changed); // Sweep until no more updates are made
+
+ /* The transformation is completed. */
+
+}
+
« src/core/SkDraw.cpp ('K') | « src/gpu/GrTextStrike.h ('k') | src/gpu/GrTextStrike_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698