diff --git a/pinpoint.c b/pinpoint.c index 9adf699..80f0920 100644 --- a/pinpoint.c +++ b/pinpoint.c @@ -68,6 +68,7 @@ static PinPointPoint pin_default_point = { .text = NULL, .position = CLUTTER_GRAVITY_CENTER, .font = "Sans 60px", + .slide_number_font = "Sans 20px", .text_color = "white", .text_align = PP_TEXT_LEFT, .use_markup = TRUE, @@ -85,6 +86,15 @@ static PinPointPoint pin_default_point = { .camera_framerate = 0, /* auto */ .camera_resolution = {0, 0}, /* auto */ + .slide_number = 0, + .total_slides = 0, + .show_slide_number = FALSE, + + .pic_filename = NULL, + .pic_scale = 0.25, + .pic_xrelpos = 0.75, + .pic_yrelpos = 0.25, + .data = NULL, }; @@ -384,6 +394,47 @@ pp_get_text_position_scale (PinPointPoint *point, } void +pp_get_pic_position_scale (PinPointPoint *point, + float stage_width, + float stage_height, + float pic_width, + float pic_height, + float *pic_x, + float *pic_y, + float *pic_scale) +{ + float w, h; + float x, y; + float sx = 1.0; + float sy = 1.0; + float padding; + + float pic_base_scale = point->pic_scale; + float pic_base_rel_xpos = point->pic_xrelpos; + float pic_base_rel_ypos = point->pic_yrelpos; + + pp_get_padding (stage_width, stage_height, &padding); + + w = pic_width; + h = pic_height; + + sx = stage_width * pic_base_scale / w; + sy = stage_height * pic_base_scale / h; + + if (sy < sx) + sx = sy; + if (sx > 1.0) /* avoid enlarging text */ + sx = 1.0; + + x = stage_width * pic_base_rel_xpos; + y = stage_height * pic_base_rel_ypos ; + + *pic_scale = sx; + *pic_x = x; + *pic_y = y; +} + +void pp_get_shading_position_size (float stage_width, float stage_height, float text_x, @@ -451,6 +502,7 @@ parse_setting (PinPointPoint *point, START_PARSER IF_PREFIX("stage-color=") point->stage_color = STRING; IF_PREFIX("font=") point->font = STRING; + IF_PREFIX("slide-number-font=") point->slide_number_font = STRING; IF_PREFIX("text-color=") point->text_color = STRING; IF_PREFIX("text-align=") ENUM(point->text_align, PPTextAlign, STRING); IF_PREFIX("shading-color=") point->shading_color = STRING; @@ -460,6 +512,10 @@ parse_setting (PinPointPoint *point, IF_PREFIX("transition=") point->transition = STRING; IF_PREFIX("camera-framerate=") point->camera_framerate = INT; IF_PREFIX("camera-resolution=") RESOLUTION (point->camera_resolution); + IF_PREFIX("pic=") point->pic_filename = STRING; + IF_PREFIX("pic-scale=") point->pic_scale = FLOAT; + IF_PREFIX("pic-xrelpos=") point->pic_xrelpos = FLOAT; + IF_PREFIX("pic-yrelpos=") point->pic_yrelpos = FLOAT; IF_EQUAL("fill") point->bg_scale = PP_BG_FILL; IF_EQUAL("fit") point->bg_scale = PP_BG_FIT; IF_EQUAL("stretch") point->bg_scale = PP_BG_STRETCH; @@ -475,6 +531,8 @@ parse_setting (PinPointPoint *point, IF_EQUAL("bottom-right") point->position = CLUTTER_GRAVITY_SOUTH_EAST; IF_EQUAL("no-markup") point->use_markup = FALSE; IF_EQUAL("markup") point->use_markup = TRUE; + IF_EQUAL("no-slide-number") point->show_slide_number = FALSE; + IF_EQUAL("slide-number") point->show_slide_number = TRUE; DEFAULT point->bg = g_intern_string (setting); END_PARSER @@ -637,10 +695,16 @@ static void serialize_slide_config (GString *str, } STRING(font,"font="); + STRING(slide_number_font,"slide-number-font="); STRING(text_color,"text-color="); STRING(shading_color,"shading-color="); FLOAT(shading_opacity, "shading-opacity="); + STRING(pic_filename,"pic="); + FLOAT(pic_scale, "pic-scale="); + FLOAT(pic_xrelpos, "pic-xrelpos="); + FLOAT(pic_yrelpos, "pic-yrelpos="); + STRING(transition,"transition="); STRING(command,"command="); if (point->duration != 0.0) @@ -664,6 +728,15 @@ static void serialize_slide_config (GString *str, g_string_append (str, "[no-markup]"); } + if (point->show_slide_number != reference->show_slide_number) + { + g_string_append (str, separator); + if (point->show_slide_number) + g_string_append (str, "[slide-number]"); + else + g_string_append (str, "[no-slide-number]"); + } + #undef FLOAT #undef INT #undef STRING @@ -731,6 +804,8 @@ pp_parse_slides (PinPointRenderer *renderer, GString *notes_str = g_string_new (""); GList *s; PinPointPoint *point, *next_point; + int current_slide_number = 0; + int total_slides = 0; if (renderer->source) { @@ -769,6 +844,29 @@ pp_parse_slides (PinPointRenderer *renderer, pp_slides = NULL; point = pin_point_new (renderer); + { + gboolean start_of_line = TRUE; + int pos; + + for (pos = 0, total_slides = 0; + slide_src[pos] + ; pos ++) + { + switch (slide_src[pos]) + { + case '\n': + start_of_line = TRUE; + break; + case '-': + if (start_of_line) { + total_slides++; + } + default: + start_of_line = FALSE; + } + } + } + /* parse the slides, constructing lists of slide/point objects */ for (p = slide_src; *p; p++) @@ -854,6 +952,9 @@ pp_parse_slides (PinPointRenderer *renderer, if (notes_str->str[0]) point->speaker_notes = g_strdup (notes_str->str); + point->slide_number = current_slide_number++; + point->total_slides = total_slides; + renderer->make_point (renderer, point); g_string_assign (slide_str, ""); diff --git a/pinpoint.h b/pinpoint.h index 3f26630..0e2039d 100644 --- a/pinpoint.h +++ b/pinpoint.h @@ -90,6 +90,7 @@ struct _PinPointPoint const char *text; /* the text of the slide */ ClutterGravity position; const char *font; + const char *slide_number_font; PPTextAlign text_align; const char *text_color; gboolean use_markup; @@ -111,6 +112,16 @@ struct _PinPointPoint gint camera_framerate; PPResolution camera_resolution; + gint slide_number; + gint total_slides; + + gboolean show_slide_number; + + const char *pic_filename; + float pic_scale; + float pic_xrelpos; + float pic_yrelpos; + void *data; /* the renderer can attach data here */ }; @@ -159,6 +170,16 @@ pp_get_text_position_scale (PinPointPoint *point, float *text_scale); void +pp_get_pic_position_scale (PinPointPoint *point, + float stage_width, + float stage_height, + float pic_width, + float pic_height, + float *pic_x, + float *pic_y, + float *pic_scale); + +void pp_get_shading_position_size (float stage_width, float stage_height, float text_x, diff --git a/pp-cairo.c b/pp-cairo.c index 5ee7f21..c3d7da6 100644 --- a/pp-cairo.c +++ b/pp-cairo.c @@ -285,11 +285,16 @@ _cairo_get_svg (CairoRenderer *renderer, static void _cairo_render_background (CairoRenderer *renderer, - PinPointPoint *point) + PinPointPoint *point, + float *bg_width_return, + float *bg_height_return) { char *full_path = NULL; const char *file; + *bg_width_return = renderer->width; + *bg_height_return = renderer->height; + if (point == NULL || point->bg == NULL) return; @@ -341,7 +346,6 @@ _cairo_render_background (CairoRenderer *renderer, if (surface == NULL) break; - bg_width = cairo_image_surface_get_width (surface); bg_height = cairo_image_surface_get_height (surface); @@ -351,6 +355,15 @@ _cairo_render_background (CairoRenderer *renderer, &bg_x, &bg_y, &bg_scale_x, &bg_scale_y); + *bg_width_return = bg_width * bg_scale_x; + if ( *bg_width_return > renderer->width ) { + *bg_width_return = renderer->width; + } + *bg_height_return = bg_height * bg_scale_y; + if ( *bg_height_return > renderer->height ) { + *bg_height_return = renderer->height; + } + cairo_save (renderer->ctx); cairo_translate (renderer->ctx, bg_x, bg_y); cairo_scale (renderer->ctx, bg_scale_x, bg_scale_y); @@ -393,6 +406,15 @@ _cairo_render_background (CairoRenderer *renderer, &bg_x, &bg_y, &bg_scale_x, &bg_scale_y); + *bg_width_return = bg_width * bg_scale_x; + if ( *bg_width_return > renderer->width ) { + *bg_width_return = renderer->width; + } + *bg_height_return = bg_height * bg_scale_y; + if ( *bg_height_return > renderer->height ) { + *bg_height_return = renderer->height; + } + cairo_save (renderer->ctx); cairo_translate (renderer->ctx, bg_x, bg_y); cairo_scale (renderer->ctx, bg_scale_x, bg_scale_y); @@ -440,6 +462,53 @@ _cairo_render_background (CairoRenderer *renderer, } static void +_cairo_render_pic (CairoRenderer *renderer, + PinPointPoint *point) +{ + char *full_path = NULL; + const char *file; + + if (point == NULL || point->pic_filename == NULL) + return; + + file = point->pic_filename; + + if (renderer->path && file) + { + char *dir = g_path_get_dirname (renderer->path); + full_path = g_build_filename (dir, file, NULL); + g_free (dir); + + file = full_path; + } + + { + cairo_surface_t *surface; + float pic_x, pic_y, pic_width, pic_height, pic_scale; + + surface = _cairo_get_surface (renderer, file); + if (surface != NULL) { + pic_width = cairo_image_surface_get_width (surface); + pic_height = cairo_image_surface_get_height (surface); + + pp_get_pic_position_scale (point, + renderer->width, renderer->height, + pic_width, pic_height, + &pic_x, &pic_y, + &pic_scale); + + cairo_save (renderer->ctx); + cairo_translate (renderer->ctx, pic_x, pic_y); + cairo_scale (renderer->ctx, pic_scale, pic_scale); + cairo_set_source_surface (renderer->ctx, surface, 0., 0.); + cairo_paint (renderer->ctx); + cairo_restore (renderer->ctx); + } + } + g_free (full_path); +} + +static void _cairo_render_text (CairoRenderer *renderer, PinPointPoint *point) { @@ -510,12 +579,107 @@ out: g_object_unref (layout); } +static void +_cairo_render_slide_number (CairoRenderer *renderer, + PinPointPoint *point, + float bg_width, float bg_height) +{ + PangoLayout *layout; + PangoFontDescription *desc; + PangoRectangle logical_rect = { 0, }; + ClutterColor text_color, + shading_color; + + float text_x, text_y, text_width, text_height, text_scale; + float shading_x, shading_y, shading_width, shading_height; + + char buf[128]; + + if (point == NULL) + return; + + if ( ! point->show_slide_number ) { + return; + } + + snprintf(buf, sizeof(buf), "%d/%d", + point->slide_number + 1, + point->total_slides); + + layout = pango_cairo_create_layout (renderer->ctx); + desc = pango_font_description_from_string (point->slide_number_font); + pango_layout_set_font_description (layout, desc); + pango_layout_set_text (layout, buf, -1); + pango_layout_set_alignment (layout, point->text_align); + + pango_layout_get_extents (layout, NULL, &logical_rect); + text_width = (logical_rect.x + logical_rect.width) / 1024; + text_height = (logical_rect.y + logical_rect.height) / 1024; + if (text_width < 1) + goto out; + + { + int old_position = point->position; + + point->position = CLUTTER_GRAVITY_SOUTH_EAST; + + pp_get_text_position_scale (point, + bg_width, bg_height, + text_width, text_height, + &text_x, &text_y, + &text_scale); + + point->position = old_position; + + text_x = renderer->width / 2 + bg_width / 2 - 2 * text_width * text_scale; + text_y = renderer->height / 2 + bg_height / 2 - 2 * text_height * text_scale; + } + + pp_get_shading_position_size (renderer->height, renderer->width, /* XXX: is this right order?? */ + text_x, text_y, + text_width, text_height, + text_scale, + &shading_x, &shading_y, + &shading_width, &shading_height); + + clutter_color_from_string (&text_color, point->text_color); + clutter_color_from_string (&shading_color, point->shading_color); + + cairo_set_source_rgba (renderer->ctx, + shading_color.red / 255.f, + shading_color.green / 255.f, + shading_color.blue / 255.f, + shading_color.alpha / 255.f * point->shading_opacity); + cairo_rectangle (renderer->ctx, + shading_x, shading_y, shading_width, shading_height); + cairo_fill (renderer->ctx); + + cairo_save (renderer->ctx); + cairo_translate (renderer->ctx, text_x, text_y); + cairo_scale (renderer->ctx, text_scale, text_scale); + cairo_set_source_rgba (renderer->ctx, + text_color.red / 255.f, + text_color.green / 255.f, + text_color.blue / 255.f, + text_color.alpha / 255.f); + pango_cairo_show_layout (renderer->ctx, layout); + cairo_restore (renderer->ctx); + +out: + pango_font_description_free (desc); + g_object_unref (layout); +} + void cairo_renderer_render_page (CairoRenderer *renderer, PinPointPoint *point) { - _cairo_render_background (renderer, point); + float bg_width, bg_height; + + _cairo_render_background (renderer, point, &bg_width, &bg_height); + _cairo_render_pic (renderer, point); _cairo_render_text (renderer, point); + _cairo_render_slide_number (renderer, point, bg_width, bg_height); cairo_show_page (renderer->ctx); } diff --git a/pp-clutter.c b/pp-clutter.c index da5d1fa..65c5003 100644 --- a/pp-clutter.c +++ b/pp-clutter.c @@ -155,6 +155,8 @@ typedef struct PinPointRenderer *renderer; ClutterActor *background; ClutterActor *text; + ClutterActor *slide_number; + ClutterActor *pic; float rest_y; /* y coordinate when text is stationary unused */ ClutterState *state; @@ -164,6 +166,7 @@ typedef struct ClutterActor *midground; ClutterActor *foreground; ClutterActor *shading; + ClutterActor *slide_number_shading; #ifdef USE_CLUTTER_GST GstElement *pipeline; /* used for the custom camera pipeline */ @@ -221,6 +224,38 @@ pp_clutter_render_adjust_background (ClutterRenderer *renderer, clutter_actor_set_position (data->background, bg_x, bg_y); } +static void +pp_clutter_get_background_size (ClutterRenderer *renderer, + PinPointPoint *point, + float *width, float *height, float *x, float *y) +{ + float bg_x, bg_y, bg_width, bg_height, bg_scale_x, bg_scale_y; + ClutterPointData *data = point->data; + + if (!data) + return; + + if (CLUTTER_IS_RECTANGLE (data->background)) + { + clutter_actor_get_size (renderer->stage, &bg_width, &bg_height); + } + else + { + clutter_actor_get_size (data->background, &bg_width, &bg_height); + } + + pp_get_background_position_scale (point, + clutter_actor_get_width (renderer->stage), + clutter_actor_get_height (renderer->stage), + bg_width, bg_height, + &bg_x, &bg_y, &bg_scale_x, &bg_scale_y); + + *width = bg_width * bg_scale_x; + *height = bg_height * bg_scale_y; + *x = bg_x; + *y = bg_y; +} + #ifdef HAVE_CLUTTER_X11 static void pp_set_fullscreen (ClutterStage *stage, gboolean fullscreen) @@ -1191,6 +1226,26 @@ clutter_renderer_make_point (PinPointRenderer *pp_renderer, "color", &color, NULL); } + + if ( point->show_slide_number ) { + char buf[128]; + + snprintf( buf, sizeof(buf), "%d/%d", + point->slide_number + 1, + point->total_slides ); + + data->slide_number = g_object_new (CLUTTER_TYPE_TEXT, + "font-name", point->slide_number_font, + "text", buf, + "line-alignment", point->text_align, + "color", &color, + NULL); + } + + if (point->pic_filename) { + data->pic = _clutter_get_texture (renderer, point->pic_filename); + clutter_actor_set_depth (data->pic, 0.0); + } clutter_container_add_actor (CLUTTER_CONTAINER (renderer->foreground), data->text); @@ -1991,7 +2046,6 @@ show_slide (ClutterRenderer *renderer, gboolean backwards) NULL); } - if (data->background) clutter_actor_animate (data->background, CLUTTER_LINEAR, 1000, @@ -2001,6 +2055,9 @@ show_slide (ClutterRenderer *renderer, gboolean backwards) else { GError *error = NULL; + float bg_width, bg_height, bg_x, bg_y; + int use_bg_as_base = 1; + /* fade out global group of texts when using a custom .json template */ clutter_actor_animate (renderer->foreground, CLUTTER_LINEAR, 500, @@ -2040,6 +2097,7 @@ show_slide (ClutterRenderer *renderer, gboolean backwards) if (data->background2) /* parmanently steal background */ { clutter_actor_reparent (data->background, data->background2); + clutter_actor_reparent (data->pic, data->background2); } } @@ -2065,20 +2123,62 @@ show_slide (ClutterRenderer *renderer, gboolean backwards) if (data->foreground) { clutter_actor_reparent (data->text, data->foreground); + + if ( point->show_slide_number ) { + clutter_actor_reparent (data->slide_number, data->foreground); + } } clutter_actor_set_opacity (data->background, 255); + bg_width = clutter_actor_get_width (renderer->stage); + bg_height = clutter_actor_get_height (renderer->stage); + bg_x = 0.0; + bg_y = 0.0; + + if ( use_bg_as_base ) { + pp_clutter_get_background_size (renderer, + point, + &bg_width, &bg_height, &bg_x, &bg_y); + } + + { + float pic_x, pic_y, pic_width, pic_height, pic_scale; + + clutter_actor_get_size (data->pic, &pic_width, &pic_height); + + pp_get_pic_position_scale (point, + bg_width, + bg_height, + pic_width, pic_height, + &pic_x, &pic_y, + &pic_scale); + + pic_x += bg_x; + pic_y += bg_y; + + g_object_set (data->pic, + "depth", 0.0, + "scale-x", pic_scale, + "scale-y", pic_scale, + "x", pic_x, + "y", pic_y, + NULL); + } + { float text_x, text_y, text_width, text_height, text_scale; clutter_actor_get_size (data->text, &text_width, &text_height); pp_get_text_position_scale (point, - clutter_actor_get_width (renderer->stage), - clutter_actor_get_height (renderer->stage), + bg_width, + bg_height, text_width, text_height, &text_x, &text_y, &text_scale); + text_x += bg_x; + text_y += bg_y; + g_object_set (data->text, "depth", 0.0, "scale-x", text_scale, @@ -2094,8 +2194,8 @@ show_slide (ClutterRenderer *renderer, gboolean backwards) clutter_color_from_string (&color, point->shading_color); pp_get_shading_position_size ( - clutter_actor_get_width (renderer->stage), - clutter_actor_get_height (renderer->stage), + bg_width, + bg_height, text_x, text_y, text_width, text_height, text_scale, @@ -2132,6 +2232,84 @@ show_slide (ClutterRenderer *renderer, gboolean backwards) } } + if ( point->show_slide_number ) { + float text_x, text_y, text_width, text_height, text_scale; + int old_position; + + clutter_actor_get_size (data->slide_number, &text_width, &text_height); + + old_position = point->position; + point->position = CLUTTER_GRAVITY_SOUTH_EAST; + + if ( bg_width > clutter_actor_get_width (renderer->stage) ) { + bg_width = clutter_actor_get_width (renderer->stage); + } + if ( bg_height > clutter_actor_get_height (renderer->stage) ) { + bg_height = clutter_actor_get_height (renderer->stage); + } + + pp_get_text_position_scale (point, + bg_width, + bg_height, + text_width, text_height, + &text_x, &text_y, + &text_scale); + point->position = old_position; + + text_x = clutter_actor_get_width (renderer->stage) / 2 + bg_width / 2 - 2 * text_width * text_scale; + text_y = clutter_actor_get_height (renderer->stage) / 2 + bg_height / 2 - 2 * text_height * text_scale; + + g_object_set (data->slide_number, + "depth", 0.0, + "scale-x", text_scale, + "scale-y", text_scale, + "x", text_x, + "y", text_y, + NULL); + + if (clutter_actor_get_width (data->slide_number) > 1.0) + { + ClutterColor color; + float shading_x, shading_y, shading_width, shading_height; + clutter_color_from_string (&color, point->shading_color); + + pp_get_shading_position_size ( + clutter_actor_get_width (renderer->stage), + clutter_actor_get_height (renderer->stage), + text_x, text_y, + text_width, text_height, + text_scale, + &shading_x, &shading_y, + &shading_width, &shading_height); + + if (!data->slide_number_shading) + { + data->slide_number_shading = clutter_rectangle_new_with_color (&black); + + clutter_container_add_actor ( + CLUTTER_CONTAINER (data->midground), data->slide_number_shading); + } + + g_object_set (data->slide_number_shading, + "depth", -0.01, + "x", shading_x, + "y", shading_y, + "opacity", (int)(point->shading_opacity*255), + "color", &color, + "width", shading_width, + "height", shading_height, + NULL); + } + else /* no text, fade out shading */ + if (data->slide_number_shading) + g_object_set (data->slide_number_shading, "opacity", 0, NULL); + if (data->foreground) + { + clutter_actor_reparent (data->slide_number, data->foreground); + } + } + + if (!backwards) clutter_actor_raise_top (data->json_slide);