Files
gtk/ottie/ottieparser.c
Benjamin Otte c2ed71af7a Ottie: Add
2020-12-27 01:12:11 +01:00

593 lines
13 KiB
C

/*
* Copyright © 2020 Benjamin Otte
*
* 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.1 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 <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "ottieparserprivate.h"
#include "ottietransformprivate.h"
#include <gsk/gsk.h>
void
ottie_parser_emit_error (JsonReader *reader,
const GError *error)
{
g_printerr ("Ottie is sad: %s\n", error->message);
}
void
ottie_parser_error_syntax (JsonReader *reader,
const char *format,
...)
{
va_list args;
GError *error;
va_start (args, format);
error = g_error_new_valist (JSON_PARSER_ERROR,
JSON_PARSER_ERROR_INVALID_DATA,
format, args);
va_end (args);
ottie_parser_emit_error (reader, error);
g_error_free (error);
}
void
ottie_parser_error_value (JsonReader *reader,
const char *format,
...)
{
va_list args;
GError *error;
va_start (args, format);
error = g_error_new_valist (JSON_PARSER_ERROR,
JSON_PARSER_ERROR_INVALID_DATA,
format, args);
va_end (args);
ottie_parser_emit_error (reader, error);
g_error_free (error);
}
void
ottie_parser_error_unsupported (JsonReader *reader,
const char *format,
...)
{
va_list args;
GError *error;
va_start (args, format);
error = g_error_new_valist (JSON_PARSER_ERROR,
JSON_PARSER_ERROR_INVALID_DATA,
format, args);
va_end (args);
ottie_parser_emit_error (reader, error);
g_error_free (error);
}
gboolean
ottie_parser_parse_array (JsonReader *reader,
const char *debug_name,
guint min_items,
guint max_items,
guint *out_n_items,
gsize start_offset,
gsize offset_multiplier,
OttieParseFunc func,
gpointer data)
{
guint i;
if (!json_reader_is_array (reader))
{
if (min_items > 1)
{
ottie_parser_error_syntax (reader, "Expected an array when parsing %s", debug_name);
if (out_n_items)
*out_n_items = 0;
return FALSE;
}
else
{
if (!func (reader, start_offset, data))
{
if (out_n_items)
*out_n_items = 0;
return FALSE;
}
if (out_n_items)
*out_n_items = 1;
return TRUE;
}
}
if (json_reader_count_elements (reader) < min_items)
{
ottie_parser_error_syntax (reader, "%s needs %u items, but only %u given",
debug_name, min_items, json_reader_count_elements (reader));
return FALSE;
}
max_items = MIN (max_items, json_reader_count_elements (reader));
for (i = 0; i < max_items; i++)
{
if (!json_reader_read_element (reader, i) ||
!func (reader, start_offset + offset_multiplier * i, data))
{
json_reader_end_element (reader);
if (out_n_items)
*out_n_items = i;
return FALSE;
}
json_reader_end_element (reader);
}
if (out_n_items)
*out_n_items = i;
return TRUE;
}
gboolean
ottie_parser_parse_object (JsonReader *reader,
const char *debug_name,
const OttieParserOption *options,
gsize n_options,
gpointer data)
{
if (!json_reader_is_object (reader))
{
ottie_parser_error_syntax (reader, "Expected an object when parsing %s", debug_name);
return FALSE;
}
for (int i = 0; ; i++)
{
const OttieParserOption *o = NULL;
const char *name;
if (!json_reader_read_element (reader, i))
break;
name = json_reader_get_member_name (reader);
for (gsize j = 0; j < n_options; j++)
{
o = &options[j];
if (g_str_equal (o->name, name))
break;
o = NULL;
}
if (o)
{
if (!o->parse_func (reader, o->option_data, data))
{
json_reader_end_element (reader);
return FALSE;
}
}
else
{
ottie_parser_error_unsupported (reader, "Unsupported %s property \"%s\"", debug_name, json_reader_get_member_name (reader));
}
json_reader_end_element (reader);
}
json_reader_end_element (reader);
return TRUE;
}
gboolean
ottie_parser_option_skip (JsonReader *reader,
gsize offset,
gpointer data)
{
return TRUE;
}
gboolean
ottie_parser_option_boolean (JsonReader *reader,
gsize offset,
gpointer data)
{
gboolean b;
b = json_reader_get_boolean_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
json_reader_end_element (reader);
return FALSE;
}
*(gboolean *) ((guint8 *) data + offset) = b;
return TRUE;
}
gboolean
ottie_parser_option_double (JsonReader *reader,
gsize offset,
gpointer data)
{
double d;
d = json_reader_get_double_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
*(double *) ((guint8 *) data + offset) = d;
return TRUE;
}
gboolean
ottie_parser_option_int (JsonReader *reader,
gsize offset,
gpointer data)
{
gint64 i;
i = json_reader_get_int_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
if (i > G_MAXINT || i < G_MININT)
{
ottie_parser_error_value (reader, "Integer value %"G_GINT64_FORMAT" out of range", i);
return FALSE;
}
if (i == OTTIE_INT_UNSET)
{
ottie_parser_error_unsupported (reader, "The Integer value %d is a magic internal value of Ottie, file a bug", OTTIE_INT_UNSET);
return FALSE;
}
*(int *) ((guint8 *) data + offset) = i;
return TRUE;
}
gboolean
ottie_parser_option_string (JsonReader *reader,
gsize offset,
gpointer data)
{
char **target;
const char *s;
s = json_reader_get_string_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
target = (char **) ((guint8 *) data + offset);
g_clear_pointer (target, g_free);
*target = g_strdup (s);
return TRUE;
}
gboolean
ottie_parser_option_blend_mode (JsonReader *reader,
gsize offset,
gpointer data)
{
GskBlendMode blend_mode;
gint64 i;
i = json_reader_get_int_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
switch (i)
{
case 0:
blend_mode = GSK_BLEND_MODE_DEFAULT;
break;
case 1:
blend_mode = GSK_BLEND_MODE_MULTIPLY;
break;
case 2:
blend_mode = GSK_BLEND_MODE_SCREEN;
break;
case 3:
blend_mode = GSK_BLEND_MODE_OVERLAY;
break;
case 4:
blend_mode = GSK_BLEND_MODE_DARKEN;
break;
case 5:
blend_mode = GSK_BLEND_MODE_LIGHTEN;
break;
case 6:
blend_mode = GSK_BLEND_MODE_COLOR_DODGE;
break;
case 7:
blend_mode = GSK_BLEND_MODE_COLOR_BURN;
break;
case 8:
blend_mode = GSK_BLEND_MODE_HARD_LIGHT;
break;
case 9:
blend_mode = GSK_BLEND_MODE_SOFT_LIGHT;
break;
case 10:
blend_mode = GSK_BLEND_MODE_DIFFERENCE;
break;
case 11:
blend_mode = GSK_BLEND_MODE_EXCLUSION;
break;
case 12:
blend_mode = GSK_BLEND_MODE_HUE;
break;
case 13:
blend_mode = GSK_BLEND_MODE_SATURATION;
break;
case 14:
blend_mode = GSK_BLEND_MODE_COLOR;
break;
case 15:
blend_mode = GSK_BLEND_MODE_LUMINOSITY;
break;
default:
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known blend mode", i);
return FALSE;
}
if (blend_mode != GSK_BLEND_MODE_DEFAULT)
ottie_parser_error_value (reader, "Blend modes are not implemented yet.");
*(GskBlendMode *) ((guint8 *) data + offset) = blend_mode;
return TRUE;
}
gboolean
ottie_parser_option_3d (JsonReader *reader,
gsize offset,
gpointer data)
{
double d;
d = json_reader_get_double_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
if (d != 0)
{
ottie_parser_error_value (reader, "3D is not supported.");
}
return TRUE;
}
gboolean
ottie_parser_option_direction (JsonReader *reader,
gsize offset,
gpointer data)
{
OttieDirection direction;
gint64 i;
i = json_reader_get_int_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
switch (i)
{
default:
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known direction", i);
G_GNUC_FALLTHROUGH;
case 0:
direction = OTTIE_DIRECTION_FORWARD;
break;
case 1:
case 2:
direction = OTTIE_DIRECTION_BACKWARD;
break;
}
*(OttieDirection *) ((guint8 *) data + offset) = direction;
return TRUE;
}
gboolean
ottie_parser_option_line_cap (JsonReader *reader,
gsize offset,
gpointer data)
{
GskLineCap line_cap;
gint64 i;
i = json_reader_get_int_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
switch (i)
{
case 1:
line_cap = GSK_LINE_CAP_BUTT;
break;
case 2:
line_cap = GSK_LINE_CAP_ROUND;
break;
case 3:
line_cap = GSK_LINE_CAP_SQUARE;
break;
default:
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known line cap", i);
return FALSE;
}
*(GskLineCap *) ((guint8 *) data + offset) = line_cap;
return TRUE;
}
gboolean
ottie_parser_option_line_join (JsonReader *reader,
gsize offset,
gpointer data)
{
GskLineJoin line_join;
gint64 i;
i = json_reader_get_int_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
switch (i)
{
case 1:
line_join = GSK_LINE_JOIN_MITER;
break;
case 2:
line_join = GSK_LINE_JOIN_ROUND;
break;
case 3:
line_join = GSK_LINE_JOIN_BEVEL;
break;
default:
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known line join", i);
return FALSE;
}
*(GskLineJoin *) ((guint8 *) data + offset) = line_join;
return TRUE;
}
gboolean
ottie_parser_option_fill_rule (JsonReader *reader,
gsize offset,
gpointer data)
{
GskFillRule fill_rule;
gint64 i;
i = json_reader_get_int_value (reader);
if (json_reader_get_error (reader))
{
ottie_parser_emit_error (reader, json_reader_get_error (reader));
return FALSE;
}
switch (i)
{
case 1:
fill_rule = GSK_FILL_RULE_WINDING;
break;
case 2:
fill_rule = GSK_FILL_RULE_EVEN_ODD;
break;
default:
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known fill rule", i);
/* XXX: really? */
fill_rule = GSK_FILL_RULE_EVEN_ODD;
break;
}
*(GskFillRule *) ((guint8 *) data + offset) = fill_rule;
return TRUE;
}
gboolean
ottie_parser_option_transform (JsonReader *reader,
gsize offset,
gpointer data)
{
OttieShape **target;
OttieShape *t;
t = ottie_transform_parse (reader);
if (t == NULL)
return FALSE;
target = (OttieShape **) ((guint8 *) data + offset);
g_clear_object (target);
*target = t;
return TRUE;
}