[cairo-commit] src/cairo.c

Carl Worth cworth at kemper.freedesktop.org
Wed Dec 6 12:42:53 PST 2006


 src/cairo.c |   55 ++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 44 insertions(+), 11 deletions(-)

New commits:
diff-tree ce58f874fe25334961807a2cecf6066b18569c05 (from d0eff3919646e8a4c9981c349e33060fdb27c94e)
Author: Dan Amelang <dan at amelang.net>
Date:   Tue Dec 5 23:45:15 2006 -0800

    Change _cairo_lround to use arithmetic rounding
    
    This fixes the text rendering bug reported here:
    
        https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=217819
    
    No performance impact on x86. On the 770, I see minor speedups in text_solid
    and text_image (~1.05x).

diff --git a/src/cairo.c b/src/cairo.c
index ce6a728..30672d7 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -3197,27 +3197,60 @@ _cairo_restrict_value (double *value, do
 	*value = max;
 }
 
-/* This function is identical to the C99 function lround, except that it
- * uses banker's rounding instead of arithmetic rounding. This implementation
- * is much faster (on the platforms we care about) than lround, round, rint,
- * lrint or float (d + 0.5).
+/* This function is identical to the C99 function lround(), except that it
+ * performs arithmetic rounding (instead of away-from-zero rounding) and
+ * has a valid input range of [INT_MIN / 4, INT_MAX / 4] instead of
+ * [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems
+ * than other commonly used methods for rounding (lround, round, rint, lrint
+ * or float (d + 0.5)).
  *
- * For an explanation of the inner workings of this implemenation, see the
- * documentation for _cairo_fixed_from_double.
+ * The reason why this function is much faster on x86 than other
+ * methods is due to the fact that it avoids the fldcw instruction.
+ * This instruction incurs a large performance penalty on modern Intel
+ * processors due to how it prevents efficient instruction pipelining.
+ *
+ * The reason why this function is much faster on FPU-less systems is for
+ * an entirely different reason. All common rounding methods involve multiple
+ * floating-point operations. Each one of these operations has to be
+ * emulated in software, which adds up to be a large performance penalty.
+ * This function doesn't perform any floating-point calculations, and thus
+ * avoids this penalty.
+  */
+/* XXX needs inline comments explaining the internal magic
  */
-#define CAIRO_MAGIC_NUMBER_INT (6755399441055744.0)
 int
 _cairo_lround (double d)
 {
     union {
+        uint32_t ui32[2];
         double d;
-        int32_t i[2];
     } u;
+    uint32_t exponent, most_significant_word, least_significant_word;
+    int32_t  integer_result;
+
+    u.d = d;
 
-    u.d = d + CAIRO_MAGIC_NUMBER_INT;
 #ifdef FLOAT_WORDS_BIGENDIAN
-    return u.i[1];
+    most_significant_word  = u.ui32[0];
+    least_significant_word = u.ui32[1];
 #else
-    return u.i[0];
+    most_significant_word  = u.ui32[1];
+    least_significant_word = u.ui32[0];
 #endif
+
+    exponent = 1052 - ((most_significant_word >> 20) & 0x7FF);
+    integer_result  = ((most_significant_word & 0xFFFFF) | 0x100000) << 10;
+    integer_result |= (least_significant_word >> 22);
+
+    if (most_significant_word & 0x80000000)
+        integer_result = -integer_result;
+
+    integer_result >>= exponent;
+
+    if (exponent > 30)
+        integer_result = 0;
+
+    integer_result = (integer_result + 1) >> 1;
+
+    return integer_result;
 }


More information about the cairo-commit mailing list