summaryrefslogtreecommitdiffstats
path: root/libgimpbase/gimpunit.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libgimpbase/gimpunit.c755
1 files changed, 755 insertions, 0 deletions
diff --git a/libgimpbase/gimpunit.c b/libgimpbase/gimpunit.c
new file mode 100644
index 0000000..e0aa626
--- /dev/null
+++ b/libgimpbase/gimpunit.c
@@ -0,0 +1,755 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpunit.c
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ *
+ * 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 3 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, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpbase-private.h"
+#include "gimpunit.h"
+
+
+/**
+ * SECTION: gimpunit
+ * @title: gimpunit
+ * @short_description: Provides a collection of predefined units and
+ * functions for creating user-defined units.
+ * @see_also: #GimpUnitMenu, #GimpSizeEntry.
+ *
+ * Provides a collection of predefined units and functions for
+ * creating user-defined units.
+ **/
+
+
+static void unit_to_string (const GValue *src_value,
+ GValue *dest_value);
+static void string_to_unit (const GValue *src_value,
+ GValue *dest_value);
+
+GType
+gimp_unit_get_type (void)
+{
+ static GType unit_type = 0;
+
+ if (! unit_type)
+ {
+ const GTypeInfo type_info = { 0, };
+
+ unit_type = g_type_register_static (G_TYPE_INT, "GimpUnit",
+ &type_info, 0);
+
+ g_value_register_transform_func (unit_type, G_TYPE_STRING,
+ unit_to_string);
+ g_value_register_transform_func (G_TYPE_STRING, unit_type,
+ string_to_unit);
+ }
+
+ return unit_type;
+}
+
+static void
+unit_to_string (const GValue *src_value,
+ GValue *dest_value)
+{
+ GimpUnit unit = (GimpUnit) g_value_get_int (src_value);
+
+ g_value_set_string (dest_value, gimp_unit_get_identifier (unit));
+}
+
+static void
+string_to_unit (const GValue *src_value,
+ GValue *dest_value)
+{
+ const gchar *str;
+ gint num_units;
+ gint i;
+
+ str = g_value_get_string (src_value);
+
+ if (!str || !*str)
+ goto error;
+
+ num_units = gimp_unit_get_number_of_units ();
+
+ for (i = GIMP_UNIT_PIXEL; i < num_units; i++)
+ if (strcmp (str, gimp_unit_get_identifier (i)) == 0)
+ break;
+
+ if (i == num_units)
+ {
+ if (strcmp (str, gimp_unit_get_identifier (GIMP_UNIT_PERCENT)) == 0)
+ i = GIMP_UNIT_PERCENT;
+ else
+ goto error;
+ }
+
+ g_value_set_int (dest_value, i);
+ return;
+
+ error:
+ g_warning ("Can't convert string '%s' to GimpUnit.", str);
+}
+
+
+/**
+ * gimp_unit_get_number_of_units:
+ *
+ * Returns the number of units which are known to the #GimpUnit system.
+ *
+ * Returns: The number of defined units.
+ **/
+gint
+gimp_unit_get_number_of_units (void)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_number_of_units != NULL,
+ GIMP_UNIT_END);
+
+ return _gimp_unit_vtable.unit_get_number_of_units ();
+}
+
+/**
+ * gimp_unit_get_number_of_built_in_units:
+ *
+ * Returns the number of #GimpUnit's which are hardcoded in the unit system
+ * (UNIT_INCH, UNIT_MM, UNIT_POINT, UNIT_PICA and the two "pseudo unit"
+ * UNIT_PIXEL).
+ *
+ * Returns: The number of built-in units.
+ **/
+gint
+gimp_unit_get_number_of_built_in_units (void)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_number_of_built_in_units
+ != NULL, GIMP_UNIT_END);
+
+ return _gimp_unit_vtable.unit_get_number_of_built_in_units ();
+}
+
+/**
+ * gimp_unit_new:
+ * @identifier: The unit's identifier string.
+ * @factor: The unit's factor (how many units are in one inch).
+ * @digits: The unit's suggested number of digits (see gimp_unit_get_digits()).
+ * @symbol: The symbol of the unit (e.g. "''" for inch).
+ * @abbreviation: The abbreviation of the unit.
+ * @singular: The singular form of the unit.
+ * @plural: The plural form of the unit.
+ *
+ * Returns the integer ID of the new #GimpUnit.
+ *
+ * Note that a new unit is always created with its deletion flag
+ * set to %TRUE. You will have to set it to %FALSE with
+ * gimp_unit_set_deletion_flag() to make the unit definition persistent.
+ *
+ * Returns: The ID of the new unit.
+ **/
+GimpUnit
+gimp_unit_new (gchar *identifier,
+ gdouble factor,
+ gint digits,
+ gchar *symbol,
+ gchar *abbreviation,
+ gchar *singular,
+ gchar *plural)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_new != NULL, GIMP_UNIT_INCH);
+
+ return _gimp_unit_vtable.unit_new (identifier, factor, digits,
+ symbol, abbreviation, singular, plural);
+}
+
+/**
+ * gimp_unit_get_deletion_flag:
+ * @unit: The unit you want to know the @deletion_flag of.
+ *
+ * Returns: The unit's @deletion_flag.
+ **/
+gboolean
+gimp_unit_get_deletion_flag (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_deletion_flag != NULL, FALSE);
+
+ return _gimp_unit_vtable.unit_get_deletion_flag (unit);
+}
+
+/**
+ * gimp_unit_set_deletion_flag:
+ * @unit: The unit you want to set the @deletion_flag for.
+ * @deletion_flag: The new deletion_flag.
+ *
+ * Sets a #GimpUnit's @deletion_flag. If the @deletion_flag of a unit is
+ * %TRUE when GIMP exits, this unit will not be saved in the users's
+ * "unitrc" file.
+ *
+ * Trying to change the @deletion_flag of a built-in unit will be silently
+ * ignored.
+ **/
+void
+gimp_unit_set_deletion_flag (GimpUnit unit,
+ gboolean deletion_flag)
+{
+ g_return_if_fail (_gimp_unit_vtable.unit_set_deletion_flag != NULL);
+
+ _gimp_unit_vtable.unit_set_deletion_flag (unit, deletion_flag);
+}
+
+/**
+ * gimp_unit_get_factor:
+ * @unit: The unit you want to know the factor of.
+ *
+ * A #GimpUnit's @factor is defined to be:
+ *
+ * distance_in_units == (@factor * distance_in_inches)
+ *
+ * Returns 0 for @unit == GIMP_UNIT_PIXEL.
+ *
+ * Returns: The unit's factor.
+ **/
+gdouble
+gimp_unit_get_factor (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_factor != NULL, 1.0);
+
+ return _gimp_unit_vtable.unit_get_factor (unit);
+}
+
+/**
+ * gimp_unit_get_digits:
+ * @unit: The unit you want to know the digits.
+ *
+ * Returns the number of digits set for @unit.
+ * Built-in units' accuracy is approximately the same as an inch with
+ * two digits. User-defined units can suggest a different accuracy.
+ *
+ * Note: the value is as-set by defaults or by the user and does not
+ * necessary provide enough precision on high-resolution images.
+ * When the information is needed for a specific image, the use of
+ * gimp_unit_get_scaled_digits() may be more appropriate.
+ *
+ * Returns 0 for @unit == GIMP_UNIT_PIXEL.
+ *
+ * Returns: The suggested number of digits.
+ **/
+gint
+gimp_unit_get_digits (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_digits != NULL, 2);
+
+ return _gimp_unit_vtable.unit_get_digits (unit);
+}
+
+/**
+ * gimp_unit_get_scaled_digits:
+ * @unit: The unit you want to know the digits.
+ * @resolution: the resolution in PPI.
+ *
+ * Returns the number of digits a @unit field should provide to get
+ * enough accuracy so that every pixel position shows a different
+ * value from neighboring pixels.
+ *
+ * Note: when needing digit accuracy to display a diagonal distance,
+ * the @resolution may not correspond to the image's horizontal or
+ * vertical resolution, but instead to the result of:
+ * `distance_in_pixel / distance_in_inch`.
+ *
+ * Returns: The suggested number of digits.
+ **/
+gint
+gimp_unit_get_scaled_digits (GimpUnit unit,
+ gdouble resolution)
+{
+ gint digits;
+
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_digits != NULL, 2);
+
+ digits = ceil (log10 (1.0 /
+ gimp_pixels_to_units (1.0, unit, resolution)));
+
+ return MAX (digits, gimp_unit_get_digits (unit));
+}
+
+/**
+ * gimp_unit_get_identifier:
+ * @unit: The unit you want to know the identifier of.
+ *
+ * This is an untranslated string and must not be changed or freed.
+ *
+ * Returns: The unit's identifier.
+ **/
+const gchar *
+gimp_unit_get_identifier (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_identifier != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_identifier (unit);
+}
+
+/**
+ * gimp_unit_get_symbol:
+ * @unit: The unit you want to know the symbol of.
+ *
+ * This is e.g. "''" for UNIT_INCH.
+ *
+ * NOTE: This string must not be changed or freed.
+ *
+ * Returns: The unit's symbol.
+ **/
+const gchar *
+gimp_unit_get_symbol (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_symbol != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_symbol (unit);
+}
+
+/**
+ * gimp_unit_get_abbreviation:
+ * @unit: The unit you want to know the abbreviation of.
+ *
+ * For built-in units, this function returns the translated abbreviation
+ * of the unit.
+ *
+ * NOTE: This string must not be changed or freed.
+ *
+ * Returns: The unit's abbreviation.
+ **/
+const gchar *
+gimp_unit_get_abbreviation (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_abbreviation != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_abbreviation (unit);
+}
+
+/**
+ * gimp_unit_get_singular:
+ * @unit: The unit you want to know the singular form of.
+ *
+ * For built-in units, this function returns the translated singular form
+ * of the unit's name.
+ *
+ * NOTE: This string must not be changed or freed.
+ *
+ * Returns: The unit's singular form.
+ **/
+const gchar *
+gimp_unit_get_singular (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_singular != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_singular (unit);
+}
+
+/**
+ * gimp_unit_get_plural:
+ * @unit: The unit you want to know the plural form of.
+ *
+ * For built-in units, this function returns the translated plural form
+ * of the unit's name.
+ *
+ * NOTE: This string must not be changed or freed.
+ *
+ * Returns: The unit's plural form.
+ **/
+const gchar *
+gimp_unit_get_plural (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_plural != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_plural (unit);
+}
+
+static gint print (gchar *buf,
+ gint len,
+ gint start,
+ const gchar *fmt,
+ ...) G_GNUC_PRINTF (4, 5);
+
+static gint
+print (gchar *buf,
+ gint len,
+ gint start,
+ const gchar *fmt,
+ ...)
+{
+ va_list args;
+ gint printed;
+
+ va_start (args, fmt);
+
+ printed = g_vsnprintf (buf + start, len - start, fmt, args);
+ if (printed < 0)
+ printed = len - start;
+
+ va_end (args);
+
+ return printed;
+}
+
+/**
+ * gimp_unit_format_string:
+ * @format: A printf-like format string which is used to create the unit
+ * string.
+ * @unit: A unit.
+ *
+ * The @format string supports the following percent expansions:
+ *
+ * <informaltable pgwide="1" frame="none" role="enum">
+ * <tgroup cols="2"><colspec colwidth="1*"/><colspec colwidth="8*"/>
+ * <tbody>
+ * <row>
+ * <entry>% f</entry>
+ * <entry>Factor (how many units make up an inch)</entry>
+ * </row>
+ * <row>
+ * <entry>% y</entry>
+ * <entry>Symbol (e.g. "''" for GIMP_UNIT_INCH)</entry>
+ * </row>
+ * <row>
+ * <entry>% a</entry>
+ * <entry>Abbreviation</entry>
+ * </row>
+ * <row>
+ * <entry>% s</entry>
+ * <entry>Singular</entry>
+ * </row>
+ * <row>
+ * <entry>% p</entry>
+ * <entry>Plural</entry>
+ * </row>
+ * <row>
+ * <entry>%%</entry>
+ * <entry>Literal percent</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ *
+ * Returns: A newly allocated string with above percent expressions
+ * replaced with the resp. strings for @unit.
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_unit_format_string (const gchar *format,
+ GimpUnit unit)
+{
+ gchar buffer[1024];
+ gint i = 0;
+
+ g_return_val_if_fail (format != NULL, NULL);
+ g_return_val_if_fail (unit == GIMP_UNIT_PERCENT ||
+ (unit >= GIMP_UNIT_PIXEL &&
+ unit < gimp_unit_get_number_of_units ()), NULL);
+
+ while (i < (sizeof (buffer) - 1) && *format)
+ {
+ switch (*format)
+ {
+ case '%':
+ format++;
+ switch (*format)
+ {
+ case 0:
+ g_warning ("%s: unit-menu-format string ended within %%-sequence",
+ G_STRFUNC);
+ break;
+
+ case '%':
+ buffer[i++] = '%';
+ break;
+
+ case 'f': /* factor (how many units make up an inch) */
+ i += print (buffer, sizeof (buffer), i, "%f",
+ gimp_unit_get_factor (unit));
+ break;
+
+ case 'y': /* symbol ("''" for inch) */
+ i += print (buffer, sizeof (buffer), i, "%s",
+ gimp_unit_get_symbol (unit));
+ break;
+
+ case 'a': /* abbreviation */
+ i += print (buffer, sizeof (buffer), i, "%s",
+ gimp_unit_get_abbreviation (unit));
+ break;
+
+ case 's': /* singular */
+ i += print (buffer, sizeof (buffer), i, "%s",
+ gimp_unit_get_singular (unit));
+ break;
+
+ case 'p': /* plural */
+ i += print (buffer, sizeof (buffer), i, "%s",
+ gimp_unit_get_plural (unit));
+ break;
+
+ default:
+ g_warning ("%s: unit-menu-format contains unknown format "
+ "sequence '%%%c'", G_STRFUNC, *format);
+ break;
+ }
+ break;
+
+ default:
+ buffer[i++] = *format;
+ break;
+ }
+
+ format++;
+ }
+
+ buffer[MIN (i, sizeof (buffer) - 1)] = 0;
+
+ return g_strdup (buffer);
+}
+
+/*
+ * GIMP_TYPE_PARAM_UNIT
+ */
+
+#define GIMP_PARAM_SPEC_UNIT(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_UNIT, GimpParamSpecUnit))
+
+typedef struct _GimpParamSpecUnit GimpParamSpecUnit;
+
+struct _GimpParamSpecUnit
+{
+ GParamSpecInt parent_instance;
+
+ gboolean allow_percent;
+};
+
+static void gimp_param_unit_class_init (GParamSpecClass *class);
+static gboolean gimp_param_unit_value_validate (GParamSpec *pspec,
+ GValue *value);
+
+/**
+ * gimp_param_unit_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for a unit param object
+ *
+ * Since: 2.4
+ **/
+GType
+gimp_param_unit_get_type (void)
+{
+ static GType spec_type = 0;
+
+ if (! spec_type)
+ {
+ const GTypeInfo type_info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_unit_class_init,
+ NULL, NULL,
+ sizeof (GimpParamSpecUnit),
+ 0, NULL, NULL
+ };
+
+ spec_type = g_type_register_static (G_TYPE_PARAM_INT,
+ "GimpParamUnit",
+ &type_info, 0);
+ }
+
+ return spec_type;
+}
+
+static void
+gimp_param_unit_class_init (GParamSpecClass *class)
+{
+ class->value_type = GIMP_TYPE_UNIT;
+ class->value_validate = gimp_param_unit_value_validate;
+}
+
+static gboolean
+gimp_param_unit_value_validate (GParamSpec *pspec,
+ GValue *value)
+{
+ GParamSpecInt *ispec = G_PARAM_SPEC_INT (pspec);
+ GimpParamSpecUnit *uspec = GIMP_PARAM_SPEC_UNIT (pspec);
+ gint oval = value->data[0].v_int;
+
+ if (!(uspec->allow_percent && value->data[0].v_int == GIMP_UNIT_PERCENT))
+ {
+ value->data[0].v_int = CLAMP (value->data[0].v_int,
+ ispec->minimum,
+ gimp_unit_get_number_of_units () - 1);
+ }
+
+ return value->data[0].v_int != oval;
+}
+
+/**
+ * gimp_param_spec_unit:
+ * @name: Canonical name of the param
+ * @nick: Nickname of the param
+ * @blurb: Brief description of param.
+ * @allow_pixels: Whether "pixels" is an allowed unit.
+ * @allow_percent: Whether "percent" is an allowed unit.
+ * @default_value: Unit to use if none is assigned.
+ * @flags: a combination of #GParamFlags
+ *
+ * Creates a param spec to hold a units param.
+ * See g_param_spec_internal() for more information.
+ *
+ * Returns: a newly allocated #GParamSpec instance
+ *
+ * Since: 2.4
+ **/
+GParamSpec *
+gimp_param_spec_unit (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ gboolean allow_pixels,
+ gboolean allow_percent,
+ GimpUnit default_value,
+ GParamFlags flags)
+{
+ GimpParamSpecUnit *pspec;
+ GParamSpecInt *ispec;
+
+ pspec = g_param_spec_internal (GIMP_TYPE_PARAM_UNIT,
+ name, nick, blurb, flags);
+
+ ispec = G_PARAM_SPEC_INT (pspec);
+
+ ispec->default_value = default_value;
+ ispec->minimum = allow_pixels ? GIMP_UNIT_PIXEL : GIMP_UNIT_INCH;
+ ispec->maximum = GIMP_UNIT_PERCENT - 1;
+
+ pspec->allow_percent = allow_percent;
+
+ return G_PARAM_SPEC (pspec);
+}
+
+/**
+ * gimp_pixels_to_units:
+ * @pixels: value in pixels
+ * @unit: unit to convert to
+ * @resolution: resolution in DPI
+ *
+ * Converts a @value specified in pixels to @unit.
+ *
+ * Returns: @pixels converted to units.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_pixels_to_units (gdouble pixels,
+ GimpUnit unit,
+ gdouble resolution)
+{
+ if (unit == GIMP_UNIT_PIXEL)
+ return pixels;
+
+ return pixels * gimp_unit_get_factor (unit) / resolution;
+}
+
+/**
+ * gimp_units_to_pixels:
+ * @value: value in units
+ * @unit: unit of @value
+ * @resolution: resloution in DPI
+ *
+ * Converts a @value specified in @unit to pixels.
+ *
+ * Returns: @value converted to pixels.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_units_to_pixels (gdouble value,
+ GimpUnit unit,
+ gdouble resolution)
+{
+ if (unit == GIMP_UNIT_PIXEL)
+ return value;
+
+ return value * resolution / gimp_unit_get_factor (unit);
+}
+
+/**
+ * gimp_units_to_points:
+ * @value: value in units
+ * @unit: unit of @value
+ * @resolution: resloution in DPI
+ *
+ * Converts a @value specified in @unit to points.
+ *
+ * Returns: @value converted to points.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_units_to_points (gdouble value,
+ GimpUnit unit,
+ gdouble resolution)
+{
+ if (unit == GIMP_UNIT_POINT)
+ return value;
+
+ if (unit == GIMP_UNIT_PIXEL)
+ return (value * gimp_unit_get_factor (GIMP_UNIT_POINT) / resolution);
+
+ return (value *
+ gimp_unit_get_factor (GIMP_UNIT_POINT) / gimp_unit_get_factor (unit));
+}
+
+/**
+ * gimp_unit_is_metric:
+ * @unit: The unit
+ *
+ * Checks if the given @unit is metric. A simplistic test is used
+ * that looks at the unit's factor and checks if it is 2.54 multiplied
+ * by some common powers of 10. Currently it checks for mm, cm, dm, m.
+ *
+ * See also: gimp_unit_get_factor()
+ *
+ * Returns: %TRUE if the @unit is metric.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_unit_is_metric (GimpUnit unit)
+{
+ gdouble factor;
+
+ if (unit == GIMP_UNIT_MM)
+ return TRUE;
+
+ factor = gimp_unit_get_factor (unit);
+
+ if (factor == 0.0)
+ return FALSE;
+
+ return ((ABS (factor - 0.0254) < 1e-7) || /* m */
+ (ABS (factor - 0.254) < 1e-6) || /* dm */
+ (ABS (factor - 2.54) < 1e-5) || /* cm */
+ (ABS (factor - 25.4) < 1e-4)); /* mm */
+}