libgimpwidgets: fix percentage use in size-entry arithmetic when lower-bound != 0

In GimpSizeEntry, the value corresponding to 0%, as per
gimp_size_entry_set_size(), may be non-zero.  This works correctly
when using the size entry in percentage mode, but not when using
precentage as part of arithmetic.

Fix this by adding an 'offset' parameter to eevl's unit-resolution
callback, which can be specifies a constant value to add as part
of unit conversion, after scaling the converted value by the
conversion factor.  In GimpSizeEntry, use this parameter to offset
percentages by their lower bound.
This commit is contained in:
Ell
2020-01-06 11:41:25 +02:00
parent 93f935865f
commit 68c250b63e
4 changed files with 56 additions and 26 deletions

View File

@ -237,6 +237,7 @@ gimp_eevl_complete (GimpEevl *eva)
{ {
GimpEevlQuantity result = {0, 0}; GimpEevlQuantity result = {0, 0};
GimpEevlQuantity default_unit_factor; GimpEevlQuantity default_unit_factor;
gdouble default_unit_offset;
/* Empty expression evaluates to 0 */ /* Empty expression evaluates to 0 */
if (gimp_eevl_accept (eva, GIMP_EEVL_TOKEN_END, NULL)) if (gimp_eevl_accept (eva, GIMP_EEVL_TOKEN_END, NULL))
@ -249,6 +250,7 @@ gimp_eevl_complete (GimpEevl *eva)
eva->options.unit_resolver_proc (NULL, eva->options.unit_resolver_proc (NULL,
&default_unit_factor, &default_unit_factor,
&default_unit_offset,
eva->options.data); eva->options.data);
/* Entire expression is dimensionless, apply default unit if /* Entire expression is dimensionless, apply default unit if
@ -257,6 +259,7 @@ gimp_eevl_complete (GimpEevl *eva)
if (result.dimension == 0 && default_unit_factor.dimension != 0) if (result.dimension == 0 && default_unit_factor.dimension != 0)
{ {
result.value /= default_unit_factor.value; result.value /= default_unit_factor.value;
result.value += default_unit_offset;
result.dimension = default_unit_factor.dimension; result.dimension = default_unit_factor.dimension;
} }
return result; return result;
@ -282,21 +285,25 @@ gimp_eevl_expression (GimpEevl *eva)
if (new_term.dimension != evaluated_terms.dimension) if (new_term.dimension != evaluated_terms.dimension)
{ {
GimpEevlQuantity default_unit_factor; GimpEevlQuantity default_unit_factor;
gdouble default_unit_offset;
eva->options.unit_resolver_proc (NULL, eva->options.unit_resolver_proc (NULL,
&default_unit_factor, &default_unit_factor,
&default_unit_offset,
eva->options.data); eva->options.data);
if (new_term.dimension == 0 && if (new_term.dimension == 0 &&
evaluated_terms.dimension == default_unit_factor.dimension) evaluated_terms.dimension == default_unit_factor.dimension)
{ {
new_term.value /= default_unit_factor.value; new_term.value /= default_unit_factor.value;
new_term.value += default_unit_offset;
new_term.dimension = default_unit_factor.dimension; new_term.dimension = default_unit_factor.dimension;
} }
else if (evaluated_terms.dimension == 0 && else if (evaluated_terms.dimension == 0 &&
new_term.dimension == default_unit_factor.dimension) new_term.dimension == default_unit_factor.dimension)
{ {
evaluated_terms.value /= default_unit_factor.value; evaluated_terms.value /= default_unit_factor.value;
evaluated_terms.value += default_unit_offset;
evaluated_terms.dimension = default_unit_factor.dimension; evaluated_terms.dimension = default_unit_factor.dimension;
} }
else else
@ -438,7 +445,8 @@ gimp_eevl_quantity (GimpEevl *eva)
if (eva->current_token.type == GIMP_EEVL_TOKEN_IDENTIFIER) if (eva->current_token.type == GIMP_EEVL_TOKEN_IDENTIFIER)
{ {
gchar *identifier; gchar *identifier;
GimpEevlQuantity result; GimpEevlQuantity factor;
gdouble offset;
gimp_eevl_accept (eva, gimp_eevl_accept (eva,
GIMP_EEVL_TOKEN_ANY, GIMP_EEVL_TOKEN_ANY,
@ -450,7 +458,8 @@ gimp_eevl_quantity (GimpEevl *eva)
identifier[consumed_token.value.size] = '\0'; identifier[consumed_token.value.size] = '\0';
if (eva->options.unit_resolver_proc (identifier, if (eva->options.unit_resolver_proc (identifier,
&result, &factor,
&offset,
eva->options.data)) eva->options.data))
{ {
if (gimp_eevl_accept (eva, '^', NULL)) if (gimp_eevl_accept (eva, '^', NULL))
@ -465,12 +474,19 @@ gimp_eevl_quantity (GimpEevl *eva)
"Exponent is not a dimensionless quantity"); "Exponent is not a dimensionless quantity");
} }
result.value = pow (result.value, evaluated_exponent.value); if (offset != 0.0)
result.dimension *= evaluated_exponent.value; {
gimp_eevl_error (eva,
"Invalid unit exponent");
}
factor.value = pow (factor.value, evaluated_exponent.value);
factor.dimension *= evaluated_exponent.value;
} }
evaluated_quantity.value /= result.value; evaluated_quantity.value /= factor.value;
evaluated_quantity.dimension += result.dimension; evaluated_quantity.value += offset;
evaluated_quantity.dimension += factor.dimension;
} }
else else
{ {

View File

@ -42,16 +42,18 @@ typedef struct
* GimpEevlUnitResolverProc: * GimpEevlUnitResolverProc:
* @identifier: Identifier of unit to resolve or %NULL if default unit * @identifier: Identifier of unit to resolve or %NULL if default unit
* should be resolved. * should be resolved.
* @result: Units per reference unit. For example, in GIMP the * @factor: Units per reference unit. For example, in GIMP the
* reference unit is inches so resolving "mm" should * reference unit is inches so resolving "mm" should
* return 25.4 since there are 25.4 millimeters per inch. * return 25.4 since there are 25.4 millimeters per inch.
* @offset: Offset to apply after scaling the value according to @factor.
* @data: Data given to gimp_eevl_evaluate(). * @data: Data given to gimp_eevl_evaluate().
* *
* Returns: If the unit was successfully resolved or not. * Returns: If the unit was successfully resolved or not.
* *
*/ */
typedef gboolean (* GimpEevlUnitResolverProc) (const gchar *identifier, typedef gboolean (* GimpEevlUnitResolverProc) (const gchar *identifier,
GimpEevlQuantity *result, GimpEevlQuantity *factor,
gdouble *offset,
gpointer data); gpointer data);

View File

@ -122,7 +122,8 @@ static gint gimp_size_entry_eevl_input_callback (GtkSpinButton *spinne
gdouble *return_val, gdouble *return_val,
gpointer *data); gpointer *data);
static gboolean gimp_size_entry_eevl_unit_resolver (const gchar *ident, static gboolean gimp_size_entry_eevl_unit_resolver (const gchar *ident,
GimpEevlQuantity *result, GimpEevlQuantity *factor,
gdouble *offset,
gpointer data); gpointer data);
@ -1287,6 +1288,7 @@ gimp_size_entry_eevl_input_callback (GtkSpinButton *spinner,
{ {
GimpSizeEntryField *other_gsef; GimpSizeEntryField *other_gsef;
GimpEevlQuantity default_unit_factor; GimpEevlQuantity default_unit_factor;
gdouble default_unit_offset;
options.ratio_expressions = TRUE; options.ratio_expressions = TRUE;
@ -1303,7 +1305,9 @@ gimp_size_entry_eevl_input_callback (GtkSpinButton *spinner,
options.ratio_invert = TRUE; options.ratio_invert = TRUE;
} }
options.unit_resolver_proc (NULL, &default_unit_factor, options.data); options.unit_resolver_proc (NULL,
&default_unit_factor, &default_unit_offset,
options.data);
options.ratio_quantity.value = other_gsef->value / options.ratio_quantity.value = other_gsef->value /
default_unit_factor.value; default_unit_factor.value;
@ -1392,7 +1396,8 @@ gimp_size_entry_eevl_input_callback (GtkSpinButton *spinner,
static gboolean static gboolean
gimp_size_entry_eevl_unit_resolver (const gchar *identifier, gimp_size_entry_eevl_unit_resolver (const gchar *identifier,
GimpEevlQuantity *result, GimpEevlQuantity *factor,
gdouble *offset,
gpointer data) gpointer data)
{ {
GimpSizeEntryField *gsef = (GimpSizeEntryField *) data; GimpSizeEntryField *gsef = (GimpSizeEntryField *) data;
@ -1400,9 +1405,11 @@ gimp_size_entry_eevl_unit_resolver (const gchar *identifier,
GimpUnit unit; GimpUnit unit;
g_return_val_if_fail (gsef, FALSE); g_return_val_if_fail (gsef, FALSE);
g_return_val_if_fail (result != NULL, FALSE); g_return_val_if_fail (factor != NULL, FALSE);
g_return_val_if_fail (offset != NULL, FALSE);
g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gsef->gse), FALSE); g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gsef->gse), FALSE);
*offset = 0.0;
for (unit = 0; for (unit = 0;
unit <= gimp_unit_get_number_of_units (); unit <= gimp_unit_get_number_of_units ();
@ -1422,34 +1429,36 @@ gimp_size_entry_eevl_unit_resolver (const gchar *identifier,
case GIMP_UNIT_PERCENT: case GIMP_UNIT_PERCENT:
if (gsef->gse->unit == GIMP_UNIT_PERCENT) if (gsef->gse->unit == GIMP_UNIT_PERCENT)
{ {
result->value = 1; factor->value = 1;
result->dimension = 0; factor->dimension = 0;
} }
else else
{ {
/* gsef->upper contains the '100%'-value */ /* gsef->upper contains the '100%'-value */
result->value = 100*gsef->resolution/gsef->upper; factor->value = 100*gsef->resolution/(gsef->upper - gsef->lower);
result->dimension = 1; /* gsef->lower contains the '0%'-value */
*offset = gsef->lower/gsef->resolution;
factor->dimension = 1;
} }
/* return here, don't perform percentage conversion */ /* return here, don't perform percentage conversion */
return TRUE; return TRUE;
case GIMP_UNIT_PIXEL: case GIMP_UNIT_PIXEL:
result->value = gsef->resolution; factor->value = gsef->resolution;
break; break;
default: default:
result->value = gimp_unit_get_factor (unit); factor->value = gimp_unit_get_factor (unit);
break; break;
} }
if (gsef->gse->unit == GIMP_UNIT_PERCENT) if (gsef->gse->unit == GIMP_UNIT_PERCENT)
{ {
/* map non-percentages onto percent */ /* map non-percentages onto percent */
result->value = gsef->upper/(100*gsef->resolution); factor->value = gsef->upper/(100*gsef->resolution);
result->dimension = 0; factor->dimension = 0;
} }
else else
{ {
result->dimension = 1; factor->dimension = 1;
} }
/* We are done */ /* We are done */

View File

@ -42,24 +42,27 @@ typedef struct
static gboolean static gboolean
test_units (const gchar *ident, test_units (const gchar *ident,
GimpEevlQuantity *result, GimpEevlQuantity *factor,
gdouble *offset,
gpointer data) gpointer data)
{ {
gboolean resolved = FALSE; gboolean resolved = FALSE;
gboolean default_unit = (ident == NULL); gboolean default_unit = (ident == NULL);
*offset = 0.0;
if (default_unit || if (default_unit ||
(ident && strcmp ("in", ident) == 0)) (ident && strcmp ("in", ident) == 0))
{ {
result->dimension = 1; factor->dimension = 1;
result->value = 1.; factor->value = 1.;
resolved = TRUE; resolved = TRUE;
} }
else if (ident && strcmp ("mm", ident) == 0) else if (ident && strcmp ("mm", ident) == 0)
{ {
result->dimension = 1; factor->dimension = 1;
result->value = 25.4; factor->value = 25.4;
resolved = TRUE; resolved = TRUE;
} }