mutter/clutter/clutter-units.c
Emmanuele Bassi ee883f30d4 Add units-from-em conversion
An em is a unit of measurement in typography, equal to the point
size of the current font.

It should be possible to convert a value expressed in em to
ClutterUnits by using the current font and the current DPI as
stored by the default backend.
2009-01-21 17:35:47 +00:00

464 lines
12 KiB
C

/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By: Tomas Frydrych <tf@openedhand.com>
* Emmanuele Bassi <ebassi@openedhand.com>
*
* Copyright (C) 2007 OpenedHand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:clutter-units
* @short_description: A logical distance unit.
*
* Clutter units are logical units with granularity greater than that of the
* device units; they are used by #ClutterActorBox and the units-based family
* of #ClutterActor functions. To convert between Clutter units and device
* units, use %CLUTTER_UNITS_FROM_DEVICE and %CLUTTER_UNITS_TO_DEVICE macros.
*
* #ClutterUnit<!-- -->s can be converted from other units like millimeters,
* typographic points (at the current resolution) and percentages. It is
* also possible to convert fixed point values to and from #ClutterUnit
* values.
*
* In order to register a #ClutterUnit property, the #ClutterParamSpecUnit
* #GParamSpec sub-class should be used:
*
* |[
* GParamSpec *pspec;
*
* pspec = clutter_param_spec_unit ("width",
* "Width",
* "Width of the actor, in units",
* 0, CLUTTER_MAXUNIT,
* 0,
* G_PARAM_READWRITE);
* g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
* ]|
*
* A #GValue holding units can be manipulated using clutter_value_set_unit()
* and clutter_value_get_unit(). #GValue<!-- -->s containing a #ClutterUnit
* value can also be transformed to #GValue<!-- -->s containing integer
* values - with a loss of precision:
*
* |[
* static gboolean
* units_to_int (const GValue *src,
* GValue *dest)
* {
* g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (src), FALSE);
*
* g_value_init (dest, G_TYPE_INT);
* return g_value_transform (src, &dest);
* }
* ]|
*
* The code above is equivalent to:
*
* |[
* static gboolean
* units_to_int (const GValue *src,
* GValue *dest)
* {
* g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (src), FALSE);
*
* g_value_init (dest, G_TYPE_INT);
* g_value_set_int (dest,
* CLUTTER_UNITS_TO_INT (clutter_value_get_unit (src)));
*
* return TRUE;
* }
* ]|
*
* #ClutterUnit is available since Clutter 0.4
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib-object.h>
#include <gobject/gvaluecollector.h>
#include "clutter-units.h"
#include "clutter-private.h"
#define DPI_FALLBACK 96.0
/**
* clutter_units_mm:
* @mm: millimeters to convert
*
* Converts a value in millimeters to #ClutterUnit<!-- -->s at
* the current DPI.
*
* Return value: the value in units
*
* Since: 1.0
*/
ClutterUnit
clutter_units_mm (gdouble mm)
{
ClutterBackend *backend;
gdouble dpi;
backend = clutter_get_default_backend ();
dpi = clutter_backend_get_resolution (backend);
if (dpi < 0)
dpi = DPI_FALLBACK;
return mm * dpi / 25.4;
}
/**
* clutter_units_pt:
* @pt: typographic points to convert
*
* Converts a value in typographic points to #ClutterUnit<!-- -->s
* at the current DPI.
*
* Return value: the value in units
*
* Since: 1.0
*/
ClutterUnit
clutter_units_pt (gdouble pt)
{
ClutterBackend *backend;
gdouble dpi;
backend = clutter_get_default_backend ();
dpi = clutter_backend_get_resolution (backend);
if (dpi < 0)
dpi = DPI_FALLBACK;
return pt * dpi / 72.0;
}
/**
* clutter_units_em:
* @em: em to convert
*
* Converts a value in em to #ClutterUnit<!-- -->s at the
* current DPI.
*
* Return value: the value in units
*
* Since: 1.0
*/
ClutterUnit
clutter_units_em (gdouble em)
{
ClutterBackend *backend;
const gchar *font_name;
gdouble dpi;
ClutterUnit retval = 0;
backend = clutter_get_default_backend ();
dpi = clutter_backend_get_resolution (backend);
font_name = clutter_backend_get_font_name (backend);
if (G_LIKELY ((font_name && *font_name != '\0')))
{
PangoFontDescription *font_desc;
gdouble font_size = 0;
font_desc = pango_font_description_from_string (font_name);
if (G_LIKELY (font_desc != NULL))
{
gint pango_size;
gboolean is_absolute;
pango_size = pango_font_description_get_size (font_desc);
is_absolute =
pango_font_description_get_size_is_absolute (font_desc);
if (!is_absolute)
font_size = ((gdouble) font_size) / PANGO_SCALE;
pango_font_description_free (font_desc);
}
/* 10 points at 96 DPI is 12 pixels */
retval = 1.2 * font_size * dpi / 96.0;
}
return retval;
}
static GTypeInfo _info = {
0,
NULL,
NULL,
NULL,
NULL,
NULL,
0,
0,
NULL,
NULL,
};
static GTypeFundamentalInfo _finfo = { 0, };
static void
clutter_value_init_unit (GValue *value)
{
value->data[0].v_int = 0;
}
static void
clutter_value_copy_unit (const GValue *src,
GValue *dest)
{
dest->data[0].v_int = src->data[0].v_int;
}
static gchar *
clutter_value_collect_unit (GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
value->data[0].v_int = collect_values[0].v_int;
return NULL;
}
static gchar *
clutter_value_lcopy_unit (const GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
gint32 *units_p = collect_values[0].v_pointer;
if (!units_p)
return g_strdup_printf ("value location for `%s' passed as NULL",
G_VALUE_TYPE_NAME (value));
*units_p = value->data[0].v_int;
return NULL;
}
static void
clutter_value_transform_unit_int (const GValue *src,
GValue *dest)
{
dest->data[0].v_int = CLUTTER_UNITS_TO_INT (src->data[0].v_int);
}
static void
clutter_value_transform_int_unit (const GValue *src,
GValue *dest)
{
dest->data[0].v_int = CLUTTER_UNITS_FROM_INT (src->data[0].v_int);
}
static const GTypeValueTable _clutter_unit_value_table = {
clutter_value_init_unit,
NULL,
clutter_value_copy_unit,
NULL,
"i",
clutter_value_collect_unit,
"p",
clutter_value_lcopy_unit
};
GType
clutter_unit_get_type (void)
{
static GType _clutter_unit_type = 0;
if (G_UNLIKELY (_clutter_unit_type == 0))
{
_info.value_table = & _clutter_unit_value_table;
_clutter_unit_type =
g_type_register_fundamental (g_type_fundamental_next (),
I_("ClutterUnit"),
&_info, &_finfo, 0);
g_value_register_transform_func (_clutter_unit_type, G_TYPE_INT,
clutter_value_transform_unit_int);
g_value_register_transform_func (G_TYPE_INT, _clutter_unit_type,
clutter_value_transform_int_unit);
}
return _clutter_unit_type;
}
/**
* clutter_value_set_unit:
* @value: a #GValue initialized to #CLUTTER_TYPE_UNIT
* @units: the units to set
*
* Sets @value to @units
*
* Since: 0.8
*/
void
clutter_value_set_unit (GValue *value,
ClutterUnit units)
{
g_return_if_fail (CLUTTER_VALUE_HOLDS_UNIT (value));
value->data[0].v_int = units;
}
/**
* clutter_value_get_unit:
* @value: a #GValue initialized to #CLUTTER_TYPE_UNIT
*
* Gets the #ClutterUnit<!-- -->s contained in @value.
*
* Return value: the units inside the passed #GValue
*
* Since: 0.8
*/
ClutterUnit
clutter_value_get_unit (const GValue *value)
{
g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (value), 0);
return value->data[0].v_int;
}
static void
param_unit_init (GParamSpec *pspec)
{
ClutterParamSpecUnit *uspec = CLUTTER_PARAM_SPEC_UNIT (pspec);
uspec->minimum = CLUTTER_MINUNIT;
uspec->maximum = CLUTTER_MAXUNIT;
uspec->default_value = 0;
}
static void
param_unit_set_default (GParamSpec *pspec,
GValue *value)
{
value->data[0].v_int = CLUTTER_PARAM_SPEC_UNIT (pspec)->default_value;
}
static gboolean
param_unit_validate (GParamSpec *pspec,
GValue *value)
{
ClutterParamSpecUnit *uspec = CLUTTER_PARAM_SPEC_UNIT (pspec);
gint oval = CLUTTER_UNITS_TO_INT (value->data[0].v_int);
gint min, max, val;
g_assert (CLUTTER_IS_PARAM_SPEC_UNIT (pspec));
/* we compare the integer part of the value because the minimum
* and maximum values cover just that part of the representation
*/
min = uspec->minimum;
max = uspec->maximum;
val = CLUTTER_UNITS_TO_INT (value->data[0].v_int);
val = CLAMP (val, min, max);
if (val != oval)
{
value->data[0].v_int = val;
return TRUE;
}
return FALSE;
}
static gint
param_unit_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2)
{
if (value1->data[0].v_int < value2->data[0].v_int)
return -1;
else
return value1->data[0].v_int > value2->data[0].v_int;
}
GType
clutter_param_unit_get_type (void)
{
static GType pspec_type = 0;
if (G_UNLIKELY (pspec_type == 0))
{
const GParamSpecTypeInfo pspec_info = {
sizeof (ClutterParamSpecUnit),
16,
param_unit_init,
CLUTTER_TYPE_UNIT,
NULL,
param_unit_set_default,
param_unit_validate,
param_unit_values_cmp,
};
pspec_type = g_param_type_register_static (I_("ClutterParamSpecUnit"),
&pspec_info);
}
return pspec_type;
}
/**
* clutter_param_spec_unit:
* @name: name of the property
* @nick: short name
* @blurb: description (can be translatable)
* @minimum: lower boundary
* @maximum: higher boundary
* @default_value: default value
* @flags: flags for the param spec
*
* Creates a #GParamSpec for properties using #ClutterUnit<!-- -->s.
*
* Return value: the newly created #GParamSpec
*
* Since: 0.8
*/
GParamSpec *
clutter_param_spec_unit (const gchar *name,
const gchar *nick,
const gchar *blurb,
ClutterUnit minimum,
ClutterUnit maximum,
ClutterUnit default_value,
GParamFlags flags)
{
ClutterParamSpecUnit *uspec;
g_return_val_if_fail (default_value >= minimum && default_value <= maximum,
NULL);
uspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_UNIT,
name, nick, blurb,
flags);
uspec->minimum = minimum;
uspec->maximum = maximum;
uspec->default_value = default_value;
return G_PARAM_SPEC (uspec);
}