mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-18 07:47:17 +00:00
gstutils: API: Add fraction helper functions
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.
This commit is contained in:
parent
c84d17bcaf
commit
e4257e8d5f
5 changed files with 191 additions and 93 deletions
|
@ -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
|
||||
|
|
152
gst/gstutils.c
152
gst/gstutils.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
122
gst/gstvalue.c
122
gst/gstvalue.c
|
@ -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;
|
||||
gdouble src = g_value_get_double (src_value);
|
||||
gint n, d;
|
||||
|
||||
/* initialize fraction being converted */
|
||||
F = src_value->data[0].v_double;
|
||||
if (F < 0.0) {
|
||||
F = -F;
|
||||
negative = TRUE;
|
||||
}
|
||||
gst_util_double_to_fraction (src, &n, &d);
|
||||
gst_value_set_fraction (dest_value, n, d);
|
||||
}
|
||||
|
||||
V = F;
|
||||
/* initialize fractions with 1/0, 0/1 */
|
||||
N1 = 1;
|
||||
D1 = 0;
|
||||
N2 = 0;
|
||||
D2 = 1;
|
||||
N = 1;
|
||||
D = 1;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
/* 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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue