Commit dd8a1205 authored by Federico Mena Quintero's avatar Federico Mena Quintero

Merge branch 'float-comparison'

parents 8228c59a 3431a1b3
......@@ -112,3 +112,44 @@ rendering functions. It carries the vtable for rendering in the
`state` field, and other values which are changed as rendering
progresses.
# Comparing floating-point numbers
Librsvg sometimes needs to compute things like "are these points
equal?" or "did this computed result equal this test reference
number?".
We use `f64` numbers in Rust, and `double` numbers in C, for all
computations on real numbers. These types cannot be simply compared
with `==` effectively, since it doesn't work when the numbers are
slightly different due to numerical inaccuracies.
Similarly, we don't `assert_eq!(a, b)` for floating-point numbers.
Most of the time we are dealing with coordinates which will get passed
to Cairo. In turn, Cairo converts them from doubles to a fixed-point
representation (as of March 2018, Cairo uses 24.8 fixnums with 24 bits of
integral part and 8 bits of fractional part).
So, we can consider two numbers to be "equal" if they would be represented
as the same fixed-point value by Cairo. Librsvg implements this in
the [`ApproxEqCairo` trait][ApproxEqCairo] trait. You can use it like
this:
```rust
use float_eq_cairo::ApproxEqCairo; // bring the trait into scope
let a: f64 = ...;
let b: f64 = ...;
if a.approx_eq_cairo(&b) { // not a == b
... // equal!
}
assert!(1.0_f64.approx_eq_cairo(&1.001953125_f64)); // 1 + 1/512 - cairo rounds to 1
```
As of March 2018 this is not implemented for the C code; the hope is
that we will move all that code to Rust and we'll be able to do this
kind of approximate comparisons there.
[ApproxEqCairo]: rsvg_internals/src/float_eq_cairo.rs
......@@ -107,6 +107,10 @@ It is perfectly fine to [ask the maintainer][maintainer] if you have
questions about the Autotools setup; it's a tricky bit of machinery,
and we are glad to help.
Please read the file [`ARCHITECTURE.md`][arch]; this describes the
overall flow of the source code, so hopefully it will be easier for
you to navigate.
### Testing changes
The most direct way to test a change is to have an example SVG file
......@@ -150,3 +154,4 @@ the future. Of course all help is appreciated!
[tests-readme]: tests/README.md
[rsvg-bench]: https://gitlab.gnome.org/federico/rsvg-bench
[rsvg-rs]: https://github.com/selaux/rsvg-rs
[arch]: ARCHITECTURE.md
......@@ -25,7 +25,7 @@ dependencies = [
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -34,7 +34,7 @@ name = "cairo-sys-rs"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -44,8 +44,8 @@ name = "cssparser"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser-macros 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dtoa-short 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -57,18 +57,19 @@ dependencies = [
[[package]]
name = "cssparser-macros"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"procedural-masquerade 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "downcast-rs"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
......@@ -78,7 +79,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dtoa-short"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -89,6 +90,14 @@ name = "either"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "float-cmp"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
......@@ -112,7 +121,7 @@ dependencies = [
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
......@@ -121,7 +130,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -132,13 +141,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itertools"
version = "0.7.6"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -156,7 +165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.36"
version = "0.2.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
......@@ -169,9 +178,14 @@ name = "memchr"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pango"
version = "0.3.0"
......@@ -181,7 +195,7 @@ dependencies = [
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -193,7 +207,7 @@ dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -236,6 +250,14 @@ name = "pkg-config"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "procedural-masquerade"
version = "0.1.5"
......@@ -246,13 +268,21 @@ name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -262,26 +292,29 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.6"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.4.2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rsvg_internals"
......@@ -290,17 +323,18 @@ dependencies = [
"cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
"downcast-rs 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"downcast-rs 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
......@@ -323,6 +357,16 @@ dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.12.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
......@@ -340,11 +384,21 @@ dependencies = [
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ucd-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
......@@ -394,22 +448,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6b5695f59fd036fe5741bc5a4eb20c78fbe42256e3b08a2af26bbcbe8070bf3"
"checksum cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6e18fecaeac51809db57f45f4553cc0975225a7eb435a7a7e91e5e8113a84d"
"checksum cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a807ac3ab7a217829c2a3b65732b926b2befe6a35f33b4bf8b503692430f223"
"checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
"checksum downcast-rs 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "917042ca6e6c9fe735a63cd4d5d4c43c64ea8deb456fc5465d3f78df24e68d86"
"checksum cssparser-macros 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ce714f82ed2ca0e026ee351b7f25b2d70f81cf6f0f3214537a2edb67cd4d4d0"
"checksum downcast-rs 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5b28487662892f2f2755292dfac47435fb72375e3f5861c7a59a76abdfb1e21f"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"
"checksum dtoa-short 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59020b8513b76630c49d918c33db9f4c91638e7d3404a28084083b87e33f76f2"
"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
"checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9b0452824cc63066940f01adc721804919f0b76cdba3cfab977b00b87f16d4a"
"checksum glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9693049613ff52b93013cc3d2590366d8e530366d288438724b73f6c7dc4be8"
"checksum gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60d507c87a71b1143c66ed21a969be9b99a76df234b342d733e787e6c9c7d7c2"
"checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
"checksum itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "23d53b4c7394338044c3b9c8c5b2caaf7b40ae049ecd321578ebdc2e13738cd1"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3c2bd9b9d21e48e956b763c9f37134dc62d9e95da6edb3f672cacb6caf3cd3"
"checksum pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e81c404ab81ea7ea2fc2431a0a7672507b80e4b8bf4b41eac3fc83cc665104e"
"checksum pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34f34a1be107fe16abb2744e0e206bee4b3b07460b5fddd3009a6aaf60bd69ab"
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
......@@ -417,18 +473,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
"checksum procedural-masquerade 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1bcafee1590f81acb329ae45ec627b318123f085153913620316ae9a144b2a"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5be5347bde0c48cfd8c3fdc0766cdfe9d8a755ef84d620d6794c778c91de8b2b"
"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
"checksum regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a62bf8bb734ab90b7f234b681b01af396e5d39b028906c210dc04fa1d5e9e5b3"
"checksum regex-syntax 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48d7391e7e90e06eaf3aefbe4652464153ecfec64806f3bf77ffc59638a63e77"
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
......
......@@ -18,6 +18,7 @@ pango-sys = "0.5.0"
cssparser = "0.23"
lazy_static = "1.0.0"
phf = "0.7.21"
float-cmp = "0.4.0"
[dependencies.cairo-sys-rs]
version = "0.5.0"
......
......@@ -3,7 +3,8 @@ use glib_sys;
use cairo::MatrixTrait;
use glib::translate::*;
use util::*;
use float_eq_cairo::ApproxEqCairo;
// Keep this in sync with ../../rsvg-private.h:RsvgBbox
#[repr(C)]
......@@ -20,8 +21,8 @@ impl RsvgBbox {
}
pub fn is_empty(&self) -> bool {
from_glib(self.virgin) || double_equals(self.rect.width, 0.0)
|| double_equals(self.rect.height, 0.0)
from_glib(self.virgin) || self.rect.width.approx_eq_cairo(&0.0)
|| self.rect.height.approx_eq_cairo(&0.0)
}
}
......
use float_cmp::ApproxEq;
// The following are copied from cairo/src/{cairo-fixed-private.h, cairo-fixed-type-private.h}
const CAIRO_FIXED_FRAC_BITS: u64 = 8;
const CAIRO_MAGIC_NUMBER_FIXED: f64 = (1u64 << (52 - CAIRO_FIXED_FRAC_BITS)) as f64 * 1.5;
fn cairo_magic_double(d: f64) -> f64 {
d + CAIRO_MAGIC_NUMBER_FIXED
}
fn cairo_fixed_from_double(d: f64) -> i32 {
let bits = cairo_magic_double(d).to_bits();
let lower = bits & 0xffffffff;
lower as i32
}
/// Implements a method to check whether two `f64` numbers would have
/// the same fixed-point representation in Cairo.
///
/// This generally means that the absolute difference between them,
/// when taken as floating-point numbers, is less than the smallest
/// representable fraction that Cairo can represent in fixed-point.
///
/// Implementation detail: Cairo fixed-point numbers use 24 bits for
/// the integral part, and 8 bits for the fractional part. That is,
/// the smallest fraction they can represent is 1/256.
pub trait FixedEqCairo {
fn fixed_eq_cairo(&self, other: &Self) -> bool;
}
impl FixedEqCairo for f64 {
fn fixed_eq_cairo(&self, other: &f64) -> bool {
// FIXME: Here we have the same problem as Cairo itself: we
// don't check for overflow in the conversion of double to
// fixed-point.
cairo_fixed_from_double(*self) == cairo_fixed_from_double(*other)
}
}
/// Checks whether two floating-point numbers are approximately equal,
/// considering Cairo's limitations on numeric representation.
///
/// Cairo uses fixed-point numbers internally. We implement this
/// trait for `f64`, so that two numbers can be considered "close
/// enough to equal" if their absolute difference is smaller than the
/// smallest fixed-point fraction that Cairo can represent.
///
/// Note that this trait is reliable even if the given numbers are
/// outside of the range that Cairo's fixed-point numbers can
/// represent. In that case, we check for the absolute difference,
/// and finally allow a difference of 1 unit-in-the-last-place (ULP)
/// for very large f64 values.
pub trait ApproxEqCairo: ApproxEq {
fn approx_eq_cairo(&self, other: &Self) -> bool;
}
impl ApproxEqCairo for f64 {
fn approx_eq_cairo(&self, other: &f64) -> bool {
let cairo_smallest_fraction = 1.0 / f64::from(1 << CAIRO_FIXED_FRAC_BITS);
self.approx_eq(other, cairo_smallest_fraction, 1)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn numbers_equal_in_cairo_fixed_point() {
assert!(1.0_f64.fixed_eq_cairo(&1.0_f64));
assert!(1.0_f64.fixed_eq_cairo(&1.001953125_f64)); // 1 + 1/512 - cairo rounds to 1
assert!(!1.0_f64.fixed_eq_cairo(&1.00390625_f64)); // 1 + 1/256 - cairo can represent it
}
#[test]
fn numbers_approx_equal() {
// 0 == 1/256 - cairo can represent it, so not equal
assert!(!0.0_f64.approx_eq_cairo(&0.00390635_f64));
// 1 == 1 + 1/256 - cairo can represent it, so not equal
assert!(!1.0_f64.approx_eq_cairo(&1.00390635_f64));
// 0 == 1/256 - cairo can represent it, so not equal
assert!(!0.0_f64.approx_eq_cairo(&-0.00390635_f64));
// 1 == 1 - 1/256 - cairo can represent it, so not equal
assert!(!1.0_f64.approx_eq_cairo(&0.99609365_f64));
// 0 == 1/512 - cairo approximates to 0, so equal
assert!(0.0_f64.approx_eq_cairo(&0.001953125_f64));
// 1 == 1 + 1/512 - cairo approximates to 1, so equal
assert!(1.0_f64.approx_eq_cairo(&1.001953125_f64));
// 0 == -1/512 - cairo approximates to 0, so equal
assert!(0.0_f64.approx_eq_cairo(&-0.001953125_f64));
// 1 == 1 - 1/512 - cairo approximates to 1, so equal
assert!(1.0_f64.approx_eq_cairo(&0.998046875_f64));
// This is 2^53 compared to (2^53 + 2). When represented as
// f64, they are 1 unit-in-the-last-place (ULP) away from each
// other, since the mantissa has 53 bits (52 bits plus 1
// "hidden" bit). The first number is an exact double, and
// the second one is the next biggest double. We consider a
// difference of 1 ULP to mean that numbers are "equal", to
// account for slight imprecision in floating-point
// calculations. Most of the time, for small values, we will
// be using the cairo_smallest_fraction from the
// implementation of approx_eq_cairo() above. For large
// values, we want the ULPs.
//
// In the second assertion, we compare 2^53 with (2^53 + 4). Those are
// 2 ULPs away, and we don't consider them equal.
assert!(9_007_199_254_740_992.0.approx_eq_cairo(&9_007_199_254_740_994.0));
assert!(!9_007_199_254_740_992.0.approx_eq_cairo(&9_007_199_254_740_996.0));
}
}
......@@ -5,6 +5,7 @@
extern crate cairo;
extern crate cairo_sys;
extern crate cssparser;
extern crate float_cmp;
extern crate glib;
extern crate glib_sys;
extern crate itertools;
......@@ -142,6 +143,7 @@ mod cnode;
mod color;
mod drawing_ctx;
mod error;
mod float_eq_cairo;
mod gradient;
mod handle;
mod image;
......
......@@ -10,6 +10,7 @@ use attributes::Attribute;
use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use error::*;
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use node::*;
......@@ -18,7 +19,7 @@ use parsers::{parse, Parse};
use parsers::ParseError;
use path_builder::*;
use property_bag::PropertyBag;
use util::*;
use util::utf8_cstr;
use viewbox::*;
// markerUnits attribute: https://www.w3.org/TR/SVG/painting.html#MarkerElement
......@@ -120,7 +121,7 @@ impl NodeMarker {
let marker_width = self.width.get().normalize(draw_ctx);
let marker_height = self.height.get().normalize(draw_ctx);
if double_equals(marker_width, 0.0) || double_equals(marker_height, 0.0) {
if marker_width.approx_eq_cairo(&0.0) || marker_height.approx_eq_cairo(&0.0) {
// markerWidth or markerHeight set to 0 disables rendering of the element
// https://www.w3.org/TR/SVG/painting.html#MarkerWidthAttribute
return;
......@@ -419,7 +420,7 @@ pub fn path_builder_to_segments(builder: &RsvgPathBuilder) -> Vec<Segment> {
}
fn points_equal(x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
double_equals(x1, x2) && double_equals(y1, y2)
x1.approx_eq_cairo(&x2) && y1.approx_eq_cairo(&y2)
}
// If the segment has directionality, returns two vectors (v1x, v1y, v2x, v2y); otherwise,
......@@ -897,7 +898,8 @@ mod parser_tests {
#[cfg(test)]
mod directionality_tests {
use super::*;
use std::f64::consts::*;
use float_cmp::ApproxEq;
use std::f64;
fn test_bisection_angle(
expected: f64,
......@@ -910,7 +912,7 @@ mod directionality_tests {
super::angle_from_vector(incoming_vx, incoming_vy),
super::angle_from_vector(outgoing_vx, outgoing_vy),
);
assert!(double_equals(expected, bisected));
assert!(expected.approx_eq(&bisected, 2.0 * PI * f64::EPSILON, 1));
}
#[test]
......
......@@ -5,6 +5,8 @@ use glib::translate::*;
use std::f64;
use std::f64::consts::*;
use float_eq_cairo::ApproxEqCairo;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct LargeArc(pub bool);
......@@ -124,7 +126,7 @@ impl RsvgPathBuilder {
let mut k5: f64;
let n_segs: i32;
if x1 == x2 && y1 == y2 {
if x1.approx_eq_cairo(&x2) && y1.approx_eq_cairo(&y2) {
return;
}
......@@ -140,9 +142,10 @@ impl RsvgPathBuilder {
rx = rx.abs();
ry = ry.abs();
// Check the radius against floading point underflow.
// A bit further down we divide by the square of the radii. Check
// that we won't divide by zero.
// See http://bugs.debian.org/508443
if (rx < f64::EPSILON) || (ry < f64::EPSILON) {
if ((rx * rx) < f64::EPSILON) || ((ry * ry) < f64::EPSILON) {
self.line_to(x2, y2);
return;
}
......@@ -161,7 +164,7 @@ impl RsvgPathBuilder {
// Compute the center
k1 = rx * rx * y1_ * y1_ + ry * ry * x1_ * x1_;
if k1 == 0.0 {
if k1.approx_eq_cairo(&0.0) {
return;
}
......@@ -183,7 +186,7 @@ impl RsvgPathBuilder {
k4 = (-y1_ - cy_) / ry;
k5 = (k1 * k1 + k2 * k2).abs().sqrt();
if k5 == 0.0 {
if k5.approx_eq_cairo(&0.0) {
return;
}
......@@ -196,7 +199,7 @@ impl RsvgPathBuilder {
// Compute delta_theta
k5 = ((k1 * k1 + k2 * k2) * (k3 * k3 + k4 * k4)).abs().sqrt();
if k5 == 0.0 {
if k5.approx_eq_cairo(&0.0) {
return;
}
......@@ -304,3 +307,25 @@ pub extern "C" fn rsvg_path_builder_add_to_cairo_context(
let cr = unsafe { cairo::Context::from_glib_none(raw_cr) };
builder.to_cairo(&cr);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn survives_degenerate_arcs() {
let mut builder = RsvgPathBuilder::new();
builder.move_to(0.0, 0.0);
builder.arc(
0.0,
0.0,
f64::EPSILON, // deliberately close to 0 to try to trigger division by 0
f64::EPSILON,
0.0,
LargeArc(true),
Sweep::Positive,
1.0,
1.0,
);
}
}
......@@ -2,6 +2,7 @@ use cairo;
use libc;
use std::cell::RefCell;
use std::f64;
use std::rc::*;
use cairo::MatrixTrait;
......@@ -13,12 +14,12 @@ use bbox::*;
use coord_units::CoordUnits;
use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use node::*;
use parsers::parse;
use property_bag::PropertyBag;
use util::*;
use viewbox::*;
coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
......@@ -352,7 +353,7 @@ fn set_pattern_on_draw_context(
let scaled_width = pattern_width * bbwscale;
let scaled_height = pattern_height * bbhscale;
if scaled_width.abs() < DBL_EPSILON || scaled_height.abs() < DBL_EPSILON || pw < 1 || ph < 1 {
if scaled_width.abs() < f64::EPSILON || scaled_height.abs() < f64::EPSILON || pw < 1 || ph < 1 {
return false;
}
......@@ -414,7 +415,7 @@ fn set_pattern_on_draw_context(
pushed_view_box = false;
}
if scwscale != 1.0 || schscale != 1.0 {
if !scwscale.approx_eq_cairo(&1.0) || !schscale.approx_eq_cairo(&1.0) {
let mut scalematrix = cairo::Matrix::identity();
scalematrix.scale(scwscale, schscale);
caffine = cairo::Matrix::multiply(&caffine, &scalematrix);
......
......@@ -10,12 +10,12 @@ use aspect_ratio::*;
use attributes::Attribute;
use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use node::*;
use parsers::{parse, Parse};
use property_bag::{OwnedPropertyBag, PropertyBag};
use util::*;
use viewbox::*;
use viewport::{draw_in_viewport, ClipMode};
......@@ -306,7 +306,7 @@ impl NodeTrait for NodeUse {
// width or height set to 0 disables rendering of the element
// https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
if double_equals(nw, 0.0) || double_equals(nh, 0.0) {
if nw.approx_eq_cairo(&0.0) || nh.approx_eq_cairo(&0.0) {
drawing_ctx::release_node(draw_ctx, raw_child);
return;
}
......
......@@ -14,12 +14,6 @@ pub fn clone_fallback_name(fallback: &Option<String>) -> Option<String> {