Hook in rough distance field support for fonts

R=bsalomon@google.com

Review URL: https://codereview.chromium.org/41213003

git-svn-id: http://skia.googlecode.com/svn/trunk@12229 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
jvanverth@google.com 2013-11-11 20:54:09 +00:00
parent 05edd02a7c
commit d830d13c27
19 changed files with 3296 additions and 1857 deletions

View File

@ -27,6 +27,17 @@
],
},
],
[ 'skia_distancefield_fonts == 1',
{
'defines': [
'SK_DISTANCEFIELD_FONTS=1',
],
}, {
'defines': [
'SK_DISTANCEFIELD_FONTS=0',
],
},
],
[ 'skia_os == "win"',
{
'defines': [

View File

@ -94,6 +94,7 @@
'skia_win_debuggers_path%': '',
'skia_shared_lib%': 0,
'skia_opencl%': 0,
'skia_distancefield_fonts%': 0,
# These variables determine the default optimization level for different
# compilers.
@ -153,6 +154,7 @@
'skia_profile_enabled%': '<(skia_profile_enabled)',
'skia_shared_lib%': '<(skia_shared_lib)',
'skia_opencl%': '<(skia_opencl)',
'skia_distancefield_fonts%': '<(skia_distancefield_fonts)',
'skia_static_initializers%': '<(skia_static_initializers)',
'ios_sdk_version%': '6.0',
'skia_win_debuggers_path%': '<(skia_win_debuggers_path)',

38
gyp/edtaa.gyp Executable file
View File

@ -0,0 +1,38 @@
#
# Copyright 2013 Google Inc.
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
{
'targets': [
{
'target_name': 'edtaa',
'type': 'none',
'conditions': [
[ 'skia_distancefield_fonts', {
'type': 'static_library',
'sources': [
'../third_party/edtaa/edtaa3func.cpp',
],
'include_dirs': [
'../third_party/edtaa/',
],
'all_dependent_settings': {
'include_dirs': [
'../third_party/edtaa/',
],
},
}],
],
},
],
}
# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:

View File

@ -83,6 +83,7 @@
'dependencies': [
'angle.gyp:*',
'core.gyp:*',
'edtaa.gyp:*',
'utils.gyp:*',
],
'includes': [
@ -132,6 +133,17 @@
'GR_ANDROID_PATH_RENDERING=1',
],
}],
[ 'skia_distancefield_fonts', {
'sources': [
'<(skia_include_path)/gpu/GrDistanceFieldTextContext.h',
'<(skia_src_path)/gpu/GrDistanceFieldTextContext.cpp',
'<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.cpp',
'<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.h',
],
'defines': [
'GR_DISTANCEFIELD_FONTS=1',
],
}],
[ 'skia_os == "linux" or skia_os == "chromeos"', {
'sources!': [
'../src/gpu/gl/GrGLDefaultInterface_none.cpp',

View File

@ -0,0 +1,46 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrDistanceFieldTextContext_DEFINED
#define GrDistanceFieldTextContext_DEFINED
#include "GrTextContext.h"
class GrTextStrike;
/*
* This class implements GrTextContext using distance field fonts
*/
class GrDistanceFieldTextContext : public GrTextContext {
public:
GrDistanceFieldTextContext(GrContext*, const GrPaint&, SkColor, SkScalar textRatio);
virtual ~GrDistanceFieldTextContext();
virtual void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top,
GrFontScaler*) SK_OVERRIDE;
private:
GrTextStrike* fStrike;
SkScalar fTextRatio;
void flushGlyphs(); // automatically called by destructor
enum {
kMinRequestedGlyphs = 1,
kDefaultRequestedGlyphs = 64,
kMinRequestedVerts = kMinRequestedGlyphs * 4,
kDefaultRequestedVerts = kDefaultRequestedGlyphs * 4,
};
SkColor fSkPaintColor;
SkPoint* fVertices;
int32_t fMaxVertices;
GrTexture* fCurrTexture;
int fCurrVertex;
};
#endif

View File

@ -1727,10 +1727,30 @@ void SkDraw::drawText(const char text[], size_t byteLength,
SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
#if SK_DISTANCEFIELD_FONTS
const SkMatrix* ctm = fMatrix;
const SkPaint* paintRef = &paint;
SkPaint paintCopy;
uint32_t procFlags = fProcs ? fProcs->fFlags : 0;
if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
paintCopy = paint;
paintCopy.setTextSize(SkDrawProcs::kBaseDFFontSize);
paintCopy.setLCDRenderText(false);
paintRef = &paintCopy;
}
if (procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag) {
ctm = NULL;
}
SkAutoGlyphCache autoCache(*paintRef, &fDevice->fLeakyProperties, ctm);
#else
SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
#endif
SkGlyphCache* cache = autoCache.getCache();
// transform our starting point
#if SK_DISTANCEFIELD_FONTS
if (!(procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag))
#endif
{
SkPoint loc;
fMatrix->mapXY(x, y, &loc);
@ -1789,16 +1809,41 @@ void SkDraw::drawText(const char text[], size_t byteLength,
SkFixed fx = SkScalarToFixed(x) + d1g.fHalfSampleX;
SkFixed fy = SkScalarToFixed(y) + d1g.fHalfSampleY;
#if SK_DISTANCEFIELD_FONTS
SkFixed fixedScale;
if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
fixedScale = SkScalarToFixed(paint.getTextSize()/(float)SkDrawProcs::kBaseDFFontSize);
}
#endif
while (text < stop) {
const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
#if SK_DISTANCEFIELD_FONTS
if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
fx += SkFixedMul_portable(autokern.adjust(glyph), fixedScale);
} else {
fx += autokern.adjust(glyph);
}
#else
fx += autokern.adjust(glyph);
#endif
if (glyph.fWidth) {
proc(d1g, fx, fy, glyph);
}
#if SK_DISTANCEFIELD_FONTS
if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
} else {
fx += glyph.fAdvanceX;
fy += glyph.fAdvanceY;
}
#else
fx += glyph.fAdvanceX;
fy += glyph.fAdvanceY;
#endif
}
}
@ -1956,7 +2001,23 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
}
SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
#if SK_DISTANCEFIELD_FONTS
const SkMatrix* ctm = fMatrix;
const SkPaint* paintRef = &paint;
SkPaint paintCopy;
uint32_t procFlags = fProcs ? fProcs->fFlags : 0;
if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
paintCopy = paint;
paintCopy.setTextSize(SkDrawProcs::kBaseDFFontSize);
paintRef = &paintCopy;
}
if (procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag) {
ctm = NULL;
}
SkAutoGlyphCache autoCache(*paintRef, &fDevice->fLeakyProperties, ctm);
#else
SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
#endif
SkGlyphCache* cache = autoCache.getCache();
SkAAClipBlitterWrapper wrapper;
@ -1998,8 +2059,16 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
if (SkPaint::kLeft_Align == paint.getTextAlign()) {
while (text < stop) {
#if SK_DISTANCEFIELD_FONTS
if (procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag) {
tms.fLoc.fX = *pos;
tms.fLoc.fY = *(pos+1);
} else {
tmsProc(tms, pos);
}
#else
tmsProc(tms, pos);
#endif
SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + d1g.fHalfSampleX;
SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + d1g.fHalfSampleY;

View File

@ -57,6 +57,22 @@ struct SkDraw1Glyph {
struct SkDrawProcs {
SkDraw1Glyph::Proc fD1GProc;
#if SK_DISTANCEFIELD_FONTS
uint32_t fFlags;
enum Flags {
/**
* Disable baked glyph transforms
*/
kSkipBakedGlyphTransform_Flag = 0x1,
/**
* Scale glyphs to get different point sizes
*/
kUseScaledGlyphs_Flag = 0x2,
};
static const int kBaseDFFontSize = 32;
#endif
};
/**

View File

@ -2234,6 +2234,9 @@ static void text_draw_init(const SkPaint& paint,
SkBitSet& glyphsUsed,
SkDraw& myDraw, SkXPSDrawProcs& procs) {
procs.fD1GProc = xps_draw_1_glyph;
#if SK_DISTANCEFIELD_FONTS
procs.fFlags = 0;
#endif
size_t numGlyphGuess;
switch (paint.getTextEncoding()) {
case SkPaint::kUTF8_TextEncoding:

View File

@ -123,7 +123,11 @@ void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
return;
}
if (NULL == fStrike) {
#if SK_DISTANCEFIELD_FONTS
fStrike = fContext->getFontCache()->getStrike(scaler, true);
#else
fStrike = fContext->getFontCache()->getStrike(scaler);
#endif
}
GrGlyph* glyph = fStrike->getGlyph(packed, scaler);

View File

@ -0,0 +1,278 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrDistanceFieldTextContext.h"
#include "GrAtlas.h"
#include "GrDrawTarget.h"
#include "GrFontScaler.h"
#include "GrIndexBuffer.h"
#include "GrTextStrike.h"
#include "GrTextStrike_impl.h"
#include "SkPath.h"
#include "SkRTConf.h"
#include "SkStrokeRec.h"
#include "effects/GrDistanceFieldTextureEffect.h"
static const int kGlyphCoordsAttributeIndex = 1;
SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
"Dump the contents of the font cache before every purge.");
GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
const GrPaint& paint,
SkColor color,
SkScalar textRatio)
: GrTextContext(context, paint)
, fTextRatio(textRatio) {
fSkPaintColor = color;
fStrike = NULL;
fCurrTexture = NULL;
fCurrVertex = 0;
fVertices = NULL;
fMaxVertices = 0;
}
GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
this->flushGlyphs();
}
static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
unsigned r = SkColorGetR(c);
unsigned g = SkColorGetG(c);
unsigned b = SkColorGetB(c);
return GrColorPackRGBA(r, g, b, 0xff);
}
void GrDistanceFieldTextContext::flushGlyphs() {
if (NULL == fDrawTarget) {
return;
}
GrDrawState* drawState = fDrawTarget->drawState();
GrDrawState::AutoRestoreEffects are(drawState);
drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
if (fCurrVertex > 0) {
// setup our sampler state for our text texture/atlas
SkASSERT(GrIsALIGN4(fCurrVertex));
SkASSERT(fCurrTexture);
GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
// This effect could be stored with one of the cache objects (atlas?)
drawState->addCoverageEffect(
GrDistanceFieldTextureEffect::Create(fCurrTexture, params),
kGlyphCoordsAttributeIndex)->unref();
if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
fPaint.numColorStages()) {
GrPrintf("LCD Text will not draw correctly.\n");
}
// We don't use the GrPaint's color in this case because it's been premultiplied by
// alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
// the mask texture color. The end result is that we get
// mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
int a = SkColorGetA(fSkPaintColor);
// paintAlpha
drawState->setColor(SkColorSetARGB(a, a, a, a));
// paintColor
drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor));
drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
} else {
// set back to normal in case we took LCD path previously.
drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
drawState->setColor(fPaint.getColor());
}
int nGlyphs = fCurrVertex / 4;
fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
nGlyphs,
4, 6);
fDrawTarget->resetVertexSource();
fVertices = NULL;
fMaxVertices = 0;
fCurrVertex = 0;
SkSafeSetNull(fCurrTexture);
}
}
namespace {
// position + texture coord
extern const GrVertexAttrib gTextVertexAttribs[] = {
{kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
{kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
};
};
void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
GrFixed vx, GrFixed vy,
GrFontScaler* scaler) {
if (NULL == fDrawTarget) {
return;
}
if (NULL == fStrike) {
fStrike = fContext->getFontCache()->getStrike(scaler, true);
}
GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
if (NULL == glyph || glyph->fBounds.isEmpty()) {
return;
}
SkScalar sx = SkFixedToScalar(vx);
SkScalar sy = SkFixedToScalar(vy);
/*
// not valid, need to find a different solution for this
vx += SkIntToFixed(glyph->fBounds.fLeft);
vy += SkIntToFixed(glyph->fBounds.fTop);
// keep them as ints until we've done the clip-test
GrFixed width = glyph->fBounds.width();
GrFixed height = glyph->fBounds.height();
// check if we clipped out
if (true || NULL == glyph->fPlot) {
int x = vx >> 16;
int y = vy >> 16;
if (fClipRect.quickReject(x, y, x + width, y + height)) {
// SkCLZ(3); // so we can set a break-point in the debugger
return;
}
}
*/
if (NULL == glyph->fPlot) {
if (fStrike->getGlyphAtlas(glyph, scaler)) {
goto HAS_ATLAS;
}
// try to clear out an unused plot before we flush
fContext->getFontCache()->freePlotExceptFor(fStrike);
if (fStrike->getGlyphAtlas(glyph, scaler)) {
goto HAS_ATLAS;
}
if (c_DumpFontCache) {
#ifdef SK_DEVELOPER
fContext->getFontCache()->dump();
#endif
}
// before we purge the cache, we must flush any accumulated draws
this->flushGlyphs();
fContext->flush();
// try to purge
fContext->getFontCache()->purgeExceptFor(fStrike);
// need to use new flush count here
if (fStrike->getGlyphAtlas(glyph, scaler)) {
goto HAS_ATLAS;
}
if (NULL == glyph->fPath) {
SkPath* path = SkNEW(SkPath);
if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
// flag the glyph as being dead?
delete path;
return;
}
glyph->fPath = path;
}
GrContext::AutoMatrix am;
SkMatrix translate;
translate.setTranslate(sx, sy);
GrPaint tmpPaint(fPaint);
am.setPreConcat(fContext, translate, &tmpPaint);
SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
return;
}
HAS_ATLAS:
SkASSERT(glyph->fPlot);
GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
glyph->fPlot->setDrawToken(drawToken);
GrTexture* texture = glyph->fPlot->texture();
SkASSERT(texture);
if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
this->flushGlyphs();
fCurrTexture = texture;
fCurrTexture->ref();
}
if (NULL == fVertices) {
// If we need to reserve vertices allow the draw target to suggest
// a number of verts to reserve and whether to perform a flush.
fMaxVertices = kMinRequestedVerts;
fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
SK_ARRAY_COUNT(gTextVertexAttribs));
bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
if (flush) {
this->flushGlyphs();
fContext->flush();
fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
SK_ARRAY_COUNT(gTextVertexAttribs));
}
fMaxVertices = kDefaultRequestedVerts;
// ignore return, no point in flushing again.
fDrawTarget->geometryHints(&fMaxVertices, NULL);
int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
if (fMaxVertices < kMinRequestedVerts) {
fMaxVertices = kDefaultRequestedVerts;
} else if (fMaxVertices > maxQuadVertices) {
// don't exceed the limit of the index buffer
fMaxVertices = maxQuadVertices;
}
bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
0,
GrTCast<void**>(&fVertices),
NULL);
GrAlwaysAssert(success);
SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
}
SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
SkScalar width = SkIntToScalar(glyph->fBounds.width());
SkScalar height = SkIntToScalar(glyph->fBounds.height());
SkScalar scale = fTextRatio;
dx *= scale;
dy *= scale;
sx += dx;
sy += dy;
width *= scale;
height *= scale;
GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
GrFixed tw = SkIntToFixed(glyph->fBounds.width());
GrFixed th = SkIntToFixed(glyph->fBounds.height());
fVertices[2*fCurrVertex].setRectFan(sx,
sy,
sx + width,
sy + height,
2 * sizeof(SkPoint));
fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
SkFixedToFloat(texture->normalizeFixedY(ty)),
SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
SkFixedToFloat(texture->normalizeFixedY(ty + th)),
2 * sizeof(SkPoint));
fCurrVertex += 4;
}

