From 6d751bf9514683a893668bc3b3822e433277a377 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 16 Aug 2010 13:23:43 +0100 Subject: [PATCH 01/13] Release Clutter 1.3.12 (developers snapshot) --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6465596e2..8e0c4ae44 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ # - increase clutter_interface_version to the next odd number m4_define([clutter_major_version], [1]) m4_define([clutter_minor_version], [3]) -m4_define([clutter_micro_version], [13]) +m4_define([clutter_micro_version], [12]) m4_define([clutter_release_status], [m4_if(m4_eval(clutter_micro_version % 2), [1], [git], From dc7d62a42a9b111d03626a8bc72c956869f4b7b8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 16 Aug 2010 13:37:57 +0100 Subject: [PATCH 02/13] Post-release version bump to 1.3.13 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 8e0c4ae44..6465596e2 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ # - increase clutter_interface_version to the next odd number m4_define([clutter_major_version], [1]) m4_define([clutter_minor_version], [3]) -m4_define([clutter_micro_version], [12]) +m4_define([clutter_micro_version], [13]) m4_define([clutter_release_status], [m4_if(m4_eval(clutter_micro_version % 2), [1], [git], From 83a8d0b3bb2ad47db6afd32bd2aa503814486e07 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Thu, 12 Aug 2010 17:39:59 +0100 Subject: [PATCH 03/13] cookbook: Added example code for texture cross-fading Added simple image viewer which loads image file names from a directory, displays the first one, then displays the next in the list with each key press. Uses the primitive fade front in/fade back out approach. Also adapted Emmanuele's example code which uses Cogl to produce a similar effect, but within a single texture. This code loads two images specified on the command line and cross-fades between them. --- doc/cookbook/examples/Makefile.am | 4 + .../examples/textures-crossfade-cogl.c | 174 ++++++++++++++++++ doc/cookbook/examples/textures-crossfade.c | 174 ++++++++++++++++++ 3 files changed, 352 insertions(+) create mode 100644 doc/cookbook/examples/textures-crossfade-cogl.c create mode 100644 doc/cookbook/examples/textures-crossfade.c diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am index d80a7da2c..cc9c7c4a1 100644 --- a/doc/cookbook/examples/Makefile.am +++ b/doc/cookbook/examples/Makefile.am @@ -11,6 +11,8 @@ noinst_PROGRAMS = \ layouts-stacking \ layouts-stacking-diff-sized-actors \ events-mouse-scroll \ + textures-crossfade \ + textures-crossfade-cogl \ $(NULL) INCLUDES = \ @@ -40,5 +42,7 @@ textures_sub_texture_SOURCES = textures-sub-texture.c layouts_stacking_SOURCES = layouts-stacking.c layouts_stacking_diff_sized_actors_SOURCES = layouts-stacking-diff-sized-actors.c events_mouse_scroll_SOURCES = events-mouse-scroll.c +textures_crossfade_SOURCES = textures-crossfade.c +textures_crossfade_cogl_SOURCES = textures-crossfade-cogl.c -include $(top_srcdir)/build/autotools/Makefile.am.gitignore diff --git a/doc/cookbook/examples/textures-crossfade-cogl.c b/doc/cookbook/examples/textures-crossfade-cogl.c new file mode 100644 index 000000000..029d91dd8 --- /dev/null +++ b/doc/cookbook/examples/textures-crossfade-cogl.c @@ -0,0 +1,174 @@ +#include +#include + +static gchar *source = NULL; +static gchar *target = NULL; +static guint duration = 1000; + +static GOptionEntry entries[] = { + { + "source", 's', + 0, + G_OPTION_ARG_FILENAME, &source, + "The source image of the cross-fade", "FILE" + }, + { + "target", 't', + 0, + G_OPTION_ARG_FILENAME, &target, + "The target image of the cross-fade", "FILE" + }, + { + "duration", 'd', + 0, + G_OPTION_ARG_INT, &duration, + "The duration of the cross-fade, in milliseconds", "MSECS" + }, + + { NULL } +}; + +static void +_update_progress_cb (ClutterTimeline *timeline, + guint elapsed_msecs, + ClutterTexture *texture) +{ + CoglHandle material = clutter_texture_get_cogl_material (texture); + if (material == COGL_INVALID_HANDLE) + return; + + /* You should assume that a material can only be modified once, after + * its creation; if you need to modify it later you should use a copy + * instead. Cogl makes copying materials reasonably cheap + */ + CoglHandle copy = cogl_material_copy (material); + + gdouble progress = clutter_timeline_get_progress (timeline); + + /* Create the constant color to be used when combining the two + * material layers; we use a black color with an alpha component + * depending on the current progress of the timeline + */ + CoglColor constant; + cogl_color_set_from_4ub (&constant, 0x00, 0x00, 0x00, 0xff * progress); + + /* This sets the value of the constant color we use when combining + * the two layers + */ + cogl_material_set_layer_combine_constant (copy, 1, &constant); + + /* The Texture now owns the material */ + clutter_texture_set_cogl_material (texture, copy); + cogl_handle_unref (copy); + + clutter_actor_queue_redraw (CLUTTER_ACTOR (texture)); +} + +static CoglHandle +load_cogl_texture (const char *type, + const char *file) +{ + GError *error = NULL; + + CoglHandle retval = cogl_texture_new_from_file (file, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_ANY, + &error); + if (error != NULL) + { + g_print ("Unable to load %s image: %s\n", type, error->message); + g_error_free (error); + exit (EXIT_FAILURE); + } + + return retval; +} + +static int +print_usage_and_exit (const char *exec_name, + int exit_code) +{ + g_print ("Usage: %s -s -t [-d ]\n", exec_name); + return exit_code; +} + +int +main (int argc, char *argv[]) +{ + clutter_init_with_args (&argc, &argv, + " - Crossfade", entries, + NULL, + NULL); + + if (source == NULL || target == NULL) + return print_usage_and_exit (argv[0], EXIT_FAILURE); + + /* Load the source and target images using Cogl, because we need + * to combine them into the same ClutterTexture. + */ + CoglHandle texture_1 = load_cogl_texture ("source", source); + CoglHandle texture_2 = load_cogl_texture ("target", target); + + /* sizes of textures */ + gfloat source_width, source_height, target_width, target_height; + source_width = cogl_texture_get_width (texture_1); + source_height = cogl_texture_get_height (texture_1); + target_width = cogl_texture_get_width (texture_2); + target_height = cogl_texture_get_height (texture_2); + + /* Create a new Cogl material holding the two textures inside two + * separate layers. Layer 0 is the one which will end + * up being visible. + */ + CoglHandle material = cogl_material_new (); + cogl_material_set_layer (material, 1, texture_1); + cogl_material_set_layer (material, 0, texture_2); + + /* Set the layer combination description for the second layer; the + * default for Cogl is to simply multiply the layer with the + * precendent one. In this case we interpolate the color for each + * pixel between the pixel value of the previous layer and the + * current one, using the alpha component of a constant color as + * the interpolation factor. + */ + cogl_material_set_layer_combine (material, 1, + "RGBA = INTERPOLATE (PREVIOUS, " + "TEXTURE, " + "CONSTANT[A])", + NULL); + + /* The material now owns the two textures */ + cogl_handle_unref (texture_1); + cogl_handle_unref (texture_2); + + /* Create a Texture and place it in the middle of the stage; then + * assign the material we created earlier to the Texture for painting + * it + */ + ClutterActor *stage = clutter_stage_new (); + clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade"); + clutter_actor_set_size (stage, 600, 600); + clutter_actor_show (stage); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + + ClutterActor *texture = clutter_texture_new (); + clutter_actor_set_size (texture, source_width, source_height); + clutter_texture_set_cogl_material (CLUTTER_TEXTURE (texture), material); + clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5)); + clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5)); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture); + cogl_handle_unref (material); + + /* The timeline will drive the cross-fading */ + ClutterTimeline *timeline = clutter_timeline_new (duration); + g_signal_connect (timeline, "new-frame", G_CALLBACK (_update_progress_cb), texture); + + /* animate */ + clutter_timeline_start (timeline); + + clutter_main (); + + g_object_unref (timeline); + + return EXIT_SUCCESS; +} diff --git a/doc/cookbook/examples/textures-crossfade.c b/doc/cookbook/examples/textures-crossfade.c new file mode 100644 index 000000000..420f43d8b --- /dev/null +++ b/doc/cookbook/examples/textures-crossfade.c @@ -0,0 +1,174 @@ +#include +#include +#include + +static guint stage_side = 600; +static guint animation_duration_ms = 1500; + +static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; + +typedef struct { + ClutterActor *front; + ClutterActor *back; + ClutterState *transitions; + GSList *image_paths; + guint next_image_index; +} State; + +static gboolean +load_next_image (State *app) +{ + /* don't do anything if already animating */ + ClutterTimeline *timeline = clutter_state_get_timeline (app->transitions); + + if (clutter_timeline_is_playing (timeline) == 1) + { + g_debug ("Animation is running already"); + return FALSE; + } + + if (!app->next_image_index) + app->next_image_index = 0; + + gpointer next = g_slist_nth_data (app->image_paths, app->next_image_index); + + if (next == NULL) + return FALSE; + + gchar *image_path = (gchar *)next; + + g_debug ("Loading %s", image_path); + + CoglHandle *cogl_texture; + cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (app->front)); + + if (cogl_texture != NULL) + { + /* copy the current texture into the background */ + clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (app->back), cogl_texture); + + /* make the back opaque and front transparent */ + clutter_state_warp_to_state (app->transitions, "show-back"); + } + + /* load the next image into the front */ + GError *error = NULL; + clutter_texture_set_from_file (CLUTTER_TEXTURE (app->front), + image_path, + &error); + + if (error != NULL) + { + g_warning ("Error loading %s\n%s", image_path, error->message); + g_error_free (error); + return FALSE; + } + + /* fade in the front texture and fade out the back texture */ + clutter_state_set_state (app->transitions, "show-front"); + + app->next_image_index++; + + return TRUE; +} + +static gboolean +_key_pressed_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + State *app = (State *)user_data; + + load_next_image (app); + + return TRUE; +} + +int +main (int argc, char *argv[]) +{ + if (argc < 2) + { + g_print ("Usage: %s \n", argv[0]); + exit (EXIT_FAILURE); + } + + State *app = g_new0 (State, 1); + app->image_paths = NULL; + + /* + * NB if your shell globs arguments to this program so argv + * includes non-image files, they will fail to load and throw errors + */ + guint i; + for (i = 1; i < argc; i++) + app->image_paths = g_slist_append (app->image_paths, argv[i]); + + GError *error = NULL; + + /* UI */ + ClutterActor *stage; + ClutterLayoutManager *layout; + ClutterActor *box; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade"); + clutter_actor_set_size (stage, stage_side, stage_side); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_BIN_ALIGNMENT_CENTER); + + box = clutter_box_new (layout); + clutter_actor_set_size (box, stage_side, stage_side); + + app->back = clutter_texture_new (); + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->back), TRUE); + + app->front = clutter_texture_new (); + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->front), TRUE); + + clutter_container_add_actor (CLUTTER_CONTAINER (box), app->back); + clutter_container_add_actor (CLUTTER_CONTAINER (box), app->front); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); + + /* animations */ + app->transitions = clutter_state_new (); + clutter_state_set (app->transitions, NULL, "show-front", + app->front, "opacity", CLUTTER_EASE_IN_CUBIC, 255, + app->back, "opacity", CLUTTER_EASE_IN_CUBIC, 0, + NULL); + clutter_state_set (app->transitions, NULL, "show-back", + app->front, "opacity", CLUTTER_LINEAR, 0, + app->back, "opacity", CLUTTER_LINEAR, 255, + NULL); + clutter_state_set_duration (app->transitions, + NULL, + NULL, + animation_duration_ms); + + /* display the next (first) image */ + load_next_image (app); + + /* key press displays the next image */ + g_signal_connect (stage, + "key-press-event", + G_CALLBACK (_key_pressed_cb), + app); + + clutter_actor_show (stage); + + clutter_main (); + + g_slist_free (app->image_paths); + g_object_unref (app->transitions); + g_free (app); + + if (error != NULL) + g_error_free (error); + + return EXIT_SUCCESS; +} From c230fd8dfdb43c98faa3147d2f669e0b5e44702b Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 16 Aug 2010 16:16:07 +0100 Subject: [PATCH 04/13] cookbook: Made code examples more consistent Modified the code example for the Clutter API version of the cross-fade to use the same command line as the COGL version. This also simplifies the explanation in the recipe. Also made the COGL code sample more consistent with the Clutter API code sample. --- .../examples/textures-crossfade-cogl.c | 17 +- doc/cookbook/examples/textures-crossfade.c | 184 +++++++----------- 2 files changed, 75 insertions(+), 126 deletions(-) diff --git a/doc/cookbook/examples/textures-crossfade-cogl.c b/doc/cookbook/examples/textures-crossfade-cogl.c index 029d91dd8..7ab572146 100644 --- a/doc/cookbook/examples/textures-crossfade-cogl.c +++ b/doc/cookbook/examples/textures-crossfade-cogl.c @@ -109,16 +109,8 @@ main (int argc, char *argv[]) CoglHandle texture_1 = load_cogl_texture ("source", source); CoglHandle texture_2 = load_cogl_texture ("target", target); - /* sizes of textures */ - gfloat source_width, source_height, target_width, target_height; - source_width = cogl_texture_get_width (texture_1); - source_height = cogl_texture_get_height (texture_1); - target_width = cogl_texture_get_width (texture_2); - target_height = cogl_texture_get_height (texture_2); - /* Create a new Cogl material holding the two textures inside two - * separate layers. Layer 0 is the one which will end - * up being visible. + * separate layers. */ CoglHandle material = cogl_material_new (); cogl_material_set_layer (material, 1, texture_1); @@ -145,25 +137,22 @@ main (int argc, char *argv[]) * assign the material we created earlier to the Texture for painting * it */ - ClutterActor *stage = clutter_stage_new (); + ClutterActor *stage = clutter_stage_get_default (); clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade"); clutter_actor_set_size (stage, 600, 600); clutter_actor_show (stage); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); ClutterActor *texture = clutter_texture_new (); - clutter_actor_set_size (texture, source_width, source_height); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture); clutter_texture_set_cogl_material (CLUTTER_TEXTURE (texture), material); clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5)); clutter_actor_add_constraint (texture, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5)); - clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture); cogl_handle_unref (material); /* The timeline will drive the cross-fading */ ClutterTimeline *timeline = clutter_timeline_new (duration); g_signal_connect (timeline, "new-frame", G_CALLBACK (_update_progress_cb), texture); - - /* animate */ clutter_timeline_start (timeline); clutter_main (); diff --git a/doc/cookbook/examples/textures-crossfade.c b/doc/cookbook/examples/textures-crossfade.c index 420f43d8b..093c3e04e 100644 --- a/doc/cookbook/examples/textures-crossfade.c +++ b/doc/cookbook/examples/textures-crossfade.c @@ -1,171 +1,131 @@ #include -#include #include -static guint stage_side = 600; -static guint animation_duration_ms = 1500; - static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; -typedef struct { - ClutterActor *front; - ClutterActor *back; - ClutterState *transitions; - GSList *image_paths; - guint next_image_index; -} State; +static gchar *source = NULL; +static gchar *target = NULL; +static guint duration = 1000; + +static GOptionEntry entries[] = { + { + "source", 's', + 0, + G_OPTION_ARG_FILENAME, &source, + "The source image of the cross-fade", "FILE" + }, + { + "target", 't', + 0, + G_OPTION_ARG_FILENAME, &target, + "The target image of the cross-fade", "FILE" + }, + { + "duration", 'd', + 0, + G_OPTION_ARG_INT, &duration, + "The duration of the cross-fade, in milliseconds", "MSECS" + }, + + { NULL } +}; static gboolean -load_next_image (State *app) +load_image (ClutterTexture *texture, + gchar *image_path) { - /* don't do anything if already animating */ - ClutterTimeline *timeline = clutter_state_get_timeline (app->transitions); - - if (clutter_timeline_is_playing (timeline) == 1) - { - g_debug ("Animation is running already"); - return FALSE; - } - - if (!app->next_image_index) - app->next_image_index = 0; - - gpointer next = g_slist_nth_data (app->image_paths, app->next_image_index); - - if (next == NULL) - return FALSE; - - gchar *image_path = (gchar *)next; - - g_debug ("Loading %s", image_path); - - CoglHandle *cogl_texture; - cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (app->front)); - - if (cogl_texture != NULL) - { - /* copy the current texture into the background */ - clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (app->back), cogl_texture); - - /* make the back opaque and front transparent */ - clutter_state_warp_to_state (app->transitions, "show-back"); - } - - /* load the next image into the front */ GError *error = NULL; - clutter_texture_set_from_file (CLUTTER_TEXTURE (app->front), - image_path, - &error); + + gboolean success = clutter_texture_set_from_file (CLUTTER_TEXTURE (texture), + image_path, + &error); if (error != NULL) { g_warning ("Error loading %s\n%s", image_path, error->message); g_error_free (error); - return FALSE; + exit (EXIT_FAILURE); } - /* fade in the front texture and fade out the back texture */ - clutter_state_set_state (app->transitions, "show-front"); - - app->next_image_index++; - - return TRUE; -} - -static gboolean -_key_pressed_cb (ClutterActor *actor, - ClutterEvent *event, - gpointer user_data) -{ - State *app = (State *)user_data; - - load_next_image (app); - - return TRUE; + return success; } int main (int argc, char *argv[]) { - if (argc < 2) + clutter_init_with_args (&argc, &argv, + " - cross-fade", entries, + NULL, + NULL); + + if (source == NULL || target == NULL) { - g_print ("Usage: %s \n", argv[0]); + g_print ("Usage: %s -s -t [-d ]\n", argv[0]); exit (EXIT_FAILURE); } - State *app = g_new0 (State, 1); - app->image_paths = NULL; - - /* - * NB if your shell globs arguments to this program so argv - * includes non-image files, they will fail to load and throw errors - */ - guint i; - for (i = 1; i < argc; i++) - app->image_paths = g_slist_append (app->image_paths, argv[i]); - GError *error = NULL; /* UI */ ClutterActor *stage; ClutterLayoutManager *layout; ClutterActor *box; + ClutterActor *front, *back; + ClutterState *transitions; clutter_init (&argc, &argv); stage = clutter_stage_get_default (); - g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade"); - clutter_actor_set_size (stage, stage_side, stage_side); - clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, 600, 600); + clutter_actor_show (stage); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_BIN_ALIGNMENT_CENTER); box = clutter_box_new (layout); - clutter_actor_set_size (box, stage_side, stage_side); + clutter_actor_set_size (box, 600, 600); - app->back = clutter_texture_new (); - clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->back), TRUE); + back = clutter_texture_new (); + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (back), TRUE); - app->front = clutter_texture_new (); - clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->front), TRUE); + front = clutter_texture_new (); + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (front), TRUE); - clutter_container_add_actor (CLUTTER_CONTAINER (box), app->back); - clutter_container_add_actor (CLUTTER_CONTAINER (box), app->front); + clutter_container_add_actor (CLUTTER_CONTAINER (box), back); + clutter_container_add_actor (CLUTTER_CONTAINER (box), front); clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); /* animations */ - app->transitions = clutter_state_new (); - clutter_state_set (app->transitions, NULL, "show-front", - app->front, "opacity", CLUTTER_EASE_IN_CUBIC, 255, - app->back, "opacity", CLUTTER_EASE_IN_CUBIC, 0, + transitions = clutter_state_new (); + clutter_state_set (transitions, NULL, "show-front", + front, "opacity", CLUTTER_EASE_IN_CUBIC, 255, + back, "opacity", CLUTTER_EASE_IN_CUBIC, 0, NULL); - clutter_state_set (app->transitions, NULL, "show-back", - app->front, "opacity", CLUTTER_LINEAR, 0, - app->back, "opacity", CLUTTER_LINEAR, 255, + clutter_state_set (transitions, NULL, "show-back", + front, "opacity", CLUTTER_LINEAR, 0, + back, "opacity", CLUTTER_LINEAR, 255, NULL); - clutter_state_set_duration (app->transitions, - NULL, - NULL, - animation_duration_ms); + clutter_state_set_duration (transitions, NULL, NULL, duration); - /* display the next (first) image */ - load_next_image (app); + /* make the back opaque and front transparent */ + clutter_state_warp_to_state (transitions, "show-back"); - /* key press displays the next image */ - g_signal_connect (stage, - "key-press-event", - G_CALLBACK (_key_pressed_cb), - app); + /* load the first image into the back */ + load_image (CLUTTER_TEXTURE (back), source); + + /* load the second image into the front */ + load_image (CLUTTER_TEXTURE (front), target); + + /* fade in the front texture and fade out the back texture */ + clutter_state_set_state (transitions, "show-front"); clutter_actor_show (stage); clutter_main (); - g_slist_free (app->image_paths); - g_object_unref (app->transitions); - g_free (app); + g_object_unref (transitions); if (error != NULL) g_error_free (error); From e332236b87871b09922ab3ada1a2a5726d06130a Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 16 Aug 2010 16:39:13 +0100 Subject: [PATCH 05/13] cookbook: Added a longer slideshow example Cross-fading between two images is straightforward, but cycling between more than two is more efficient if done by copying COGL textures between the two textures, rather than trying to reposition the textures. The example demonstrates how to reuse a pair of textures to cycle through multiple images. --- doc/cookbook/examples/Makefile.am | 2 + .../examples/textures-crossfade-slideshow.c | 182 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 doc/cookbook/examples/textures-crossfade-slideshow.c diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am index cc9c7c4a1..35aba90dd 100644 --- a/doc/cookbook/examples/Makefile.am +++ b/doc/cookbook/examples/Makefile.am @@ -13,6 +13,7 @@ noinst_PROGRAMS = \ events-mouse-scroll \ textures-crossfade \ textures-crossfade-cogl \ + textures-crossfade-slideshow \ $(NULL) INCLUDES = \ @@ -44,5 +45,6 @@ layouts_stacking_diff_sized_actors_SOURCES = layouts-stacking-diff-sized-actors. events_mouse_scroll_SOURCES = events-mouse-scroll.c textures_crossfade_SOURCES = textures-crossfade.c textures_crossfade_cogl_SOURCES = textures-crossfade-cogl.c +textures_crossfade_slideshow_SOURCES = textures-crossfade-slideshow.c -include $(top_srcdir)/build/autotools/Makefile.am.gitignore diff --git a/doc/cookbook/examples/textures-crossfade-slideshow.c b/doc/cookbook/examples/textures-crossfade-slideshow.c new file mode 100644 index 000000000..c48be83f3 --- /dev/null +++ b/doc/cookbook/examples/textures-crossfade-slideshow.c @@ -0,0 +1,182 @@ +/* + * Simple slideshow application, cycling images between + * two ClutterTextures + * + * Run by passing one or more image paths or directory globs + * which will pick up image files + * + * When running, press any key to go to the next image + */ +#include +#include + +static guint stage_side = 600; +static guint animation_duration_ms = 1500; + +static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; + +typedef struct { + ClutterActor *front; + ClutterActor *back; + ClutterState *transitions; + GSList *image_paths; + guint next_image_index; +} State; + +static gboolean +load_next_image (State *app) +{ + /* don't do anything if already animating */ + ClutterTimeline *timeline = clutter_state_get_timeline (app->transitions); + + if (clutter_timeline_is_playing (timeline) == 1) + { + g_debug ("Animation is running already"); + return FALSE; + } + + if (!app->next_image_index) + app->next_image_index = 0; + + gpointer next = g_slist_nth_data (app->image_paths, app->next_image_index); + + if (next == NULL) + return FALSE; + + gchar *image_path = (gchar *)next; + + g_debug ("Loading %s", image_path); + + CoglHandle *cogl_texture; + cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (app->front)); + + if (cogl_texture != NULL) + { + /* copy the current texture into the background */ + clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (app->back), cogl_texture); + + /* make the back opaque and front transparent */ + clutter_state_warp_to_state (app->transitions, "show-back"); + } + + /* load the next image into the front */ + GError *error = NULL; + clutter_texture_set_from_file (CLUTTER_TEXTURE (app->front), + image_path, + &error); + + if (error != NULL) + { + g_warning ("Error loading %s\n%s", image_path, error->message); + g_error_free (error); + return FALSE; + } + + /* fade in the front texture and fade out the back texture */ + clutter_state_set_state (app->transitions, "show-front"); + + app->next_image_index++; + + return TRUE; +} + +static gboolean +_key_pressed_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + State *app = (State *)user_data; + + load_next_image (app); + + return TRUE; +} + +int +main (int argc, char *argv[]) +{ + if (argc < 2) + { + g_print ("Usage: %s \n", argv[0]); + exit (EXIT_FAILURE); + } + + State *app = g_new0 (State, 1); + app->image_paths = NULL; + + /* + * NB if your shell globs arguments to this program so argv + * includes non-image files, they will fail to load and throw errors + */ + guint i; + for (i = 1; i < argc; i++) + app->image_paths = g_slist_append (app->image_paths, argv[i]); + + GError *error = NULL; + + /* UI */ + ClutterActor *stage; + ClutterLayoutManager *layout; + ClutterActor *box; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade"); + clutter_actor_set_size (stage, stage_side, stage_side); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + + layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_BIN_ALIGNMENT_CENTER); + + box = clutter_box_new (layout); + clutter_actor_set_size (box, stage_side, stage_side); + + app->back = clutter_texture_new (); + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->back), TRUE); + + app->front = clutter_texture_new (); + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->front), TRUE); + + clutter_container_add_actor (CLUTTER_CONTAINER (box), app->back); + clutter_container_add_actor (CLUTTER_CONTAINER (box), app->front); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); + + /* animations */ + app->transitions = clutter_state_new (); + clutter_state_set (app->transitions, NULL, "show-front", + app->front, "opacity", CLUTTER_EASE_IN_CUBIC, 255, + app->back, "opacity", CLUTTER_EASE_IN_CUBIC, 0, + NULL); + clutter_state_set (app->transitions, NULL, "show-back", + app->front, "opacity", CLUTTER_LINEAR, 0, + app->back, "opacity", CLUTTER_LINEAR, 255, + NULL); + clutter_state_set_duration (app->transitions, + NULL, + NULL, + animation_duration_ms); + + /* display the next (first) image */ + load_next_image (app); + + /* key press displays the next image */ + g_signal_connect (stage, + "key-press-event", + G_CALLBACK (_key_pressed_cb), + app); + + clutter_actor_show (stage); + + clutter_main (); + + g_slist_free (app->image_paths); + g_object_unref (app->transitions); + g_free (app); + + if (error != NULL) + g_error_free (error); + + return EXIT_SUCCESS; +} From 1b2606a850288b2f8f943ae2e433b14e34a944f7 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 17 Aug 2010 10:56:06 +0100 Subject: [PATCH 06/13] cookbook: Modified ordering of statements in cross-fade example Changed the order of statements in the sample code to match the order they will be explained in the walk-through in the recipe. --- doc/cookbook/examples/textures-crossfade.c | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/cookbook/examples/textures-crossfade.c b/doc/cookbook/examples/textures-crossfade.c index 093c3e04e..94f6f4771 100644 --- a/doc/cookbook/examples/textures-crossfade.c +++ b/doc/cookbook/examples/textures-crossfade.c @@ -97,27 +97,27 @@ main (int argc, char *argv[]) clutter_container_add_actor (CLUTTER_CONTAINER (box), front); clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); - /* animations */ - transitions = clutter_state_new (); - clutter_state_set (transitions, NULL, "show-front", - front, "opacity", CLUTTER_EASE_IN_CUBIC, 255, - back, "opacity", CLUTTER_EASE_IN_CUBIC, 0, - NULL); - clutter_state_set (transitions, NULL, "show-back", - front, "opacity", CLUTTER_LINEAR, 0, - back, "opacity", CLUTTER_LINEAR, 255, - NULL); - clutter_state_set_duration (transitions, NULL, NULL, duration); - - /* make the back opaque and front transparent */ - clutter_state_warp_to_state (transitions, "show-back"); - /* load the first image into the back */ load_image (CLUTTER_TEXTURE (back), source); /* load the second image into the front */ load_image (CLUTTER_TEXTURE (front), target); + /* animations */ + transitions = clutter_state_new (); + clutter_state_set (transitions, NULL, "show-back", + front, "opacity", CLUTTER_LINEAR, 0, + back, "opacity", CLUTTER_LINEAR, 255, + NULL); + clutter_state_set (transitions, NULL, "show-front", + front, "opacity", CLUTTER_EASE_IN_CUBIC, 255, + back, "opacity", CLUTTER_EASE_IN_CUBIC, 0, + NULL); + clutter_state_set_duration (transitions, NULL, NULL, duration); + + /* make the back opaque and front transparent */ + clutter_state_warp_to_state (transitions, "show-back"); + /* fade in the front texture and fade out the back texture */ clutter_state_set_state (transitions, "show-front"); From 1e4578d1dd6dc7d638ac2d4902e303d07504039f Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 17 Aug 2010 11:20:20 +0100 Subject: [PATCH 07/13] cookbook: Don't need to set keep-aspect-ratio for simple example --- doc/cookbook/examples/textures-crossfade.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/cookbook/examples/textures-crossfade.c b/doc/cookbook/examples/textures-crossfade.c index 94f6f4771..856625b12 100644 --- a/doc/cookbook/examples/textures-crossfade.c +++ b/doc/cookbook/examples/textures-crossfade.c @@ -88,10 +88,7 @@ main (int argc, char *argv[]) clutter_actor_set_size (box, 600, 600); back = clutter_texture_new (); - clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (back), TRUE); - front = clutter_texture_new (); - clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (front), TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (box), back); clutter_container_add_actor (CLUTTER_CONTAINER (box), front); From 8c0c2924ae467d265aabf7b7c472eb0474c459e7 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 17 Aug 2010 11:51:33 +0100 Subject: [PATCH 08/13] cookbook: Renamed front/back to top/bottom in cross-fade example Front/back seems like the wrong terminology when discussing actors arranged in layers. Top/bottom fits better with Clutter API function names and other recipes, so renamed variables. --- .../examples/textures-crossfade-slideshow.c | 44 +++++++++---------- doc/cookbook/examples/textures-crossfade.c | 38 ++++++++-------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/doc/cookbook/examples/textures-crossfade-slideshow.c b/doc/cookbook/examples/textures-crossfade-slideshow.c index c48be83f3..bc7973a13 100644 --- a/doc/cookbook/examples/textures-crossfade-slideshow.c +++ b/doc/cookbook/examples/textures-crossfade-slideshow.c @@ -16,8 +16,8 @@ static guint animation_duration_ms = 1500; static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; typedef struct { - ClutterActor *front; - ClutterActor *back; + ClutterActor *top; + ClutterActor *bottom; ClutterState *transitions; GSList *image_paths; guint next_image_index; @@ -48,20 +48,20 @@ load_next_image (State *app) g_debug ("Loading %s", image_path); CoglHandle *cogl_texture; - cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (app->front)); + cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (app->top)); if (cogl_texture != NULL) { /* copy the current texture into the background */ - clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (app->back), cogl_texture); + clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (app->bottom), cogl_texture); - /* make the back opaque and front transparent */ - clutter_state_warp_to_state (app->transitions, "show-back"); + /* make the bottom opaque and top transparent */ + clutter_state_warp_to_state (app->transitions, "show-bottom"); } - /* load the next image into the front */ + /* load the next image into the top */ GError *error = NULL; - clutter_texture_set_from_file (CLUTTER_TEXTURE (app->front), + clutter_texture_set_from_file (CLUTTER_TEXTURE (app->top), image_path, &error); @@ -72,8 +72,8 @@ load_next_image (State *app) return FALSE; } - /* fade in the front texture and fade out the back texture */ - clutter_state_set_state (app->transitions, "show-front"); + /* fade in the top texture and fade out the bottom texture */ + clutter_state_set_state (app->transitions, "show-top"); app->next_image_index++; @@ -133,25 +133,25 @@ main (int argc, char *argv[]) box = clutter_box_new (layout); clutter_actor_set_size (box, stage_side, stage_side); - app->back = clutter_texture_new (); - clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->back), TRUE); + app->bottom = clutter_texture_new (); + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->bottom), TRUE); - app->front = clutter_texture_new (); - clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->front), TRUE); + app->top = clutter_texture_new (); + clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->top), TRUE); - clutter_container_add_actor (CLUTTER_CONTAINER (box), app->back); - clutter_container_add_actor (CLUTTER_CONTAINER (box), app->front); + clutter_container_add_actor (CLUTTER_CONTAINER (box), app->bottom); + clutter_container_add_actor (CLUTTER_CONTAINER (box), app->top); clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); /* animations */ app->transitions = clutter_state_new (); - clutter_state_set (app->transitions, NULL, "show-front", - app->front, "opacity", CLUTTER_EASE_IN_CUBIC, 255, - app->back, "opacity", CLUTTER_EASE_IN_CUBIC, 0, + clutter_state_set (app->transitions, NULL, "show-top", + app->top, "opacity", CLUTTER_EASE_IN_CUBIC, 255, + app->bottom, "opacity", CLUTTER_EASE_IN_CUBIC, 0, NULL); - clutter_state_set (app->transitions, NULL, "show-back", - app->front, "opacity", CLUTTER_LINEAR, 0, - app->back, "opacity", CLUTTER_LINEAR, 255, + clutter_state_set (app->transitions, NULL, "show-bottom", + app->top, "opacity", CLUTTER_LINEAR, 0, + app->bottom, "opacity", CLUTTER_LINEAR, 255, NULL); clutter_state_set_duration (app->transitions, NULL, diff --git a/doc/cookbook/examples/textures-crossfade.c b/doc/cookbook/examples/textures-crossfade.c index 856625b12..de83bf1a7 100644 --- a/doc/cookbook/examples/textures-crossfade.c +++ b/doc/cookbook/examples/textures-crossfade.c @@ -70,7 +70,7 @@ main (int argc, char *argv[]) ClutterActor *stage; ClutterLayoutManager *layout; ClutterActor *box; - ClutterActor *front, *back; + ClutterActor *top, *bottom; ClutterState *transitions; clutter_init (&argc, &argv); @@ -87,36 +87,36 @@ main (int argc, char *argv[]) box = clutter_box_new (layout); clutter_actor_set_size (box, 600, 600); - back = clutter_texture_new (); - front = clutter_texture_new (); + bottom = clutter_texture_new (); + top = clutter_texture_new (); - clutter_container_add_actor (CLUTTER_CONTAINER (box), back); - clutter_container_add_actor (CLUTTER_CONTAINER (box), front); + clutter_container_add_actor (CLUTTER_CONTAINER (box), bottom); + clutter_container_add_actor (CLUTTER_CONTAINER (box), top); clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); - /* load the first image into the back */ - load_image (CLUTTER_TEXTURE (back), source); + /* load the first image into the bottom */ + load_image (CLUTTER_TEXTURE (bottom), source); - /* load the second image into the front */ - load_image (CLUTTER_TEXTURE (front), target); + /* load the second image into the top */ + load_image (CLUTTER_TEXTURE (top), target); /* animations */ transitions = clutter_state_new (); - clutter_state_set (transitions, NULL, "show-back", - front, "opacity", CLUTTER_LINEAR, 0, - back, "opacity", CLUTTER_LINEAR, 255, + clutter_state_set (transitions, NULL, "show-bottom", + top, "opacity", CLUTTER_LINEAR, 0, + bottom, "opacity", CLUTTER_LINEAR, 255, NULL); - clutter_state_set (transitions, NULL, "show-front", - front, "opacity", CLUTTER_EASE_IN_CUBIC, 255, - back, "opacity", CLUTTER_EASE_IN_CUBIC, 0, + clutter_state_set (transitions, NULL, "show-top", + top, "opacity", CLUTTER_EASE_IN_CUBIC, 255, + bottom, "opacity", CLUTTER_EASE_IN_CUBIC, 0, NULL); clutter_state_set_duration (transitions, NULL, NULL, duration); - /* make the back opaque and front transparent */ - clutter_state_warp_to_state (transitions, "show-back"); + /* make the bottom opaque and top transparent */ + clutter_state_warp_to_state (transitions, "show-bottom"); - /* fade in the front texture and fade out the back texture */ - clutter_state_set_state (transitions, "show-front"); + /* fade in the top texture and fade out the bottom texture */ + clutter_state_set_state (transitions, "show-top"); clutter_actor_show (stage); From 0486c56a41870ac1ebb3a0200060fb80282a1f25 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 17 Aug 2010 14:54:32 +0100 Subject: [PATCH 09/13] cookbook: Removed unused constant --- doc/cookbook/examples/textures-crossfade.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/cookbook/examples/textures-crossfade.c b/doc/cookbook/examples/textures-crossfade.c index de83bf1a7..e6b94cff5 100644 --- a/doc/cookbook/examples/textures-crossfade.c +++ b/doc/cookbook/examples/textures-crossfade.c @@ -1,8 +1,6 @@ #include #include -static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; - static gchar *source = NULL; static gchar *target = NULL; static guint duration = 1000; From e54d3e716b5f9a00d59a006e70926d5fe31fd4a3 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 17 Aug 2010 15:46:48 +0100 Subject: [PATCH 10/13] cookbook: Added video of two texture cross-fade Added a video showing the two texture cross-fade. Modified the example code to animate on key press, so this video could be captured. Also altered the stage size to minimise the video size. --- doc/cookbook/Makefile.am | 1 + doc/cookbook/examples/textures-crossfade.c | 21 ++++++++++++++---- .../textures-crossfade-two-textures.ogv | Bin 0 -> 81903 bytes 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 doc/cookbook/videos/textures-crossfade-two-textures.ogv diff --git a/doc/cookbook/Makefile.am b/doc/cookbook/Makefile.am index 2104d2bd2..a0c4bd7b7 100644 --- a/doc/cookbook/Makefile.am +++ b/doc/cookbook/Makefile.am @@ -53,6 +53,7 @@ VIDEO_FILES = \ videos/animations-rotating-container-reverses-direction.ogv \ videos/textures-split-go.ogv \ videos/events-mouse-scroll.ogv \ + videos/textures-crossfade-two-textures.ogv \ $(NULL) EXTRA_DIST = \ diff --git a/doc/cookbook/examples/textures-crossfade.c b/doc/cookbook/examples/textures-crossfade.c index e6b94cff5..0ba9ccbec 100644 --- a/doc/cookbook/examples/textures-crossfade.c +++ b/doc/cookbook/examples/textures-crossfade.c @@ -28,6 +28,16 @@ static GOptionEntry entries[] = { { NULL } }; +static gboolean +start_animation (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + ClutterState *transitions = CLUTTER_STATE (user_data); + clutter_state_set_state (transitions, "show-top"); + return TRUE; +} + static gboolean load_image (ClutterTexture *texture, gchar *image_path) @@ -75,7 +85,7 @@ main (int argc, char *argv[]) stage = clutter_stage_get_default (); clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade"); - clutter_actor_set_size (stage, 600, 600); + clutter_actor_set_size (stage, 400, 300); clutter_actor_show (stage); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); @@ -83,7 +93,7 @@ main (int argc, char *argv[]) CLUTTER_BIN_ALIGNMENT_CENTER); box = clutter_box_new (layout); - clutter_actor_set_size (box, 600, 600); + clutter_actor_set_size (box, 400, 300); bottom = clutter_texture_new (); top = clutter_texture_new (); @@ -113,8 +123,11 @@ main (int argc, char *argv[]) /* make the bottom opaque and top transparent */ clutter_state_warp_to_state (transitions, "show-bottom"); - /* fade in the top texture and fade out the bottom texture */ - clutter_state_set_state (transitions, "show-top"); + /* on key press, fade in the top texture and fade out the bottom texture */ + g_signal_connect (stage, + "key-press-event", + G_CALLBACK (start_animation), + transitions); clutter_actor_show (stage); diff --git a/doc/cookbook/videos/textures-crossfade-two-textures.ogv b/doc/cookbook/videos/textures-crossfade-two-textures.ogv new file mode 100644 index 0000000000000000000000000000000000000000..b9c3c1b84b7439163331de48bf573dc11e9d0d80 GIT binary patch literal 81903 zcmb@t1zcQ9moM6lySuvw4ekVYCqQry?(XjH?!f~DcMl#y0s(@%yG!3D=X^6~?z}fM z_ucpZHMMH_s$I2rcWw4ovanDCKmh*6WY)6WYu5m+s|zS%ZslxgW@HM0{FfR`_x{QJ zKk)xB1t|T8*rA-Jc&}QSK#&}eA;87b%-+ce5&{Uo2Veq#2>>ALs}{8Y+?WCYz&{@V zAfxwBlvmXsyjBR-0VKpxq=3rc3XSdU%m7Sa$!q-A;tEjz7`_JiPk;eS-r3u^nAy3! zQTKE(<0EskGBvYj`KuUYg#Vc9HR5ZoWFjAS6b;ZnkP!Hn2P0Vf-pav}S;@(Q%*M+2 zFDV%tGaDH@D=RN64?8;DZ( z4>McPU+&$LbhmAj)_6@7GO}pSIXi4IswpMpaBMNgXd&cqB4Gc&fNoo>W|8#uNfYDA zgICvPhvC|?N}Udqo=OMp#gjKX)?5wVqd|i{Ig%;}AI?XBI1Z**G@w(H0Sg|=m>eu1 zLJEWE6N%u###!9v@??r1GGZACPHmntuG5Ta5({Kck|;#oP0nncGhfGDzqmi5KMkO_9w?ev-wXC9SLy5fyfy=7`}%sP%y{0-rjY#;^F}Zuzyf+idw)ry3s7B zE95%4v091boXboQhYK`LIQ9S*)V7;@dYF3X&MO0P(aH^__uR>MTq@uq`ArJ@@4eW;q{EGztO`!e= zgQ1{+U>%r10skT}5PvurxW5S;z(@asi?9CgBVm@>&jjYQKB0ttKQXXRORs=l459r_gl|F!R!SU!EqsHqA$rc$-@(H(V^Kf)?gFS;IzB~dD@O4-J= z)6eO|SWD`QCoTM~hO}tZP6e3M4hM0hd=Fj}IBX;D9h)<;&k>F~>%_Qhd7>pQBbnJ% z6(5E;m@`*Xw(iD%LoRg_=!JD&KhZ%ca-!@lmFtvk>;72_njr?Rn+g;yQSF4rg^!_kD&F&RyLRl(PgbRNO{ zkN`pcy7z@|3pskwHLP)MP~k44@1O5?N0#*B!oWgg^G;2c}{K-SE=IPrS8ilF?VpMYJ0;k%N=c_cE2Hf zw6b*A##3Niq`LeN!#zRJ7Iu@I`fGTNa_JV<5yIk&9Br<8IS1{xH%6_f{9Fh$H`)}| z$j7^kL*|CtKfFv^LGe))I{ko$8&=G%-`0!FIsHsE&cJm>@>-9zaY*49 zR3%Rl-`&s))R;t$Fc-aZc9@xrf10UME#m7g)(wky7t>pe<|2Dk1qq=Qge)K z7NlG7C-%zPda~~DCzMc=P3bc1p?&_{D53c4i>q<+#xFn(S_=6LlP#DNRO2tM7r4$5 zW5DJ{D`xlFXEWnUY0a*DCs@>C66R+!3yBN2Eu)CHw$%$`#dM+SEx>Ha#qF3DUwFvy z&?78`yQm>6em+l*!lKFtkF1w-zNh zyalS=hL#{R<@(7Xzg-wd+#c01C`>{rm~@L1`>h7|6aL$Yr4R2Fge|dusVPTqR`8N0 zJ-XQQV5{==gzVs52z?o@d8L#U*1Bk|rFVtdePl}A0U$(RNY6^m@IZAHo? z$MDuogo-oZ`XYW#Q}VQCB$XcLnKEA*t-3k}H<{fk8`9RXs#m_!Imu2mkZ|?-=b%Z9{Qs*+pBE)A6|<3?;4O zJrJy~1519ZZgA#zIFH08+GZQ|)iXy!T2flpio@2q=$6TW{Bq50jOCc*dU(UTS_^={ z)^qjLTUPk-6i7Dl-F$I84W06&{|k#3&!c18x4}`iGTWw9Nq82iv8)JGq3X~1EiE`_ z{#cZb0{Og=8dPvf{$CpES5;aOAZ@kZPUB#*q@BWSUL6L4-B=6LxF@MBQIyOcFe=5B z-qaYaet^T^NeLaDe?HNWF*6l&UT?0%({CNe$$aD(H-XhZmo1+Q3V5A~k|_aC^6o9v z<(!t8bJIrKzphn%lNx6jnN()Ra$mHG2gqqf=A!?8KhB zC}AjV&1kIhv@mCSwY71TD?U;Bw05#*mN~^XjldJt6hTg|#P6O4#EwbLK2#?5B%r0Z zqYp*(Z&eLvHvi~1e-E-3?bHvN2f2@grUq#YFzJ}vR+{5ZOVRU@Ng-6qs$_Q@P(VoG zV7E~tmKphP+RE2k<&WV^uOD$@s|Lhqzu)v5{ScX=Xj=sed;evkeGd0}A9Iu(O`u6Z z6nV;@mX!(ssL>Z!G)Z9E8xHVEf_RdC1#)U=;)L@FSjLdR>2f%% z-yap5y$}9V{0{E)${qu~$3{P=UD8u1RE@9HS>g zrQ~_iJ9NOB3*Oo^#lrmW%=6m#n{6q-G|dYQ_1I0SFbZJr9$R0YL}OhW$EF_X@mSn5 zQ_WUo1-7a~{wE;Dnfxq=t=X6)&%2GWq0a6ry#->@6GW4+QkD-r1==5AvqMLb@M6lW zGMgJ#g<<_E_E2|s$26F#NmX!b)DEVvP)s@?3zE}rkfZZEmeB)VmbKGoemo6D(F)SrbE!h%CPZHpT z*{$Li>*D7a2aA->4u|4TU(t%G;3eihcV>$3`U*b`87^rFfPDp@FR0a1|BKoCvjc+q z*B0Y-xAnR?G|Yh20{*q11HAiN`MNuL)%j-)(=z{KZ}i`Exxg+oZJnrHw;j`6OQ+qs zx81iZkE`CNzNcNU-T4)vfAIHU(W>7+1plG?ZEy^qw%jgJ$Eeu&}U5A9?%>zyK`_JVQf6!@f+B?$OcF3Ewz?M8Nd!!YvPMiQF4t)2y z&n^J`cjrINM`DG7h$hYXJ%XWos{*d)-ez6AN9)>J>eKlEK}6b$N@4NC5C*LpLL}ma zFo3twXegR>g^nf!+ai+X`OO=aXD;X(_KO7abo$4>;P{-eHe4ALMfYz+`kK~X-9viM zzB;?Ewb_Z2Q9u{(FLZ=ydhRSaui#D-@;C+)4ukR3Z=)`#2~0l;)G?g~#$+9A%>w-hNPm=roj_ptMN*D?dL-*9e7<&|=JZ4hDPHvBMl(zEqfb4*RfZM1#_@7uv@81D- z(7i*NJ=&+VLWb1LCoPy3l6f5%rgR$z(_hlk(qA&Gx0pMuXOHid*mTtmAFS9w)S@Hrs)88{A033E)`*bIg{tJ!Pk{C-m8i{jV{?X|1 zlt3isqgmPw1>}=yj6aL=;o~7!mUAa$+PZnXup_es54NZ%D!&cA!IlY#cXFe49iDGk zC3=}8DQ&k}^g1&=dSNykk+kceKVTrV%Gq#HTEIsN1)<`x{c?BUVReAT@eJ}9D6&~d za5NSb9SUH-P_%a5JAo*`g@*~B-9w`W!E@+{DY|B6ju1I!W^qXAOibi(aL^)6j1F+H zJNzd``zJ)K1s0@{|92BU#%5(4A5)HlDdNH3^GrudUB5quk69w-4E&_ar1v+>nkO&n z-UOx>m3Hv9r&<_bET+k!{#qv0p_U7n+1M$#a{0mjy&bfUuO(67ebmg9=1vp8alEk1 zy^?%-4ft_mQE1i|(PXEyii-`~h-!XU<~ll)m#KR2`I{f|X>@G~GXk77XB*RJT4e6F zpI2mffqr;DROkf6tKEx6gRp*t4+r&uZYqXkrk^zvmrve=AMqhOohwKFRKxm+3e&N# zVZ?jK9~xC>cQ1IWUN5y?ST$6tyf))6!YSSP76)RE{sv~LF>L8GW^!C%GM}Eiy6d96 zb-cn-^zy=NG>;SVJ|i8XOKuL=v8KoKlEc=+_u!d>-(5>MRs7L>cC&L_UW5W!ehD5I zFdmm89>nQz*m*r3w~mb8QMo)h4>oi@bM*RQNA|cMp}r?C!~W>M)u;aV3890kfuUR4DW?;<>F#G^#v>^a3V@?o7oLN6HdBvp)4*gE+mFw zfQOA7j*nhZMk>G;q0Q++?lNPK0;T}G?#3S+;P_O8TVi+svI7qR;4M8~X*6#RLs zYeF-guM$m4N=hxAa1$>b=Xp;zE`z22TbUb)=Nzy;1#(00c2UK5&`T)FP2CdozW~fzRJkm4gbQ ztm`E4Ux$En=}kKGjY<%I^ICc61a}c-e8ocI#03ofU3wQfq<2_wbZC3BRu}1oguHuu zdvf2>x2J+VR=5+tndDa}&0amr1zdK$T1!cs;EHL8Sh=lS1VTd)-qK)aN2VA@4Usiv(6r#l^*i zxVTW?$$?ehcK09Sxnno4p1Y=@ib8>7NacUZ01s76KQ5U48eGw-l*&dNYc6_l-&$gV zx||oa$k#opE<9+!SK%MvVbVmLNRNbs#8b%FxC z0p5d8oW%I*joTR8EH;Mfu%FPB$?KU1QNhzEB_**p^VW-x51RB1e>_%s?5W}Rwchll zLKZ1WQDt^MJ{G5LUl@vlACvF*V* zxKpzS%0n&S*@`P1Gm+P}`SQLQDA5r# z7z7DJWHSE?5bQQncHFFQ_qHz>hUKW`D{L1XM1rc=XEK)(HC7RdjCLRyEiOP!V*;R^fo+B-v^&^=`3qhc=k{%pBoE}#4xtJ!j;RWKuG{qyIK*ZbRIe;R_h!q*<>9~SxJEBo%} zAG|>hc3FNo_-e`ya}cEFj!E^brzfe8I8a6BN-U|JW|nE8(krM_pR4x@^)qFdf(~AA z=--(sc65-FyMoV85+bDWT3T9~kqBXcfH7cudkmkeE0@Ct^Kuzm_U6F3A(VnsVf0jM=)n61J{$dqq48E6uumkK}7_k6%3jN2tkE`*gq; zS6p(i+EP_{a{B63RaJ38n9uYHS(${7l>9zi;2~b9RK(8%oU({#ZIM$!P5mdQ!k8-< ztRw6XpM!tsz1MRZ`-H7bpJukHfzJS)riV9G9sWAr%OgRRz~21w{Rh7_2chu&wK%W9 z$TY>;Pdr|MKbez%Tnj2PS4)8{wN&lE<$zHKS@F=`-U$1tTX~HyR_(_=yJ9fqr`Kez zUL+&@*MEA>KYzH9?iyt#=J}P+72qo~ztk3UVCq*~1|Iyu3o|%UsYqp@S*kKafwotB zsXD?Z+~AicuvUI{?^;kn4ffMdP9-&P0Z*~|wYA0JRroQ(CTtlK<{bayPFUEhEdgVY zFdmd)RO1>D*WD0Va;BDm(+lp1ZbshSjIcmf42s!&rcoOxO%KiODQ9~B)|$7-cSRLQ zGeT}XD_Ig?8NP``ZaGs32Utx(l&nuU3WH~^jtu3H&w48g1jJwh0?^Ij>tbC(Fy{Jh z6)@rRmIF9oa8~P?q)k~>5$r7kEU~a+35v)Auq0U#YO6)qcxI|^L;BH+-%AQzWhlT| z&LCw7#TTD?3Eh@O_Y(Z}Jr$vYff{=E9)|78I|FNYQlzqgldX|_Zl?g7eC~j&g_YpZ zRrIH1E1%m=3AJofjkV6)pQc3OwewNqkAR{AU9+hQktJ>1_vPhfJ5Hcwn`ZJk_>wz& za_7>sb43+DnZ(uTbMRr^>^ywPW9e^Lt*KX0nxmzdJvVjN|vTCg9`CX-MIIF|={+(P@yW#gYBw+G({JSvI+I;ccEA$(eHRrTh5! z8f3Mnhu5YHaXju#FT0GRN2fld2#5VlbokSlIoOv56Fwx&a>zyd+ltOAJXQFPg_Wuj z`x53PEvCX?vOH=(#_ekkBz+d#=%J0$p`QO{_xT^xFBY%tQX#_O8+L(T=iBMBO)k?z zFk_w?H(IhxvhjGc<2s>+9mA7D$~7M>`0daV%bjMzE*s6lYQ=|=L)xmVoz2;d#~!mg z2l*dwO~%wGx!)KzG~mr2V!6KGbVb7mHHrOYDZA^Oeml9^5x3bk#hipvn%g8 zH#aX+e`*Zv#z@Xk`%g5Xo%u#|{V_K;eSU$rZl*1dZeZXVa=Y0KYA^iK3!DX=B%U^N zNGMK$G4`4Ks24|?IQJEKg5V~eMr%;25sZfOg@qCG`}Q1D@ zxw-j%e)(pbrwi%n=#A(ZPe(^ALQ~V7l6JWn_-86${a*;?(1E#eb2IJW2LpDsk@MH{ zzf}K2nno*6#P{j2F&xFi(B@|1L^PINJEJu1*L1}CK)Q#bg%c48zg(CiXI}66KxC{S zubD+Q`LJ(QHu`p-mvcA@Q6sPW_{gT`5RZd}z<1;PaPon06n#fqjUAdFedGMeF}tom zYfZp*yqd5mL_7}bU^NksV{*F1A8x#p_LcGs8nZe(MhqIW;l)T&qDKrGZvA_=3VfE= zdJ8zH<0B=SoZ&IK@%=_k=dI`E5UH{LaN{l9i9BlBxN!D#WbH(HUwy{CR{v`gb(J)3 zh9zD7f!6Y^(hpnQzymOT3shS5Y4&sC)G+DlLCwM z2xxaauU)N&GBWVOdo!W{QSZWws zZ9IOAR7CMdQ5s0rq9uvLo~%`KQWYTBU9FxRxu;1iEA-gsu3&E4boyi2x|0>ld=kE}(KPm`lgn%Z^S zYV278`MYcu&o4pez0Bji;n->bU;BF8Mz>QG-z5a9MQri1Yvb%M<&w&~AwqM?9?rZ~ z|1dTFo~haeY_&_EH_l4Ue@dRS@(MZAKe zLru5Yz|!8n$tX4~Tc?tOg3MQVadG{tmOZjOvkuC)m6cWBny!b2=~=;imuXX*>bm7Q zP0hKvxnB`+MdUTr?!H_Z$12Oq(-g#Lhqdh%#ugS*U$t37IF>(tvW*6VOP&cargQ1kZky;)gto7>Ogr%f=EX?j^QKk&;jr=TFwU5sB{+OSk}nX_5$ z*tecj97T?89UvscTC*1?#|vw2Zf=bTySaJHJZ(OphW%*77U#D3(Wt|1y2~*ZjAa<^ z6my>`e$5Pu1me!znpz6V$(rNmTc+yb2wPiQ6O$Y25u@C|V06?YNM#3V>F#Im>-gu7 zveqHauDH0tQ;^g7Y#!q$pW~dXe-r0yj>dUin;t@7^0RU`&KKTh@g(e|KIM=3`tdjj zc$}fQ00)8|uC#Bwt_=Ljgy0Qv#o*K#un|JV3RQ=y7MuoY5OJ8q`Yb+gC-S_?)81Hy zA>zCdG*H5e7)6wb#$meB**C#q(tTliUvE4O4HmqwH~a)P9W@?`#*u3gX;5|=s*!Ry z4h5@p5OJcp5Z``DdjoU#8tooxt=|5w<&n*Js4k7^eS?zex-!@o90~0EhQq;57oIiE z=#3f0P_$B>@Y-wGk@0tF_r@grj^;^V8%6o|%?e4wd-YbI-+x(P`u4{YTC7mH!g#1w z)Oe`F!yBhgr&D_dT$Qz^+-q^hqd0GD3EgMk*xeh?&%DVS$9WyBZY@~zI~r$X4V=3{ zY~37VD4IuI?vqw58mR5K;vsmk;ZE<)s1S#F-EH2Zwv?2S7=kl8X8b?;j)=!8L8;rXvm4ajsm{^>b8{N@?=Nt!a@g=?H zgbCIuQJ&G#l0s)e^yOK%Pvs}MR4;pR{#UkdY4<<5;ohIV8|b7watqP8&l;N5{nVQI zL1l(*0kf8UaTLZalinHr=cfkA`1j|{EgNqZ$6rdzwxe;GMQgMdye^A5RZe%9Vvqb+ zta`X6(LL;S8C#31ukH9fN<{1g@x8X9lt}7E}Kw!<&Z}#)P87d6phwbfVPR%w{9XHDAEjN8t-hm! zkPxz5JYjEnvxHf%V}-a$srD1Zp`2}YMNt-qP+J&`CQQiVN%zY1PRQn$QQyq`+RTNA z{oI^6iU4xA;}oWaHn#&M3%G(Px}yK`vnYY3vetx{e0|_iM^0ZF?C2pCMD62oZgRjL zUKHp&2MK^|zoQCbzmWYv1oY(3Idz9`J5VK6bJ%#VB;@0PI!$7K71xEG=gwA4VrOnz zGHU(@`rI5G?2nBc?57qsfMF{_w^M^2za{E!N+Bg--piaV5MB~&l5JW@lzZJ`lh=in zgTS4ljnVJ#Har|$HA~T&$GT1IvF+I{r>i}FdF(qk&RNOE7BE?Fm%*3so53QRrGsVg zbt*fwjd#j9=Q4X=;qm75cca_!r8bOn&Mq%c!F`;b2@IF>A`BPuezcn3GuV2y$8ELE z1P}DtBnOl!NW$)u4+3qed)x>f;SJ;v_?*}{Kn)Kg+29RdVAJwmK5gv^yJift#hBpz zzchq+!^~Pv@Q!d|^tP!}+{8{RFnWu`OUz^(%J143I}V$7eh2Bc_i$_-jJ81ea zp%fBQla*bl8N1U??G9s@7CPpAzlry+MRs?0pLCyO+IK{$Ml+w*x&H&kNLA#T?<&7) zB!H<{b*MIRM#7aV<3M?6$Q4b{vTFKj{zM1oS}igN@ZL7HR3DpGoNaz3eNWzf2=7!BKx-n zLnA?yTAO(5V}l0Ra)gk1X z;v3k1=Ml2hNIZ*@`eP){Kjif}xg-Q!Nn$Fv2G%6zD$1*Wgnccsio#});*ShG#J^+5 z-)oAdn$rJonf`xdZO~B)9mLIA!~4*o-a&sBN3n(_rmKW>5En&<5)nmrPY{ReLr+Hx z?Y}qrERL80gF`^H(hh4!yIw0>a51t<7VorJs9--c>*LafeDfgsYy zo;VM(_-M2>9fe3i-k3Yjk{yYVBJ(2p<>~Y3uGOxE-PyRy6z_&C$4seT&u&;Rf_wMQ zKW?KWOQDivX#0VP&KxY9Y7+7IpXxek7u0PHEN>3!M$%ZgQ(N$tY^9m6j{EVKJv}CW z*t2xFBH=s9;b`t~+kf&%pR)eGyKv*70@_PL|NL>n6S=~E?aE(f;;y_&Ro9|elJrrb zWTPPDm}B1IRQQ=z3Hm^ARpR+>=v~j@+Z!4=@0Z6Rb4&UUs`a#y!-^&(U8e*c6`@6` z1d8X4O%de#nJP*3e-2b8CD5LivPe!HU--X#IE zcyQg#M?d9`7q~hdXEP|wcf%-_O=hFx+AT=oN3=N&MRb^tYfM$g6MN&i4|C+ifTsL& zcIRvsI$Xun6&Nd&sux6v=nT$dOh|5!iu^Mc;`2%l7%PSa?mUH@GhY0YAyaw!>SIiP z4FnzqYgfk*?eZ3od@LuqAtgx_p0MD32zEUk1>JVIon};5G~$4NF1eks1$*NSOm{t0 z)&=-oKK(F1UTU#fm|UXajk%GxZkG41du$QmVvDihkeaev4sVR+wu{qBJWimdH&9Kz zUcQPZ<&L7~i3oXfOEzpXNKGXtUAjDG=|t>QOR!O4>g}`Ch6!<^SKUp{d|aH;0|J3= zSB!MDAxckssJoqnL64^$`Lbz`FD_n{a+cUEzGLY`t4Uf>SB@Iq+4L)yIlCZ``>L0E z8MT;7SM%B~?-XNu<@w~#Q!L9ESkPlSHddOs#I}a!N4g@g=|hw|n*^H#@x&9+MkPHu z2=ub0zQpkZt%hDsNd{-u(R$*voB}I_$aI zNF;v=^q5!Fc5T;74%2PNCUV-htlb-@8|URsi=5~riXot0%DW|MM49LkM8l@5&<#+p zNTsXLSFeMXoe7m?(9~9qN>Q&hEq8wrsF0BTRiT)`C7wW~umB+rHj|KbM@UgGDg`@A zfNf)xUx|upRH{ItYFQze3N1j0Bv8R)z$MW>RcM*XGAPfai6?Tw5xrWa{N+pU?qY-i zjuuEJokSJr5J096EekDs@f!5q%asuZ5Hdvms6z9vH2UgA&}-_o5MZr5IN82x>p#*{ zNQ2XW!z8}I#xtmatA8zJ`w#h3B?Gts^=)2H)l&9SS-T0a|4g2EA{9IYIL2#4u%NHM z)JDBrF>3Kc!E(5G7EL7okJw&G194@#aY?9H*{C^JfWIsLt{`sJnK zR|!E(v5VPa`&3t+D84#;6Q+2ow_9Y&PWh-$IzWV*IgqezJa^-OOUg9kM(dDeqd_lEtK^?Jpd7%J;MU-J zK3oxKRqtuN@FqC(GaH^B=DqlwW`z(OE2FH6htp=ZNRIg6;x_%p_uvb@?ubOTJ#aM% zh5`C79b|nI@~pvidt*MI$0p+Vjgp5@(4u~{8;%9VgHSM#orln%`PuONyldq~_^PmF z%+TZBzQ+|mgSvF%66%y?Xg6^42{HHfBY}vd_x)||&axuQ+v?94xDUPs*(?_?-z-3Z z5BmJUf@?4q!m3aXK>zIah2M}r0$DXs_IGoeB3E|-0%hav&&lTQ-}O;GXn*tm4i7#* zT=q|u{7=RBcllRt(@Z?ugMIgu7wFy?Uk zoP#G)AsH1#S4zM@&$!Fg?G%bY$fTRPr+1MGjjf_K2G>&By}VZ_ce8totC`L#3;v|O z6sgmxuB0+x(J7DVFDTUikJ0eS1`PJZk=)A(NXo&ux~jY*d_~Xc`alla$j`Z5iYMIM z_`4Ysh60LvM+4t`C+sTKr`hr?mp4f#a&`qykP^B|o0n|qQRt0TJ8~W;&i(V4dG9lN zcFfrts^E(FWCk!I7EyHfgfY9Gr7Od%((F>eN#$)IV!uml+gNex#E0xY2DE| z**wwFLUKRN!R~wlG$}gqo^Hs!w3==_dhyTSIG*?lipEd(O!o|?OwM~=abEGgZ%`?g zj3syrKG^>?!N~&Kbm1xB+vSv&U71Z*5W+;U25|JabmX*7N7C$i-SqrI&fK4_MygDS zX<>8fWj0(io0!R3KUw;fWJXCLLzN_RQcrd3NR<>N&0wSkR71#6?=QnJg{iYd=U=T7{ z{|Xl>e>f!nC-5IL7I27H4x61veX6%21YeDnXI+iu?q9~asT^O~kzR9v15JPl9GH8} z_Adhlvs8{e(R`nz*IIKJOhsNzGdvC7< zP;6}+nk}`;Idytv*y{nn1VL2@3@ZA(|1A`38!+DBaJm^L;OD^;+{g90`R922rxO>$ z^Jm9i*gI1E237-ykq``Wtkh=Hx0ai%C z-$2in?rQvxC|&zqFkb`-#dOePIKG2ktlzb_pv9bus`~hYVnj~_`qHvrE?(xK$!#!* zI!en<5hbl%KQIeeIl}(>8V1ZgUnARlSsS07IKi22%q?qm2$I=6J?C8ahb$5f@Q>-A z|IHHG_=MOaAScTXM*}Gr9M{(3VF_E~WGg$Eq|ivN<7}EzI|QQk%;(+qpVo06)vKVc z?z+n<(d{^XQ~IVbGdW+W@yJ;ibmBa*thVEUE4aJ7+j#+^c53rT*?sG$+Y8E5bY5lG zYMF9=*ZZy_+8BD?uOKsTeI_=%1{-(Rbg!Y{A)9A0N>r7gF}{Q$#Hnf&CSJjZ$4Tj zw`-)vq+_Dn&4;Uz?Q#TXBkJUYXLHt0uyM%>U*GJMgM%{G)U9@|*`s0VWSYn}=g!33 zPLMKZ+)*&``y2jfB)0a^+4cbdE6eggi(=3Kk~=#rWX8Vd{vaA*PfAX;2m5=7)t%rC z8fN^l<^5wvT>PIjtt~;f&^dZeChYjFtbW>q?FGN7>wP4-2d8M(8Y2oS^KGld4&v78 zg_Iz}?#3hTnzN+tYCwL!?_{u$gjYr`L=S)!BPVHtoMA7$sR zTh$GC&*j%fdOr!?AZW||`q= zcXI0H9YvXYDY1S@8OL*p<=foIM#zEenV#NxiR5n*^*a%KU&Tc`?c|c9?9}J!{{Ytl zVB?)5#dSZb#1Y!M4{aveMq%&7?51Pu+YW~z3EQlZSf_q-j`+CR(36)(I!!SkQRlcK zZnbfanDDd`R%*`U!;Z9vo#aR*phrbuhbyrc|q>>TQyf40DT4>tLQSH%}bX`}m27p?Ko`J=w6ix%Bme zB?5n;FBk?w$*EkWGQQ07{e*X+LJ*^cquO7Ol9_Dl-E0l*zp++vTg1&=;+~BJj|B zE%{;8^`?yA2ltn?mHJWj37Gzkc^#|6h`(R&)BndqrLQmXeZe{-p!r+&N{C-y>YK;) z1I06eKo()(jniBmxB(r4p3Pgj?!@Sh$6MCLitdF<$&apAa-khhIM(hnaspnJa)Jh- zj3^I~fKfv*yYHr(Hi{dSv%rFAr_1HbWPOJ0hZ48*HjZ)|Ys-S`AQFRuWJ5hj=zXx( z7t{S?J(Geu0pn;#MRTou;NT4i%K=wVD0Y7%yx5y~9Qc3p> znZK?V5!9snVC*Z%q&wA;*Xk#S_h_W>NrP=oOm9e< zGZkoFi`viBb&PY*W3!01<6x;y#98QY|mZCdSWbev%{t#N6km9P1-k1@Yv3eL)c?Y~2Vn_U;zFxEy>N4xs`CQ5fYxzjRRm zz^<$vc9P7Xa1a&Rp+K%leE+nGO<5p@LZ{+WzalR7mJ$n`h*wC$eWq zLuDh;^1n6{%94bJZ7BPB8~8UGb>7fclSfh3+E5P)S-!9THITc0$nZfUJ(Mf_Ci~s2 z^u@XHw;$^_0YxM4u;$c9Ki@I*C%)rs?)+j*9$ZIk`F+l2Iy`hKZS6B$gi#FM8>w=2 zA0I+ei2gp-fbjCdwa59TY#oEA3v0SPfxFbJR|LG2~KL+pXy@KLf~ zz9;#}yKsnA$+;mJ4*^vBWL-MYO&svQ&s;+^D^;k5gb8Br6UOiTG!(N2@68$%u_x1o z5Fq8ODE?MTJUiY2Q~f#i2^&si{hI#IGv-FD?O7t02Ka8Cl6uAZt_j_AgC7R(g7)2rcRI(A`XxAG_yl>` zI3)^Q7^y6r-?v%j&^NRpkms(4$bI{Z`le}sv^YAshU^}{__~|VP>Qqc%LiQa8q{f< z=XTRFXMf?(n7!vmyZ4xzEc22yS|iJi=XXS$A^!FgoPqP~ettdVt~rasHEjZeB!Dw% z`K_n%?ewRfYYwwClVLs-bN_Wr4+w$n4L-K{#UO2uEU+5H$V9(RjEMHdOW{`` zGKi_Lcwc5#kYrL5c>Y*Who?Ql7(M8s;AG8{$8w0MGJf`omw|;48+P=ZFBPe$W+JVV z{yy}P4AY{g`oQI)T~$A`*d`)yAM4||8V9G5RX!l0t!aWP=|h$bRa=n^Z7=rgg1o5S zwqVw0!5MYYZd?>E)5W0dn_a#WZly1UX6@q_0FI@QOreP1JWbQ~K181RvOSR~uINuE zF3g}tUJ1b_2UsZu8dR(u-31YoVpiPo@stOgd9L{YU{`n&`iG!NQN6C4lp;Mea7^-I zep)CnkcgO*r+lY(Cp{g^5IOu#k>XY@@@J{5w~p{nOZ%|& z22N#qx~}KF>yjH}8CLL#gwj4Q@xoI+GK-I>v+)J`MlW_l&lzW-rtlw~SlE4H)j1=+ zoJ3_2hzsTSWX!0u|8gmkTZNx7l*@#CADxppS~gCK9`Y_dF(s^VfQ~3(7M|?pUgHeb zqOwNEq_-S*^s7PeV%}56H0(#mcv!(CQcTD&S)X?V4!uVfNq)Rs93n`oF^UEiNB8V> zS@~?ftmtMwKnU@`lQSO$(XWjK0=W#RY!}Qzbj}`oh5+z&DnJAn02suB3Imb{qPYkD z0*a7{EC2w|03IM20u{gSX0Pu7YA3+(u^#|yhwYr=*MxfCrzm~;rb`X~AA@iQ^s(@sAOjzHJTkgG>pND(iE7M`XUYb#>w!zrSx z6&v&Blaud?7OsUBmz+x$0n^@7PzL~{>dxcs7FFvw2}tW<0%c+B>(2IA@0GO<1JBwS z9sTT&?z&Y;nYHI%o6>1oZ3E^Bmh5P}4(&P57ngiae1mLv6|{7$HTJ|SA7ee*1IL;g zyOszC4?>;myt8R+>RcC-rL=$LR39$Yc9x9TlQ=^UIkZ1yX0!}XVIEJh??Jo$LX|m> zDVc9e8E#%$`O(m2=ac~Jmc`bN{oE;^QQk6j+sYa|b=sVzWo=0K(T`N9&DCt%PIsqU z&skn@VRpLWyje0Dm{L`Za;x=WYAa)LYfnSLn&6UZ?x#tv_sacS+s{NN!;3W|dqk%> zfzFrF!sc@P!v0cEdxB{WrxOid##!d%BS&_vu z7!;zIw(&y|=kpi3n?-nNZ`hv|r*+bwdNV4jj4(Og*d;_%r1x8#LroP?7-4WHD@A00 zU$aZYUo81Ch@VWDL}PddDct?|q!Iq}b`>@iwCxYFSoOW>emt#CUwB#l**(0Pe+*LW zyLns(fv!Lg2A4h8eu@S@-H$Byw5f8vXbV(H+p6-0vT9as_p!Fs)rqtiwzizj#?l)X zs;I0gd10>Uok{vWFifu(mVGzvq92M0o;(RE>3yf1v-UCl#EJw@BqT(h4tM!#EjzFQ z7~hqF&;mq)`It>Sj5FIK=fboT(oPpX8BZ`?CSL;=&i!E4OS zawin!=?etyjeI&UIQGE*4LH_cwc2|4U~Z&wg3Ph?X0NWA!Yf7P>zyt zUk-n11YJHfvS%3#6CZvOVuVC_F2P$I1m!j;f+m8F1}KZ6AppR!gI^9)miUDf0o4o4 zG6X`f!Wi|#K~WKRpD%)6^Fr;kJk}Aq&iDA{9Wn#E;TWXK+(N}eC8wtnm=G9KVOLPs z>v|_L357EX{be_@#|y=^>S7Y)x@`BD57aB>Ac%!u5zPPzwwT& z1t5BUJgCT+x?gM*L1gZ`WmPcGx>0TSTs)F$)Z$T?``NzR5|y&k>{Mp63-{);4CWNh zdD`Mtdt)0X#N5TbmX>_uaFFtyy)kS8}VmEb+)b z%NW=8={3<4FFaNrj|`>u!JfRb#zD@)*54n?-Xj8rP{WJ`CNts0fWbrayb9bh(B-q|`byuLu3u4gy_+ zcpgu`fCA#{>PH|qYY$KBuSP+jy`dgP$-d3gsvWC_SYav|zWJb%y+iSp005xQkRF~3 z2Pz4fd}c1PD8&Il6S6lh8}L@sGPy>xL_<#m8s?q213Q2!NesfZh(QGC#$5maqC1wY z5(BXNJ&O<##erNnLEmS;iU9C0lD3%SrP$NrliOgPA7=zk_*Y<2$f>wqAA9Mw(#G8>^#Q2;5To-C3%0hZ{Wd5(m})d{w*Nq&krs04<@;Tn>Y@` zztdT&BXPDOiKrY6*C~XdV4aDiM4Oc?%PBDf1av(CT#)alW7f$^$lni~8P2;>^kuo- z)P-`EGvi@k=FjvV97I%91b#JOA;&EGUsSzyR9oE}G#Wg#xVyW%yF0~7kz&QINU>tU z-GUUSI20}JPARU%g1c*R$xYwi_uX~xSu5-8oPUy)le70cGxN-5dk55Fo<6-ty+8^w zgfHwD>zI-0h>0>J;(sqG)Mw%b7lpKOH6*1+M3s0djIT%2`0naYPNFGh;fC**N@+q9 z%)}n4N3{NX^AmR5=|Q<*JI$yo(SR`f7XJ^v;*!irOz)X5eVFKMbcPpY!xEBQt_Wcj zh6@0mDiINc0D^mm$OM4><_G{RKnngX=&8?tPm9v8tf&3Ue?6vl0TRGuMx3IGd)%(O zjj*{oC@s|d)xmtB;qLjs7`u{kNRB)}qbGurf}7VQM-lUFwVQ7L!*K%e81?!4jM-iv znMFcy4cerld|O27^ar0Xf^deZ_+30Pqlf-UvhmA)5yDy~mk%fWQE&Bwv4>@jT;BwS zn7KYYMvGMkbmtGJ0u{NJE)}ZjEYp{K&W!Q#Ypb|j7m|(Iwyjr zdhhLOhPnxeer4htZ><6qSMieKn6Q2Y(J8A0U^6})NuGXj37dJIYG&(zBQ4AjH_dsR zcRbOKRR0uWf%48;n=3KojkN{e+^C=asw(suF6aWuS}>Yr*oqFk{l8Qt3&8)5e|^LM zN@`b+nr_^G91au%TJ+x;3LSw$hyVMhz<2{sULk|6;$K!bV4;>xIxv}85{>NfmuE!d z!=EZdb2hnezh~qJ@Yv_d-xE#J6@4YX{1ECV?`w`pN6g1)=Mz&oC@h}7Z{v;|@y6{x zRBrQmG$}_z$#akO^z=5@IY*3&IZO|0`s2gfOkXN70SVeZp+kW2VVV!$yMqc-Y!eM7 z`z(A|`H6}@v8Lchu|FbWcrQx*ODvr{NrIn(nE7g66#o);)!X}EF=+UgU$ol`Wz;@0 z?6;$CqGQ^NR#1bNu~54VfDgIQ%>=TY;ZEoerd3XcY#GnrLWC51 zEbtSA+UZBE4ckKTQ{%oL+8V^oJLMFA2F!**QqTDfo38#0v+m6%$9OvHt^TQ}T{&$n z|M)BD3k%lAYti~uJNLEi-4-+rHG);&_9kkA3gK8YVwWj&5S%Hmk-w5(N-xWC51`7g zt^D?_S#Y**g~a$sv!iF@dQ0X8`>tV+-Wh9GJBh`-;U^-hCf{#OOC{|RpT;bloP_}n z6-Wtb$1HAc_1^dFjc<ybkWzQf?9e4ENNIAGrd(j%QD#K0oKD8VA-a&R5`1N9uJa z?)ZJ273O#I5ea})P4E4KA*}Z5t!bTRMDZ5K3Ep#W?f(3ITUBj0R=xs#`1#FDkfY*b z+HsAqERemSzm>TRXZdvXoAx()zwGRMCZp^&vvka5H}_AD0eklT-9DakSqyRQG0|bS z1e$*a(tlyaT^enj>8<1>m04hQ5#2Zqq^wE}u<=Fy(vaaAx~+QHc!~nj1=i`bCs6qa z*%A$Q$C+x|0ryw37OEz1J{!&d8UA_e-&#)r43OMJAjjd;Pp2SmJe8a1c&onS^UO^! zQJnx*|Bm_aoQ%QFpw{snHRM(`4vuCqE9yNtKKF!F* zL?NS&1gLGeLX)Aj&q9IFbozU~y^H)N*{DSJQ60?l9)%)kNFUuJ4a(s)&&*spQ=&C> zah&Ql!>AjW$I5k4KvehmbW1KR_aksdJEfQo-hdzrQyiMtZV0i3XfaYs1 zqG50-HHox}b_#VNyg&4znE6o=9xI^`5ewcMCpnymo>>K(NlX$rql10bJf;1#Z#h+K^+$+i(BI@(WR`s&eB?4TizOknFuwo?BWsk`jJABMXwybQDJ@k|ccGN?NGj6#W`NxCQ3|=r8Qc49}C5*Z7{miOBU%6BSQY?;%C) z(Xhs|0NAOS$U%e}CbUAF zD7%rs*dzCVujC^k&#YNq98iC2o*Z)P?F;x0@B#n~rF$~aLa6`)KsYM+BsBm2GySzg zy}Qo2O|>_z-c%kV8*7HU5&L#<^Aj(EAV>W90>J zkxJJE+HmwZxx`{XjbBob^~&HW`3jY?S*PrJ`qT_T9-Gu^Y+QV0hZCXNQ%es188QMU z^4PPs_;0P!ZMjPNU-1@hYCszlkE8tWK2cw^daDHhCcZ4}T~+i8%0rQKj-+ z7d%4)`n{M@buVG3HpZX0FMP$XPd;uU^0|2CWILH7Xe<{E%O$s9x?gM}cM~(_8FTVb@of16!HGfA&YDOti zYa;rma;TGXA1+li>*^)l!#arfC;Da7V*M5;i(%?$|4=mysf7@_!RR3Q)x4eT5B-NTCIuSo_$Q(<+-4l;9|<+v`@_jr5gM(k}Jdx%Ah zDm`RB%_7ggnFyh+w0>x<+$5J_B~@7z9&AhbW_dF0>}D~FM%P5U0$@X7|NegFY+$si zN*|eG1=e59GOPTTdPMFzt=kIc+ae(@V=|2s7{XrIuxZ1{;)TtCY1}%qfM0L$>yX(# zb(IR#BdT5FX=Twh3TDWhB{V8``~}mxlYw-VA*>KD^4LR04vUjb-QBK1+J)DV76BLT{-L6 z@%`wQW#UzNuHk;R(9V0}biZffY{IeR+%hyx>YU|n-TBLZ%~YUSa$%WrqHHhF{Vr1PGMEnK477)Q`di*pC{h5(qYkbDQi{pQAIgET^ z`7FH(Nn2_4NEzi^%Lb1@AKO+Y$E?Pjg`*;43b__8v~(zjaX-C5O;F6+Li2*bv4OXsL1&ID zptY=*E6~y+iIid9Qn%Fxr_8;?#Evl{QUgD|Qa_b5Be&qB@U4AYsOEY%{Rb%D*NsG4 z)8K%H-L^`z6Dg_KhrOeeuR#s;=LnEip_jDYVVDbDc?rBzX4X8weVHfPjx5~>rJ18< zRtdoDic+kNn_Z;<=tNX&&R!lWi&nZK5y5K?s|18nzDCN3MM3af{ zVZ_Ye^cKo=I8SKc0A*w2_=RHk#vT-??6t5y0J{M*@3|WamGmE+-jGlh|D7nNM1stR z(zy#(3*32A9boPb0N^OoyUeH1z41hL?i-2-1qWtcX@U^7<)o99X+d&8pX37dikM)) zZ4ZFr0uZd6sr6|P)sViX!5)zx??c}orj%rii>L#7CgILnZNp$Wd%7bWr2?%zeL@;t zCI||H0@vbX{A#I9sepy2-^u7nhRoK6v2o@-Z}TTnIRyxj^`^1$+n8j>(=p`nw9|}n z!2RCysV9G|fWzm~r@rez0kc|Om94#bZ>rcNl0_X}I>)!90>1l>=&+87;of@d_0Cf3 z@t{wZCrdL2-&B~&hat_*t79sxDUafe_Zu!xmlugjfzV5oos>#%T8oERU;i5M57ya-`}9-u+!lUJPkGAp|Qk14DerR2B;% z&9)B;`p$h|<>k<<*t|A5ug-H{MKW`|?9?GV4JOo&Ga#^xz9UNA$97yQ+hs)@s`Mh0 zcFz-`e8eQ@p`SZG{)2SNT$3CMo5>|@EZ54S7#>IRgHVs-p}5`Ql~#SpX6G4yc#7hj zma9_;#ftZ(mx-9K=kc=K)A3<$+>aPkH+r1N1Q{gy$`qmZvt+8T*DTmvYx>N?VJ$QLF9J*pI33_3~con&f&>rn{xl-JyKDvUpr8T7ak`zH-;W90)U7K7> zT(+H_W&SPUp6V@i95-QUF7-U4&C9eg@im_i>9dpE(P|9;*=zc z`TJo*C*=#a!v(kX#1>*#8q%c+n~K$qvJ(qfGk1K{?%Pc#hBOenUUEz059UpZRfL#?QO>=ZQTwuI z$5+UueIZDDa*D*h9z)_??8yuF8xb^~%=qwi_K7H~%xJ8SD%gFJ=N*A5u=xXAd<$Mn zqm^Xd!)~-1X4HO~wLZ_vsDyRMkLOqqA=oFPh;hHAS=)Q0+JDIl^e5e0>C?7(5)}$z zx-}?YM>HDGIxr5og^AQP%4d=Kjt%>)OLkAi3*>jA{Y|FCjDs|03Zn-S@k-ZaYGRT~ zC`lNEy)-o5L>!CEI7zsvC?gntglo@IjZb~P#0ww`1IaT_+8J*+YC4>-w@s#zM7HT- z%vGzq0spMJBGIWH>&*)z)IChkHx$9GQ#P@1kgfM>^@~0G#KR~L9FNz@a4m~`B`c|0uAj0wwzizm{moH&O*BA9s-gsC`*_`ZQ z$f?LN-q=ap2Y>MM{#0Lue&X^d@1Cuko;%|9qLD9wt+U5j#*j2oIXu}wjTw_NO#D5; zrtELbhMFJipAE7jPYxVFW-THYKzFkT5DtJxaRFFjgw%$s8B3wq6#xza$hKr;RAk?! zzDv>JXu&dPn6ZGGt#_eMYa36ifN99NgA{q^AY2v9AmI5lv?T;AlM!<@qZ8XsVLA^b z;IUZ10)L6!P>sdjic{WF`uioGlBLts_o6rxjU8N6pCs0)Sb`5EfjQJrIZ+u*DtB+U1#=j zU|;@oGvj;AfnrwAg<+dL-yo#S>Om&_c(q0`r=Cc48~*wIDg!>SCyN&rqpC4NElDHX zkJ4B*rKWkBMwRMc^~AOY7d}>}h9v~bN)YzN1S9U5!K!4|q^BBudU9c5$6a+bd4D0? zKTE%hHti<+w)QiZAflbV6?|J>{LM&d`zf;;)}CjB^_VH&)$rI8-pHS;q&f}T+5Q>j zus_*+pLQR%$Ka@a-;1p1aU2nyPKAuj<-NmO=f5;MCY;WJ^%ZEXY6ZurzK} zpWLh=0jd6OpQ-Ag$GQ@5Dhm;C`-D^TcLcwlVASUqoE48Sl#4Xk)z2Yxwsnv%(9d#2#+Mi)(_@U2IHPfd25x zg5m=0!EY%GjZn-juGry2Dd=lxOqAv-!eiILDifaV`sdey><8dvSC#4aAN!XY$`xPz zp?@0f$e6{8RdpuTT%>RaiS^?y2+S`it~Ul|D)xYHwG` z*I4!Lu-ZVtTxQ5{iqr#-^m&xv-D16Y9-SM>z`{{eV^cieq9=O~WA(O%{(eK7h%)If!Wn4Q zSjQ4mBX(trR_gng(qGe0Vy`uB|Hs4j3BtB_+qFZr(AMWkvFmXwoyjj4&wN*WfTQH3 zNfw_fAHCUoLZb+qXv`&bR#VNc)1mf{CVZtZv8Lg(+LHgn%j0vAWr^wqr({moBkeZk zFSRAHqNsysaRbRK;yW3Bn_LZEM6kVAC$!d6nGcVYq-+W&9e}$6mNhdYvNfzU&VYfk z^|;9SXk$`%cuziMHo(j-lBufBTp8If`SPL20-sDfB=AlEG!7r zg`A(J1r?l6SH$3o1u`p;OQVmLuQFDO=R^tW$ri9lye=F_%i!(ho5jIV%2wt<)gCif zG#?o7JQ2P7Q}J=F9)930FJs=NG$iu_rro-GW2!-BdeS9|pR{PzYObgFQux{J>AI@> z7M6OMsAr6q^MbEu=GocFju(o5$i!(7r28wz%X-WC>ZxHPw@CF1q}heU(|J5{wcW8j z5$MsfI3GQ}`gGS*@BM4P*(&1>)1jH18%LW#UtwZ*MXo)g&*zO-F8M9j7WrrU&#jCj z*bnwd^7yU_W`g-Y4t~+O7kfl6^tE*%`7ql5sQ42_Um>OVbm5C5Ot;x8txU%pMLU*) z#?|8(t4tSC8!2ab1P#?pVbMm_em@z=zDh3{DHn2@tUaHwuoFR^cS-Nfi3Dk+#dBu@ z(_)nkFcIO%0I8zZn^G40RMUJCJa!?&bUS?UYS;8dJa{knd86VU%8ccitz$2TUv+Ln z9hPzAtSS+;$m?6+)=}Sar%3h~s#R z_KC3Nkw76F$h(}npRZ!D?c{x-HZ!`K6oDjvo)GG^a=nPLIcXn@qKcH673h7jw5iKf zTX%GE^i7IV!1OqAEU2*>2mn_`0NCA3{BMp7cyO*jX&Q}B41?6VFhZ8db!fl>fmg|O zG6MFfxWo&iam`atB!qE42RrEJcXhEPsh8!wK%vi)w67?e5Gbr&s0(s>B?nHdH&bEZLC%0uJ2`e~U1GB7#QVw^l z!tQr>;UZaQbSBE&Z9M8D3cUH!DU}q=NT>k+lln#sK*CuksZ*FQ!PtAB7_sq?$3rv= z**wKKS3$f>0Emh2L_{LgB3=NfkU>=57XUaaK%&DrvV@KO&>F@Z%Gzla{5#4Zq3y^R z8Xg`RN)W;Y-zaxu6;=P`Lk4`ze%i5G$v2!&CeoJa7e%*Nc^R6|(D~vJ)hc4c&*Jsi z+mz))3*gl+e(`}B0-W;(>~j+~WNDngA2Reu&u<1|uMxz4*WM>r70m9oKZ&BAkVTi& zZi2!xj{NpE3wUJmF7G4)1SG+%5j8b*u6~p+7k<}_yCXroB(tKX(ar-l>73?gA5*V& z;3l!NF0q_DHd-Jk_RdYTXJRh-LPAIHYSV zP_kT%(Vb;O?ZiZT&!P{Yfgy^z+w4HQ%lk9Ku#;gyiCs7(^~AC{u8Fv+1D$*dhl3P+ z66TRie?Dog+@W4f-kTr&gkl<|#g zd9!Z?^dC@Gsa15N)Vjelt8g=oksCP6*m~q%aC2>=|^`m>B)SNo|}>1ts4;{Ea!NfYxhhR352^VwQuJ@>Bpdvo) z;nnl72x4a*=s*>YR{bGIPRR zh%{5!FVdty9BORfR`+jN7%R*b#ZH{b zMhOK3;0uiXDan@NZ;3J}Z3ug-{Y`kSmYA!Yk&cNFKyf*oS$P}aed{<4hfZ-YaW*$em9%^E~Z3unYSL5QhZFV#!DDK;&8v5!P z>-I?7;%;LC%T=Xa7Vfcs&JC(Q8RGj#d3pb;Zd)C#9(OgKhd^>}c9)kOGyu5huI=xa zzY8?|{k4AR;pywp#Nk^}+QfW{AhPBjWb_$rJmK^0+1bbEzs-oJ6_g)AUn^B16JA<{ zduPjCZL@`t?B(;@JFkG1Kd#?$s{BiCQ`ehauZ{_4KNLO;5nI8?8gmeN%JUC0}6 zZ*gk*B){r0p4BqhuVX@S+?*c>z(s;1rYxPGV z1^)(}i{b)lMp>B=(a)Z`E9bjl0z-R3>Lk1H7Kn~&1ht0@_=HBgJT)8lbo&`P7QN~=pKQ6(^O zR9AG-&GL)AxU~NLrabinl|XU>iJoy?Y^ZGE{S-lVYnVEExwJiN_Yh@yy=9hsWN?DR z*HyvVv1hk5@^!-z4c?`?tmtD|ihkqQJ4C0ll23z|Fg$FHY7P!~t?j z((`c2l_^K6Yv`+L&hl1oY)oxN3}Bn@0F@=5aXF=>`0voL^!Vh&Ng{yC;(ve$hKGpB ztX)7%a+hBoG)re}VRN(pT2sH(@eqX%4Ds$YRD4y?_Met$y`}Vc@`0i7{b<1=pKVIU z`jGuxEB)DInp}0A0DV7pX?u+F(vJ9mu}n*yafklHtFCi!F~^~%O#94nz;&Bs`(4PS zScuqH4596ip(b%16ELCTIHi9~F|MJQ?JLh$fwsk|JKwvE@}9rd@n;<6`_YoQu#~= z^L15+37h4|cXH6JZoklyp4gDP^{%daw(l;Q+p?Ef{?}2LB98$Kx0k7-x${@kHO;jBc!m z=3qoTwmlAZbwx*}2A~T8%q$!X0m(9tbRa(2eHJ#{{%^IE2-rbhHUmp;>aN^?fj0gs zbO$LZ`KL?>VY0>CX1G*H zsC`Isx?1%>AOOdR{!PA&mK0(>UACH~Rr# zOtimGA2dCccsy+$8MohZF9n55k53ZE?&psXnS~3RB=)0kcHjfef-mWc`5@`XYD8f> zB}(SOc$O)0pM54)po#q`3p4LNhvF#vF`CJffr*?GhW9`Qz3B}=j<%pP%T=WwX>&X+ zc`l%=NST&TWl`3`GF>h7kTb>R(Prf1h~#~);*?*9wEZkJxtE)cHu4llZ8rzAM1dRk zDA!c(IT^&4EkllpAKB(Z#e&eg~U#_PXrp@f*$PIEF$_8zs2$~ zgu*UFHl9=#!=C6Ik;DNBol`rS7Hx?yR2^Zb4Sti2ju};o#`mvMoS*GdooUy>F(Z^- zBFn!C>2NwT?>UAh?`lzFV7Sb3ben?$-Z(kLdBY#Nt_fYcgIwi3K11}NN1o7AC?@pP z{Zrm&=*yPlst7K_=R!sUL6qH1T1s*I9}71Bl@1v&SG8c5NG|*THC4x8FtjLRCf%b6 z53w|dSl&fViUm^=^B){ykN|t`l>U}ceR$Bl??zq-@-_HvU60o*E@Vpe6o`wPtgG?H zjW%C4Q5M$36ctAMhUUm_xoZvPuGWb2w)U*gHH#QC{_FCoPJh6hVvRD2aFp%JHtJL& zy6d}n1UbKL^p9Ap(;dNC$W=IM(uL>UY1<|(V5_1ZJ%Ef0Em~H_Ujm+-l=%DK(y0@; zgL~#ncy63vzkz#HDGS`C)u4~~50r|;waJR2BDL9o2SP@#HiQ;{S&s_>ND6)zfG!Mx zgOwM50SILOxL*VS8kRFmoRgtSj8qc!KMc4edYVR%*!_a$aE?-6i?Vj--+!*Zc89V? z1v!eg2K^YtdejSPIx*!3t!3!XfymUiZ~@LD&aAD2?ze(q?f1lrV&beqn{IB`(29owW?#cYUoFYZG=PYe<7uGI8Ed6VHn z$olVp>$@&r99-^mV7S-QjA}T~^3q>WvSs^Kv3PLp`4bS;vk|z9Mdy^%$FOdjOA8b|8i~YvfjQ7GQM>Sgv&vl)h*6{y;UXS=+PZj&u1N5~X?m1-IyqlIogjn$5uW<*# z-w@(u7{wm8DE);@eD)Enap3mzm~pIn`!7{H@)CTda9xDoK|OGfUQ?GNnZKH1my#jgrY`s0C)YKA3lAcpOGBajl%2 z&89clP`*YJ;VIQY*DDol=hrh=0-+(FYr%_4b<*8Wl1eS&bvblIIAMXu7-T~jUtfmD zyDBqyo!jwQ-4C6;|K|?2|2KERj(^G(GVJWR9Q>Z{|5dA?(CvTY&;OqbP$>DocmMR; z1GRDj;F*K-ThTWy|14N27%&Ugzyd1u!IGRFhlFB%j;N4ex2=~?L>^<0sG(v3sFf1? z*1YZ|_*Jr!^Q8d7Wae7R@MI7e9>K2SJ`2%xcv6|YX z-zUIL+SCtEzwe^y#VKHX((pSb9`?{K>G z0FVJbR=NQ&R#vy~;aVxAToAA);Lb!e*v`*T49NiAEdUpkH^+!%NQg?zbnlpgjR8d> zfC6bIJT&7_*iH(8@X>(Kr}fkxF`=QbBn#Pvl-`vXU}2-OGGnq+M)*8@RAO9N1kcsa z9%Z7;a22lLk_39nkg?z9FBI=WA|p@4kegAm8|$rT=^XReLRC+SxKz3LCZ){Jn57LHcH_hDep@ce$7L3qM3kGoUNV zW_Vf74({)ad8*mr?N0SsLW#T>$lZd9Epu)ym#R;Htg>>kvtXxJX5bYNV*Dx!{p$Az zXSV?LFlNqlfdkn^3Jd3?iPJDyB~zuMJ{Z-o$?Y41N^+?y1J9>$>C~A>Y1FY?R_uD! zdX*KgVvSI>L-FM%c!)u&sL~;Jp4x}vra&H;;TH=61ln5I8@``OU5cU)bpnc%E(LFi zIBFAYIahUr&c+Eh<8~_V)7xi4XxO*?SY6xhc7H0#H7>lj;tyFXMk-D#fX6Vg)gKs{ zFJ)1>)=fIj@bEjtFqs*dS=|Y3rps<}Jfld<9bJLfFdknIfq40bV7ZQRO0|t!2tA4#9U+o@K{bbaUF*H*0n&vbjWx=@v=iAOqWU|NXIw%^=t4)CjmzeuUaRIChQ z{rR(3rwr`uUJ`J5^oc5=9QO>>tX$AAZauEHvdp0idRe>nyP4yL4{?2Z4AQISTOg0O z3kO21l?^WlS-Iqg+Ro9Hf2ui`2Rr#fs&<_%rm`efL>IcmS(_whUPSb-unvkw{^IGqs+`Bx;b4v z`CR3Gb1J{kv9-PL^m^M_6YFIRY38*h{*0kvROQ(&x}G<7-Nm_az zOLJ>$M3rF&O+YA$Tl-koIdL!*RuF3{A8YO4A=tcGU zRkDI$k+?3ll8-D^@+E2U#BpFA%i#)oX?Z=&mS4S+Mqvv=kpios9b2sv7BqE56YMSv|Wz- zG-{wH4zdyO0U3B{Nx?$(Iy=i0&%beU7>;KV(8evwekh2mQW>i^%+eC%3H)`Eh(D8| z*SCl~gE*f$nr6Ul#9Z|X0Q4zm%|wU2&Y9b10x??VbUc;lSos?VQ}O2&*ZL9b&iM1c zv6Nf4+xPRQ?pBqpFu#;lr_Rm2AI~yk>zh}jXP5dw=v&zr*1+cQ?uyp&n^WSOA91#1 zj<%B}b<)(}{$)}ZuVmslah#v}_04sjHv-7oj{sQ#V<`&W2irDs5`=tHR2OeAuLhq0 zg%yFypGKTwWKUs5p1-U?-##0Z;yEDh{C1hez@kVhbuzu~{l=lQU%J{9N%3@E?|0OZ zN@_$!n#)o5upc42Kz2 zWI5dytUU)|YgJuVRhJuw1e_as%-^=P55eUDr!MfXCo#kB-*rdTkikfX<9I^V;+f+~ zu>kQU(V487ni{d2FKwR72O2`V{Qso5m>NPA@0zUZ{)18J5{f)A3jKA>TpzXE$2P_< z*T37g6U5&6Y_-vQoGton?)QJmc_4B0JukujLEe6`J^gUgOKb97;t5GpYn@XRzX7zV$)so($d;pHV}^!CeBd-9(B>- zlm?PI&&{(1WSpGhfWncUJLkSaaY`E0e)AKz#x7XFo~Bq9066n1$K*OxAw^%&RrgQ_ zgT9hm)!Hcm(EOi8k_iH+AwU-vb&qWk;(!~KW9H+lfVIuP0)j?5%iHu)bpg?^Mn}1 zPjSFyU^%CqXLZThsFnam7f58~o%JjfW|D=T_e` ze=bE45GObfbXFL_-TaDj-usmh@F=>V6$HxpUF+*fgxMhtb)M5$?F=|GlypOA#}SPQ z!70UU6~KO|0=XiUoSZK$V$R#NuH(i^ea$&}`0-S@*OyXC=3FQP5=}3WM^_T1qx|xI z_VCSty_2b|ydK<*R-?up1c`QG`|xpZVc!jyq}*@l}(HIX-QMQ60|1`47hp~VODbmesBE>C4tdwWvU-1eN< zsj`8SC z7a1$TgIB&A7WAcOr*8%o#*!4MOQ@u%wYXlRM|Yl7Dv* zMQr79i^2_c3a^x8(K6qV{)S7svNaX7u}mSE76qQt?_f(P)A6{YaodH0-~M%<-M;0D zCS6ujg)k0nqXiS?BB1Xo3rn;BK`@F1R67*RB*DJOsC zGEybTmMR!X6%DK^!r=@;mWdgRY7D^+N5o^Qk_qmSR;THme2Z;*B!5LDEzkBUO>p=p zvN$n3TSi&zjp+}#2i}Qq>eX9#F#yx6j1EdlAHFdywc{gWM7ewhjo@!Og(9b4yA^SC z3{z&rf9Ne9<}}&R<2Hc}n9CTDlszP0sjK0YnP`c!RphGhqRtlui8;{Vgn&I}ywne7 zT!`mb`cZ5>Z19!WThTMB)zq}8^pWDvu1K**(M**n-4JXLy&2O%UV&^+k#YqfO2ZGC zc7>ZLSX)e1_8Jq5wBiysUIO3~QHV^Q?Jnf5CP5S~LQbO=g2vU47G6RY7*4icCSv>{ zcw2F5MrMhun4z)a^gW3#J^EI%$ zvuUr6R-yY@G{k^LW-_c5R{-KG|GEwxVicI&G7Zd#N*?Gzki542SsNcao#|#u!XIUJ zB^Pwb+_jRL$+x*dL#LGV-^+3sM=s8dPN|zm`3!bRCx{ zR4RRNhpbwzTPa0BdB=p=k}Z@a5Cp}`I}d^iz1s?jz;J5b7{YUcUMfPNO?fW|OfHNV zw(g%&0-qr!QyF3m!-XyZ2IyIGpK0i)2du|Ca?TJ8h#M4aSJ~!aLAZKW<<-4#qI&#` zRBXAWwKSF>K0cdT0H1BivJ$4Z`_=A~P9BJB#k}UNaGm zg$$4synK6G6XXHwj0Y%o4-70sjA}ehojv&Mr&KXSTFU;fHy)L_l9u<)FW%H{ZdWSu zY*8#&dwc>WUCLoDu?;v(YNI?M)dX9(RCe@zWKnoUPEPn&U;W_OKYrw0LN4W{Sl?$X zH{&U}Vuj#f`9U>|@WU77&lQeYMs#JRN@;Jh31mOd`$bT0$I#UOEe^#Mdd0VLegoeE!U9u=){5mek zznu>qISBQZ8;9<|QSN@LftqM~&J(&yDqLha#Rq<4sHcwu27YGSU^EO&@kJd9v3k3q zBU|&!#m{Z_;g#pL%!c|^=jymJA>wb@#5Bs$C(fPuBnq`X%K%BS-8%!1WbC)j>Z7@w z6K=e31^wO`YnObocA-U}H1vo29OUHevxqpscKJ|Ab%iZ#Y1KA)GnttIiO_+Xa6VBD z4MaEXZ$C4cT>Vi^ zB`nnlEs58vJXzc+B{Lhik+P5tAObW{_p8}z+Lna1@bu~AuqKG?ugP^k?^Q`!XZD0c z($dd~w0}#Qu$uo|zMe-31Cvk5$RlPW2vC;9H(X>QKUGbWv|n|z^17cY6DrL_tL&V= z9tIG_=iE^|8xBD$GAB6rzTOOQfBz_oN<%9#`o8(xe)(S)u4h|%)91^jQeSBw7h<)XyZC&B$MX^R{z>WW z(tMe25YBg&Fqtf!y7M3v+2zI--)TGYhk=6Cg$?!vFE;WPa!KpuZI>N4AG+&@Z6+k2 zlZKj|WG{G*-#lWw6LE+*Nf|1n<-Dff>We3hLMmdN{Y_thG}AqY#Qe(6++TNGAqjBCT%9w&YyEO7Z{b zVufW^@Tc9ctq)|v>*Yu8=M?Sqw9}f{m-x@Bw{KxyvY66u)B8n!>zxBT{ZluSVqPM? z`U~Ic|5{5Q;lhOd6NHN9BTB^1ncZ*it!AJT|DyE<}XRoyv<%T%0I*8oIXwn^%2nocYyk52rux5KzFuz zgc591YrnB(d$Xejh0u=f0yiQA<zR4BqjR9_AR8F83Hf*7Qk5oHj+M^hDHBLSk7sFdVsuz;Z4^Uj)1)2|`t9-sik zt2qC&Is48Yhf4?=OiMC9WSUZNimgxO%M$ z8R!DA#yOUA20tqGiluX29?xA?S#f6KO6# z=L$~Hq@llUcjjkzySCin>lEn0q~lN6jtb({R0Hi_ewNg4ExSYSEh7Oa2TY=(-Kle& z(CzO+IhyyhfNIr_H@wW?KSB5N{X#bM41ba4 zB!wBsK0BfBpdiiuG3-@x3y(mZamCmOC2n=-B*h7xPo*%P&&)U~oi)g)1Z)mMU`_lDzX=2%TC8nji*?cFb5%0X(CpWG3 z`=>Q;Yn&|3hMSq4?#T~dr*0}Z1KMfB77?q?2LRO-o_zgOtCuHx%}95GOP)5%!MWPu zzW{*+o094FwegGdUdvxFZsGRcGZ{JU<6y_B@|iT@^rN-vhl~x6+xBz6#T z))i%PNerEvs!Vauhgs^B0$Geva?(=nxDB^6XO;MN&6`bauPMB5A$Evp!f}`8Kf=d< z8D$i&Y0Cxd&N7&?Ku7q*(!DGX!3%C|DAS&pAaVVwnjNn27*PgPo1 z`4J8av`m;(_*Dd7N#oGS!9r6WC8`>!crCTH#g3Lm+L%Ivg{s*a6mSNntVLf# z;&qq{^oiy|OOnjlc4mJ0ohMZ5WqxbWz(W+|;qBCN-)1msVb(+4SZ-~2=pJYqzs?q( z0P6h&`RQ$61X%3V1YiXaBx4)h7iBr`)9vb+Hvo;;FhK5E1rBw1uMQO z_dFld<{D2%Ls;S2j!6xl9=W(=3>st2b#p^Imh-E(N2gj1L!T$^f`e&3(wYXA2D`5X zW+FUk1}t$z^q-x&?+I$8 z%r`&oAUX+6bk}qXtmJzC-cnUXUvV4oDi@)1pJA-82vk5Y@&*_G3yv_lIr;oJ*GDS8 zt5W~gw>k^-a*FCE1gCXF;~Wdch|ymPA55!Ms!Vl+91Gq0mUfE^x$L8XziV88t#B~m z+Na^rAEb#LG~HF;gKWbrqdwDm>*%<4vqlc3y2%77{!sz@Y_8V^KK=ZMTp`=vKf4^6 zUjQ@%z$l_4tXwm*sPXrSz%LT}JfPt`SZrw?d zihj#9&6%?T0Aw^c_mM_KPdm-!=8|ugu~pb0AA{MzQS3gRoNTZ=tw{{vvu-|sXgP4X z3BEPh!v#c;d?xt-fU-b;jx~iadQrFdg4N4om z&eAo1u&mdc1iuWu2z&PWkmMx@{vauG^7!0pjneb{hpoB1`axj(*p4XYB^A8&Qp=*=AMJz9#) zNHeo~h;5O$-gQOOeo*9m*PXa?LUpLBrlzh;t^Iudask?Iq%N1TuMRJ_vdCt5j}mg337dyoTTgnN*!Z!Ne^M1N`)%yTP~{Ly z1U`O&z<=U@aS-HOddgj{T5??^4!8=XK7^~UouS5eQp%cA6D5KYLE*d&YPq%oJ+#s! zsmKbrL=+_1rqsa5p-I*eplJdy6I0y&BIntFl3H~$$Tz8*Wo!P+o-cU=7ijolISDDO zK>`WYh8{Pe3UQ0gG@q3@KdqRI{cTk0<;dxk6&5=uNy+af?rp@JZQvPe05P--#57J_ zU!H*tBEQfIW|V>~twCIDD&#>KY&ve?Ha|;77|TR%a1GAB!+W#QAO(s~7Wc!s^&uK} zMh(mIa(qjT*$aB2LZ%yZg~hZ6t^KODLtRh_!h?c~?Fcd{LW7Q`6W3w`$V}Iuhwwac z8C;)y+8XK1R2> z=DyJ`4$Rgi%wR7VNvTU5RcdDMq=z9B!GO8Q60(wRupQJ|);7!-P~+5rF>2=;_97=y zP5nV0X8mi_&^|V#ud-NHl-AN!7PyNC0J~{6sD-J?W5SM~*+bL4kr2bC!T_T6`q+P@ z<<~N#XiuE%I#VmD>7ZXJAkjk0LF~2Zv{004$qSQ`sLq2!=zN2*LQ3CNj*hvWeo8Hs zaggSJ<2#7u8+_nIkVJzgFx@ARBtw&Ivq^;%XA*xSFbyY$?~jmjW!noUig%kPrT8&8 z$+s7m;WA1xDq^ufcThG8kGVG`0!Uc@T$3;NiH;^?0jJ{q5LQ(Ji8(dtUWJqsF;MC? z7+QjeyZ7TA>3xHUyB&Y;C=fX2@CvNAI+>Hw|9D)bPvqLF<3ZussIq(}l|m=`*n3TD zd5dx|pQ1#k_9+5_QsKW>{sCoh*xW?~)d>^t+_Dbo8fA(Q;{SLnMYrko zIYdgP@BJ{DdY;TLj1hHuc{z!7OW&LN__$5MJ$mMH&g3$G7MP==0cCKS@Hl1BC~Uz` zWjRCf*=IDGPZfr5LkgB?Y@1{=s0QsV&wI2FwMsRWrEP0%bgNYjgJIf@`k{9J$2$Kf zz2q$Y2hYsNMYa+Hqm)?qWHI`Nf2%Y0Z9wJT2&nMj9Masd_gQZe-W0nvV;0sUKE(xz z4bAa%uv+yST11Tv#d6-Tb6l}RDrx8ZMNltYq0bMb3s)x|7GYIxx?q6fYnz;>iNw|Q zH#-7K_aRt3jo?_SR?3Yaa93wi6&IS=9BPMxoH3BfQf-%ZCm2eP z8$!vPzz0`CJb_{mRpv$~cqX$PWgSYu@o9p6PhWn+OfOhK!G7Q1f)yIHJ=6U}ZG=uB zOeo{uBsq4mwl<75Xr>#n0b9znqU<@e^JMOz8v1bk7%Hzc6%e(7-b?oP)bT3gj_}DZ zgUHvfZp%9lZAmILI{}X+l*)lVPk_*K5S7E~@6Ceq<>lPkFUt?Ca4Gb`6Al`yt)ht- z-f#YjqmHT1R^j!Z*}HkVcrc9p-6zN1XD-t0IZslRcN{9%*-9j6vNk?MG}+?f z`ls@PBdK3|o{XbZ$~n_Jv!QMk$8qb6u${?q)A?o@-iN_qubAtmx7N~=RxOSAkc@~m zXTv_ai<_tm0WbUUTEYcWBU6oqG5t)KBR75Q#jT$;hIqpsk{aUQMvxE9KkMWXF{0V% z9vmMDSzu|QW#s;v2uPNGPvQ>q7e4^o*;i$basy!E%#Xuf4{ZzQ-e6xq4pntnSW6eC zFW}L9NTjQ(rPm%BPo0LE)xoZ zL&JJXkt)|$!UT}0yYSf;=TgYbjJ(T{O5`n~OV*-AAr}i&Xnm4S$(tvq^b-y0d!mjn z_z1y@euGRyM@DytS3aIe@1ILI+xN4`Vm6UPmEZlB)}xq||KvH)H5WfWe{HH+;EC|k z;)#S#`Lf@C$xP5)z=F8TT8W&(LjlECzjG%u0=bM6m>oNvDz}{$l{ZjmuD==GOP=QKYw|5BM-L>}A%SJ;@^QRCL*~i1jkbePXSN-Y_35EXPTMI`R za|lUDO1cb6y7hspvIx1Ii#j~ED0lhD{>L3dR-76NH3RxTp2P9K-xp;2kLN&u?6JV_ zKk5EI_{K-{{|+1f-zPQycX0JTrX&A9K_+=AC)!@pswyEQ0tpz&Aw`f#8k4L|IY=to-za}2=A?h4qw5yNM14iZE(Y}v}!QBkWbRMj*SDyMt zdadn4Y3QvV2I!&I_VJdi{76KYOs$T8VSQyt8|?kRv^Vd2{_=jdd%dsgSp|30Pe~`p zLLAzAWBUwQ1?M*l@m(8HXK}2HV840VF=J!SxSrF{dS!2TYbSG7gz($X)=ok}$Uf_d zo{sGfj-ONmwFAi;G=jUt(0K(N$~OWp;?O?>LQ3IT9(Z|t1@*K3plla8fA+K_Pa3r2 z_jCjRBgOL7U@28Lkz}DIq(Y-%aiajCQE=q2!3h+&P{2?X*iaR6{7$gn)sD}|+f4Vy z)D`z*^JDi?HNy7G=w^Zcon$+Z+y;BdGNF_r3`X^!%H6Z;iF_#T}jFWlh0Ri z^HAe|ha>-1%m9i!?$#_=E|zO%x-5DSGYr78bWMFwjURDcH^KkFJu`LBmUhWvdCg{F za}YQ4j%0MqQS7xI{`Be>4Fd9%LOiIoI{+YVQMh_&@v^3&ON zIG}7A|JZCvxnlZix4DO4rkUM7JiYLFxtO@axnoT(XQ?(UhGBh0Q6_=1i5WfpMU356 zBIw60Ry%a}C^;~VV~#~a8zp5M-y8ATLTHx$$g>pRHxx5{DZiBGoU}ux2g_dzMSQG@ zh69#1HtrqArJ;G|^*a<@0zF<1k>SQx&?4^M_gPe%2|J<^^Zj2+!K^H}YTqaX#10GI zPPEATSo(%TkqREdmjl0JoN^h@CMg?8B%jr~nrnK_m67DIJFtcEq){$QEJM-gD#GYjXn9g`ai+=@ym~y}o}F?(#4TnZusVk}GE1 zlwenos({rWzj!^gT!;K|FFO zA{D>K*Tk(++x6{c|K#OImjwk0&E!qS8Z)Du>5n#r5WWs0j4fI}?-v*()* z?Nwu3E#XamLtkqR z;uCy@h54sYr5y`RIz3C%%7Q8iV{V9Md$2*R}ycFMl^*FZSHFG z>P^9=_mn7?ukVL{w4)4PAEI<8sBm$(G%lobM%f`A6Q^GNW;Bc7U^q|%Ay5JJ?>d2_ zgyEOs0L=G-wrSR_PQ}4SQ_GkyR%cBcQWWJ%A7*q1vCKrwGz1F5J$!e3?_+JujSii8 zGoxfz)z@)xW&EoO;9&7kVwcqk=Dn9-JIECH_5cGTkm8K$q#S_eQM)PVPq+U=?-U3A zC|da2nX@|9Q&ak(bzK=WCj5J%hxL5akRm_Mi|3a-os0LCc_`7yS0bf%!5_ST2)%Y-6cS`02peOD;_9&Cy4-QsNo!D$nv9KTsoYV&DMcUcxAeKpbq@b@~%D&$wT292I zYHw7K%b*$+*Pt%EYZxy0IFHNv;Lpt{vD*yQ*YwboANSTwA?964@VWL*FN8hPz?7$ zEKWgy#$HWmA9ck-)@SuyMNcDYL`C7-5pINNGEIg<9M5GmIy z(0(XY%vwc3M`B>VI@(PzH2*bq_fTM=Kx{^Zn^& z;y5lH2qp-M>>FhxWvEK-9#dl5m!=$K1)0dUg05(m-I!zRhs+xe@+k6MI5aYGx(7-i zssd~v)D+f&A`?=nOC;$5UR#@pia$It^{8!d}< zheC$t2}4-6`b{I3cFx}K3AirQI$bOl0e8lO zf$Bmo-=BN;i~P&OQT8c^dO{Qh38pm_UZ!^BMeuwS$FW$&A!49NO*nISI2)Qm5D04B zd|!+kP>6}fHC*@^3!t$ZVJLsLsq&3!LT@36FF}?Wz=U{E)9kLH0F8$9_y%7f`K3_! z3W;z3tJA@V5D3^O25Xd81lOjhf-3bMN(q3@^aCdTlTwq{D(W2F7>$vCD(&k1TZY?= zEkhyL>Lu;ue=Q#Jx9y&5zQ>cYw_@I4?r$Qf*6Rr@6fJcZ*L+#_228}#i)|2h`W`Zb zEE{nC0ow5-L4hd;(j$w%OCteJ;R{o1VSNkdji2Uh<7zVlq$#6hZI>$5o^NQT!G$2^ zfAf@a%guiiw*D#iQykrG-+Z zr6!)sjZ*6z`CPYy7&1J{r?G zw002nkEgp++x$4ioOCZY!$#lbm)^HioKow6Z%vnpkgF@L<~|T@$C|FyRCvsrL_b0Qpa5BdH?kp^e)cQiB^-!TFg+}eZ%43 z`TJkeSjZ`ckkw?xqOOw@8-NPL<*qylf&z%rroxFr5^9>h=aSG>R}=#<);XUqNufC^ zFu&-(nn;!7{GIoGotZcOATIn8avQ6n`i2`YG$&(xf+l{aOZKiQRTwDCP4YEN494;5lK1B=cUs{a+v#~5nL67Wg_CvK z79P8avApr>!k)ET>Gkg%W%TI&et*8x8(^{hX7b!+#NKs7Mzn*E5_*5Rt?`82d^7!~ zvv;$#Qhl@L6Z(K5;y2F;x9&n^yJPZ;UQYi{AdD)MG{v-Q)8#zS-GpDrr~6JDWr8lw z-6UkAb*Pw(-d7EHgRmUNAAd`04pgAS9jCR7PP&!#t$h|Pa(Bn6tgJMfKZ6W+EVVX1 z-G++6`3oRFo~^U9Gr5w*$UJ=q>apSBp(GpNQ(L0mzTJ=NruDNk(7(2}jzk;krNty! zfg~HQ_!?Hpk`l^&bNO0Ha3mCvxwvCR{lGG&DJxNSaU*=nOeW(OWEzlOT-i;XdVbn7 z9=5PBJh%Ya=1WdbcQEkr@jpGzCln^Q+FS{I;BfzUWnp-+*FcDxg_M-cE}}9@>}*f^ z`ue(|qw@6cliWvW4FQP(we$5n4alFvoI|wrX$*%Z#8s;UB``Xq|D#3GgeIFu-;-vY zo;J+%yKFbG*ug(ZMcoUh8knlk#xIDpn&@!o1TEW=C~HHznsuRrRTAXE(9(L^Gt#k) zux5mc0+38}t9$crG2r|>Vr9A8Sm`G1 z^!|Rk(Q@qd%fI5~3V9-~_~Jyx?v@va_nn#Z``C>So`sLrjkouWfG59}j~VdImT|zZ zrCyk{+mBLFuI5OV46YX+ogkh6zxb7X{eR+DEJXMEL~P)kNEY-phhy7$x8OK_PeMn> zJ1IL=WA8Fo7F6)Nm`fAl^}lAqgppd~V*S$AF{z$r!9G1*!z>xKt1M#X?b|I}4oq4I%Hi{L+x?$G|B7pZud;J$L32oBn9)8X#0dzMf9jrB`3W`G$<#+6PJ@&O ztUq!{clh(a5|tXX;`7wctb#`ar>B!C6Q-p!tgPX~_GbGB{&R~t16XE4`9r&k zHUPqWb0|O-EID*6RH~RgA};)HJOwnuw(#iotY3c3s-WL)%_id}f+EyoTo=M7M)zZ2 z711m5O}(?x8WcJ%sX3D5w0!6yv2&HC3A8yM#r&-ng@7y|K3g@C3ha-TlvNAD<9`Uh z*?N+Lx7DqZv3yssa%~g^No*)AInw&bCTE%7Hp`0)cc$qr2Q81UkE=0$J#@6Zto%`} z?3wLaD^URZY5Yad%7WCvUP0Z>@|dQ^Vr!$A#)uuEh6t(-V5nQ^UQfm@J5iA$w;^g* z;@&4HUFlruV3+sOL&Hgct~b7<_Lst?S_3rclo5QjF{zH*zM8_AMs3H*vSHi2?b5Kh z&8zv(u0_^pTdz91Yg*|FYJ^{#yN$fDoyE>rZNUKNs+=8kc!~psrLeSPgm2@$zC2ay z_Ln8$!BDK@(Q+`lWp#u79=Z;B{8WskB8OgLz43~4iG{_gRKHH#p{gG#GceQCo&L84 zqzPUiAMUHb}kCZ$e%(To3brYHlYa7T2iWkk!J z0bj=B&^d4tt&PNhnS-@65!v8Iel;uE{(qFQ!z3=pzlAiLxTzmeqk?_DyheFyXBo(RC(8*QqCeX1N3hKE&r z_-gVMLk;W-Y;_tNGo5oPtr;eC5ejZ;0pL@$7vI0qTz*9OyYXtKIMXAiBNjdd=aT1Z z1cdscm6_ZZa4q;(;8f1`ljkmEKtuz}Ecie3o|4`hWo39eU*+c6 z2n#-@{B#*bYft;&`C{~?C%1BjSnILX@BR&9TMvvEx*K~OV$byMAhp_@_nTx8_Ejdk zt};oAikz0EodtX(HLlgMApyy1kA)#>5zIbv>|Pzf=px8KJdyx?Hya>C)RzxPQqf8O zgYCi&>Mp2PZ@jM;8&Vn%ihA&{L|c#Ae7xLf%rQ}bu;33p&0C!vQ=W}^g8dH;eDwPr zqqqoDd?MVAWxEJJ9|JND?i(Y+<3CYJNG}m)mvJbMO9^zzN`BsG7S2)fP#ImL6`sx? z{DkpVC!ejc$iNB9)>a>cfY3*w9mY1~N(oE@zfWTST}%mx%Z#$Wp!Hwxdv9b9w7Gnh zp-c%x>u+BA?f3VI#@pxg_F7zJ6a(vm=W|oP0-q^O9t#Prgxd$>HiZPFxZAWkAg3pXTr*Paw}(L`V(`?RcSNSQ z(1YBU!b}fH0=Lb7{x6gnzA5Yn+NAb8Xvkl)djTKQ^NF=N^v&PGRj0~7R{H)4u|Kdv zbi%!h=iH)wAyJvef%8IN_#Voh*h2ZS=u_$GZSRwRN^WT)Kf{G9do#XW{};r=D*YY}ad-H|Q#{%Z3js z^DD;1U266+bYTWG46aiBYAvq|1JW?Hu((Fl?b|0)o=(W!*k)M%N^W!44`m*PA}@#* zJICcszO4UqK0n;~%6jGq9wfWgRB{6nXBUtY36C8U`6dczFQHVL=pIl@pq#c;Hl>DMr9@gZD| zT}~_Ipq+; z#+yTHJp~zfJUoBp>mItDCxV z?r)+{B@Strl3Iu6M(x$%F3+hAPrxDc_EzqPy`e#kPy0x|3Q+rOL#%dURQsr088gC=N_Ml9a8^&^zM;0+@Ox2@@OF~;^|7Pkefu1>p{ z+Ve(Qs9U+qEFLp{PcJLme_!pK6(eUd9zA~2)hsqX0mF>+9WEth#h{gOzystj-N@oL~SWq=1OdMxTLE|Sga z4CUPwYRfXIC)tf~reKlPLFGnCT9~hFz~LG|^JYb?Hi_ zOFWx-ZC`BJ)0f$fcz$iK$jEatjmV5WtI=J_uHMO&RiaHu{d)YgoYI9VAiAophb;L# zkz^Zv8Db|FQ2C?bTVINE!AY|jeF=L@{!eY+lC;$DUn%mKYc}0eHf!|<=5>P&T+Ox& z73G$b1}q#7`ug0}!5S8*&DJJhmd&Cgc)~(X#IKB$s9vb`xBqMZesk__`^`*e_rrOdb)IpZ>VQp!?ttAnd(Hw|k3(-M_QM;EMNk6UShi`skIj z6#q1ok#PDkERW4T1mJXFqd_FFjz^6{oDBYw9y!IIZt^M~HzfA*a+49)1-ho;k^z|P zBf+mCu#v_IlcosW{KsbfQ7euv2NPQ5GUU(RbEo-KbY6pZFnV52uJ6FK{$3r@|AAQ) zLWbTh?tlI=p3u%{FnmW7BDFa&-4St{$#on*FJ+C02=JUv;Ss z-z>cMd>oJeO~_?5<^G$tE*wbJY(jH(&wb$hXGhgU$}rzS(f#Kwr>(x)3a4!oif`hA zlioeTn(nVFvp&kQKYLU1dj&Cij^I|rj5TA#|z^<=IX<~J4s{t zpXzy!vtV|#jhx?Wx*d`qV`-Ff+>SE^Vl^QWVBC`&q4J;r9SGX=bCKcJWB zlqbm0y=8n5>ZM~ZG9hyx9$}RZ2b^3q21nMEpNyrFN~jzW$)km&+)ltPD>@wQM?Vy& zkux_+>~EdC_)|2X0p5S+`pbzZZ+D>5n0Sz4ts)>6(ARfupQu15ULNDc>69lk|tFXaLjHVIaV?wUK#{|;d#Uth3>$?=*z65 zr~R<{85V?t1K`6rN7w~W1nt?d(Lwj)k^(r$$2eQ&paYw5no67QHVtVa^Gc4coDbKWal&+G{{E%krPYIVGuplx*=e$n>p2#ZYq^P9?iC=M+t zy$`_(eS^q_)_z3LT&Ld+t#@yo=dze8$c^=xbgjv^i+k6@p=Llv6A%mWl;2qm;|hJ( zv5yqq7)DaJb&!jHhc3AOmoEbLal)aInGLaxaJx!Djz!LWs_6!jxb1%X&26Mf8;eV{ z?4myED7;&yG)De(D2BU&5Te8;`lj)HxOe8`x%R=#L|i3`EqMqa3Q{8DE&@g;7+S}- zNT8TAByt1g1eRMn7V2uIkJp$HZ8<>6j=$)Ei|yy2x+@E!c|j`FA+4?iYP1!-5=vQV zWo9M6!WXM?=MlvV(53vzExlS%K=M!~JEsCgdZI+?#D3zIIIgU6?8oh3V}l$&iV^5h z9JWPPmOgf5CwZL^M4aBmvkwH+ye-q$Eh94t55`0F1DyL482EEkd?0As!Iq@%VsUYx zzbv@~B9KCwKvkx2vfjbg78X-|?SheX>}L-kI_!uQj|_|AhnyO+CRczWf~flz zkI0OeR#!(?ZHpL;*cV2&PgmC`4*#>eUgjMQQ;dK@+Qasw5|i!RKGtGo2@T+RtJ$IK*;MRFwK!_G?+HzXWRp*eGE1tpc=oC1dc74fF8C!7Dq?kRpMP(a6 z6_QprA!i#@-RTu#ce!f6Lm~lW=%bE=!tPN1x;d11P@trYR=ZN|dzA9@&|7}I?XDR> z4rP8vvFOY-6U-=(O8A(H@vAQ~{+P2okZReSE0SsRrUL~v%ET~nS+6|B?;8_+%6fYy z|1gG5hXUGt^zM8}jBuJqeOmxV3313Yu;#721pQ7ZP2k#Xq*2I#!=CwrRe9}pJ!B5k zb^D1r|B1oXw+v8O!R$@6@}i6rA^P%}M;*rAHDIrsjW+^4pmi~c&-wFLpMVd3eZ}I! zB)w`v5&Jb>skt+xq@l4}>x3*WB}yKJtjV=hAR5t6!PsrC!gPd&e$E zm0Ia(CGH|%3WSAC42nMk5lcqo!~|I)gOT3KYVo&t0A~}AwEobObwvAEk`tRj^w|fj z8c4B^k5X9bJ1g95sn`G+6_&Y8KdTikfIwY{D$oPbS{t1Yk*N<808hN@8Pu2^)Cce$ zFYkkILxqCSwj@DujsRy^448y{I!r7;s01Bsyewwm%j(NkFY&Exdei`$J}I%bwhf8A z!{IdL8Zu1>s5DM5@L~_~s1V?fd{vvJr1T9X`r$r3=WC#zV}dAg1*R!N4P^8E+vaWb zl~>tKt?}5d*)aGj<)mSJ{kq4PH_#U0eCW~PTRp#gpq(}@JmbETfWihVrx8}Pz9(U= zF+VZbLE0Sx0S{HAY6^h!rnB^=c?ObW6j>5BICEH1GLPL3o0&Y;QYSIVW7_L6#y6WT zvpckC-2hNl)Rp>jZys`IUmrTpG$vJo-AeX|mfDb)&F(KtAgK;tH+Bt?L-8m`JQ#kZ zj_p6x6CGWdFhg%Op<;w}yz|hzT^ZI34qh&?&{HSGHFtJ&oSESm$*1{rBsX2q%QuRA zSN_b8sI}~7wA827wRGj!@A_Ib$|2AWm1flBh6y5H9h6Y)9k+U5-ohu|`NM)&`;K+oK{BwS!Df0j@TUCvX<=6Suq zW}?olXYMYL*J>uV=`Z|@87~t-4)$4oe8zDg``wQpP;)H0^dgAGW0`Yw&~8=2hTcXq z@&wTFgz@oO=&I*NwO%IKqr3UJ-X|-t90$cLU~PM{*n~u@ph%iIOID5>N&@?L87&V) zw-aLrnq7?uWJaZVE2&i1Tl&ES?t#f1)@`+2xZVhBAMkW>izEzZcUp^blJB07!8cQk zUPX{ubTwKEM9HPIun2d)xR~ZYF0%pIdLfLcZc?&atln^w` zd8Pcs7umX-;V~c++R1_-_?^8jHE}OkZa&xAziIYFd_MB0Uc{tC{=x3_-U@YnFZsN3 ze~SLresO&*bn|)_EOdhena{S>cEIfTAG~}F*{Edc!;!`8A z#_0Vf3rvV~D`b0WyAh?Q;qaHljiVOevhkI$bX8J)rh=v$q@`CPgAIQp1j>C{QQ>@r z8Eo>5ds^CT-BI_7+7-3bqndWw*w02vE=~ls=nx~?YeUD5KC9G!W}?MfP1k`X*bA2- z{?2C_{u!YSYVc_0pD{t8Om|nDQ89TKU1%x5Y@X?o4e<>POU8O-b%xUtEU6PO`78b0IUmn%$7HedD*$PVo@}({qTRkNS7zOfqag z!~ew&vFhOpoBjoa-$glzJf@wwj}AoprtJFHk_BXRV-K6|&HUia&2752qK8ONQ&-1= zlHHwY(w-e(V0`8xkv9cG>=(ATInd8Kjtd4iH~-q6%1}?&l;S&kc&K>GXJ<@ouOSLp z{k`Dx>0kI!wpAxi9z;$FLDu5w0a1|P5+JlH60`(tAr@ruSUiXV8&fLivAGre=?Os{ zY&+9^byF#S^)k{+xEU%tM2do76kd?j?$wWTELYRCRu|`XB%X_=ZokZ7~yx(s~ zWL-?wTw%Gv0`GD6JMjY+>YuJB*AcmkB+iA?^@9=$U45EG6`Dj7Xpsh7&cTLg7-^jP z_UvPG*y)15KyJu_Y=_Ub-z?OiNO8+++SXb3yOk7M#GCJ4x0cYspUgfjgq`)K$NX-h zf#)Gu`%x^+76LqbbiZus#EufmdF(AH7phwnAKxGLLKYsz#MA{9K?fn0Iz~#|f2-{q z;wMC(-H)h-L$}7WUxW5#)^5|WnRbrUCQC~_OT%PnvUez@Ax7*h`4A5Z+{sUl5C&nn zGwLpLc>5Iui+(2hYJlfTQqY~nb(2Y@RE_`q`|EqRYRT~VQFDrufZhBtKSv0pi7@s? zN+Bj}vOvLM!f5|6;nU@cMYTQ@QwdAz!KfBQM>J$J??TPZ9d!-$|DrwXWf9rI%PKpHLHsSVK7Sbccmo{ zPo$hg7$a@1Q|{4W1L9JaPLac<4)jBYze!06cFneL&NOrYtTCs{tzi8Ts^x&;ii=bS zE{C}R{&hxF233O+TzM+vGdh2#TXQw;$eNG|W~+P6MFSSm`9_}sA3g1vOO7JB3MFZ? zXbWu>nn#fvX1!81T+P;bhKKGDK*NifGW7ncG}G(I)zQeC}B*)ykz?G73VT5g6q`@ zxN7F=uc`Jr8=X^B(wc4D9Q0-k6?GLE@NEX4$XFEWt{6)O$_iQV@sd5iTIyI+R@7i= zs*PN`kg76NROMozTKy3csRaE(1sUZ zrp{1NnWAApQ^o!{xrm`A1Ju@RTX$}2SehMgU-8o)Np_LUOoDYaQVS^6$U$g!~pY0H<^Ywog$d_rB?=A1BKHrqj6A632h z?RqHBN-gx&dSD&$dc&aD=~rA!9toYgtAqa2yM+^ElpnPkjI|1y-;{o@bg`uAy58)c zYSR5Vm1g_2fvw!*wBuJ>GzgA%%taG8ABHmYPMYR}WD02;{VRkNz$&>0cZk^a@k%(d zWbJ^|{jFJ&A1eNp3Q7vW%-|~~jXj=yABRd>nj`w%A$0e91r+pOU@^F|utVkgUVXB| zx$`eeT*o;7L^3zNd~Mp49`Aa7gNL%2Gl}1EQgmy0T-HO}jv*0Nr6I8Va_Z z=2NHUH^i!xcJH>BfMhpO%w}!gk}M@}KOMv&&H|l>NWFhe{}x<^1j%9a(E8uR@`e6HFb)osfZ{(jjKYPXvq{C$WtD`@7cOxyJMo%9fv#A!zi=mc@Fh`^lt z)YX1=9`skt^DX3bGLPWI!PIP$rEY?!X)aV!a^?&S=>}_MthH=x585a4NRkL-0T%Ypnv(NZ+kEV@kxz7Aj=d$gP9&QDM;-pe*|c zP`2z4V4Z9kmXlS#%RE>%R|7mVr=7lH$JU+WVA^Ib{~87m+38^fDuWhR2{3|cLb7Qs zrcX^sbU&{Lt|ELj{l1zBu+v)(TpSu&0uUi1ddPaoIwQH-+s8!kkO4YHDoAUtE_SU@ zL@N3R%r<=olf+Tt4G>ILF@E{Pl6~cSVc{YmhZ?Z9$8&~vp6~bY@WUja&-VlsX|6PU+PzExZ*?|oItxBrR<{X-U(BG>&@6$w zNM}`A&!JB&Ex||UNUleJSM^{{Xq_fRdPwb@>@$E&QdZd-1@l1aT*FbkyE?amlln=P~`9rl~h_B&_r!Eu-P)fBBbJ|mApNwYw`1jG% z6-@`gcXD7he9bz)1h#mn4jPS^4;O1S=}qCiv?Sq0{dU}FE-wBsPgOD&5!@a#hLUeE zrXN?NaaD-wGeL*e2No+O>Pl66eN=#l3e`ZltEkY5ZE%tDL;Y)_a{v^pwT%UNB5hsHY4WcY#B!uQiLZ@1LH5 zc^uytht&4@ZnM%qFe3jE#J4ESB~wh%dK?;wO- zNUGHz0@Fu~-amYMM+VMU(9VNT){E>4_vqn-LP`FXY%1MM?-H+CKX&C5+Y({E*!gxT zA8r=?RnvI;M*Fs^4ZuheJ4%qOGPI#5^8%pWef(Ay@lXCl)$r+kCSdG9|Ko!viWHs3 z4xw=8tHH|0hk4*5_0QJlIVI~A-l;6hER&88SK~zBN8%3l9+!14P!^B6iY{~(D$3!5 z+dRqyJAW(*NS482MHHYK|9FO)QTyx5wTe_ik!AuVhyV!cgt;|{sz%Jv`!V$~~eTxvF!Q9e|&A*~kg;;x&#w&U(YIX7i3p}`g<#M_sW#e~z_Q+yL<##4` zCzyw@upAV%V(|Zif;oRmTyfInh7XRUa1=^!yIzjFZw;`7guTNI&q zbBA#RknrSpDv*Z~&M2znPuQ%mHXbaP%=O_N0kfEM)n0r6{#@RiSk}o`+w@5^0Vw~K zK}?>>K+9fQe1HHnQDNmjsrC|be<*S!Dk>yIOj+1CWC~~yWU_zRiLe3TVRc_W0);U< z;3{Vbump@hZ3Jj_a{eJ!EF^}_V~LRt!bF6Vw?Q@KzV3$pAjUd@P}p) zkfNV)&KgKQ&aj?XlAKdqfxq0u_lQ1R%i4Lhq00N|dak4zhZ|r9-Q>EOb`|MbAv!+j z6j%Ihv1%}}FBsCd=%)&eR(7o0Z&*~-D?C<>Y4rO@@3~Jd&sQ3=Ehz(v6;JR6otUZ3 zq3fvEnbOi|eb)UBY>JERB*wIJF+n8mJlNzMIt$5J5n~2$Yp|bL!S`t$I7Bfr!Oi<1t_NMLg_<6V{Xo>c@0=F@$Z^}%vbn!YY(Vm$45nurhugSBj# zP6OQOT14S!<+iQzE%nk{&GY(2an-Op$NX>kJ~s$zCyKP%?GMj&@aA})y|mu#uYoa5r%ROmmF#pei!opqv)3YFnv`OCw&Z zC=Ht+T~bL;OMp#p?uYlgK?Z3hp><|*aCbG-08eEH+_U&FFOP$*$`%+_^4i*$b5@W zZJ%zVbNM}3akfakdX!SXonO>@4!_?;E4(gx^}O%cK?$0y)4byF2>)c4L! z|K|4ikUK*q25gr#czV|aNrVQL$G@@uG{g*iN$_To5T$6shsp4wG+%gqJh@-0-SpkI zJ725sCxTi$XKKZ5^m@F!Z=vn+eSFnX-w$J$*21x{jd>r;K5Z3K5tB_S%Y*6-3;;ll z=t?nf(*lHMSHt5w6XH6SMX(FZRgXjQ6Gx&pwQHm+%@hkZUe7r;l zJ$%WaTllx{5drNgA9V;5@uRNCL^Ld(`I#ilnJrCQVLOpVI_?`ZmGns>3T=|Snd2OP`jtz8U%6~q2oZ>}4iz!B zLU80U<@S)dau!qZA6@Jy3gYw?7Eh4Ibb`e~0m-{XfWej!Ng%J6{-w~^6p89>u?=C2 zDg2KvmUh2cSO9A8=JBN&dywkpg~&<``X3>I5GzN&AOfE*!HP})Tq$-4^(K5IT!M)V~5IY9T)vnUf$w{PHCpo;Ux*&+nm*O8dA{oy;==qK%H@C-_ zRyX2{e?JFS^H8U(yafGA{+_R8BG`(Pk_cvZ{l1tKi{*DA30(3xuPw|WW!0>uVMv4N zI?3VZDJT8UhZY((K$VUP83|T|oLm`+N*W0ePXVaR1qpuhxpZGydE`fZU7e}vS{v!w zY=w{2nea}tZc_Q-CE4+ddTGdt~4o%EV;%IQgP~jKNQJ#z$U2;_QxYtduD+^zM0^jt{HPJP;@9wQNP4B52KP%Q2bF9X5Tk1kjt5INkqh#fVJRW<<=wrTT_?PovlQ=3(218{sngFs+1Ky|3mrTFw%j6&Yf6{hCIsBgmDZ77!~mYK&UQ*M-0KbV&S;CPMd>i@hq?Zpkx+Mf)Lv1oCqtIi)E#u ziFxI%S8$6UvE9IIjc|S9*O@+>xx&>?28q?Vh6BTlS+0vE^Dg%)x$%E#d6q#Ozgs_h zHY|4NnaqQBn#-Cz|1RC&;LTsQpEtXup$a6DSp3eo>c#@tr54Nqf7-o;v zL4w2e;#VQQU}+0$?za8h#OEDA??*gQuOX!uwd8PDBfyzbOdj5~q5YJ!hEuo8#tZ!U zT9icF0ZZ0Um1;%!gjVu9>}o@oCUnaWm(||QmsQ6fA-@!wG9VJzPx?prsodne?&uFj zFD#)$EzULCLB2c({FttDGUu!6{l?e#a}piqCwA*#d#w+S*A{80lL+ZkqOvoxf2;XR z+KDfg_?GjDlx7ZOSyB=mv$mXQ7QI-A=kLO3iHpK9Y=B#K0p^Y7l{I|*IkUCxIVBCO z*9Cm|j=;P7`Xn`KWD-}BZ!0yYaMy}uK;Ois?&2=io05V z3M9T0m&Ihdg^dHaT&F=2MJlMUQ(HcjEU)|2XtzG{)9QVW3)gSb+P;B21 zUnm8X=Dj%vM?xk>-{vfGto3zg1%cwQoJPhmEG0bel8CMbZs)RvcZzN(=_?2cCGRU(HYwlx#zb$O zZ2}+M;uv+G4h%+Y6CX2CAo)^-Gy+#=0dE%OPxvX_;!BQUq7KAAfVtGbF!MP;D(G*9 z#3YGBAFQscKrY&s0hRLWfGy8}Jzmr=ktA?=d1>T%tzqG;aBy&;JkD3jdJ?~#3igwJ zl_6x-OVE9HQti(NrZv1>LZC&A_U1(vcBov0+-U-_KnB!y?-U$!^QCI z!Wl@SY?XwD4Lk=x0t^xFZ4I&jy9F~MmT+Rk-pi3Wo`_3iQCOtkdgJynFlEpy;~W^4 zV|cdm+78oL9RI{{;!`zYMj;*M$QS*ZrfjUO=DVE|I3+_Tb{LCs{Tdrfj3^>vu|1B9 z=Q!+Uvxp?(kTonIRm0;vsh`QKBH}pQ-cDk?7#c=Lb~X-%FI{HLfViwc%{(!|eEbn6 z1B3NH!!A)yx?Y0_uJAg(Hoo4ofpcR8o<~55Z^Z9aJ_Es}o0!FZ`?)$KsM+f8Jg-e3 z;`G%mRDMZ>M^i6v?273=<9lxyFO*Hz%(CW}J;G$u*AETl@mR7~DRlO%uz)rSz9jmN zs9lF3@wVk`d3R*o!LWL0dcF&4&)hiVPj1j7{ultka=T8(Mpz^vO=tIT(U2wpv|`>f zA`0jW5CCYK6k(1lF&A~2k-Vs3#UBSjAxNtL(8QEQjzLI*1?z=dddh(*!(F{prLE`( zvKP9aJG?0T$b;PZzEC$v0*K$uG)xkWVUMuvY6a6Yk!e4jO4 zESYAYl}|6BInXEAA9gm~CiTmGejISL8=DCwQk*L^4inqvv()Wj{#4eX4Ebb3vs>$= zF8kdNj(A*t=j2R7l1#P=vqwSs<$Guo4zugE+T&bauV6>19KZFWcJJ3X^C^3C;v)(U zvx&?{BnbqkMIH!0H;enTZWap%(@I0Xx!VL%u8_?z7MESStxsEnndikn{>+*2KW1i= z&G5XpVN93VHtW_pe-}$^R;sB0XZQ*$4R%E1eatA!cH+bqpCa<`XiTFoiEev*n0YpP zTzM^`+0D+M`O%KMYEuC*D}5Ysyft zp1+Q=dj@ez|!-T@=dmp;hs6f*7W(@3-k}krc zez5toAaP48>+gk^g0D!*ML2S*83$_gR#dsn8w%KFva3zZKb~n%?U!gZ$$Rb+MIBqS zD-M59Ln|;d?sx^|{GdnZ`iYeL5zikx-efh#BREKRfehM2dIX(FZ*%s859XzY2fLe? zb;4i3r;lNZ67+qrU@1HTY{ zJ8!jJyR`Lkb#Ad-3$Gm9FY4lNJdU+oYf*GK)^!WeTK{u@p| zvBmbatyJ#cvR-`N5bsM~WvHtHp7yo=15`Cg(xVi~(Z}MA ze3TUfcIWV~T&sr=)v)aE+cMuD=|3<&ijH?Te#n6z)bU28bCmpjSU$ZW(HfCMG>fUy zq&NM#;dIs8o!Dg8j9R4GeMLpdX2L~wh*wJ92{PC8RH~g+;Ub+izehC#?BK5y%*0;@ z6L-2&ToMxKZH1(|99n%f1N?5f<)r%+!gJ(w`%^29X=8KAE?=omB!{T3J)%+=e>nTT zLqY?VMwEP??k^b)cAXBq9#0oGH+y{F4-Fu)&ykCbnA@5kZr98nOYPg;+ocANHQr69 zt$MUQ9w8U}SPo0f4%V9$zR&--|5U+3WTL+=KBI)rMHe>FL!iL;t-rMv#MJkP1yVuu zBwI~P*vfkMwpnzyYT;B!I3wT>q31k>YnvCg?2%+g|MN|)LZ$t|plxy1e~kZ0H}BSE zY#D@JcwW{{Y`NOoAN?J#8xy*$BQZW30BpOt2UGFXetiCn4Z%+xaTBDoL~}nqwYG-c z523j>AJq?Ef{p2vA0&*3>*NzSYBfY4( z$-%K_6Ni3GPc2|@F%*Krud5!wB}X(A^-PWiJuC% zXe}da!?P9kA4AJ+yq}GGGi{sK>=P3uqi@`(5^$%RQL&Ahy!f%pgeHeKjYJb=iNX+W zYkVA+o*oyUUM|VV$bS4F)ue?iy4&W9qKQl0J^J3>=1VX&HC6Hgc>_vHt9BAJ?Y|nT z>3D=Rimv}|qUNR&SUmNoUM{hw6SPC3~=cwIhh$w2i7{o&g!3 z{C#EoS1Ei}`t+C3_Q7luT=C|f)f7g=lF3?CD*rz^p9u^3;A%&OfI1`qa1^58j5p$G zg`_pppt-%l=$*w*Z2KR=RG%>^bjPHT?kI1S4(Y`XIg}951Roo;4cjWEnrkNYlL2l( z?p+E(@w~v09k&CKb91Fp9w_=S*6d2T+1kyxC6VV2CU2?82`J>ry@us1(#38Km#aIP zA*|D4EsP{@*;0i*uCCigdvM>IB=C|-fkeB(w{j)d<@?Cyu6tR|7-aKvLS9l z%8L6J@;_nf|N0P@!qD=s%!%CjU;%JEL@`8Jc}J);vwXna4D{IZ zncu4!__lL-0JB~arpaTFu1K#qHZl|@5)yPgBETKi9a@@#9QZnKeQCWO+f|MX;r0D7 z78$q%;QCy4J))9%VD!DPtJBjE?>i>2dS;l8&`e7!kWir~4HSp($=6A-=_HVOUCCwL znlEgoH2?a6W=jJDNM@Q}veXLF-Bzn^y75S<$`7uW(ql(>)^^k-6LPgBm=B+G49iR| z2f@Hq!Y0L`jA_PBu|f}76D>kj2);oi;3)Z6Tm1Z_dzb#M1(qdtw)FL`F~4wvo=59v zPqj))S}lR)0Fkdiq^2|{dFsDo)yk|Vy?W^kA|5B+CAQwh@-WUurN3TnlTh9$0dQ4X zZTiv^T6-&JsJboOI~_|yNof+))vrZ)YUhy}k=sqD<=UsTtZx(2PpMtDxL6YiO2 z3#lc~4Hkza=~ORRv=k0AZ&S<;T#Kh$86~#X$HUfJT3_7m(pTJSIm;93+ik`ZCb?{@ z@}brmgmgR0H=8(=bBZQQslVPT|7wOvUyO5=JJu(yo0qQ8_5Y2}U(64lvSdwEIachy z;czw8?>9a;zMO6H6Q-L_H=lb!~bB;ifR8kl|5DfHz>xy81J*E_DFg2WmY>S!l~o5@4lvg2eZ6P}O_D z^S;&llN0Kgz>SO4n82y`dm_$b>{f&(NXb5fDdlw{Sov~0FFz-uQOCs08WOx1&Oz)IbF=soUYP2gA^080^usStZ z=65DoPnss|bRE7M%v2m2H|~=p#LBSiv02(}6xY?USy@i3e7{EAU3KnUbVwnSga*;IArq3N@!hPA_sA?NLcbWM( zceoP84WaQ3B76S%_~lc%x%;~&T6f{*;!nrpKV?=UTMXhVP2hg*OX7E;5~0w z4MOUhK04!N3&eWYo3`w#jsbf}XcPSON~!0o)M6tqA{Y33g$>>(PCvW~3M0(Dd72WC zLT26z2n^He*nz5)Tmh)@EJ=|ll|!}z%><*FH3gof%ogxSK6T}|GvPf{Jl)cGO-)U3P30SI&$bGbOu6awmWO} z8-XP6<~~(H%mI)TfZQcaS3x4a$TqF<(WaA95PnJIruAI=-rBJ}-UyA(R>hxaLNL#VKpLw?)Gll&^h znCg|`tDaqCS4Y6*JGkdjc2|Hl>SKxR|0s5DkPY;I^&iC!39>E<(EI#<9N@r}5I$WW zHiKF|M&YXTT4?#$W>J4ES$gJwQ6D%xpUG#bf;zd zwwhgGuiXDY4EbXarOR$K0}w`cB2Rf3nKVa6I6RL}llsQgc&7oS+>QU1I`17G(KP)1 z)d8PMwvcVg2e33!4IE0HJHB7+_LfXoqatfx3jNA^4yYi*WB?(Q2mUM%%>pV@iQ<8z z!BCjUfZkUmax6ql1SBv}dMffO`FASm9hfXkw7h#(`+=k^KC!Vs8zd>{0;8Ks*i(b?DcmR=;`*m`Z zb5~5G^0l3u+^~e%l!|B`WA-pP=n(x8U6=muKqVv^Z<&fIuGuAvK+3;qt}r%Tr`UKs z%*AV=Lplva!E@W3Ui^#BFA9DLJ|(fJF~K7wKC{NH;aG6`v@1?Ovqoz$UMe%Qe*WjE z2mC5lEfjP*N}L?AJLwd9_i1-xZ&jI6g>S?r>3dM2|r6WQFn;h!X7w zxF;S}7M>y^A~nmg;RIrFlD82B^ zvn|Zrxh$)4o4eFFzb0>{=jvsuKgzxzociqw+cw8HUw+Siaw|DN3Sd!Zd+pGv>4bsP zO|@7-g?`w$aNxGiSZsZ;w!42qvCi1lTR=7jPk3%@h7OokTD70AuXgnsaNO*4=(m;c zcO4@?r7C_UOP$CK^N#d511X0w>_VF=jG`G)IZI4Z^TH>wveV>PVCULW@WPvIAd{V#+h?o; zd4%>G%r_MF0mtN!$)M*@tWV1^%*j#nJ`;bNEsG+4YFT#NfKXIbk5R&!%#ydN|5nip!_o-ubkL#!&1A_rI^4N@SbyUK1B0v zo{qr#t*t|dmSZct92|gE^Wa3#4A7oOhxW+K+|z-5T%VMPQ=B|If^k@d>}nFd zWT7mNsy7oTSFIUJ(GizH7AT6O2iuHVO~igyOHg2Co{gK;_0UGHh*KS^vlLA$9*oX( zTiS0;Nl*n$?L9=azc_R^nl;cY&VE-yRcbfMl&mNCK5;UmX0FHCk|c0eX*Vjwo5UA# zR&B4;+msj8GV9#^^Lb~cv9+1?8|V00^bG7om`bnp_NVc?#+cL9#dkZCg%i3LE-p$v z>R_{^xO``(jXLb49hkzuR^-_Yvu|ownkEzpA^+2o%P-wm%<3JH2iG7c6E&M-X#?i= zGhX)mP|D)=qhQ}@3{Tkej6HU(+C=s9f80!MrP6f?N&MCr+PSyXvU@0;^r~Qwfr{bd zZ;wC0x+eENTGQ(APU@vla7=F7yy{2pt3kc(+A*bKL1_1)R46AWC%f_&#&46Uspy5R6=`O@4h}~$fg~)=_%g(;?mfdlb?C{@%l{pt+7PGCo^NeUCC)n zT}&X<+~G->zN13`0cq!W^gSAo72)9Mv3Xh^mqG3;?1Qi|?7#AhrUpyjv*w4Xi_e{o zr_@Y7Os>=3%3@Pt-LyBcNBixjhQ7VF&oV#hNygFGWqYAj>BZku?_2II^%oIJ1NxLc z!?7A3A^S}XQhN^spbPZQVd>2g+)y9K;;Wef>%(tloFDS0+y?+Lp(7}u57Jf^aefkD z!KW&D1a^E2hl5dQV^W`-#cx-a<4tqCsJktR$RKmL+4F|u&9#Apl9g3B_jbe&oS)o( z`NGo%HOH8<*wn+0Xe~bGtIM1;LYEGaOcDcBJ!jq#rffBs3;~{=Q`LKK(>2w7JLy;` zi9(CbzP@S?D|Y4m0|cT$zz@{kUTV=}5M5dtME~nA$r||#6R+YOvADR5E#Z|^>_`kd zhzCdjLg!iQ5P7%>5YCW?P6N=oLRa|BVUIYDMGCZY?NE5yZEY;?IBVh0KnkIgI;^UB zMuTTihB*HI_4-n7eSvC`2*h>2d5)K?=t^Rf_(W-fIX7nyiS+hD?Fy1d+F1S+h_$&3 z9sisw4pj`|SkHH7L_wv^=BO{? z@L{g^()~;QsrKZ|yECBdez8!|YF+Ww9+u=aQtA$U!=ik;JwdU@Rj7@oL`jR$&pM0V)?&};+(dEX6u7AD65 zXD^F6Xn|%CcyYvz!eKh}O$osSrWU=a7g{}^@#CW_H&;U$fP6wm=kqUgXe!qm0bEem z?MkAd+O2K~$bz_JzX5Uv#xFTG7=A7atd@z8M<&n-LF{KV?2z7DT(jwER=| zKjQM(f|a#@}@ye!JWzH8``iY+1z=dhTDx*5ZCBq1_7mJ-U0ZmY0{5f?oxD*GlP> z4ZGR*pso;lPDAxw~jSc>x175d_A%_E$o=-I5Q@7O;K&C=Du9tfbC*BVK$JuksX zFaeWK>HOO`*NZ zqdw2Y%@y*#7J_8&>`&!Tk43ZS<;4bFg{BI5>*#f-5<1w_T4HaIE`d_hayN+1a%uP9 zAXmzzxCcR&bd_qT*#_2hS1AtP6kJ)*qUit&!4H1$dNxmu(KJ?qJvolAcRt4+++UYu zGRofLsqeMYmp?RtINvp4F%1-uyeU*^@!5;i?$7f3V!e(7KNR%<*Sqc$55kn)JP7uG zyi)-+SfXJ@)ahZ1a{1D)1eN@OuvxM%PZTUjaF? zjsevJRd((_RT=C!01U2-KNSEUfW0*;JOltld{soX5WyIwszf5kkwiumA=?U~_(1X} zhgHT>5l`!b%QJE5g+_!a;DXP~%PR;{!36eU1F7ULAHP{=tbvwDx*@BJwQbK9C-{Z1 z4@U{(J}fOAF)b}E9*#;B>+9FAkQ`f&wCp|+IwK=5`80ge)M>Ua^1$BSw5(qd^I52< z7}J7XVi;;P@*DDlR3_m#H1geG0uIDDb}ND}hEvEO;zQE2P6uqnJ}iLCW_!jpDhwhm z@3brs-%v8X*T~r?AT{LtXC4v&lY5i_(U&irsC!ET@P=xKvzE^*_DTe|q+Gg*6%VPn zpw5g3Bapp{JsRm9r4|26_ydgYiveB* zKS7b=8~)QfNsVERv+65E_++@XwX+-vsldP>EVm*(Vq#)FJ#9>fUk;FR!t(essHdlw zrKd+UB3$76n%R3TGNjasUr!rGz#Fw!#y|7~ih!X~#3}H-LfZ$NDRP;aDINE03um^j zPjtz{Z9eoG?#N0PBx`&s=aHeN{w{-GBtwr~uT6QD)j=< zn2yuKzZQbIM!q#y$_MjXF}G|nt8Y8kH#z#5`KQL5@nRe+NPwdU+RAueQvrv;s8+g~ z+W(4AC>URk^CcX2@8J$jn#`?74VHOhG9FfW!R3f{I96);D0G1GpAaXa zDnbWYVbW}A+jMFpXB_+w9Z}B_meS3b1(Pl2Ivw7XkNMkmsea&?O zgrcweRV#BVkp@#lud#@wxpL^c?9IQifGLB@;m!uQTUfZobMFk8@0~g=JYp2>%V_o) zb*`&U)3wO+02Y7kjYQcbc(Ys2YV&+f>&D#^I8)~NircTOh#F2D>rPvojm6CkgReS| z)^=`=h4#>h9UM7MtBKcqpS;HYKAe><+@vz=>Z(D~NOd}{W&-AejWl_yTz`+fWGuQl z&25Dk>$=rILW*dvE1FT9at}A4tp1eKxh9z&raZ?*07p#MA$zH0%3*7J6VbXP&u$wu|Ap zyPM2nP@R#zUwHj>rG6dL3RsiQE?buCZmoSVJlbt&gQsq2f+%D9&B}o?%}oDbQ&=?{ckn%-(h+zKxR7y9n7S-o(w(g0<5e{6+WmT{arW>??0DIw6dEf)Ja;0H>v?#NK{i4$f}@Va-i_KMY4B ziI_rs<`IkfcH<_w442qW8OJJP-?)Epu83l!#+b*Lcds&P7@*f+>OVN)gGSK3zA=1k zT76^YonkRmRX0}2hgXk7YCLcp_RlT*SOQL+@=#-?YC$C&q#citsC3=meHs^!EQb!d z)i=QZL2E`VVqweU+vd{oj;P&D|G8UALjX3DxTlhJCwZR{dH0&FtxdsA?zD_IpIJd% zC8Ir&r%cwJ`#%D^OmZu0WoLJ!&eaeCT;KivOU}QcU%RcOcLPUG!q;XXzS`13-z|LIw` zhK~7r+^HwGfBG4`lkabw8y3l(N$`r$OeOE-fM#g4*?%=1Gs>_=*wFKNkfOjhV8;aOA%q*kwa5sh8?2$ zoV@tF_5SGD}lJaFX7p zV!UE(G%jgu&}cTCkRLLETJ%SpWc6vO9rzw_waOLC0Vhg`BNQKbx5^ld(=ubR94YSk z_eEnRyfW>KCn44N9^oRg#b#S=cp|yX5v5URTiQaSI`_w=tY)Wvtoc-=xWk@D@X)DV zuNEPjex#U}NJ}343WDUl1=?y*Y)oWk2_>d4Zh zDyrnGBb>^F=2coBSJ0+KNBbX1+38eVY&}#Tn6AQ;FLI5{2ppXDCp){poOQ6cgTP)r zy@`*mOPDfY4FTg8>qg4smwyCDFr3NbuwXq799Wv0>ERs9pzIkr$EZui=;TGu|K z4dV+=h2+dlej|6XZJM5~MpISvmAQKpLvRn^T8tZyC67Z8u#7SC6Q1#7pvxn#Q~%_h&JM~x-?zmfWk zW?3d-nov``5%?gxr(oC^Rn>r3Wg^S5VDyF4Mal!8%+Hpl)z8jG@R?WX71s2kCfS0# z0km@TW!3AH5jw}XX_cR7N=*M5_MiHDwmviZWV<>2`##D!AAHbO!YJEcyW7IefbYWB z%wd~*Hqp{n7kH?Je zR8MZ~d>ma6*7LZrY62)B2zg`vLe@)u5JGg2XPAFX9EiTXu5YT-h-su&Av7ZLfI4C&m8 z1Fh!hM4Qp#Co2=GV>I05xD>L=wHmCZDWxr)jZun=euhxIlcP$t*ft|zI7}Sg zxN2?SbFvy?h&sz5Q_?nodV1No>Ur4M<7V4^fG>I1S8QP2TUn^UE%SKS*w|Q=p(AC9 z2DytX%_YyC5I0w@w{Umr>-wzcL8q0D-NK*7gsIts3B|-cF9zgOVll?`0SikG|-`Sfvef`I64dqvi=#*1W{`0thQWR%5y@ zS6dI`O;y(7>-uu$M9emWi-&%i;v$>YI-5!(&*{1G%=RBO6j~n(U!%tlKZaZo%HQWI))Vx5``#RKpij2hJYm^zuDHkS`U2Jh@)9UUZ8N6bFMA!U;1HC!~I5#5|o2lq$!%mOW4X z{5ayDcRh1qAZ1dG9-Vh*vJu%R>~@|vC6H8UBH?qr5Mm>0)~l`tm+;Aw=FZm4)CA+* z9iFSwZNRFe5a>x~ebrKj2k>muLex{()S*H<2i2F$zGCgZ-qGR%cFq9vMd0=(2?Esw zt03p9yFl=Q3InZ+N77%SOW>MRFR1mJ)992s*1Y1AH-<1MGaNkCbnZa#iDGkc+0hKz zF=0@R>V(jsV>t%R&E@oq1!OBEATkOalBVC5R!-n8`0c{G7=T=!S0h`dnw-7>09=0n zz|I3Xbn0b57I&W?AJiHeKJO9W6G6X!dExz_Wbh6c{HD=B&XD5p7L4iu+g_gkXo_)K z-mann1Q0xeKj8dD%RhQ`phIN*e$vNHIwq#0ma8SmusZLoB#h?`xG;|W6`_h5rT!An zWCiYe`>W0%9yM!*GoK)B@)Y4KzEW4DkeOszPxF?Jqvj0ENHqKSc3fYom<01lJ`H9v zkspF4)tmGq71;ap*_6$hBZ=B1>+f)1y(H7!fFK?!yA+937>Py@akkno^iT zkx%t4nSdNG_x*NOvwABNW{`$FTPaYONPr&Ad>KtBu-OQ>4IIrF<)pDm7R9Zl^7FZg z+Z@ldCVz%GuXBNSL z&vS(UKHR+pcXWVX>qL8Ixa-eTgjN{>DLqgZhcwnK;9mnksGGZsYl*5K6(=B08dp7` z2;rml_Q1aF&tS5N@2}h2=jwN)ISAkJ-rm)q+ zg_Z~F2xBNs5-6zU!-=rMj-T(IZ+8-xSU+-wi>Iy+)YvD3tl0W<^0igcpk-lbEEkCGxlu-KbqE&PQ9KMTN5L2%^uG|FIOu21a4E1s}d2Z(H- zzb0JlLn$_X3nStPN4~4)W6{g&V}2Xnpe}>?@w10{DmKfk)#AxxOQ85<8kR|d477f< zP3spKssIT90GcTpS|lndjgF319VuXso(7qU956jr3#+dD)g)R3K%Uox^ZUzAK|vHQ zR=^%EE-_XGl?8EpRRw1NU^@3@{6s`3JUrn1vQK;z9|s2K|LQaLJsu9;m486EW*i#} zgV?c=k&$Ucz(4tPY*uVuX`m=Ph-MgJFb)MC!#RCeGwyq0-G>;e45d>RumG)<2WUoR zT)%^|A4$l}Jlw7uc`H>szYsi&gH@gXiO2eEg)89Pvi9SJ69# zAwxR8Cowu=uDL$B(n$z$6D2Q!TA9>CCm24+ABKYJMY$#C2!l9$CIU5qJ9(#z5wzi@w#?{A-#8LW#Kq=OF{##|8jneT)$&4h@+StNS?pp|)2T9`ucw7x? zazNR}`C*gAzbbJi(0`i(WY^6qCKMQC>Y$VVrkh1rhY^*0M|LzAm)HJd^r;De*=$>V zYzahBv^E1EoiFHB-nS(&x-+%OW8r>j&@OIJvU0}_?y$KT{c4nvQN_vWmahYc#OQ!J z7qoc#oSwWmiOG=BSTN1wN@3fYn8{++3!^ies-D>VzHjRzJqudUB5z<{<00!{f@Hu}&LHSdWCdw8hydB%l9fW8Mian();o9I+iTnCEVQyf~ z*rClC!+OBpbZ+LUmwKMEKal}Gi6TxiU%ek7gGznGIHmJ7_&_zK-RJ@Rie=<&Alq*S z|2P{^*VSEm2S4wR+1WVlbv*riH~V|@v8llRWHx@YdusREo?MJF@!Jo4^?Wqu&u&7IEL+h^*mO*m`Cz=cC6VTH+k5fBW0q8owPMZVox{`pli396 zK$y}6L2OOW;n#`Om8NuIq$leA^a&sE_!YSIQ*Ng>#;uvd4tx)&a{|MV@T&dVm@C|j zTs;+|0i|_MfJ*}8SQzlpfZdmVj{cFVHFA?C@wQiwTdxHgA19z!noS9eApNxc<0|i% z>m3m&im46$1ikr;2$X=v45ENT^s7z;7YR5HE6*ks)62=K)p(J;CFGV4zo}yX6~zo9 zhn$sFg-t5DE(jAP#M=8Fj7S*_IpguZ$82zK5Pq1g4GS~>%ZGH{$ApE&9{4cf^_`V+ zD?vtB_@OA%=7>h;ple&`vFq|z+SL&B?yyO<=Ei*m3iKzM%=KI<%{E&L0KjYEzt_9- zt`n4;2U=wqr_FM-dBYhWpfH&>F+89wg&9la1gH{3Aemg4wNMiWEOKpt05P^sr3Lu) zx)wWrMDl2mjk4SD1SEdS6yhVYCuN&$HxYnq+u>%jVH2bIV6##OB*|@ezg(bSbgS|G z&SV;atw0|wfYwhcclC)&>)J8fNh&eiZm^j4dbtf+#);}iUv7v>1WK=j@n?V>GQP}< zTkxugNhmkepPk8&J5c+GOTp>%9xNxH7=7$(@%gWj=Mm(8ZHDE#Ip z$c0o!(=01V@Q*Sr&I2317C@=?^j_7(C$CwB;q)0Jg?DB-@e^j0=A!cs>q@hd+TQBh z_6trws~!dEa)3mYBR=6lJi_C-&V5svTnG|o!(z4>s94B=e16T*+CLN>>c&)XV>|Fq z)hI@*^8XRxfDn((|LQ*?95Q5OK$4E<0au=c{$Qn5_{s>`Vf_EEddPpo$pJUDIkDDI ze7fm))4+P(AiavNQ9o6*FX}|?j6nEp`2IFPi!MasDcs*z)Cpny|7qR-nG8( zx6a!6)ZTl}o|$v@e)joLn!KdS!3pj}L`g^NQ(t@z=2uup<87s2EYa}|4z|qrb{{WK zSt19uJ55gs*@&g?P2WM;SQT7w$jVM(KtxEvGoq!Ekng%Wp(GT;Z7=z4zv)p{^5Q+Z zZ`G|&Y`@nv;I||bMvMj=^U58fyR4f^@|LJgI}dMC*hU;(baa2o*b z2|F75>l4#l(syWy)Mp(P8BbUPG2SVov67-C-V;C-kL|e&FapCK&9tEeusofCmrkT%lL}fk<n4hA#WWvhnFSIOsAvk8{!mJbt!ukDfTl8A>nw8WY z(T@s5FapvYf}CXV`0fv78zOn=ycL?{j)W2eQA2f{Txm?0{lLjfWoVXKm}h_@S<;_a z?V8wIM$TfhTqZ|loW^f7LU}lT?=pupXmja8cWw8U{3?r4L4#@Oh9O!Otn!=@?WyyA z=riswzWfFs5(ZV0pP*TNi^0&4)m()sWY<$-{@Ltm)MdNVBsC;J3 zOkf+<4tz*+;ZNhw#kk{{RmN1@I){I<-rQ?aq z)%t^OIgpR1^ZwF$edS}@<9?SuWe%Fczz615Ojdh>(cwR%1Qg>xJyguI&OfX`mkKVu zAEcN@v_iSi&aC(eMBJMVwnf-m^(M}cGNl>$Rx+TW%(yfeF1^6#ZSy-4z0mj~_e$p8 zR%H%mNWlxR6{AJs=pxn%65RGn*GSD8O$b9nHW$N;jX&BOKW2n6`ZAQckNk^j@i>^* zY|K&U4_1Ug+5^VTpnR(Yfu}4Os8*gyW=Z(Pwn^;YVC00Jyl6b&IrishyQFhgWj*4_ z$o-k&B}Q1Y)|S)cM5qstU!eW;$K!weyQ|4G^ctGD;UmYMJxmjf-NOuc>dbkp$Vrm> z5gkRofL4W*M%%3H|!2IG%x>equTeoKFj*)zMP%>z$a z(gT5r+~L}ty>1eEr~VPL21eSy3{--LH5W8;`0UxSU`^>IIx`O@ts6%6eViqo8D;?% z;!7cLw)q+wdwmn&X`~T2aTw9^=KKt`d7$MSwucIUech%$bqhcQcqI8-JPn&hmHv~3 zqYfqpa{K?Pyw3SI-GQ%}IHQ-hz&Us^?*-WcFgs!{6d2~>k18w`)H#|jk}r)SimMucfrBIAxJRM2{}1A zNr;|0ZVc-VvzfwPn8L2Sh9|XBsiwsZ*AVy8Ypn&!a0&p zrE>z#%#Kx}9H`jxHLnLvfkjj76^zn0Fmu zzQy~AgXu)q^+w_~I8FHwFWcTld5B~%U2woUD-+L%1Day8Tko8{%e=$yh)YOSEcMG% z9gEMFq)W@him}Ou1IE|n6SLG*ru-+SIBL@AVe#(L|LVl%*Z6@wvm-&wk4bsAoG9&} z1AODYNSL9 z?e%0H(g}B+J)Z5Cv8lvGGnnMR<>LcZ5)C5367iCfHiBU)qATrQ>`PA(RG=X+n!Lts zaRNTt$2&~wx#cI)`vQlgVoskE19^g$K1_OY!Yuez1g1C@D1qz!X!KgDx(N@Pb=dFv zQgJx%QP&|>cNW&*>c&vILmcM1)*AZPwoPfXY&sBmab^|f!w*aKAs_*p;xsy3Xt3#( zp`j`AHsfwcFy{T-8hI1bjFZtrwBPs8(GsbGJ`kHwHQkf_3~zeF6qOrmB9p0()mnnn zOGLtalE-AE@-RpKp@}BCV1{H~2zFbXM7De~0v){%u87NOq`=A%WSbT>Es6nZ@{?rz zsji#;blIz&UchW@=w85%r-&X3TPR zC-@Wpj2fRqvDJr5BEqPFIh*2HNca>40ui^edQJ^V47aje{@*j0-wVr^=_Jp)>!CrC z&)AZ%6tbbgi}y^}%PpZrY;|H9)EB0&E}&$DKU$(B-#>lW3oamf|5dWnHjRqt#j!i| zxdt`6r_jPdrd+LF7HdTtTbN;L%a4@q8o{Xw#_&VYNIRJu)&|iDbrYz*7)i)}SQLn` zLj05UI`sBLv&`oaPiLaR{o8KzhlOp&WElR4piG>mRc!Yn+u=O7M$mD{{XWdihzI_r zN2FU5iR}5a->g>-Y-hzCQ_D7^a`0uwJ-O^>JAlhF=+FV#lt#$AT1)L{64Xdq(diBsr@3<~V7(nTw^Q5t&=ocXQQjoOX2P9ldX7f7?oRHV@~t|1yO#GfwTvycFCK)r z-}DH6W~(Ms?SI*C+(CY)pq`yCrb9CQ;dp!(gvn-F-o81~9EO(CsW$f*JNW(1D!Nnd?AiTh1FFktuIRw=y0B5(VvAs;U%wPXzD za%ZQ?*8Y@f!)C|Yvo~1RY&wGuogy<)FTB*B?UiZyC~Xjhsv7(PE@RV?uW3%FUOTwd zQPx~~pE0p;jPWY=%={3czlwD6zmUfVZ=%rLiB^WLQczIvFeq*ZMuMWS2~v7 zDwu6EbGI2LDal29+)Oip%T1_DnJWqQss(GOsW|5rV6_YAVRLgeQy1e)3PgCQDBFyk z*lCeF;_gs_GeL?C-lTg&CM$mhYkVI|8Wqv<%-sdL2PzV;BF6TgBA9TeiCMD zc*CC2zU&K^+TLYjPfH{?5CjP<3Rqw|JHtB-Rs?3ZGp$LZKm{`{R>Qch2UY~2@alt| zMLP0ANI1`Smw3YWVh!hH0bv{h)%d**90HJl#WN5-uK-Pi)wMgT8xtDE+)6dhO8qj|GF8OW{oCM8}DcqYD8j*NVU%z3Z?I zDX9P1Cg^=#*j9W{$bBWI&X>B1L~@cZ0h1KCh{MZE4>Nv^cI1VziG$9nu2oc?E#Hn& zyYEe3zyOj*z|!Ye-6VKYbsvBf@UrW}9|2jA01ps{8{2y~Gz0?=TkwGADNJ|@h7+h% zc;4S^#osJI&;Xg7oSYape!?86gqC?cIRVqf;KUN$qBl0yUW)E;$&689{y-bvTOaQw zS?y+g>gMLANQ)Dn?he0d2$|3?zmpylflCk6%X~L}83P>7n~`0FhC?sePz#o<_2ER@ z-qu3jgGN!h92?y$%LUH##Rdfb3r1^z`i7`VFD<-WvTR)p@K8b7ha$Ahy~#<)_MPO9 zh61{G5G#X}cROt;wQg?Sr?lZ4?@E?0KG0_A!z%NKa&YGiN&uzXXc_HV_a-N7vBrtG z(IuhqmlJNBnBbh(D7@17y2HE>qrwtTsB#Th@b!0FyV*B=E=iPj6h3kX>YFH_!izgkVI_F4L-u^rWrOIQKorTR&M#*HTXkE;I}7Tw%zZ_jK8 z>YFvFX&o0s3vfu0co`T0K&@3 zaw~qIz(O!>;BVvpi-gEOv{(~057e}A5P%!HxRz=>j0PU%LN!KT+p;Q7I~v7Q4!#2y zL$$KLZ3HP<6@^InAdQIiM74;7i0Jl^$Zj7^;=bp>$;rva#(HsWx-5@G>ZvmDcW0tP{8WZDo?E53x>4O0+aOf0=f<(Pftc=$tI?}6AMt6U~IFA2i?M@ z=#s_xqWWG8>@2ZKxzTRRY>IEUpP_lx{0ehd9Er?bb=tM0xDJKgZQ zpWWS@@kcF+pJ}$B3Xpx2brw3>rm3RbOwp-iWnDS%j$GBrI|UV;=u{{~BSm6O@);!5 zoR6McEIixo8thMo7I?ypzB@cnPdKNgas9*sM!rp!lH^=2P%ffz6_JQ%eO9PC{*v{H z_=-4fld5(F@Twem!43qaB(G43a$`th~Y(*~Z4AHb6ILzedP;2A{v<5gsG-`FO z=3MM9SH4{YI58pvd|z>b%11nqCnp#(_0$4CFz07Taj);M*3R&%oW`zdp;w^MCJtH! zkEmVMZL~tDrKjvf(+4s4-xup_I@fCy>nwx2-f&aizY(l?r;oFCY*0S23f+G(h4jC> zt2;Bk3_u_dOC9c555dnWAKv2a{4z&9I;1`dNxHj3;=JZb>N`G8PKFpwy#T~oK@G~M zY}(Vf3h(YJ61<0RZ(02Onz@dT=A~u)?(FY=`8-ky&vC$4aKBuCQ^&LSi0Xb(QmR8S z6~R-$FI{=%Yo6r0JLok}YKf$MfT;uU?RGEp^z3wFP!75HUq9lz%=t^owL;yuP2jD$ z(!kF>0V^S&H)x`|Z`@ylP{|@bR3#piROy%H(?9f0;X`_^E_4F_lsg9s4Gpv*{IethTd z6X2shJ3Fgs5Hox$=_Bg{kf4B@ZCBR;{RWB|Y|hG|b9c9&AEsG^S^``-`jx{pL>mlJ zvI4hRkeH$2Y*{C2Kbdr)Kx!X$ls*(}K3uk)x&I5SmN!0(e+~ZcgKv1t?SjLEbP`)q zg|u{2WGtQ&=0QPmOA#+0aI=(g(s*t>TKLTX!xZDx(IDbEFSmw95dr@h7 zP*A3L&!dY8$&ik4ET?dwI#>Y9b*m$SPvDgO^ZWQxAthZSyrl^Yz3SzzzQk-M2rGyt_IeB0Na$1?gXd? zpoE%z0)BW&Z#bR6g+CP`d)Ee}-_Kk*H3`3}>0LWgD#>=Y8h$a5C6pmX7{yVmBN3b< zk=WaN ztwUfyn4m-8pqO5RI-{GwUgEOCPA9O?C2F-tR(?o&Qfxo{gZPTsVOXj6RLz(8E~iW| z^}hm_j+`UhvILwT+uQJ1WlLaw)PQs+iyxIR=#pnZGIqM@=)6X(g6eivfBHTbrY^Y` z)RD4~{elB^hqzVP0+JX!1UU~_Ey|}fmxuL9_<{i)y_k3n% zrl9yl5T$TVb*-4;sd2fW@Etc(e0+?f#dFEpo;yf^p#GixDql3DvFsh~>-_PR$=%My zT6k01j4*H`N-HWbGgw-7G3d{3k26!2JH3U}+n(;cs$labcCa4uA2l`q`0-sT84FvG z5t)UikY97FM8A)Tl@}WcNq-~@01E&tkLbMfAPWO$rR+B=u_VO11St@HF%$1=AD^cN zLk6Hf1vHusrQiC7hU{K_aFUM)4DMoPrUn3-%b=CC45%?5z4nnJ^LJUC9jP7~8n%hY zamRWyC~Rb)WsAQ%6Z7E|3&XlG0~MC zaeQe}a>hV2>8nXn0|nbQUUIeV_vGYvr@Y(yCpdbqN!p0@MAU^T*=m|{(OxR%oevgu zdai!NK0eI*90W~e@WL3r!a1uVAV4$`hzbqK{x>2I^`W@{(Vz6y6s6UBOMtOntm-JuZ`=Rjx3IvD zG~!igqWtU)*w-1Ul;8WedH}n!I7XdPrcj&LKyHy&A7w-Uh*!T>ov0Ad_y)UTjxV?~ zIREJKb?Q|%%lZ-gJQbtx>42CaoO8i`z93`SO0nU4N^P*a6Ktll!NwlQ8ud0gy`c+`zI z_!zwJ;_sMm_1D@9zHIc&NFm;bzZwsxR@(Krs%`h(kM-DnlQHf!=IOeU{k99+tuQDi zfqpdM3+Ijh-b>+=IF-`p=iu$mx6A|UB4&uhjKVRosU!Dn5TB_`;T|I=%)6@D^(bqn z$M8oES9STYM;O0Cs*;z(c9s0ANqfWBJ1K!Duh*Mc*Z-I=4H!gUUA-PTPO-rdi%F>; zOhdR%6{Oe1eVRIL$oKgQ&k1BPbE(+zxF*9}Z2x}ZpPkz zS_>T5C04BrK!H45%-z>(7dj3>ebZ+p2uLFlu|BKSV_8krgXF#Kw_nThMlbovJjmb1$5)?BgO($kLc8HoC9AVcn|Rrg^Y zn3`l7-A9zjNr2NA{x6sE7-#A;%XX+{kE^NayLuro&38G9PvEwJo5D7YWtN9ND%m7BRM)Sv4YzG1XJoWD@*$f!_S+M$PJiNg!Q$8f`ZB%z1UQE zczD!kGs0^TM%IrB0E1#2J44JkC@6T~VcStaHy{RiQ)J*C{!vPPj)rmX0oBIdsF5|) zEiWh!E1j4)cgD;`K!iRha2Qwg3`_iToOJ_A#;4k%CdxY8c{b}^)|)I zihC;DP4KaY4V%@Tzd<7a)*Vd76z2lU=n{SrgPLTsXh8 zCeG=_ePw#}v^>m!_-`EfTlOon#=na2e+U67`2@Xq(T@mfy}0ck6qX)SzElcD2J|Gs8;^bLoYtr z03V?Iw=Eyjo&^JKW9WHN161Idv6H0@(XsoY>d7>gMP7cz|G}PYiG6@-u%~Y`chA7t zR~GL7ln`csBWZ*6%Utwdg71a?awpM?Lm3_Rzw7${Da%q=A-y?0PzpbuF1b2BIXydW z%^8LtmwER42gv0NpYFkrFK_PTfaKoXFgKVw;n?T!#^XxACA!1K4@x?L8JwZPrISk*fh z)0Vs4^+Fy>O%qUD-h{<^6&GCkj(eKe4lbNVN(RVBe}WCiX8Gc-d%tf?3;Ja9nWE44loZ7?|wN)B7Z9e2ud6e zXRBJks0CG#j2}N$dEFv>;24Y`>|H}>#Tdv29 z6iHI2tpsxB2TB$Ox=h|6Uq!I-Baxx-N5(@UrJM|NQq9{}iZwOAvdt&w7^ zwH)6cf9Lz;dZml|wn_8Po~lKpeavh?qdT7A4-4>MU4 znO7F)KaAUm7N1o@SW@JgkoZZu`9%~wK=hUSrS|9KxQFjQjfSalYhCueQ{#TEmVA?0 zGIUV-JejjpNv$^-MOgAVNgBvpbhKmOkzxfse3roaiP_a$yJTJMuZTLe#{l1sdotk_Of!_$dhr0 zUfT~{V22C-wu##fWB5j~ckj7y69b#?!0u5OxaXmK7KTwP-N5?V<$Zix!|6)Zft4VmAJS;J5GbF$j!yDRiDXzgV`o$M~EoX#y~Xcmv@$Yot+?ZMYS zo6GT6m8{S6YniSON~L9T@DF3>0W~u)h_JEd9)p-$Hqz> zrPbR?)s4Y@d%6`0Gy+wHOYE(;HE%%YSFjkdGv*Zn0_Jfj>#N zQ<(EeJ0bN2*LcYbuDd~3>(K(Y$&%;4w=Sab$vL$eZmXK$Ottg-65n{)W{0$sO5{8) zI7bPcqiNCgjAsp9S=myr@6Yz>HXbdxEO7AVK`1;~cslM6>D&C~w3;12Hs zF#LEuF4%@@_9;l3wB|%e_1%D!hX2x3l`t-(i(Yg^9S34|~;r{s6m#ERZ*7 z`02WfhycHHL_lN5`J#WHA1t8d*X2D$M>Pd_dyr}@<_*!tJQ3ykt{7mO+1=^>dGo+s zclX7C>>K-9CqE?MH}c%HY{(@Cc|qcFl#Dt(Uan#()oB;e#R057fi}$Kq`at@2;Da7$&i~q!>utEagdaRWOS45=;j*=A zHOd@oG1C6Q?zR2pTg|%fw)@xMazbnx++cjjYTZ1y zrMwP4cFIqnJX#H30|`id_LNYz@IMjTQ<4APJE0{u2R$2I@XT z1a$kQadd})a(sxpZa*ppD!9RFfAt8IrZWE(;|X|8?-3#hNlq5-PDTleP8Kc#1ph-1 zSR_!Iumy;4`pbzD!;~7P0mL}EL*=?d{suZIrcabgr<)6S^_PEh|K=4&|Jy7AlnRuj z%zxMX%jAC={r@4u`KbGQ`n5@SMXW`heV_n(q|*zi8G&bxWojS@?RPp%(Dm{l4wIrH z+DYfcT@0-a=e-XDgC}e|Oss)QN@{qGxvi(Io$6t4$UcHD)`{3tre~&wS?R(-+>tOK z7H5OO%8Ez{uO11xdvuwyI!1ys)gPLRvn5i({OYqXTdkkpxJY>MGM2p>vh>`if|( zvnQgewcO+7AQ=60nP>_bw4bMd*Eu8k2+13;EpdRmLM`nr)93H;3CXqwz7ieZ3wtam zbmdjle?C5p&JXjuI7~qV&28e|Uu>UPU>wT4JbTlEHjG*DW`XcwOZ1aRo83m0g56%8 zei0qVjh%}~fpnMBn#-g&rn^^$o>y7Dk4=0KuqlKcaNznN(r+Ucc`du|b;mEoeY@zg z4P6XaJoY<2uQ)gLUGzUk1gWC80KV657hB{meC&tpd2eV@w`uLXsyBN7Rj);z*v6O~ ztx=0KoQ>@mm>7Ki)XtsF+sB&Mc(AM{z$Ms7oX=KPGgcm*X8?Cjuc730ov@Ji@@izE zmC-8B11=Xqd5L$SEEIGv4G7|$ExZzBv(0QwKg?W6>#EQdY=GtD9*h3TsU(OLeq6#R zYd$|GZ8Ump44oY;*vjlT0NFg{tk3U+(7C77FMxd{qFizaE{A(Aow~h9pgCD}Lyo7w z%01u#0`-c~$cFD;x6_)zF!gz#l)#tY$z5)ccwkjuH+|-)q94emGjp%TsjvY4rjBfi zdL>3GMm#FqMX_Ma`%@vl)?Dek>TIUMbX!FJz_a7$>nV7n>F=7GG!YJ9KXXL!vsnb9 zrm1tr^m|j?`5gN%pDhHL+Lx4{p9w^!Wci}j9qSr$TUZ?1hP%F7*ySy!(XM{j4s+sh zekEY`^F1x*&QE;&TulxA<8E%M8z2-50Nksc&nfl4N!f_N8Rk`M6E($P z?^{iT+f4+%hoxNkUR=3PTYce|A_vw2vlXWpz7xvgn15ejW5YVPXwPG~3%lZDjxJhx z@#x^Kw=>V)v@lIqwneU_fA1&2ho{}X--`u5({c8s5-aMG9HN~=oIG?o}C zXjkQG!fI;&(PO9197~u%ofq(d@q*CbqE>@;pXdDd$Sy(AW)2)8$_H7s&d>W##zlG) zj?9Ly7Z()>f0P0~{WL}BDB9(Ja)NnQ1fY?=?;AlhM9J$qM*Pv{qW!%3dh)S7X1>a& z!9UhRFnf%IM%v4ACCk|K(43cLc*663s?pfkycbEgDNNVos8Oxpg~@1ZvsJWqL2n-4 zh$Ix+*EZRd`W-Klp5DDt6$RWi@7#?bTSXBE>#6X?gwW>(4C|>~MfCx=9Lx2FDFy+d zY{66MtWck+oTVeO&ZUvV<_9EI%{B?JAP+uLlf4wqoYr@++S~-4;=%+a%#*MRwu)-F zSomq7X!GqDQ(l{1!XC4Ig%>ZWMfDFWELfw%HodI0nQ{5O+GY*WrG|NZ3Ye8e?RbB` z!Hvay4;bRzuUR-}UpDSAq$!qWSmr){_TaT`+4{%%vIjr8bFbgc*+?i^&Av9gH$^eW zwLMQ0&u3rd@NsV8F>P@03I`5l_J}pb=MDu0yX{o@@bQ(FhyUy-QT>k{CHH}~&RU_5 a%TO+s|G{4xEVcijApk0p@ssk=(f$V(g()-u literal 0 HcmV?d00001 From c492faecb2fda5619fa26b4eb84e6ced7a103833 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 17 Aug 2010 15:47:59 +0100 Subject: [PATCH 11/13] cookbook: Modified COGL example for consistency Changed the stage size so the COGL API example for cross-fade has a stage the same size as the two texture example. --- doc/cookbook/examples/textures-crossfade-cogl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cookbook/examples/textures-crossfade-cogl.c b/doc/cookbook/examples/textures-crossfade-cogl.c index 7ab572146..671d240cf 100644 --- a/doc/cookbook/examples/textures-crossfade-cogl.c +++ b/doc/cookbook/examples/textures-crossfade-cogl.c @@ -139,7 +139,7 @@ main (int argc, char *argv[]) */ ClutterActor *stage = clutter_stage_get_default (); clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade"); - clutter_actor_set_size (stage, 600, 600); + clutter_actor_set_size (stage, 400, 300); clutter_actor_show (stage); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); From c207820bef93bffede1733ef8c6586cda5095347 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Wed, 18 Aug 2010 11:41:10 +0100 Subject: [PATCH 12/13] cookbook: Added a recipe for cross-fading between two images The recipe covers a two texture approach (using the Clutter API) and a single texture approach (using COGL). It also discusses issues with cross-fading between images of different sizes with the COGL API, and gives a longer example of cycling through multiple images in a slideshow application. --- doc/cookbook/textures.xml | 535 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 535 insertions(+) diff --git a/doc/cookbook/textures.xml b/doc/cookbook/textures.xml index 56b661d7d..efc190de9 100644 --- a/doc/cookbook/textures.xml +++ b/doc/cookbook/textures.xml @@ -841,4 +841,539 @@ typedef struct _CoglTextureVertex { +
+ Cross-fading between two images + +
+ Problem + + You want to do a cross-fade animation (a.k.a. a dissolve + transition) between two images. + + An example use case would be creating a slideshow effect: + load an image from a file, display it in the UI, then load a second + image and cross-fade to it. +
+ +
+ Solutions + + There are two main approaches you could take: + + + + Use two ClutterTextures, one on top + of the other. + + + Use a single ClutterTexture + with the two images in separate layers inside it. + + + +
+ Solution 1: two textures + + This approach uses two ClutterTextures, + bottom and top. To begin + with, the bottom texture shows the + source image and is opaque; the + top texture is loaded with + the target image, but is not visible as + it is fully transparent. + + An animation is then used to fade in the + top texture and fade out the + bottom texture, leaving just top + visible. + + To implement this, first create the two textures inside a + ClutterBinLayout: + + + + + + + + Load the source image into the bottom + texture and the target image into the top one. + As this is the same operation each time, it makes sense to write + a function for loading an image into a texture and checking + for errors, e.g.: + + + +message); + g_error_free (error); + exit (EXIT_FAILURE); + } + + return success; +} +]]> + + + + The load_image() function can then + be called for each texture: + + + + + + + + For the animations, we use ClutterState as we + want to animate two actors at once (top + and bottom): + + + + + + + + Note that rather than set the start opacities manually + on the actors (e.g. using + clutter_actor_set_opacity()), + I've used a ClutterState to define the start + state (as well as the end state). This makes it easier to + track transitions, as they are all kept in one data structure. + + + The easing modes used for the cross-fade animation + (CLUTTER_EASE_IN_CUBIC) + can be set to whatever you like. I personally think that + ease-in modes look best for cross-fading. + + + "Warp" the two textures into the start state + (bottom opaque, top + transparent): + + + + + + + + Using clutter_state_warp_to_state() + immediately transitions to a state without animating, which + in this case sets up the initial state of the UI. + + Finally, use the ClutterState to animate + the two textures, so top fades in and + bottom fades out: + + + + + + + + Here's what it looks like: + + + + + + + Video showing a cross-fade between two textures + + + + The full code for this example + is in the + appendix. + +
+ +
+ Solution 2: one texture with two layers + + The alternative solution is to use a single texture + and the low-level COGL API to set up two different layers + inside it, one for each image. + + Then, rather than fade between two textures, + progressively combine the two layers together using an + alpha value which changes over the course of an animation + (from 0.0 at the start of the animation to 1.0 at its end). + + At any point in the cross-fade animation, you are + actually seeing a combination of the color + values in the two images (modified by an alpha component), rather + than seeing one image through the other. This can give a smoother + cross-fade effect than the two texture approach. + + As this solution is more complex + and relies on the lower-level (and more difficult to follow) + COGL API, the next section is just a short summary of how it + works; see the + sample code, which has liberal comments for more details. + + + For more about texture combining, refer to the COGL + API documentation (particularly the section about material + blend strings). You may also find it useful to get hold of + a decent OpenGL reference. (So you can look it up, what we're + doing in this solution is using a texture combiner with + interpolation as the texture combiner function.) + + +
+ Cross-fading using a texture combiner with interpolation + + The cross-fade is implemented by combining the two layers, + computing a color value for each pixel in the resulting texture. + The value for each pixel at a given point in the animation + is based on three things: + + + + The color value of the source + pixel + + + The color value of the target + pixel + + + The alpha value of a third colour at the given point + in the animation's timeline + + + + The resulting value for each RGBA color component in each pixel + is computed using an interpolation function. In pseudo-code, it + looks like this: + + + + color component value = (target pixel value * alpha) + (source pixel value * (1 - alpha)) + + + + The effect is that as the alpha increases towards 1.0, + progressively more of the target pixel's + color is used, and progressively less of the source + pixel's: so the target fades in, while + the source fades out. + + The advantage of this approach is that color and + brightness transitions only occur where pixels differ between + the two images. This means that you transitions are smoother + where you are cross-fading between images with similar color ranges + and brightness levels. + + A special case is where you're cross-fading + from an image to itself: the two texture approach can cause some + dimming during this kind of transition; but the single texture + approach results in no color or brightness changes (it's not even + a transition as such, as all the pixels are identical in + the two layers). + +
+ +
+ +
+ +
+ Discussion + +
+ Cross-fades between images of different sizes + + The code examples + (two textures, + one texture with + COGL) don't take account of the size of the images being + loaded. + + In the two texture example, this isn't so much of a problem, + as you can resize the textures individually to the images: + providing you use + clutter_texture_set_keep_aspect_ratio(), + different image sizes shouldn't be a problem. See + the slideshow + example, for a demonstration of how to cycle through + different sized images. + + In the case of the single texture approach, you will get + problems when cross-fading between two images with + different sizes. There is no easy way to maintain the aspect + ratio (as you have two layers, potentially with different sizes, + in the same texture). The last layer added to the + CoglMaterial determines the size of the texture; + so if the previous layer has different dimensions, it will + appear distorted in the UI. In the + single texture + code example, the source layer + is added first; so, if the target layer has + different dimensions, the source will + appear distorted. + + There are a few ways you can remedy this: + + + + + As you load each image into its own + CoglTexture, get its size with + cogl_texture_get_width() and + cogl_texture_get_height(). Then set the + ClutterTexture's size to the + size of the source layer. Next, as + you cross-fade, simultaneously animate a + size change in the ClutterTexture to + the target image's size. + This could work with non-realistic images where + some distortion of the image is acceptable (the target image + may be the wrong size to start with, but transition to the + correct size by the time it's fully faded in). But it can + look a bit odd for transitions between photos. + + + + If the bounds of the images you're going to be loading + are known (i.e. you know the greatest width and height + of all the images you're going to load), you can size + the ClutterTexture to those bounds. + + Then, as you load each image (e.g. you could do this in the + load_cogl_texture() function of + the code + example): + + + + Load the image into a temporary + CoglTexture (e.g. called + filetex). Then use the + CoglTexture API to get the data from it. + For example: + + + + + + + + + Create another CoglTexture as + large as the ClutterTexture (i.e. big enough + to fit any image you're going to load). Then copy the + data from filetex into a region of + it. For example: + + + + + + + + Because you're copying the image data from the + file into a region of the CoglTexture + that's the same size as the image data, it won't be + distorted. However, it's worth stressing that the + ClutterTexture needs to be as wide as + the widest image and as tall as the tallest, so + all the images can be accommodated. Otherwise this + works, but images might be clipped. + + + + + + + +
+ +
+ Slideshows + + The two texture solution can be easily extended + to cycle through multiple images. To begin with, the first + image is loaded into the top texture. Then, + the basic pattern for transitioning to the next image is as follows: + + + + Copy the data from the top texture + to the bottom texture. + + + Make the top texture transparent + and the bottom texture opaque (using + clutter_state_warp_to_state()). At this + point, it appears as though the textures haven't changed. + + + Load the next image into top. + + + When top has finished loading, + fade it in while simultaneously fading out + bottom (using + clutter_state_set_state()). + + + + The sample + code in the appendix implements this as part of + a simple slideshow application. + +
+ +
+ +
+ Full examples + + + Cross-fading between two images using two + <type>ClutterTextures</type> + + + there should be a code sample here, but there isn't... + + + + + + Cross-fading between two images using one + <type>ClutterTexture</type> and the COGL API + + + there should be a code sample here, but there isn't... + + + + + + A simple slideshow application using two + <type>ClutterTextures</type> + + + there should be a code sample here, but there isn't... + + + + +
+ +
+ From 444d315078f41295c722bdf30c4b3ff92c89cf53 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Wed, 18 Aug 2010 16:02:51 +0100 Subject: [PATCH 13/13] cookbook: Use GdkPixbuf instead of getting data from a texture Rewrote example for single texture + COGL to use a GdkPixbuf to load images, rather than reading data out of another CoglTexture. The data is then loaded from the pixbuf to a region of the CoglTexture (as before). --- doc/cookbook/textures.xml | 155 +++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 77 deletions(-) diff --git a/doc/cookbook/textures.xml b/doc/cookbook/textures.xml index efc190de9..9a2cd0142 100644 --- a/doc/cookbook/textures.xml +++ b/doc/cookbook/textures.xml @@ -1187,7 +1187,7 @@ clutter_state_set_state (transitions, "show-top"); different dimensions, the source will appear distorted. - There are a few ways you can remedy this: + There are a couple of ways you can remedy this: @@ -1209,91 +1209,92 @@ clutter_state_set_state (transitions, "show-top"); - If the bounds of the images you're going to be loading - are known (i.e. you know the greatest width and height - of all the images you're going to load), you can size - the ClutterTexture to those bounds. + Use GdkPixbuf (or similar) to load the images into a temporary + data structure. (GdkPixbuf works well for this as it can resize + the image while retaining its aspect ratio.) Then load the data from + the pixbuf into a region of a + CoglTexture which has the same dimensions as + the ClutterTexture. - Then, as you load each image (e.g. you could do this in the + Here's an example of how you can rewrite the load_cogl_texture() function of - the code - example): + the single + texture example to do this: - - - Load the image into a temporary - CoglTexture (e.g. called - filetex). Then use the - CoglTexture API to get the data from it. - For example: - - - + + -CoglPixelFormat format = cogl_texture_get_format (filetex); -guint rowstride = cogl_texture_get_rowstride (filetex); -guint width = cogl_texture_get_width (filetex); -guint height = cogl_texture_get_height (filetex); +static CoglHandle +load_cogl_texture (const char *type, + const char *file, + const guint texture_width, + const guint texture_height) +{ + GError *error = NULL; -/* allocate memory large enough for the data */ -gint size = cogl_texture_get_data (filetex, format, 0, NULL); -guchar *data = g_new0 (guchar, size); + /* + * Load image data from a file into a GdkPixbuf, + * but constrained to the size of the target ClutterTexture; + * aspect ratio is maintained + * + * texture_width and texture_height are set elsewhere to + * the width and height of the ClutterTexture + */ + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size (file, + texture_width, + texture_height, + &error); -/* get the data from the texture */ -cogl_texture_get_data (filetex, - format, - rowstride, - data); + if (error != NULL) + { + g_print ("Unable to load %s image: %s\n", type, error->message); + g_error_free (error); + exit (EXIT_FAILURE); + } + + guint rowstride = gdk_pixbuf_get_rowstride (pixbuf); + guint width = gdk_pixbuf_get_width (pixbuf); + guint height = gdk_pixbuf_get_height (pixbuf); + guchar *data = gdk_pixbuf_get_pixels (pixbuf); + + CoglPixelFormat format = COGL_PIXEL_FORMAT_RGB_888; + if (gdk_pixbuf_get_has_alpha (pixbuf) == TRUE) + format = COGL_PIXEL_FORMAT_RGBA_8888; + + /* CoglTexture with the same dimensions as the ClutterTexture */ + CoglHandle *tex = cogl_texture_new_with_size (texture_width, + texture_height, + COGL_TEXTURE_NO_SLICING, + format); + + /* + * load the texture data into a region of the full-sized texture; + * the size of the region is set from the size of the image data + * (as resized by GdkPixbuf) + */ + cogl_texture_set_region (tex, + 0, 0, /* from top-left corner of the pixbuf */ + (texture_width - width) / 2, /* center on the CoglTexture */ + (texture_height - height) / 2, /* center on the CoglTexture */ + width, height, + width, height, + format, + rowstride, + data); + + return tex; +} ]]> - - - - - Create another CoglTexture as - large as the ClutterTexture (i.e. big enough - to fit any image you're going to load). Then copy the - data from filetex into a region of - it. For example: + + - - - - - - - Because you're copying the image data from the - file into a region of the CoglTexture - that's the same size as the image data, it won't be - distorted. However, it's worth stressing that the - ClutterTexture needs to be as wide as - the widest image and as tall as the tallest, so - all the images can be accommodated. Otherwise this - works, but images might be clipped. - - + Because you're copying the image data from the + file into a region of the CoglTexture + that's the same size as the image data in the pixbuf, it isn't + distorted.