From bc399eb31794f19a8eea969f5615dc96696ac3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Wed, 30 Apr 2014 21:57:05 -0700 Subject: [PATCH] Added SDF. --- 3rdparty/sdf/LICENSE.txt | 22 ++ 3rdparty/sdf/sdf.h | 334 ++++++++++++++++++++++++++ examples/common/font/font_manager.cpp | 16 +- 3 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 3rdparty/sdf/LICENSE.txt create mode 100644 3rdparty/sdf/sdf.h diff --git a/3rdparty/sdf/LICENSE.txt b/3rdparty/sdf/LICENSE.txt new file mode 100644 index 00000000..49a4393f --- /dev/null +++ b/3rdparty/sdf/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (C) 2014 Mikko Mononen (memon@inside.org) +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. \ No newline at end of file diff --git a/3rdparty/sdf/sdf.h b/3rdparty/sdf/sdf.h new file mode 100644 index 00000000..e37874f5 --- /dev/null +++ b/3rdparty/sdf/sdf.h @@ -0,0 +1,334 @@ +/* + Copyright (C) 2014 Mikko Mononen (memon@inside.org) + 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. + */ + +#ifndef SDF_H +#define SDF_H + +// Sweep-and-update Euclidean distance transform of an antialised image for contour textures. +// Based on edtaa3func.c by Stefan Gustavson. +// +// White (255) pixels are treated as object pixels, zero pixels are treated as background. +// An attempt is made to treat antialiased edges correctly. The input image must have +// pixels in the range [0,255], 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. +// Pixels at image border are not calculated and are set to 0. +// +// The output distance field is encoded as bytes, where 0 = maxdist (outside) and 255 = -maxdist (inside). +// Input and output can be the same buffer. +// out - Output of the distance transform, one byte per pixel. +// outstride - Bytes per row on output image. +// maxdist - The extents of the output distance range in pixels. +// img - Input image, one byte per pixel. +// width - Width if the image. +// height - Height if the image. +// stride - Bytes per row on input image. +int sdfBuild(unsigned char* out, int outstride, float maxdist, + const unsigned char* img, int width, int height, int stride); + +// Same as distXform, but does not allocate any memory. +// The 'temp' array should be enough to fit width * height * sizeof(float) bytes. +void sdfBuildNoAlloc(unsigned char* out, int outstride, float maxdist, + const unsigned char* img, int width, int height, int stride, + unsigned char* temp); + +void sdfCoverageToDistance(unsigned char* out, int outstride, float maxdist, + const unsigned char* img, int width, int height, int stride); + +#endif //SDF_H + + +#ifdef SDF_IMPLEMENTATION + +#include +#include + +#define SDF_MAX_PASSES 10 // Maximum number of distance transform passes +#define SDF_SLACK 0.001f // Controls how much smaller the neighbour value must be to cosnider, too small slack increse iteration count. +#define SDF_SQRT2 1.4142136f // sqrt(2) +#define SDF_BIG 1e+37f // Big value used to initialize the distance field. + +static float sdf__edgedf(float gx, float gy, float a) +{ + float df, a1; + if ((gx == 0) || (gy == 0)) { + // Either A) gu or gv are zero, or B) both + // Linear approximation is A) correct or B) a fair guess + df = 0.5f - a; + } else { + // 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 = fabsf(gx); + gy = fabsf(gy); + if (gx < gy) { + float temp = gx; + gx = gy; + gy = temp; + } + a1 = 0.5f*gy/gx; + if (a < a1) { // 0 <= a < a1 + df = 0.5f*(gx + gy) - sqrtf(2.0f*gx*gy*a); + } else if (a < (1.0-a1)) { // a1 <= a <= 1-a1 + df = (0.5f-a)*gx; + } else { // 1-a1 < a <= 1 + df = -0.5f*(gx + gy) + sqrt(2.0f*gx*gy*(1.0f-a)); + } + } + return df; +} + +struct SDFpoint { + float x,y; +}; + +static float sdf__distsqr(struct SDFpoint* a, struct SDFpoint* b) +{ + float dx = b->x - a->x, dy = b->y - a->y; + return dx*dx + dy*dy; +} + +static float sdf__clamp01(float x) +{ + return x < 0.0f ? 0.0f : (x > 1.0f ? 1.0f : x); +} + +void sdfCoverageToDistance(unsigned char* out, int outstride, float maxdist, + const unsigned char* img, int width, int height, int stride) +{ + int x, y; + + // Zero out borders + for (x = 0; x < width; x++) + out[x] = 0; + for (y = 1; y < height; y++) { + out[y*stride] = 0; + out[width-1+y*stride] = 0; + } + for (x = 0; x < width; x++) + out[x+(height-1)*stride] = 0; + + // Calculate position of the anti-aliased pixels and distance to the boundary of the shape. + for (y = 1; y < height-1; y++) { + for (x = 1; x < width-1; x++) { + int k = x + y * stride; + float d, gx, gy, glen; + // Calculate gradient direction + gx = -(float)img[k-stride-1] - SDF_SQRT2*(float)img[k-1] - (float)img[k+stride-1] + (float)img[k-stride+1] + SDF_SQRT2*(float)img[k+1] + (float)img[k+stride+1]; + gy = -(float)img[k-stride-1] - SDF_SQRT2*(float)img[k-stride] - (float)img[k+stride-1] + (float)img[k-stride+1] + SDF_SQRT2*(float)img[k+stride] + (float)img[k+stride+1]; + if (fabsf(gx) > 0.001f && fabsf(gy) > 0.001f) { + glen = gx*gx + gy*gy; + glen = 1.0f / sqrtf(glen); + gx *= glen; + gy *= glen; + // Find nearest point on contour. + d = sdf__edgedf(gx, gy, (float)img[k]/255.0f); + d = fabsf(d); + if (img[x+y*stride] > 127) d = -d; + out[x+y*outstride] = (unsigned char)(sdf__clamp01(0.5f - d*0.5f) * 255.0f); + } else { + out[x+y*outstride] = img[x+y*stride] > 127 ? 255 : 0; + } + } + } +} + +void sdfBuildNoAlloc(unsigned char* out, int outstride, float maxdist, + const unsigned char* img, int width, int height, int stride, + unsigned char* temp) +{ + int i, x, y, pass; + float scale; + float* tdist = (float*)&temp[0]; + struct SDFpoint* tpt = (struct SDFpoint*)&temp[width * height * sizeof(float)]; + + // Initialize buffers + for (i = 0; i < width*height; i++) { + tpt[i].x = 0; + tpt[i].y = 0; + tdist[i] = SDF_BIG; + } + + // Calculate position of the anti-aliased pixels and distance to the boundary of the shape. + for (y = 1; y < height-1; y++) { + for (x = 1; x < width-1; x++) { + int k = x + y * stride; + if (img[k] > 0 && img[k] < 255) { + struct SDFpoint c = { (float)x, (float)y }; + float d, gx, gy, glen; + // Calculate gradient direction + gx = -(float)img[k-stride-1] - SDF_SQRT2*(float)img[k-1] - (float)img[k+stride-1] + (float)img[k-stride+1] + SDF_SQRT2*(float)img[k+1] + (float)img[k+stride+1]; + gy = -(float)img[k-stride-1] - SDF_SQRT2*(float)img[k-stride] - (float)img[k+stride-1] + (float)img[k-stride+1] + SDF_SQRT2*(float)img[k+stride] + (float)img[k+stride+1]; + if (fabsf(gx) < 0.001f && fabsf(gy) < 0.001f) continue; + glen = gx*gx + gy*gy; + if (glen > 0.0001f) { + glen = 1.0f / sqrtf(glen); + gx *= glen; + gy *= glen; + } + // Find nearest point on contour. + d = sdf__edgedf(gx, gy, (float)img[k]/255.0f); + tpt[k].x = x + gx*d; + tpt[k].y = y + gy*d; + tdist[k] = sdf__distsqr(&c, &tpt[k]); + } + } + } + + // Calculate distance transform using sweep-and-update. + for (pass = 0; pass < SDF_MAX_PASSES; pass++){ + int changed = 0; + + // Bottom-left to top-right. + for (y = 1; y < height-1; y++) { + for (x = 1; x < width-1; x++) { + int k = x+y*width, kn, ch = 0; + struct SDFpoint c = { (float)x, (float)y }, pt; + float pd = tdist[k], d; + // (-1,-1) + kn = k - 1 - width; + if (tdist[kn] < pd) { + d = sdf__distsqr(&c, &tpt[kn]); + if (d + SDF_SLACK < pd) { + pt = tpt[kn]; + pd = d; + ch = 1; + } + } + // (0,-1) + kn = k - 1 - width; + if (tdist[kn] < pd) { + d = sdf__distsqr(&c, &tpt[kn]); + if (d + SDF_SLACK < pd) { + pt = tpt[kn]; + pd = d; + ch = 1; + } + } + // (1,-1) + kn = k + 1 - width; + if (tdist[kn] < pd) { + d = sdf__distsqr(&c, &tpt[kn]); + if (d + SDF_SLACK < pd) { + pt = tpt[kn]; + pd = d; + ch = 1; + } + } + // (-1,0) + kn = k - 1; + if (tdist[kn] < tdist[k]) { + d = sdf__distsqr(&c, &tpt[kn]); + if (d + SDF_SLACK < pd) { + pt = tpt[kn]; + pd = d; + ch = 1; + } + } + if (ch) { + tpt[k] = pt; + tdist[k] = pd; + changed++; + } + } + } + + // Top-right to bottom-left. + for (y = height-2; y > 0 ; y--) { + for (x = width-2; x > 0; x--) { + int k = x+y*width, kn, ch = 0; + struct SDFpoint c = { (float)x, (float)y }, pt; + float pd = tdist[k], d; + // (1,0) + kn = k + 1; + if (tdist[kn] < pd) { + d = sdf__distsqr(&c, &tpt[kn]); + if (d + SDF_SLACK < pd) { + pt = tpt[kn]; + pd = d; + ch = 1; + } + } + // (-1,1) + kn = k - 1 + width; + if (tdist[kn] < pd) { + d = sdf__distsqr(&c, &tpt[kn]); + if (d + SDF_SLACK < pd) { + pt = tpt[kn]; + pd = d; + ch = 1; + } + } + // (0,1) + kn = k + width; + if (tdist[kn] < pd) { + d = sdf__distsqr(&c, &tpt[kn]); + if (d + SDF_SLACK < pd) { + pt = tpt[kn]; + pd = d; + ch = 1; + } + } + // (1,1) + kn = k + 1 + width; + if (tdist[kn] < pd) { + d = sdf__distsqr(&c, &tpt[kn]); + if (d + SDF_SLACK < pd) { + pt = tpt[kn]; + pd = d; + ch = 1; + } + } + if (ch) { + tpt[k] = pt; + tdist[k] = pd; + changed++; + } + } + } + if (changed == 0) break; + } + + // Map to good range. + scale = 1.0f / maxdist; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + float d = sqrtf(tdist[x+y*width]) * scale; + if (img[x+y*stride] > 127) d = -d; + out[x+y*outstride] = (unsigned char)(sdf__clamp01(0.5f - d*0.5f) * 255.0f); + } + } + +} + +int sdfBuild(unsigned char* out, int outstride, float maxdist, + const unsigned char* img, int width, int height, int stride) +{ + unsigned char* temp = (unsigned char*)malloc(width*height*sizeof(float)*3); + if (temp == NULL) return 0; + sdfBuildNoAlloc(out, outstride, maxdist, img, width, height, stride, temp); + free(temp); + return 1; +} + +#endif //SDF_IMPLEMENTATION diff --git a/examples/common/font/font_manager.cpp b/examples/common/font/font_manager.cpp index 34ebb8fe..c341364d 100644 --- a/examples/common/font/font_manager.cpp +++ b/examples/common/font/font_manager.cpp @@ -3,11 +3,21 @@ * License: http://www.opensource.org/licenses/BSD-2-Clause */ +#define USE_EDTAA3 1 + #include "../common.h" #include #include -#include +#include + +#if USE_EDTAA3 +# include +#else +# define SDF_IMPLEMENTATION +# include +#endif // USE_EDTAA3 + #include // wcslen #include @@ -247,6 +257,7 @@ bool TrueTypeFont::bakeGlyphSubpixel(CodePoint _codePoint, GlyphInfo& _glyphInfo static void makeDistanceMap(const uint8_t* _img, uint8_t* _outImg, uint32_t _width, uint32_t _height) { +#if USE_EDTAA3 int16_t* xdist = (int16_t*)malloc(_width * _height * sizeof(int16_t) ); int16_t* ydist = (int16_t*)malloc(_width * _height * sizeof(int16_t) ); double* gx = (double*)calloc(_width * _height, sizeof(double) ); @@ -335,6 +346,9 @@ static void makeDistanceMap(const uint8_t* _img, uint8_t* _outImg, uint32_t _wid free(data); free(outside); free(inside); +#else + sdfBuild(_outImg, _width, 2.0f, _img, _width, _height, _width); +#endif // USE_EDTAA3 } bool TrueTypeFont::bakeGlyphDistance(CodePoint _codePoint, GlyphInfo& _glyphInfo, uint8_t* _outBuffer)