mirror of https://github.com/Chlumsky/msdfgen.git
Adding a test harness and some initial tests (most fail currently)
This commit is contained in:
parent
746767a62b
commit
3b9d24a6ca
|
|
@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 2.8.12)
|
|||
|
||||
project(msdfgen)
|
||||
|
||||
include(CTest)
|
||||
|
||||
find_package(Freetype REQUIRED)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
|
@ -48,6 +50,11 @@ endfunction(folderize_sources)
|
|||
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Build Rules
|
||||
#----------------------------------------------------------------
|
||||
|
||||
|
||||
file(GLOB_RECURSE msdfgen_HEADERS
|
||||
"core/*.h"
|
||||
"lib/*.h"
|
||||
|
|
@ -77,3 +84,20 @@ target_link_libraries(lib_msdfgen ${FREETYPE_LIBRARIES})
|
|||
add_executable(msdfgen main.cpp)
|
||||
target_compile_definitions(msdfgen PRIVATE MSDFGEN_STANDALONE)
|
||||
target_link_libraries(msdfgen lib_msdfgen)
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Tests
|
||||
#----------------------------------------------------------------
|
||||
|
||||
if (BUILD_TESTING)
|
||||
file(GLOB TEST_DRIVER "test/test_msdf.py")
|
||||
file(GLOB_RECURSE TEST_SVG_SOURCES
|
||||
"test/test-*.svg")
|
||||
foreach(FILE ${TEST_SVG_SOURCES})
|
||||
get_filename_component(NAME "${FILE}" NAME)
|
||||
add_test(NAME ${NAME} COMMAND python "${TEST_DRIVER}" --svg "${FILE}" --exe "$<TARGET_FILE:msdfgen>")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
*-diff.png
|
||||
*-render.png
|
||||
*.msdf.png
|
||||
*.psdf.png
|
||||
*.sdf.png
|
||||
montage-*.png
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Simple helper so I can compare versions and get a bird's eye of the tests.
|
||||
# This isn't meant to be run as a unit test.
|
||||
|
||||
EXE=../Release/msdfgen
|
||||
|
||||
DIR=${1:-.}
|
||||
echo "Running montage in $DIR"
|
||||
|
||||
function runtest()
|
||||
{
|
||||
echo "--[[ Running $1 v$2 ]]--"
|
||||
rm -f test-*.$1.png
|
||||
rm -f test-*.$1-diff.png
|
||||
rm -f test-*.$1-render.png
|
||||
python test_msdf.py --svg-dir "$DIR" --legacy $2 --mode $1 --exe "$EXE" --montage
|
||||
}
|
||||
|
||||
function runmode()
|
||||
{
|
||||
runtest $1 2
|
||||
runtest $1 0
|
||||
compare montage-$1-[02]-diff.png -highlight-color blue montage-$1-2vs0-diff.png
|
||||
open montage-$1-2vs0-diff.png
|
||||
}
|
||||
|
||||
|
||||
runmode msdf
|
||||
#runmode sdf
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<svg viewBox="0 0 512 512" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<desc>Example fillrule-evenodd - demonstrates fill-rule:evenodd</desc>
|
||||
<path d="M0 0h512v512H0z" fill="black"/>
|
||||
<path d="M 130,35 L 203,261 11,121 249,121 57,261 z
|
||||
M 375,61 A 107,107 0 0,1 375,275 A 107,107 0 0,1 375,61 z
|
||||
M 375,119 A 49,49 0 0,1 375,217 A 49,49 0 0,1 375,119 z
|
||||
M 250,281 A 107,107 0 0,1 250,495 A 107,107 0 0,1 250,281 z
|
||||
M 250,339 A 49,49 0 0,0 250,437 A 49,49 0 0,0 250,339 z"
|
||||
fill="white"
|
||||
fill-rule="evenodd"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 594 B |
|
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<desc>Another fillrule-nonzero test, but this time using it as a default</desc>
|
||||
<path d="M0 0h512v512H0z" fill="black"/>
|
||||
<path d="
|
||||
M300 100l50,25v-50z
|
||||
M100 300l-50,25v-50z
|
||||
"
|
||||
fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 265 B |
|
|
@ -0,0 +1,12 @@
|
|||
<svg viewBox="0 0 512 512" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<desc>Example fillrule-nonzero - demonstrates fill-rule:nonzero</desc>
|
||||
<path d="M0 0h512v512H0z" fill="black"/>
|
||||
<path d="M 130,35 L 203,261 11,121 249,121 57,261 z
|
||||
M 375,61 A 107,107 0 0,1 375,275 A 107,107 0 0,1 375,61 z
|
||||
M 375,119 A 49,49 0 0,1 375,217 A 49,49 0 0,1 375,119 z
|
||||
M 250,281 A 107,107 0 0,1 250,495 A 107,107 0 0,1 250,281 z
|
||||
M 250,339 A 49,49 0 0,0 250,437 A 49,49 0 0,0 250,339 z"
|
||||
fill="white"
|
||||
fill-rule="nonzero"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 594 B |
|
|
@ -0,0 +1,13 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<desc>Illustrates a path that implicitly closes contours without a 'z'</desc>
|
||||
<path d="M0 0h512v512H0z" fill="black"/>
|
||||
<path fill="white" d="
|
||||
M278.535 276.134
|
||||
c12.314 21.92 26.598 38.42 42.854 49.503 16.5 10.837 35.094 16.255 55.782 16.255 24.874 0 45.193-8.25 60.955-24.752 15.762-16.747 23.643-38.05 23.644-63.91 0-24.875-7.265-45.563-21.795-62.064-14.532-16.5-32.757-24.752-54.676-24.752-19.95 0-38.052 8.25-54.306 24.752-16.01 16.255-33.495 44.577-52.46 84.968z
|
||||
m-45.07-39.53
|
||||
c-12.067-21.672-26.352-37.926-42.853-48.763-16.254-10.835-34.848-16.254-55.782-16.254-24.875 0-45.193 8.25-60.955 24.752
|
||||
C58.11 212.593 50.23 233.65 50.23 259.51
|
||||
c0 24.875 7.265 45.562 21.795 62.063 14.53 16.5 32.756 24.752 54.676 24.752 19.95 0 37.928-8.127 53.937-24.382 16.254-16.255 33.864-44.7 52.828-85.338z
|
||||
m26.23 67.605c-17.487 33.495-35.835 58-55.045 73.516C185.686 393.242 164.505 401 141.108 401c-33.248 0-61.448-13.792-84.598-41.376-22.905-27.584-34.357-61.694-34.357-102.33 0-43.1 10.22-77.95 30.662-104.55 20.688-26.597 47.533-39.896 80.535-39.897 23.397 0 44.33 7.635 62.803 22.905 18.47 15.023 36.942 39.898 55.414 74.624 16.747-33.987 34.85-58.985 54.306-74.994C325.33 119.128 347.003 111 370.893 111c32.754 0 60.707 13.915 83.86 41.745 23.395 27.83 35.094 62.187 35.094 103.07 0 42.854-10.344 77.58-31.032 104.18-20.442 26.35-47.164 39.527-80.165 39.527-23.398 0-44.21-7.142-62.433-21.426-17.98-14.53-36.82-39.16-56.523-73.886"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,62 @@
|
|||
<svg viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<desc>Test that validates input by removing degenerate edges and numerical robustness with regard to matching endpoints</desc>
|
||||
<path d="M0 0h512v512H0z" fill="black"/>
|
||||
<path d="
|
||||
M242.563,27.656C232.501,27.689 222.437,27.861 212.375,28.156L218.469,93.75L157.374,31.187C126.118,33.757 94.88,37.842 63.624,43.719L97.874,72.124L49.811,71.561C84.969,171.729 56.747,254.136 49.499,363.561L145.249,469.405C155.249,468.079 165.105,466.982 174.812,466.061L196.342,423.905L214.874,452.749L256.374,419.155L257.187,462.249C340.13,462.007 414.921,471.347 492.874,470.062C464.724,397.052 461.744,326.5 460.999,260.905L427.749,241.78L460.437,207.906C460.007,186.573 459.095,165.9 456.812,145.969L399.374,146.687L449.624,105.03C445.781,89.553 440.572,74.63 433.499,60.312L403.969,36.406C377.12,33.667 350.214,31.369 323.265,29.908C296.394,28.451 269.471,28.053 242.563,27.656L242.563,27.656Z
|
||||
M332.97,138.875L355.406,107.437L403.374,188.937L387.249,198.407L353.874,141.719L311.436,201.124L296.249,190.249L320.249,156.655L298.279,105.249L273.719,145.312L285.686,167.937L269.156,176.687L250.906,142.187L226.061,200.374L208.874,193.03L249.094,98.814L263.562,126.156L301.281,64.626L332.97,138.875Z
|
||||
|
||||
M106.439,120.876
|
||||
C115.724,122.286 125.63,126.156 125.63,126.156
|
||||
L119.412,143.781
|
||||
C111.103,140.092 102.474,139.126 102.474,139.126
|
||||
L106.439,120.876Z
|
||||
|
||||
M144.313,134.25
|
||||
C150.717,137.963 155.804,141.976 161.344,147.063
|
||||
L147.814,159.938
|
||||
C142.651,155.095 141.192,154.05 135,150.47
|
||||
L144.313,134.25Z
|
||||
|
||||
M173.004,166.691
|
||||
C173.797,169.713 174.909,174.55 175.126,177.683
|
||||
C175.34,180.756 174.922,186.047 174.593,189.094
|
||||
L156.063,186.688
|
||||
C156.711,182.075 156.706,176.593 155.125,172.154
|
||||
L173.004,166.691Z
|
||||
|
||||
M151.75,202.345L169.03,209.405
|
||||
C165.563,217.561 164.621,219.562 160.439,227.219
|
||||
L144.096,218.155
|
||||
C147.696,211.566 148.546,209.66 151.754,202.341
|
||||
L151.75,202.345
|
||||
L151.75,202.345Z
|
||||
|
||||
M135.28,236.438
|
||||
L152.97,242.468
|
||||
C150.756,249.362 150.418,251.226 149.407,258.658
|
||||
L130.813,256.658
|
||||
C132.13,247.384 132.559,245.058 135.278,236.437
|
||||
L135.28,236.438Z
|
||||
|
||||
M343.125,253.813C346.272,254.161 351.299,254.904 354.324,255.905C357.286,256.886 362.082,259.407 364.781,260.935L355.031,276.873C350.682,274.371 346.468,272.941 341.469,272.433L343.124,253.808L343.125,253.813Z
|
||||
|
||||
M321.826,254.741L325.751,273.625C319.97,274.644 314.639,276.275 309.312,278.75L302.5,261.344C308.798,258.515 321.826,254.741 321.826,254.741Z
|
||||
|
||||
M285.225,270.508L294.97,286.312C287.672,291.41 285.806,292.615 278.188,296.936L269.251,280.529C275.96,276.74 285.225,270.508 285.225,270.508Z
|
||||
|
||||
M150.47,273.876C151.978,278.518 154.314,282.263 157.374,286.031L143.564,298.626C141.633,296.254 138.148,291.744 136.643,289.064C135.059,286.244 133.364,281.592 132.372,278.532L150.466,273.877L150.47,273.876L150.47,273.876Z
|
||||
|
||||
M380.78,277.188C384.895,283.171 387.564,289.3 389.5,296.283L371.687,301.909C369.46,295.07 368.656,293.416 364.942,287.096L380.785,277.19L380.78,277.188L380.78,277.188Z
|
||||
|
||||
M253.833,287.766L260.345,305.438C252.187,308.821 250.108,309.559 241.685,311.904L236.717,293.904C244.248,292.022 253.833,287.766 253.833,287.766Z
|
||||
|
||||
M169.625,294.25C176.353,296.829 178.122,297.284 185.312,298.532L182.53,317.032C173.218,315.416 170.922,314.743 162.001,311.315L169.626,294.251L169.625,294.25L169.625,294.25Z
|
||||
|
||||
M219.563,297.78L222.406,316.25C215.806,317.641 209.334,318.219 202.593,318.155L202.187,299.469C209.869,299.285 211.818,299.061 219.561,297.782L219.563,297.78L219.563,297.78Z
|
||||
|
||||
M394.063,315.78C395.451,325.424 395.448,325.4 396.251,335.097L377.626,336.597C376.888,327.655 376.894,327.684 375.595,318.784L394.065,315.784L394.063,315.78L394.063,315.78Z
|
||||
|
||||
M326.5,346.595C345.393,350.055 371.413,362.782 395.375,380.47C402.835,370.635 409.8,360.07 416.688,349.375L446.75,362.345C442.064,374.555 431.76,387.863 418.187,399.531C435.204,415.697 449.057,434.046 455.187,452.471C441.381,434.363 424.117,420.741 404.907,409.657C381.52,425.503 352.225,435.587 325.281,430.095C348.196,425.712 365.925,413.535 381.095,397.501C364.633,389.997 347.297,383.624 329.845,377.407L326.501,346.595L326.5,346.595L326.5,346.595Z
|
||||
|
||||
" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<desc>Test a simple triangle for accuracy</desc>
|
||||
<path d="M0 0h512v512H0z" fill="black"/>
|
||||
<path d="
|
||||
M256,156 l100,200h-200z
|
||||
"
|
||||
fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 216 B |
|
|
@ -0,0 +1,178 @@
|
|||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from subprocess import Popen, PIPE, call
|
||||
|
||||
|
||||
class Runner:
|
||||
pass_count = 0
|
||||
fail_count = 0
|
||||
stop_on_fail = False
|
||||
results = []
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def add_fail(self, path, msg):
|
||||
print("FAIL \"%s\" %s" % (path, msg))
|
||||
self.fail_count += 1
|
||||
if self.stop_on_fail:
|
||||
sys.exit(1)
|
||||
return False
|
||||
|
||||
def add_pass(self, path, msg):
|
||||
self.pass_count += 1
|
||||
print("PASS \"%s\" %s" % (path, msg))
|
||||
return True
|
||||
|
||||
def add_output(self, out_path, render_path, diff_path):
|
||||
self.results.append({
|
||||
'out': out_path,
|
||||
'render': render_path,
|
||||
'diff': diff_path})
|
||||
|
||||
def get_outputs(self, key):
|
||||
ret = []
|
||||
for e in self.results:
|
||||
if key in e:
|
||||
ret.append(e[key])
|
||||
return ret
|
||||
|
||||
|
||||
def check_imagemagick():
|
||||
p = Popen(["compare -version"],
|
||||
stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
|
||||
output, err = p.communicate("")
|
||||
if p.returncode != 0:
|
||||
print "Cannot find ImageMagick <http://www.imagemagick.org> on your PATH. It is required to run tests."
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def test_svg(path, args, runner):
|
||||
(root, ext) = os.path.splitext(path)
|
||||
|
||||
mode = args.mode
|
||||
sz = args.sdf_size
|
||||
rsz = str(args.render_size)
|
||||
|
||||
sdf_path = "%s.%s.png" % (root, mode)
|
||||
render_path = "%s.%s-render.png" % (root, mode)
|
||||
diff_path = "%s.%s-diff.png" % (root, mode)
|
||||
|
||||
try:
|
||||
result = call([args.exe, mode, '-svg', path, '-o', sdf_path,
|
||||
# '-angle', '4',
|
||||
# '-pxrange', '2',
|
||||
# '-range', '8',
|
||||
# '-tolerance', '0.01',
|
||||
# '-legacy', str(args.legacy),
|
||||
'-scale', str(sz / 512.0), '-size', str(sz), str(sz),
|
||||
# '-exportshape', 'shape.txt',
|
||||
'-testrender', render_path, rsz, rsz
|
||||
], shell=False)
|
||||
if result != 0:
|
||||
return runner.add_fail(path, "Unable to render %s" % mode)
|
||||
except OSError as er:
|
||||
return runner.add_fail(path, "Error running %s: %s" % (args.exe, er))
|
||||
|
||||
# Use imagemagick to rasterize the reference image and compare it
|
||||
p = Popen(["compare -metric RMSE \"%s\" \"%s\" \"%s\"" % (path, render_path, diff_path)],
|
||||
stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
|
||||
output, err = p.communicate("")
|
||||
if p.returncode == 1:
|
||||
runner.add_output(sdf_path, render_path, diff_path)
|
||||
# Ran successfully, but they are different. Let's parse the output and check the actual error metric.
|
||||
# RMSE output = "<error> (<normalized error>)"
|
||||
match = re.match(r'\S+ \(([^)]+)\)', err)
|
||||
if match:
|
||||
e = float(match.group(1))
|
||||
if e > args.fail_threshold:
|
||||
return runner.add_fail(path, "Error = %s" % e)
|
||||
else:
|
||||
return runner.add_pass(path, "(Acceptable) Error = %s" % e)
|
||||
else:
|
||||
runner.add_fail(path, "(Unknown) Error metric = %s" % err)
|
||||
elif p.returncode != 0:
|
||||
return runner.add_fail(path, "Error comparing to %s [%d]: %s" % (render_path, p.returncode, err))
|
||||
|
||||
runner.add_output(sdf_path, render_path, diff_path)
|
||||
|
||||
return runner.add_pass(path, output)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Test MSDFGEN outputs")
|
||||
|
||||
parser.add_argument("--svg-dir",
|
||||
help="Directory to scan for SVG files.")
|
||||
parser.add_argument("--svg",
|
||||
help="SVG file to test")
|
||||
parser.add_argument("--mode",
|
||||
help="Algorithm: [sdf, psdf, msdf] (default=msdf)",
|
||||
default="msdf")
|
||||
parser.add_argument("--sdf_size",
|
||||
help="Size for rendered (M) image. Default = 128",
|
||||
default=128,
|
||||
type=int)
|
||||
parser.add_argument("--render_size",
|
||||
help="Size for the rendered test image. Default = 512",
|
||||
default=512,
|
||||
type=int)
|
||||
parser.add_argument("--exe",
|
||||
help="Path to MSDFGEN executable",
|
||||
default="msdfgen")
|
||||
parser.add_argument("--fail-threshold",
|
||||
help="Threshold for normalized RMSE that will flag a test as failure (Default: 0.07)",
|
||||
default=0.07,
|
||||
type=float)
|
||||
parser.add_argument("--legacy",
|
||||
help="Use legacy <Version> mode algorithm",
|
||||
default='0')
|
||||
parser.add_argument("--montage",
|
||||
help="Generate montage image(s) for results",
|
||||
default=False,
|
||||
action='store_true')
|
||||
parser.add_argument("--stop-on-fail",
|
||||
help="Stop testing after the first fail",
|
||||
default=False,
|
||||
action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.svg_dir and not args.svg:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
check_imagemagick()
|
||||
|
||||
runner = Runner()
|
||||
runner.stop_on_fail = args.stop_on_fail
|
||||
|
||||
if args.svg_dir:
|
||||
for root, dirs, files in os.walk(args.svg_dir):
|
||||
for name in files:
|
||||
if not name.endswith(".svg"):
|
||||
continue
|
||||
path = os.path.join(root, name)
|
||||
test_svg(path, args, runner)
|
||||
if args.svg:
|
||||
test_svg(args.svg, args, runner)
|
||||
|
||||
if args.montage:
|
||||
# We keep the diff montage at actual size (we want to see details)
|
||||
call(["montage -geometry +1+1 %s montage-%s-%s-diff.png" %
|
||||
(" ".join(runner.get_outputs('diff')), args.mode, args.legacy)], shell=True)
|
||||
# The others, we can let IM do some resizing because it's really just for quick glances.
|
||||
call(["montage %s montage-%s-%s-out.png" %
|
||||
(" ".join(runner.get_outputs('out')), args.mode, args.legacy)], shell=True)
|
||||
call(["montage %s montage-%s-%s-render.png" %
|
||||
(" ".join(runner.get_outputs('render')), args.mode, args.legacy)], shell=True)
|
||||
|
||||
if runner.fail_count > 0:
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Reference in New Issue