From 04fd53750abc1f792ab6d5bdf7416bd7c42451b2 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Mon, 25 Jan 2016 08:58:03 +0100 Subject: [PATCH 1/2] Add label-ft plugin This adds a FreeType-based label plugin with minimal dependencies. Is is a replacement for the label plugin, except that it lacks support for Unicode and different fonts families. It's purpose is to be included in the initrd, which isn't easily possible with the label plugin due to it's massive dependency list. Signed-off-by: Fabian Vogt --- configure.ac | 10 + src/libply-splash-graphics/ply-label.c | 5 + src/plugins/controls/Makefile.am | 6 +- src/plugins/controls/label-ft/Makefile.am | 22 ++ src/plugins/controls/label-ft/plugin.c | 501 ++++++++++++++++++++++++++++++ 5 files changed, 543 insertions(+), 1 deletion(-) create mode 100644 src/plugins/controls/label-ft/Makefile.am create mode 100644 src/plugins/controls/label-ft/plugin.c diff --git a/configure.ac b/configure.ac index 445034d..13b8c3e 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,15 @@ if test x$enable_pango = xyes; then AC_SUBST(PANGO_LIBS) fi +AC_ARG_ENABLE(freetype, AS_HELP_STRING([--enable-freetype],[enable building the FreeType-based label plugin]),enable_freetype=$enableval,enable_freetype=yes) +AM_CONDITIONAL(ENABLE_FREETYPE, [test "$enable_freetype" = yes]) + +if test x$enable_freetype = xyes; then + PKG_CHECK_MODULES(FREETYPE, [freetype2]) + AC_SUBST(FREETYPE_CFLAGS) + AC_SUBST(FREETYPE_LIBS) +fi + AC_ARG_ENABLE(gtk, AS_HELP_STRING([--enable-gtk],[enable building with gtk, disabled there is no x11 renderer]),enable_gtk=$enableval,enable_gtk=yes) AM_CONDITIONAL(ENABLE_GTK, [test "$enable_gtk" = yes]) @@ -295,6 +304,7 @@ AC_CONFIG_FILES([Makefile src/plugins/splash/script/Makefile src/plugins/controls/Makefile src/plugins/controls/label/Makefile + src/plugins/controls/label-ft/Makefile src/Makefile src/client/ply-boot-client.pc src/client/Makefile diff --git a/src/libply-splash-graphics/ply-label.c b/src/libply-splash-graphics/ply-label.c index ba1f7b1..7c65708 100644 --- a/src/libply-splash-graphics/ply-label.c +++ b/src/libply-splash-graphics/ply-label.c @@ -96,8 +96,13 @@ ply_label_load_plugin (ply_label_t *label) get_plugin_interface_function_t get_label_plugin_interface; + /* Try the pango/cairo based label plugin first... */ label->module_handle = ply_open_module (PLYMOUTH_PLUGIN_PATH "label.so"); + /* ...and the FreeType based one after that, it is not a complete substitute (yet). */ + if (label->module_handle == NULL) + label->module_handle = ply_open_module (PLYMOUTH_PLUGIN_PATH "label-ft.so"); + if (label->module_handle == NULL) return false; diff --git a/src/plugins/controls/Makefile.am b/src/plugins/controls/Makefile.am index f1621f9..284b206 100644 --- a/src/plugins/controls/Makefile.am +++ b/src/plugins/controls/Makefile.am @@ -1,4 +1,8 @@ +SUBDIRS = if ENABLE_PANGO -SUBDIRS = label +SUBDIRS += label +endif +if ENABLE_FREETYPE +SUBDIRS += label-ft endif MAINTAINERCLEANFILES = Makefile.in diff --git a/src/plugins/controls/label-ft/Makefile.am b/src/plugins/controls/label-ft/Makefile.am new file mode 100644 index 0000000..2ff864d --- /dev/null +++ b/src/plugins/controls/label-ft/Makefile.am @@ -0,0 +1,22 @@ +AM_CPPFLAGS = -I$(top_srcdir) \ + -I$(srcdir)/../../../libply \ + -I$(srcdir)/../../../libply-splash-core \ + -I$(srcdir)/../../../libply-splash-graphics \ + -I$(srcdir)/../../.. \ + -I$(srcdir)/../.. \ + -I$(srcdir)/.. \ + -I$(srcdir) + +plugindir = $(libdir)/plymouth +plugin_LTLIBRARIES = label-ft.la + +label_ft_la_CFLAGS = $(PLYMOUTH_CFLAGS) $(FREETYPE_CFLAGS) + +label_ft_la_LDFLAGS = -module -avoid-version -export-dynamic +label_ft_la_LIBADD = $(PLYMOUTH_LIBS) $(FREETYPE_LIBS) \ + ../../../libply/libply.la \ + ../../../libply-splash-core/libply-splash-core.la \ + ../../../libply-splash-graphics/libply-splash-graphics.la +label_ft_la_SOURCES = $(srcdir)/plugin.c + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/plugins/controls/label-ft/plugin.c b/src/plugins/controls/label-ft/plugin.c new file mode 100644 index 0000000..06fe73e --- /dev/null +++ b/src/plugins/controls/label-ft/plugin.c @@ -0,0 +1,507 @@ +/* ply-label.c - label control + * + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: Ray Strode + * Written by: Fabian Vogt + */ +#include "config.h" + +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + +#include "ply-pixel-buffer.h" +#include "ply-pixel-display.h" +#include "ply-utils.h" + +#include "ply-label-plugin.h" + +/* This is used if fontconfig (fc-match) is not available, like in the initrd. */ +#define FONT_FALLBACK "/usr/share/fonts/Plymouth.ttf" + +struct _ply_label_plugin_control +{ + ply_pixel_display_t *display; + ply_rectangle_t area; + + ply_label_alignment_t alignment; + long width; /* For alignment */ + + FT_Library library; + FT_Face face; + + char *text; + float red; + float green; + float blue; + float alpha; + + bool is_hidden; +}; + +ply_label_plugin_interface_t *ply_label_plugin_get_interface (void); + +/* Query fontconfig, if available, for the default font. */ +static const char * +query_fc_match() +{ + FILE *fp; + static char fc_match_out[PATH_MAX]; + + fp = popen("/usr/bin/fc-match -f %{file}", "r"); + if (!fp) + return NULL; + + fgets(fc_match_out, sizeof(fc_match_out), fp); + + pclose(fp); + + return fc_match_out; +} + +static ply_label_plugin_control_t * +create_control (void) +{ + FT_Error error; + ply_label_plugin_control_t *label; + const char *font_path; + + label = calloc (1, sizeof(ply_label_plugin_control_t)); + if(!label) + return NULL; + + label->is_hidden = true; + label->width = -1; + label->text = NULL; + + error = FT_Init_FreeType (&label->library); + if(error) + { + free(label); + return NULL; + } + + font_path = query_fc_match(); + + if(font_path) + error = FT_New_Face (label->library, font_path, 0, &label->face); + + if(!font_path || error) + { + font_path = FONT_FALLBACK; + error = FT_New_Face (label->library, font_path, 0, &label->face); + + if(error) + { + FT_Done_FreeType (label->library); + free(label); + return NULL; + } + } + + /* 12pt/100dpi as default */ + error = FT_Set_Char_Size (label->face, 12 << 6, 0, 100, 0); + if(error) + { + FT_Done_Face (label->face); + FT_Done_FreeType (label->library); + free(label); + return NULL; + } + + return label; +} + +static void +destroy_control (ply_label_plugin_control_t *label) +{ + if (label == NULL) + return; + + free (label->text); + FT_Done_Face (label->face); + FT_Done_FreeType (label->library); + + free (label); +} + +static long +get_width_of_control (ply_label_plugin_control_t *label) +{ + return label->area.width; +} + +static long +get_height_of_control (ply_label_plugin_control_t *label) +{ + return label->area.height; +} + +static FT_Int width_of_line(ply_label_plugin_control_t *label, + const char *text) +{ + FT_Int width = 0; + FT_Int last_left = 0; + + while(*text != '\0' && *text != '\n') + { + if(FT_Load_Char (label->face, *text, FT_LOAD_RENDER)) + continue; + + width += label->face->glyph->advance.x >> 6; + last_left = label->face->glyph->bitmap_left; + + ++text; + } + + return width + last_left; +} + +static void +size_control (ply_label_plugin_control_t *label) +{ + FT_Int width; + const char *text = label->text; + + if(label->is_hidden) + return; + + label->area.width = 0; + label->area.height = 0; + + /* Go through each line */ + while(text && *text) + { + width = width_of_line (label, text); + if((uint32_t) width > label->area.width) + label->area.width = width; + + label->area.height += (label->face->size->metrics.ascender - label->face->size->metrics.descender) >> 6; + + text = strchr (text, '\n'); + /* skip newline character */ + if (text) + ++text; + } + + /* If centered, area.x is not the origin anymore */ + if((long) label->area.width < label->width) + label->area.width = label->width; +} + +static void trigger_redraw (ply_label_plugin_control_t *label, + bool adjust_size) +{ + ply_rectangle_t dirty_area = label->area; + + if (label->is_hidden || label->display == NULL) + return; + + if(adjust_size) + size_control(label); + + ply_pixel_display_draw_area (label->display, + dirty_area.x, dirty_area.y, + dirty_area.width, dirty_area.height); +} + +static FT_Int +min (FT_Int a, + FT_Int b) +{ + return a < b ? a : b; +} + +static void +draw_bitmap (ply_label_plugin_control_t *label, + uint32_t *target, + ply_rectangle_t target_size, + FT_Bitmap *source, + FT_Int x_start, + FT_Int y_start) +{ + FT_Int x, y, xs, ys; + FT_Int x_end = min (x_start + source->width, target_size.width); + FT_Int y_end = min (y_start + source->rows, target_size.height); + + if((uint32_t) x_start >= target_size.width || (uint32_t) y_start >= target_size.height) + return; + + uint8_t rs, gs, bs, rd, gd, bd, ad; + rs = 255 * label->red; + gs = 255 * label->green; + bs = 255 * label->blue; + + for(y = y_start, ys = 0; y < y_end; ++y, ++ys) + for(x = x_start, xs = 0; x < x_end; ++x, ++xs) + { + float alpha = label->alpha * (source->buffer[xs + source->pitch * ys] / 255.0f); + float invalpha = 1.0f - alpha; + uint32_t dest = target[x + target_size.width * y]; + + /* Separate colors */ + rd = dest >> 16; + gd = dest >> 8; + bd = dest; + + /* Alpha blending */ + rd = invalpha * rd + alpha * rs; + gd = invalpha * gd + alpha * gs; + bd = invalpha * bd + alpha * bs; + /* Semi-correct: Disregard the target alpha */ + ad = alpha * 255; + + target[x + target_size.width * y] = (ad << 24) | (rd << 16) | (gd << 8) | bd; + } +} + +static void +draw_control (ply_label_plugin_control_t *label, + ply_pixel_buffer_t *pixel_buffer, + long x, + long y, + unsigned long width, + unsigned long height) +{ + FT_Error error; + FT_Vector pen; + FT_GlyphSlot slot; + const char *cur_c; + uint32_t *target; + ply_rectangle_t target_size; + + if (label->is_hidden) + return; + + /* Check for overlap. + TODO: Don't redraw everything if only a part should be drawn! */ + if(label->area.x > x + (long) width || label->area.y > y + (long) height + || label->area.x + (long) label->area.width < x + || label->area.y + (long) label->area.height < y) + return; + + slot = label->face->glyph; + + cur_c = label->text; + + target = ply_pixel_buffer_get_argb32_data (pixel_buffer); + ply_pixel_buffer_get_size (pixel_buffer, &target_size); + + if(target_size.height == 0) + return; /* This happens sometimes. */ + + /* 64ths of a pixel */ + pen.y = label->area.y << 6; + + /* Make sure that the first row fits */ + pen.y += label->face->size->metrics.ascender; + + /* Go through each line */ + while(*cur_c) + { + pen.x = label->area.x << 6; + + /* Start at start position (alignment) */ + if(label->alignment == PLY_LABEL_ALIGN_CENTER) + pen.x += (label->width - width_of_line(label, cur_c)) << 5; + else if(label->alignment == PLY_LABEL_ALIGN_RIGHT) + pen.x += (label->width - width_of_line(label, cur_c)) << 6; + + while(*cur_c && *cur_c != '\n') + { + /* TODO: Unicode support. */ + error = FT_Load_Char (label->face, *cur_c, FT_LOAD_RENDER | FT_LOAD_TARGET_LIGHT); + if(error) + continue; + + draw_bitmap (label, target, target_size, &slot->bitmap, + (pen.x >> 6) + slot->bitmap_left, + (pen.y >> 6) - slot->bitmap_top); + + pen.x += slot->advance.x; + pen.y += slot->advance.y; + + ++cur_c; + } + /* skip newline character */ + if (*cur_c) + ++cur_c; + + /* Next line */ + pen.y += label->face->size->metrics.height; + } +} + +static void +set_alignment_for_control (ply_label_plugin_control_t *label, + ply_label_alignment_t alignment) +{ + if (label->alignment != alignment) { + label->alignment = alignment; + trigger_redraw(label, true); + } +} + +static void +set_width_for_control (ply_label_plugin_control_t *label, + long width) +{ + if (label->width != width) { + label->width = width; + trigger_redraw(label, true); + } +} + +static void +set_text_for_control (ply_label_plugin_control_t *label, + const char *text) +{ + if (label->text != text) { + free (label->text); + label->text = strdup (text); + trigger_redraw(label, true); + } +} + +static void +set_font_for_control (ply_label_plugin_control_t *label, + const char *fontdesc) +{ + /* Only able to set size */ + + char *size_str_after; + const char *size_str; + unsigned long size; + bool size_in_pixels; + + size = 25; /* Default, if not set. */ + size_in_pixels = false; + + /* Format is "Family 1[,Family 2[,..]] [25[px]]" . + [] means optional. */ + size_str = strrchr(fontdesc, ' '); + + if(size_str) + { + size = strtoul(size_str, &size_str_after, 10); + if(size_str_after == size_str) + size = 25; /* Not a number */ + else if(strcmp(size_str_after, "px") == 0) + size_in_pixels = true; + } + + if(size_in_pixels) + FT_Set_Pixel_Sizes (label->face, 0, size); + else + FT_Set_Char_Size (label->face, size << 6, 0, 100, 0); + + /* Ignore errors, to keep the current size. */ + + trigger_redraw(label, true); +} + +static void +set_color_for_control (ply_label_plugin_control_t *label, + float red, + float green, + float blue, + float alpha) +{ + label->red = red; + label->green = green; + label->blue = blue; + label->alpha = alpha; + + trigger_redraw(label, false); +} + +static bool +show_control (ply_label_plugin_control_t *label, + ply_pixel_display_t *display, + long x, + long y) +{ + ply_rectangle_t dirty_area; + + dirty_area = label->area; + label->display = display; + label->area.x = x; + label->area.y = y; + + label->is_hidden = false; + + size_control (label); + + if (!label->is_hidden && label->display != NULL) + ply_pixel_display_draw_area (label->display, + dirty_area.x, dirty_area.y, + dirty_area.width, dirty_area.height); + + label->is_hidden = false; + + return true; +} + +static void +hide_control (ply_label_plugin_control_t *label) +{ + label->is_hidden = true; + if (label->display != NULL) + ply_pixel_display_draw_area (label->display, + label->area.x, label->area.y, + label->area.width, label->area.height); + + label->display = NULL; +} + +static bool +is_control_hidden (ply_label_plugin_control_t *label) +{ + return label->is_hidden; +} + +ply_label_plugin_interface_t * +ply_label_plugin_get_interface (void) +{ + static ply_label_plugin_interface_t plugin_interface = + { + .create_control = create_control, + .destroy_control = destroy_control, + .show_control = show_control, + .hide_control = hide_control, + .draw_control = draw_control, + .is_control_hidden = is_control_hidden, + .set_text_for_control = set_text_for_control, + .set_alignment_for_control = set_alignment_for_control, + .set_width_for_control = set_width_for_control, + .set_font_for_control = set_font_for_control, + .set_color_for_control = set_color_for_control, + .get_width_of_control = get_width_of_control, + .get_height_of_control = get_height_of_control + }; + + return &plugin_interface; +} + +/* vim: set ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */ -- 2.7.0