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"',
|
[ 'skia_os == "win"',
|
||||||
{
|
{
|
||||||
'defines': [
|
'defines': [
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
'skia_win_debuggers_path%': '',
|
'skia_win_debuggers_path%': '',
|
||||||
'skia_shared_lib%': 0,
|
'skia_shared_lib%': 0,
|
||||||
'skia_opencl%': 0,
|
'skia_opencl%': 0,
|
||||||
|
'skia_distancefield_fonts%': 0,
|
||||||
|
|
||||||
# These variables determine the default optimization level for different
|
# These variables determine the default optimization level for different
|
||||||
# compilers.
|
# compilers.
|
||||||
@ -153,6 +154,7 @@
|
|||||||
'skia_profile_enabled%': '<(skia_profile_enabled)',
|
'skia_profile_enabled%': '<(skia_profile_enabled)',
|
||||||
'skia_shared_lib%': '<(skia_shared_lib)',
|
'skia_shared_lib%': '<(skia_shared_lib)',
|
||||||
'skia_opencl%': '<(skia_opencl)',
|
'skia_opencl%': '<(skia_opencl)',
|
||||||
|
'skia_distancefield_fonts%': '<(skia_distancefield_fonts)',
|
||||||
'skia_static_initializers%': '<(skia_static_initializers)',
|
'skia_static_initializers%': '<(skia_static_initializers)',
|
||||||
'ios_sdk_version%': '6.0',
|
'ios_sdk_version%': '6.0',
|
||||||
'skia_win_debuggers_path%': '<(skia_win_debuggers_path)',
|
'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': [
|
'dependencies': [
|
||||||
'angle.gyp:*',
|
'angle.gyp:*',
|
||||||
'core.gyp:*',
|
'core.gyp:*',
|
||||||
|
'edtaa.gyp:*',
|
||||||
'utils.gyp:*',
|
'utils.gyp:*',
|
||||||
],
|
],
|
||||||
'includes': [
|
'includes': [
|
||||||
@ -132,6 +133,17 @@
|
|||||||
'GR_ANDROID_PATH_RENDERING=1',
|
'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"', {
|
[ 'skia_os == "linux" or skia_os == "chromeos"', {
|
||||||
'sources!': [
|
'sources!': [
|
||||||
'../src/gpu/gl/GrGLDefaultInterface_none.cpp',
|
'../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();
|
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);
|
SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
|
||||||
|
#endif
|
||||||
SkGlyphCache* cache = autoCache.getCache();
|
SkGlyphCache* cache = autoCache.getCache();
|
||||||
|
|
||||||
// transform our starting point
|
// transform our starting point
|
||||||
|
#if SK_DISTANCEFIELD_FONTS
|
||||||
|
if (!(procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag))
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
SkPoint loc;
|
SkPoint loc;
|
||||||
fMatrix->mapXY(x, y, &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 fx = SkScalarToFixed(x) + d1g.fHalfSampleX;
|
||||||
SkFixed fy = SkScalarToFixed(y) + d1g.fHalfSampleY;
|
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) {
|
while (text < stop) {
|
||||||
const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
|
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);
|
fx += autokern.adjust(glyph);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (glyph.fWidth) {
|
if (glyph.fWidth) {
|
||||||
proc(d1g, fx, fy, glyph);
|
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;
|
fx += glyph.fAdvanceX;
|
||||||
fy += glyph.fAdvanceY;
|
fy += glyph.fAdvanceY;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1956,7 +2001,23 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
|
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);
|
SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
|
||||||
|
#endif
|
||||||
SkGlyphCache* cache = autoCache.getCache();
|
SkGlyphCache* cache = autoCache.getCache();
|
||||||
|
|
||||||
SkAAClipBlitterWrapper wrapper;
|
SkAAClipBlitterWrapper wrapper;
|
||||||
@ -1998,8 +2059,16 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
|
|||||||
|
|
||||||
if (SkPaint::kLeft_Align == paint.getTextAlign()) {
|
if (SkPaint::kLeft_Align == paint.getTextAlign()) {
|
||||||
while (text < stop) {
|
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);
|
tmsProc(tms, pos);
|
||||||
|
#endif
|
||||||
SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + d1g.fHalfSampleX;
|
SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + d1g.fHalfSampleX;
|
||||||
SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + d1g.fHalfSampleY;
|
SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + d1g.fHalfSampleY;
|
||||||
|
|
||||||
|
@ -57,6 +57,22 @@ struct SkDraw1Glyph {
|
|||||||
|
|
||||||
struct SkDrawProcs {
|
struct SkDrawProcs {
|
||||||
SkDraw1Glyph::Proc fD1GProc;
|
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,
|
SkBitSet& glyphsUsed,
|
||||||
SkDraw& myDraw, SkXPSDrawProcs& procs) {
|
SkDraw& myDraw, SkXPSDrawProcs& procs) {
|
||||||
procs.fD1GProc = xps_draw_1_glyph;
|
procs.fD1GProc = xps_draw_1_glyph;
|
||||||
|
#if SK_DISTANCEFIELD_FONTS
|
||||||
|
procs.fFlags = 0;
|
||||||
|
#endif
|
||||||
size_t numGlyphGuess;
|
size_t numGlyphGuess;
|
||||||
switch (paint.getTextEncoding()) {
|
switch (paint.getTextEncoding()) {
|
||||||
case SkPaint::kUTF8_TextEncoding:
|
case SkPaint::kUTF8_TextEncoding:
|
||||||
|
@ -123,7 +123,11 @@ void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (NULL == fStrike) {
|
if (NULL == fStrike) {
|
||||||
|
#if SK_DISTANCEFIELD_FONTS
|
||||||
|
fStrike = fContext->getFontCache()->getStrike(scaler, true);
|
||||||
|
#else
|
||||||
fStrike = fContext->getFontCache()->getStrike(scaler);
|
fStrike = fContext->getFontCache()->getStrike(scaler);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
|
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 "GrTextStrike_impl.h"
|
||||||
#include "SkString.h"
|
#include "SkString.h"
|
||||||
|
|
||||||
|
#if SK_DISTANCEFIELD_FONTS
|
||||||
|
#include "edtaa3.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
SK_DEFINE_INST_COUNT(GrFontScaler)
|
SK_DEFINE_INST_COUNT(GrFontScaler)
|
||||||
SK_DEFINE_INST_COUNT(GrKey)
|
SK_DEFINE_INST_COUNT(GrKey)
|
||||||
|
|
||||||
@ -193,6 +197,11 @@ void GrFontCache::dump() const {
|
|||||||
static int gCounter;
|
static int gCounter;
|
||||||
#endif
|
#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
|
The text strike is specific to a given font/style/matrix setup, which is
|
||||||
represented by the GrHostFontScaler object we are given in getGlyph().
|
represented by the GrHostFontScaler object we are given in getGlyph().
|
||||||
@ -246,6 +255,15 @@ GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
|
|||||||
}
|
}
|
||||||
|
|
||||||
GrGlyph* glyph = fPool.alloc();
|
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);
|
glyph->init(packed, bounds);
|
||||||
fCache.insert(packed, glyph);
|
fCache.insert(packed, glyph);
|
||||||
return glyph;
|
return glyph;
|
||||||
@ -256,6 +274,7 @@ bool GrTextStrike::removeUnusedPlots() {
|
|||||||
return fAtlasMgr->removeUnusedPlots(&fAtlas);
|
return fAtlasMgr->removeUnusedPlots(&fAtlas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
|
bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
|
||||||
#if 0 // testing hack to force us to flush our cache often
|
#if 0 // testing hack to force us to flush our cache often
|
||||||
static int gCounter;
|
static int gCounter;
|
||||||
@ -270,18 +289,118 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
|
|||||||
SkAutoRef ar(scaler);
|
SkAutoRef ar(scaler);
|
||||||
|
|
||||||
int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
|
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(),
|
GrPlot* plot;
|
||||||
glyph->height(), storage.get(),
|
#if SK_DISTANCEFIELD_FONTS
|
||||||
&glyph->fAtlasLocation);
|
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) {
|
if (NULL == plot) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -289,3 +408,4 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
|
|||||||
glyph->fPlot = plot;
|
glyph->fPlot = plot;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +61,12 @@ private:
|
|||||||
|
|
||||||
GrFontCache* fFontCache;
|
GrFontCache* fFontCache;
|
||||||
GrAtlasMgr* fAtlasMgr;
|
GrAtlasMgr* fAtlasMgr;
|
||||||
GrAtlas fAtlas;
|
|
||||||
|
|
||||||
GrMaskFormat fMaskFormat;
|
GrMaskFormat fMaskFormat;
|
||||||
|
#if SK_DISTANCEFIELD_FONTS
|
||||||
|
bool fUseDistanceField;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GrAtlas fAtlas;
|
||||||
|
|
||||||
GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
|
GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
|
||||||
|
|
||||||
@ -75,7 +78,11 @@ public:
|
|||||||
GrFontCache(GrGpu*);
|
GrFontCache(GrGpu*);
|
||||||
~GrFontCache();
|
~GrFontCache();
|
||||||
|
|
||||||
|
#if SK_DISTANCEFIELD_FONTS
|
||||||
|
inline GrTextStrike* getStrike(GrFontScaler*, bool useDistanceField);
|
||||||
|
#else
|
||||||
inline GrTextStrike* getStrike(GrFontScaler*);
|
inline GrTextStrike* getStrike(GrFontScaler*);
|
||||||
|
#endif
|
||||||
|
|
||||||
void freeAll();
|
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) {
|
GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
|
||||||
|
#endif
|
||||||
this->validate();
|
this->validate();
|
||||||
|
|
||||||
const Key key(scaler->getKey());
|
const Key key(scaler->getKey());
|
||||||
@ -65,7 +69,9 @@ GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
|
|||||||
strike->fPrev = NULL;
|
strike->fPrev = NULL;
|
||||||
fHead = strike;
|
fHead = strike;
|
||||||
}
|
}
|
||||||
|
#if SK_DISTANCEFIELD_FONTS
|
||||||
|
strike->fUseDistanceField = useDistanceField;
|
||||||
|
#endif
|
||||||
this->validate();
|
this->validate();
|
||||||
return strike;
|
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