View File

@ -12,6 +12,10 @@
#include "GrTextStrike_impl.h"
#include "SkString.h"
#if SK_DISTANCEFIELD_FONTS
#include "edtaa3.h"
#endif
SK_DEFINE_INST_COUNT(GrFontScaler)
SK_DEFINE_INST_COUNT(GrKey)
@ -193,6 +197,11 @@ void GrFontCache::dump() const {
static int gCounter;
#endif
#if SK_DISTANCEFIELD_FONTS
#define DISTANCE_FIELD_PAD 4
#define DISTANCE_FIELD_RANGE (4.0)
#endif
/*
The text strike is specific to a given font/style/matrix setup, which is
represented by the GrHostFontScaler object we are given in getGlyph().
@ -246,6 +255,15 @@ GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
}
GrGlyph* glyph = fPool.alloc();
#if SK_DISTANCEFIELD_FONTS
// 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;
}
#endif
glyph->init(packed, bounds);
fCache.insert(packed, glyph);
return glyph;
@ -256,6 +274,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;
@ -270,18 +289,118 @@ 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 = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
glyph->height(), storage.get(),
&glyph->fAtlasLocation);
GrPlot* plot;
#if SK_DISTANCEFIELD_FONTS
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
sk_bzero(dfStorage.get(), 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
SkAutoSMalloc<1024> distXStorage(dfWidth*dfHeight*sizeof(short));
SkAutoSMalloc<1024> distYStorage(dfWidth*dfHeight*sizeof(short));
SkAutoSMalloc<1024> outerDistStorage(dfWidth*dfHeight*sizeof(double));
SkAutoSMalloc<1024> innerDistStorage(dfWidth*dfHeight*sizeof(double));
SkAutoSMalloc<1024> gxStorage(dfWidth*dfHeight*sizeof(double));
SkAutoSMalloc<1024> gyStorage(dfWidth*dfHeight*sizeof(double));
short* distX = (short*) distXStorage.get();
short* distY = (short*) distYStorage.get();
double* outerDist = (double*) outerDistStorage.get();
double* innerDist = (double*) innerDistStorage.get();
double* gx = (double*) gxStorage.get();
double* gy = (double*) gyStorage.get();
dfPtr = (unsigned char*) dfStorage.get();
EDTAA::computegradient(dfPtr, dfWidth, dfHeight, gx, gy);
EDTAA::edtaa3(dfPtr, gx, gy, dfWidth, dfHeight, distX, distY, outerDist);
for (int i = 0; i < dfWidth*dfHeight; ++i) {
*dfPtr = 255 - *dfPtr;
dfPtr++;
}
dfPtr = (unsigned char*) dfStorage.get();
sk_bzero(gx, sizeof(double)*dfWidth*dfHeight);
sk_bzero(gy, sizeof(double)*dfWidth*dfHeight);
EDTAA::computegradient(dfPtr, dfWidth, dfHeight, gx, gy);
EDTAA::edtaa3(dfPtr, gx, gy, dfWidth, dfHeight, distX, distY, innerDist);
for (int i = 0; i < dfWidth*dfHeight; ++i) {
unsigned char val;
double outerval = outerDist[i];
if (outerval < 0.0) {
outerval = 0.0;
}
double innerval = innerDist[i];
if (innerval < 0.0) {
innerval = 0.0;
}
double 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.0/DISTANCE_FIELD_RANGE);
}
*dfPtr++ = val;
}
// copy to atlas
plot = fAtlasMgr->addToAtlas(&fAtlas, dfWidth, dfHeight, dfStorage.get(),
&glyph->fAtlasLocation);
} else {
#endif
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);
#if SK_DISTANCEFIELD_FONTS
}
#endif
if (NULL == plot) {
return false;
}
@ -289,3 +408,4 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
glyph->fPlot = plot;
return true;
}

View File

@ -61,9 +61,12 @@ private:
GrFontCache* fFontCache;
GrAtlasMgr* fAtlasMgr;
GrAtlas fAtlas;
GrMaskFormat fMaskFormat;
#if SK_DISTANCEFIELD_FONTS
bool fUseDistanceField;
#endif
GrAtlas fAtlas;
GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
@ -75,7 +78,11 @@ public:
GrFontCache(GrGpu*);
~GrFontCache();
#if SK_DISTANCEFIELD_FONTS
inline GrTextStrike* getStrike(GrFontScaler*, bool useDistanceField);
#else
inline GrTextStrike* getStrike(GrFontScaler*);
#endif
void freeAll();

View File

@ -48,7 +48,11 @@ void GrFontCache::detachStrikeFromList(GrTextStrike* strike) {
}
}
#if SK_DISTANCEFIELD_FONTS
GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler, bool useDistanceField) {
#else
GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
#endif
this->validate();
const Key key(scaler->getKey());
@ -65,7 +69,9 @@ GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
strike->fPrev = NULL;
fHead = strike;
}
#if SK_DISTANCEFIELD_FONTS
strike->fUseDistanceField = useDistanceField;
#endif
this->validate();
return strike;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrDistanceFieldTextureEffect.h"
#include "gl/GrGLEffect.h"
#include "gl/GrGLSL.h"
#include "gl/GrGLTexture.h"
#include "gl/GrGLVertexEffect.h"
#include "GrTBackendEffectFactory.h"
#include "GrTexture.h"
// The distance field is constructed as unsigned char values, so that the zero value is at 128.
// Hence our zero threshold is 128/255.
#define THRESHOLD "0.50196078431"
class GrGLDistanceFieldTextureEffect : public GrGLVertexEffect {
public:
GrGLDistanceFieldTextureEffect(const GrBackendEffectFactory& factory, const GrDrawEffect& drawEffect)
: INHERITED (factory) {}
virtual void emitCode(GrGLFullShaderBuilder* builder,
const GrDrawEffect& drawEffect,
EffectKey key,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray&,
const TextureSamplerArray& samplers) SK_OVERRIDE {
SkASSERT(1 == drawEffect.castEffect<GrDistanceFieldTextureEffect>().numVertexAttribs());
SkString fsCoordName;
const char* vsVaryingName;
const char* fsVaryingNamePtr;
builder->addVarying(kVec2f_GrSLType, "textureCoords", &vsVaryingName, &fsVaryingNamePtr);
fsCoordName = fsVaryingNamePtr;
const char* attrName =
builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str();
builder->vsCodeAppendf("\t%s = %s;\n", vsVaryingName, attrName);
builder->fsCodeAppend("\tvec4 texColor = ");
builder->fsAppendTextureLookup(samplers[0],
fsCoordName.c_str(),
kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tfloat distance = texColor.r;\n");
// this gives us a smooth step across approximately one fragment
// (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
builder->fsCodeAppend("\tfloat afwidth = 0.7071*length(vec2(dFdx(distance), dFdy(distance)));\n");
builder->fsCodeAppend("\tfloat val = smoothstep("THRESHOLD"-afwidth, "THRESHOLD"+afwidth, distance);\n");
builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
(GrGLSLExpr4(inputColor) * GrGLSLExpr1("val")).c_str());
}
virtual void setData(const GrGLUniformManager& uman,
const GrDrawEffect& drawEffect) SK_OVERRIDE {}
private:
typedef GrGLVertexEffect INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldTextureEffect::GrDistanceFieldTextureEffect(GrTexture* texture,
const GrTextureParams& params)
: fTextureAccess(texture, params) {
this->addTextureAccess(&fTextureAccess);
this->addVertexAttrib(kVec2f_GrSLType);
}
bool GrDistanceFieldTextureEffect::onIsEqual(const GrEffect& other) const {
const GrDistanceFieldTextureEffect& cte = CastEffect<GrDistanceFieldTextureEffect>(other);
return fTextureAccess == cte.fTextureAccess;
}
void GrDistanceFieldTextureEffect::getConstantColorComponents(GrColor* color,
uint32_t* validFlags) const {
if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
GrPixelConfigIsOpaque(this->texture(0)->config())) {
*validFlags = kA_GrColorComponentFlag;
} else {
*validFlags = 0;
}
}
const GrBackendEffectFactory& GrDistanceFieldTextureEffect::getFactory() const {
return GrTBackendEffectFactory<GrDistanceFieldTextureEffect>::getInstance();
}
///////////////////////////////////////////////////////////////////////////////
GR_DEFINE_EFFECT_TEST(GrDistanceFieldTextureEffect);
GrEffectRef* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random,
GrContext*,
const GrDrawTargetCaps&,
GrTexture* textures[]) {
int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
GrEffectUnitTest::kAlphaTextureIdx;
static const SkShader::TileMode kTileModes[] = {
SkShader::kClamp_TileMode,
SkShader::kRepeat_TileMode,
SkShader::kMirror_TileMode,
};
SkShader::TileMode tileModes[] = {
kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
};
GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
GrTextureParams::kNone_FilterMode);
return GrDistanceFieldTextureEffect::Create(textures[texIdx], params);
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrDistanceFieldTextureEffect_DEFINED
#define GrDistanceFieldTextureEffect_DEFINED
#include "GrEffect.h"
#include "GrVertexEffect.h"
class GrGLDistanceFieldTextureEffect;
/**
* The output color of this effect is a modulation of the input color and a sample from a
* distance field texture (using a smoothed step function near 0.5).
* It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
* coords are a custom attribute.
*/
class GrDistanceFieldTextureEffect : public GrVertexEffect {
public:
static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& p) {
AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, p)));
return CreateEffectRef(effect);
}
virtual ~GrDistanceFieldTextureEffect() {}
static const char* Name() { return "Texture"; }
virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
typedef GrGLDistanceFieldTextureEffect GLEffect;
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
private:
GrDistanceFieldTextureEffect(GrTexture* texture, const GrTextureParams& params);
virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
GrTextureAccess fTextureAccess;
GR_DECLARE_EFFECT_TEST;
typedef GrVertexEffect INHERITED;
};
#endif

