gstutils: API: Add fraction helper functions
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 16 Nov 2009 08:29:10 +0000 (09:29 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 16 Nov 2009 08:56:32 +0000 (09:56 +0100)
gst_util_greatest_common_divisor()
gst_util_double_to_fraction()
gst_util_fraction_to_double()

Using these instead of going over GValue has much lower overhead.

Also add float<->fraction transform functions for GValue.

docs/gst/gstreamer-sections.txt
gst/gstutils.c
gst/gstutils.h
gst/gstvalue.c
win32/common/libgstreamer.def

index 9de1399..40c4069 100644 (file)
@@ -2525,6 +2525,9 @@ gst_util_uint64_scale_ceil
 gst_util_uint64_scale_int
 gst_util_uint64_scale_int_round
 gst_util_uint64_scale_int_ceil
+gst_util_greatest_common_divisor
+gst_util_fraction_to_double
+gst_util_double_to_fraction
 gst_util_seqnum_next
 gst_util_seqnum_compare
 gst_util_set_object_arg
index ef9d1ca..d12aa9f 100644 (file)
@@ -39,6 +39,7 @@
 #include "gstparse.h"
 #include "gstvalue.h"
 #include "gst-i18n-lib.h"
+#include <math.h>
 
 /**
  * gst_util_dump_mem:
@@ -3798,3 +3799,154 @@ gst_util_array_binary_search (gpointer array, guint num_elements,
     }
   }
 }
+
+/* Finds the greatest common divisor.
+ * Returns 1 if none other found.
+ * This is Euclid's algorithm. */
+
+/** gst_util_greatest_common_divisor:
+ * @a: First value as #gint
+ * @b: Second value as #gint
+ *
+ * Calculates the greatest common divisor of @a
+ * and @b.
+ *
+ * Returns: Greatest common divisor of @a and @b
+ *
+ * Since: 0.10.26
+ */
+gint
+gst_util_greatest_common_divisor (gint a, gint b)
+{
+  while (b != 0) {
+    int temp = a;
+
+    a = b;
+    b = temp % b;
+  }
+
+  return ABS (a);
+}
+
+/** gst_util_fraction_to_double:
+ * @src_n: Fraction numerator as #gint
+ * @src_d: Fraction denominator #gint
+ * @dest: pointer to a #gdouble for the result
+ *
+ * Transforms a #gdouble to a #GstFraction and simplifies
+ * the result.
+ *
+ * Since: 0.10.26
+ */
+void
+gst_util_fraction_to_double (gint src_n, gint src_d, gdouble * dest)
+{
+  g_return_if_fail (dest != NULL);
+  g_return_if_fail (src_d != 0);
+
+  *dest = ((gdouble) src_n) / ((gdouble) src_d);
+}
+
+#define MAX_TERMS       30
+#define MIN_DIVISOR     1.0e-10
+#define MAX_ERROR       1.0e-20
+
+/* use continued fractions to transform a double into a fraction,
+ * see http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac.
+ * This algorithm takes care of overflows.
+ */
+
+/** gst_util_double_to_fraction:
+ * @src: #gdouble to transform
+ * @dest_n: pointer to a #gint to hold the result numerator
+ * @dest_d: pointer to a #gint to hold the result denominator
+ *
+ * Transforms a #gdouble to a #GstFraction and simplifies
+ * the result.
+ *
+ * Since: 0.10.26
+ */
+void
+gst_util_double_to_fraction (gdouble src, gint * dest_n, gint * dest_d)
+{
+
+  gdouble V, F;                 /* double being converted */
+  gint N, D;                    /* will contain the result */
+  gint A;                       /* current term in continued fraction */
+  gint64 N1, D1;                /* numerator, denominator of last approx */
+  gint64 N2, D2;                /* numerator, denominator of previous approx */
+  gint i;
+  gint gcd;
+  gboolean negative = FALSE;
+
+  g_return_if_fail (dest_n != NULL);
+  g_return_if_fail (dest_d != NULL);
+
+  /* initialize fraction being converted */
+  F = src;
+  if (F < 0.0) {
+    F = -F;
+    negative = TRUE;
+  }
+
+  V = F;
+  /* initialize fractions with 1/0, 0/1 */
+  N1 = 1;
+  D1 = 0;
+  N2 = 0;
+  D2 = 1;
+  N = 1;
+  D = 1;
+
+  for (i = 0; i < MAX_TERMS; i++) {
+    /* get next term */
+    A = (gint) F;               /* no floor() needed, F is always >= 0 */
+    /* get new divisor */
+    F = F - A;
+
+    /* calculate new fraction in temp */
+    N2 = N1 * A + N2;
+    D2 = D1 * A + D2;
+
+    /* guard against overflow */
+    if (N2 > G_MAXINT || D2 > G_MAXINT) {
+      break;
+    }
+
+    N = N2;
+    D = D2;
+
+    /* save last two fractions */
+    N2 = N1;
+    D2 = D1;
+    N1 = N;
+    D1 = D;
+
+    /* quit if dividing by zero or close enough to target */
+    if (F < MIN_DIVISOR || fabs (V - ((gdouble) N) / D) < MAX_ERROR) {
+      break;
+    }
+
+    /* Take reciprocal */
+    F = 1 / F;
+  }
+  /* fix for overflow */
+  if (D == 0) {
+    N = G_MAXINT;
+    D = 1;
+  }
+  /* fix for negative */
+  if (negative)
+    N = -N;
+
+  /* simplify */
+  gcd = gst_util_greatest_common_divisor (N, D);
+  if (gcd) {
+    N /= gcd;
+    D /= gcd;
+  }
+
+  /* set results */
+  *dest_n = N;
+  *dest_d = D;
+}
index 0760183..4fa4c02 100644 (file)
@@ -1166,6 +1166,10 @@ gpointer                gst_util_array_binary_search      (gpointer array, guint
                                                           GstSearchMode mode, gconstpointer search_data,
                                                           gpointer user_data);
 
+gint gst_util_greatest_common_divisor (gint a, gint b);
+void gst_util_fraction_to_double (gint src_n, gint src_d, gdouble *dest);
+void gst_util_double_to_fraction (gdouble src, gint *dest_n, gint *dest_d);
+
 G_END_DECLS
 
 #endif /* __GST_UTILS_H__ */
index 9dc3b2d..257cff3 100644 (file)
@@ -43,6 +43,7 @@
 #include "glib-compat-private.h"
 #include <gst/gst.h>
 #include <gobject/gvaluecollector.h>
+#include "gstutils.h"
 
 typedef struct _GstValueUnionInfo GstValueUnionInfo;
 struct _GstValueUnionInfo
@@ -81,7 +82,6 @@ static GArray *gst_value_intersect_funcs;
 static GArray *gst_value_subtract_funcs;
 
 /* Forward declarations */
-static gint gst_greatest_common_divisor (gint a, gint b);
 static gchar *gst_value_serialize_fraction (const GValue * value);
 
 static GstValueCompareFunc gst_value_get_compare_func (const GValue * value1);
@@ -3476,23 +3476,6 @@ gst_value_is_fixed (const GValue * value)
  ************/
 
 /* helper functions */
-
-/* Finds the greatest common divisor.
- * Returns 1 if none other found.
- * This is Euclid's algorithm. */
-static gint
-gst_greatest_common_divisor (gint a, gint b)
-{
-  while (b != 0) {
-    int temp = a;
-
-    a = b;
-    b = temp % b;
-  }
-
-  return ABS (a);
-}
-
 static void
 gst_value_init_fraction (GValue * value)
 {
@@ -3564,7 +3547,7 @@ gst_value_set_fraction (GValue * value, gint numerator, gint denominator)
   }
 
   /* check for reduction */
-  gcd = gst_greatest_common_divisor (numerator, denominator);
+  gcd = gst_util_greatest_common_divisor (numerator, denominator);
   if (gcd) {
     numerator /= gcd;
     denominator /= gcd;
@@ -3633,10 +3616,10 @@ gst_value_fraction_multiply (GValue * product, const GValue * factor1,
   d1 = factor1->data[1].v_int;
   d2 = factor2->data[1].v_int;
 
-  gcd = gst_greatest_common_divisor (n1, d2);
+  gcd = gst_util_greatest_common_divisor (n1, d2);
   n1 /= gcd;
   d2 /= gcd;
-  gcd = gst_greatest_common_divisor (n2, d1);
+  gcd = gst_util_greatest_common_divisor (n2, d1);
   n2 /= gcd;
   d1 /= gcd;
 
@@ -3759,85 +3742,26 @@ gst_value_transform_string_fraction (const GValue * src_value,
     gst_value_set_fraction (dest_value, 0, 1);
 }
 
-#define MAX_TERMS       30
-#define MIN_DIVISOR     1.0e-10
-#define MAX_ERROR       1.0e-20
-
-/* use continued fractions to transform a double into a fraction,
- * see http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac.
- * This algorithm takes care of overflows.
- */
 static void
 gst_value_transform_double_fraction (const GValue * src_value,
     GValue * dest_value)
 {
-  gdouble V, F;                 /* double being converted */
-  gint N, D;                    /* will contain the result */
-  gint A;                       /* current term in continued fraction */
-  gint64 N1, D1;                /* numerator, denominator of last approx */
-  gint64 N2, D2;                /* numerator, denominator of previous approx */
-  gint i;
-  gboolean negative = FALSE;
-
-  /* initialize fraction being converted */
-  F = src_value->data[0].v_double;
-  if (F < 0.0) {
-    F = -F;
-    negative = TRUE;
-  }
-
-  V = F;
-  /* initialize fractions with 1/0, 0/1 */
-  N1 = 1;
-  D1 = 0;
-  N2 = 0;
-  D2 = 1;
-  N = 1;
-  D = 1;
-
-  for (i = 0; i < MAX_TERMS; i++) {
-    /* get next term */
-    A = (gint) F;               /* no floor() needed, F is always >= 0 */
-    /* get new divisor */
-    F = F - A;
-
-    /* calculate new fraction in temp */
-    N2 = N1 * A + N2;
-    D2 = D1 * A + D2;
-
-    /* guard against overflow */
-    if (N2 > G_MAXINT || D2 > G_MAXINT) {
-      break;
-    }
-
-    N = N2;
-    D = D2;
+  gdouble src = g_value_get_double (src_value);
+  gint n, d;
 
-    /* save last two fractions */
-    N2 = N1;
-    D2 = D1;
-    N1 = N;
-    D1 = D;
-
-    /* quit if dividing by zero or close enough to target */
-    if (F < MIN_DIVISOR || fabs (V - ((gdouble) N) / D) < MAX_ERROR) {
-      break;
-    }
+  gst_util_double_to_fraction (src, &n, &d);
+  gst_value_set_fraction (dest_value, n, d);
+}
 
-    /* Take reciprocal */
-    F = 1 / F;
-  }
-  /* fix for overflow */
-  if (D == 0) {
-    N = G_MAXINT;
-    D = 1;
-  }
-  /* fix for negative */
-  if (negative)
-    N = -N;
+static void
+gst_value_transform_float_fraction (const GValue * src_value,
+    GValue * dest_value)
+{
+  gfloat src = g_value_get_float (src_value);
+  gint n, d;
 
-  /* will also simplify */
-  gst_value_set_fraction (dest_value, N, D);
+  gst_util_double_to_fraction (src, &n, &d);
+  gst_value_set_fraction (dest_value, n, d);
 }
 
 static void
@@ -3848,6 +3772,14 @@ gst_value_transform_fraction_double (const GValue * src_value,
       ((double) src_value->data[1].v_int);
 }
 
+static void
+gst_value_transform_fraction_float (const GValue * src_value,
+    GValue * dest_value)
+{
+  dest_value->data[0].v_float = ((float) src_value->data[0].v_int) /
+      ((float) src_value->data[1].v_int);
+}
+
 static gint
 gst_value_compare_fraction (const GValue * value1, const GValue * value2)
 {
@@ -4354,8 +4286,12 @@ _gst_value_initialize (void)
       gst_value_transform_string_fraction);
   g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_DOUBLE,
       gst_value_transform_fraction_double);
+  g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_FLOAT,
+      gst_value_transform_fraction_float);
   g_value_register_transform_func (G_TYPE_DOUBLE, GST_TYPE_FRACTION,
       gst_value_transform_double_fraction);
+  g_value_register_transform_func (G_TYPE_FLOAT, GST_TYPE_FRACTION,
+      gst_value_transform_float_fraction);
   g_value_register_transform_func (GST_TYPE_DATE, G_TYPE_STRING,
       gst_value_transform_date_string);
   g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_DATE,
index c9e5069..6e8ede0 100644 (file)
@@ -1056,9 +1056,12 @@ EXPORTS
        gst_uri_protocol_is_valid
        gst_uri_type_get_type
        gst_util_array_binary_search
+       gst_util_double_to_fraction
        gst_util_dump_mem
+       gst_util_fraction_to_double
        gst_util_gdouble_to_guint64
        gst_util_get_timestamp
+       gst_util_greatest_common_divisor
        gst_util_guint64_to_gdouble
        gst_util_seqnum_compare
        gst_util_seqnum_next