| 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. */
|
| +
|
| +}
|
| +
|
|
|