21
third_party/edtaa/LICENSE vendored Executable file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2009-2012 Stefan Gustavson (stefan.gustavson@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

35
third_party/edtaa/edtaa3.h vendored Executable file
View File

@ -0,0 +1,35 @@
/*
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define EDTAA_UNSIGNED_CHAR_INPUT 1
namespace EDTAA {
#if EDTAA_UNSIGNED_CHAR_INPUT
typedef unsigned char EdtaaImageType;
#else
typedef double EdtaaImageType;
#endif
void computegradient(EdtaaImageType *img, int w, int h, double *gx, double *gy);
void edtaa3(EdtaaImageType *img, double *gx, double *gy, int w, int h,
short *distx, short *disty, double *dist);
}

586
third_party/edtaa/edtaa3func.cpp vendored Executable file
View File

@ -0,0 +1,586 @@
/*
* edtaa3()
*
* Sweep-and-update Euclidean distance transform of an
* image. Positive pixels are treated as object pixels,
* zero or negative pixels are treated as background.
* An attempt is made to treat antialiased edges correctly.
* The input image must have pixels in the range [0,1],
* and the antialiased image should be a box-filter
* sampling of the ideal, crisp edge.
* If the antialias region is more than 1 pixel wide,
* the result from this transform will be inaccurate.
*
* By Stefan Gustavson (stefan.gustavson@gmail.com).
*
* Originally written in 1994, based on a verbal
* description of the SSED8 algorithm published in the
* PhD dissertation of Ingemar Ragnemalm. This is his
* algorithm, I only implemented it in C.
*
* Updated in 2004 to treat border pixels correctly,
* and cleaned up the code to improve readability.
*
* Updated in 2009 to handle anti-aliased edges.
*
* Updated in 2011 to avoid a corner case infinite loop.
*
* Updated 2012 to change license from LGPL to MIT.
*/
/*
Copyright (C) 2009-2012 Stefan Gustavson (stefan.gustavson@gmail.com)
The code in this file is distributed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "edtaa3.h"
#include <math.h>
#if EDTAA_UNSIGNED_CHAR_INPUT
#define IMG(i) ((double)(img[i] & 0xff)/256.0)
#else
#define IMG(i) (img[i])
#endif
namespace EDTAA {
/*
* Compute the local gradient at edge pixels using convolution filters.
* The gradient is computed only at edge pixels. At other places in the
* image, it is never used, and it's mostly zero anyway.
*/
void computegradient(EdtaaImageType *img, int w, int h, double *gx, double *gy)
{
int i,j,k;
double glength;
#define SQRT2 1.4142136
for(i = 1; i < h-1; i++) { // Avoid edges where the kernels would spill over
for(j = 1; j < w-1; j++) {
k = i*w + j;
if((IMG(k)>0.0) && (IMG(k)<1.0)) { // Compute gradient for edge pixels only
gx[k] = -IMG(k-w-1) - SQRT2*IMG(k-1) - IMG(k+w-1) + IMG(k-w+1) + SQRT2*IMG(k+1) + IMG(k+w+1);
gy[k] = -IMG(k-w-1) - SQRT2*IMG(k-w) - IMG(k+w-1) + IMG(k-w+1) + SQRT2*IMG(k+w) + IMG(k+w+1);
glength = gx[k]*gx[k] + gy[k]*gy[k];
if(glength > 0.0) { // Avoid division by zero
glength = sqrt(glength);
gx[k]=gx[k]/glength;
gy[k]=gy[k]/glength;
}
}
}
}
// TODO: Compute reasonable values for gx, gy also around the image edges.
// (These are zero now, which reduces the accuracy for a 1-pixel wide region
// around the image edge.) 2x2 kernels would be suitable for this.
}
/*
* A somewhat tricky function to approximate the distance to an edge in a
* certain pixel, with consideration to either the local gradient (gx,gy)
* or the direction to the pixel (dx,dy) and the pixel greyscale value a.
* The latter alternative, using (dx,dy), is the metric used by edtaa2().
* Using a local estimate of the edge gradient (gx,gy) yields much better
* accuracy at and near edges, and reduces the error even at distant pixels
* provided that the gradient direction is accurately estimated.
*/
static double edgedf(double gx, double gy, double a)
{
double df, glength, temp, a1;
if ((gx == 0) || (gy == 0)) { // Either A) gu or gv are zero, or B) both
df = 0.5-a; // Linear approximation is A) correct or B) a fair guess
} else {
glength = sqrt(gx*gx + gy*gy);
if(glength>0) {
gx = gx/glength;
gy = gy/glength;
}
/* Everything is symmetric wrt sign and transposition,
* so move to first octant (gx>=0, gy>=0, gx>=gy) to
* avoid handling all possible edge directions.
*/
gx = fabs(gx);
gy = fabs(gy);
if(gx<gy) {
temp = gx;
gx = gy;
gy = temp;
}
a1 = 0.5*gy/gx;
if (a < a1) { // 0 <= a < a1
df = 0.5*(gx + gy) - sqrt(2.0*gx*gy*a);
} else if (a < (1.0-a1)) { // a1 <= a <= 1-a1
df = (0.5-a)*gx;
} else { // 1-a1 < a <= 1
df = -0.5*(gx + gy) + sqrt(2.0*gx*gy*(1.0-a));
}
}
return df;
}
static double distaa3(EdtaaImageType *img, double *gximg, double *gyimg, int w, int c, int xc, int yc, int xi, int yi)
{
double di, df, dx, dy, gx, gy, a;
int closest;
closest = c-xc-yc*w; // Index to the edge pixel pointed to from c
a = IMG(closest); // Grayscale value at the edge pixel
gx = gximg[closest]; // X gradient component at the edge pixel
gy = gyimg[closest]; // Y gradient component at the edge pixel
if(a > 1.0) a = 1.0;
if(a < 0.0) a = 0.0; // Clip grayscale values outside the range [0,1]
if(a == 0.0) return 1000000.0; // Not an object pixel, return "very far" ("don't know yet")
dx = (double)xi;
dy = (double)yi;
di = sqrt(dx*dx + dy*dy); // Length of integer vector, like a traditional EDT
if(di==0) { // Use local gradient only at edges
// Estimate based on local gradient only
df = edgedf(gx, gy, a);
} else {
// Estimate gradient based on direction to edge (accurate for large di)
df = edgedf(dx, dy, a);
}
return di + df; // Same metric as edtaa2, except at edges (where di=0)
}
// Shorthand macro: add ubiquitous parameters dist, gx, gy, img and w and call distaa3()
#define DISTAA(c,xc,yc,xi,yi) (distaa3(img, gx, gy, w, c, xc, yc, xi, yi))
void edtaa3(EdtaaImageType *img, double *gx, double *gy, int w, int h, short *distx, short *disty, double *dist)
{
int x, y, i, c;
int offset_u, offset_ur, offset_r, offset_rd,
offset_d, offset_dl, offset_l, offset_lu;
double olddist, newdist;
int cdistx, cdisty, newdistx, newdisty;
int changed;
double epsilon = 1e-3;
double a;
/* 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++) {
distx[i] = 0; // At first, all pixels point to
disty[i] = 0; // themselves as the closest known.
a = IMG(i);
if(a <= 0.0)
{
dist[i]= 1000000.0; // Big value, means "not set yet"
}
else if (a<1.0) {
dist[i] = edgedf(gx[i], gy[i], a); // Gradient-assisted estimate
}
else {
dist[i]= 0.0; // 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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
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 = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
if(newdist < olddist-epsilon)
{
distx[i]=newdistx;
disty[i]=newdisty;
dist[i]=newdist;
changed = 1;
}
}
}
}
while(changed); // Sweep until no more updates are made
/* The transformation is completed. */
}
} // namespace EDTAA