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:
parent
05edd02a7c
commit
d830d13c27
@ -27,6 +27,17 @@
|
||||
],
|
||||
},
|
||||
],
|
||||
[ 'skia_distancefield_fonts == 1',
|
||||
{
|
||||
'defines': [
|
||||
'SK_DISTANCEFIELD_FONTS=1',
|
||||
],
|
||||
}, {
|
||||
'defines': [
|
||||
'SK_DISTANCEFIELD_FONTS=0',
|
||||
],
|
||||
},
|
||||
],
|
||||
[ 'skia_os == "win"',
|
||||
{
|
||||
'defines': [
|
||||
|
@ -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
38
gyp/edtaa.gyp
Executable 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:
|
12
gyp/gpu.gyp
12
gyp/gpu.gyp
@ -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',
|
||||
|
46
include/gpu/GrDistanceFieldTextContext.h
Executable file
46
include/gpu/GrDistanceFieldTextContext.h
Executable 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
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
278
src/gpu/GrDistanceFieldTextContext.cpp
Executable file
278
src/gpu/GrDistanceFieldTextContext.cpp
Executable 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
117
src/gpu/effects/GrDistanceFieldTextureEffect.cpp
Executable file
117
src/gpu/effects/GrDistanceFieldTextureEffect.cpp
Executable 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);
|
||||
}
|
51
src/gpu/effects/GrDistanceFieldTextureEffect.h
Executable file
51
src/gpu/effects/GrDistanceFieldTextureEffect.h
Executable 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
21
third_party/edtaa/LICENSE
vendored
Executable 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
35
third_party/edtaa/edtaa3.h
vendored
Executable 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
586
third_party/edtaa/edtaa3func.cpp
vendored
Executable 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
|
Loading…
Reference in New Issue
Block a user