diff --git a/core/render-sdf.cpp b/core/render-sdf.cpp index 3de9e47..e282285 100644 --- a/core/render-sdf.cpp +++ b/core/render-sdf.cpp @@ -7,83 +7,83 @@ namespace msdfgen { -static float distVal(float dist, double pxRange) { +static float distVal(float dist, double pxRange, float midValue) { if (!pxRange) - return (float) (dist > .5f); - return (float) clamp((dist-.5f)*pxRange+.5); + return (float) (dist > midValue); + return (float) clamp((dist-midValue)*pxRange+.5); } -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) { +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange, float midValue) { Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); for (int y = 0; y < output.height; ++y) for (int x = 0; x < output.width; ++x) { float sd; interpolate(&sd, sdf, scale*Point2(x+.5, y+.5)); - *output(x, y) = distVal(sd, pxRange); + *output(x, y) = distVal(sd, pxRange, midValue); } } -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) { +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange, float midValue) { Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); for (int y = 0; y < output.height; ++y) for (int x = 0; x < output.width; ++x) { float sd; interpolate(&sd, sdf, scale*Point2(x+.5, y+.5)); - float v = distVal(sd, pxRange); + float v = distVal(sd, pxRange, midValue); output(x, y)[0] = v; output(x, y)[1] = v; output(x, y)[2] = v; } } -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) { +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange, float midValue) { Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); for (int y = 0; y < output.height; ++y) for (int x = 0; x < output.width; ++x) { float sd[3]; interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); - *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange); + *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue); } } -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) { +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange, float midValue) { Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); for (int y = 0; y < output.height; ++y) for (int x = 0; x < output.width; ++x) { float sd[3]; interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); - output(x, y)[0] = distVal(sd[0], pxRange); - output(x, y)[1] = distVal(sd[1], pxRange); - output(x, y)[2] = distVal(sd[2], pxRange); + output(x, y)[0] = distVal(sd[0], pxRange, midValue); + output(x, y)[1] = distVal(sd[1], pxRange, midValue); + output(x, y)[2] = distVal(sd[2], pxRange, midValue); } } -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) { +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange, float midValue) { Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); for (int y = 0; y < output.height; ++y) for (int x = 0; x < output.width; ++x) { float sd[4]; interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); - *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange); + *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue); } } -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) { +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange, float midValue) { Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); for (int y = 0; y < output.height; ++y) for (int x = 0; x < output.width; ++x) { float sd[4]; interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); - output(x, y)[0] = distVal(sd[0], pxRange); - output(x, y)[1] = distVal(sd[1], pxRange); - output(x, y)[2] = distVal(sd[2], pxRange); - output(x, y)[3] = distVal(sd[3], pxRange); + output(x, y)[0] = distVal(sd[0], pxRange, midValue); + output(x, y)[1] = distVal(sd[1], pxRange, midValue); + output(x, y)[2] = distVal(sd[2], pxRange, midValue); + output(x, y)[3] = distVal(sd[3], pxRange, midValue); } } diff --git a/core/render-sdf.h b/core/render-sdf.h index 9d055f8..7f2d270 100644 --- a/core/render-sdf.h +++ b/core/render-sdf.h @@ -7,12 +7,12 @@ namespace msdfgen { /// Reconstructs the shape's appearance into output from the distance field sdf. -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0); -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0); -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0); -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0); -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0); -void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0); +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0, float midValue = .5f); +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0, float midValue = .5f); +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0, float midValue = .5f); +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0, float midValue = .5f); +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0, float midValue = .5f); +void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0, float midValue = .5f); /// Snaps the values of the floating-point bitmaps into one of the 256 values representable in a standard 8-bit bitmap. void simulate8bit(const BitmapRef &bitmap); diff --git a/main.cpp b/main.cpp index 6e21a41..7d03b83 100644 --- a/main.cpp +++ b/main.cpp @@ -157,7 +157,7 @@ static bool writeTextBitmap(FILE *file, const float *values, int cols, int rows) static bool writeTextBitmapFloat(FILE *file, const float *values, int cols, int rows) { for (int row = 0; row < rows; ++row) { for (int col = 0; col < cols; ++col) { - fprintf(file, col ? " %g" : "%g", *values++); + fprintf(file, col ? " %.9g" : "%.9g", *values++); } fprintf(file, "\n"); } @@ -203,7 +203,7 @@ static bool cmpExtension(const char *path, const char *ext) { } template -static const char * writeOutput(const BitmapConstRef &bitmap, const char *filename, Format format) { +static const char * writeOutput(const BitmapConstRef &bitmap, const char *filename, Format &format) { if (filename) { if (format == AUTO) { if (cmpExtension(filename, ".png")) format = PNG; @@ -291,6 +291,8 @@ static const char *helpText = "\tAutomatically scales (unless specified) and translates the shape to fit.\n" " -coloringstrategy \n" "\tSelects the strategy of the edge coloring heuristic.\n" + " -distanceshift \n" + "\tShifts all normalized distances in the output distance field by this value.\n" " -edgecolors \n" "\tOverrides automatic edge coloring with the specified color sequence.\n" " -errorcorrection \n" @@ -389,6 +391,7 @@ int main(int argc, const char * const *argv) { bool scaleSpecified = false; double angleThreshold = DEFAULT_ANGLE_THRESHOLD; double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD; + float outputDistanceShift = 0.f; const char *edgeAssignment = NULL; bool yFlip = false; bool printMetrics = false; @@ -597,6 +600,14 @@ int main(int argc, const char * const *argv) { argPos += 2; continue; } + ARG_CASE("-distanceshift", 1) { + double ds; + if (!parseDouble(ds, argv[argPos+1])) + ABORT("Invalid distance shift. Use -distanceshift with a real value."); + outputDistanceShift = (float) ds; + argPos += 2; + continue; + } ARG_CASE("-exportshape", 1) { shapeExport = argv[argPos+1]; argPos += 2; @@ -738,10 +749,13 @@ int main(int argc, const char * const *argv) { if (autoFrame) { double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t; Vector2 frame(width, height); - if (rangeMode == RANGE_UNIT) - l -= .5*range, b -= .5*range, r += .5*range, t += .5*range; - else if (!scaleSpecified) - frame -= pxRange; + double m = .5+(double) outputDistanceShift; + if (!scaleSpecified) { + if (rangeMode == RANGE_UNIT) + l -= m*range, b -= m*range, r += m*range, t += m*range; + else + frame -= 2*m*pxRange; + } if (l >= r || b >= t) l = 0, b = 0, r = 1, t = 1; if (frame.x <= 0 || frame.y <= 0) @@ -759,7 +773,7 @@ int main(int argc, const char * const *argv) { } } if (rangeMode == RANGE_PX && !scaleSpecified) - translate += .5*pxRange/scale; + translate += m*pxRange/scale; } if (rangeMode == RANGE_PX) @@ -879,6 +893,27 @@ int main(int argc, const char * const *argv) { default:; } } + if (outputDistanceShift) { + float *pixel = NULL, *pixelsEnd = NULL; + switch (mode) { + case SINGLE: + case PSEUDO: + pixel = (float *) sdf; + pixelsEnd = pixel+1*sdf.width()*sdf.height(); + break; + case MULTI: + pixel = (float *) msdf; + pixelsEnd = pixel+3*msdf.width()*msdf.height(); + break; + case MULTI_AND_TRUE: + pixel = (float *) mtsdf; + pixelsEnd = pixel+4*mtsdf.width()*mtsdf.height(); + break; + default:; + } + while (pixel < pixelsEnd) + *pixel++ += outputDistanceShift; + } // Save output if (shapeExport) { @@ -904,13 +939,13 @@ int main(int argc, const char * const *argv) { } if (testRenderMulti) { Bitmap render(testWidthM, testHeightM); - renderSDF(render, sdf, avgScale*range); + renderSDF(render, sdf, avgScale*range, .5f+outputDistanceShift); if (!savePng(render, testRenderMulti)) puts("Failed to write test render file."); } if (testRender) { Bitmap render(testWidth, testHeight); - renderSDF(render, sdf, avgScale*range); + renderSDF(render, sdf, avgScale*range, .5f+outputDistanceShift); if (!savePng(render, testRender)) puts("Failed to write test render file."); } @@ -927,13 +962,13 @@ int main(int argc, const char * const *argv) { } if (testRenderMulti) { Bitmap render(testWidthM, testHeightM); - renderSDF(render, msdf, avgScale*range); + renderSDF(render, msdf, avgScale*range, .5f+outputDistanceShift); if (!savePng(render, testRenderMulti)) puts("Failed to write test render file."); } if (testRender) { Bitmap render(testWidth, testHeight); - renderSDF(render, msdf, avgScale*range); + renderSDF(render, msdf, avgScale*range, .5f+outputDistanceShift); if (!savePng(render, testRender)) ABORT("Failed to write test render file."); } @@ -950,13 +985,13 @@ int main(int argc, const char * const *argv) { } if (testRenderMulti) { Bitmap render(testWidthM, testHeightM); - renderSDF(render, mtsdf, avgScale*range); + renderSDF(render, mtsdf, avgScale*range, .5f+outputDistanceShift); if (!savePng(render, testRenderMulti)) puts("Failed to write test render file."); } if (testRender) { Bitmap render(testWidth, testHeight); - renderSDF(render, mtsdf, avgScale*range); + renderSDF(render, mtsdf, avgScale*range, .5f+outputDistanceShift); if (!savePng(render, testRender)) ABORT("Failed to write test render file."); }