/* This file is an image processing operation for GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see .
*
* Copyright 2006 Øyvind Kolås
*/
#include "config.h"
#include
#ifdef GEGL_CHANT_PROPERTIES
gegl_chant_multiline (string, _("Text"), "Hello",
_("String to display (utf8)"))
gegl_chant_string (font, _("Font family"), "Sans",
_("Font family (utf8)"))
gegl_chant_double (size, _("Size"), 1.0, 2048.0, 10.0,
_("Approximate height of text in pixels."))
gegl_chant_color (color, _("Color"), "black",
_("Color for the text (defaults to 'white')"))
gegl_chant_int (wrap, _("Wrap width"), -1, 1000000, -1,
_("Sets the width in pixels at which long lines will wrap. "
"Use -1 for no wrapping."))
gegl_chant_int (alignment, _("Justification"), 0, 2, 0,
_("Alignment for multi-line text (0=Left, 1=Center, 2=Right)"))
gegl_chant_int (width, _("Width"), 0, 1000000, 0,
_("Rendered width in pixels. (read only)"))
gegl_chant_int (height, _("Height"), 0, 1000000, 0,
_("Rendered height in pixels. (read only)"))
#else
#include
#include
#include
#include
/* XXX: this struct is unneeded and could be folded directly into
* struct _GeglChant
*/
typedef struct {
gchar *string;
gchar *font;
gdouble size;
gint wrap;
gint alignment;
GeglRectangle defined;
} CachedExtent;
struct _GeglChant
{
GeglOperationSource parent_instance;
gpointer properties;
CachedExtent cex;
};
typedef struct
{
GeglOperationSourceClass parent_class;
} GeglChantClass;
#define GEGL_CHANT_C_FILE "text.c"
#include "gegl-chant.h"
GEGL_DEFINE_DYNAMIC_OPERATION (GEGL_TYPE_OPERATION_SOURCE)
static void text_layout_text (GeglChant *self,
cairo_t *cr,
gdouble rowstride,
gdouble *width,
gdouble *height)
{
GeglChantO *o = GEGL_CHANT_PROPERTIES (self);
PangoFontDescription *desc;
PangoLayout *layout;
PangoAttrList *attrs;
PangoAttribute *attr = NULL;
gchar *string;
gfloat color[4];
gint alignment = 0;
/* Create a PangoLayout, set the font and text */
layout = pango_cairo_create_layout (cr);
string = g_strcompress (o->string);
pango_layout_set_text (layout, string, -1);
g_free (string);
desc = pango_font_description_from_string (o->font);
pango_font_description_set_absolute_size (desc, o->size * PANGO_SCALE);
pango_layout_set_font_description (layout, desc);
switch (o->alignment)
{
case 0:
alignment = PANGO_ALIGN_LEFT;
break;
case 1:
alignment = PANGO_ALIGN_CENTER;
break;
case 2:
alignment = PANGO_ALIGN_RIGHT;
break;
}
pango_layout_set_alignment (layout, alignment);
pango_layout_set_width (layout, o->wrap * PANGO_SCALE);
attrs = pango_attr_list_new ();
if (attrs)
{
gegl_color_get_pixel (o->color, babl_format ("RGBA float"), color);
attr = pango_attr_foreground_new ((guint16) (color[0] * 65535),
(guint16) (color[1] * 65535),
(guint16) (color[2] * 65535));
if (attr)
{
attr->start_index = 0;
attr->end_index = -1;
pango_attr_list_insert (attrs, attr);
pango_layout_set_attributes (layout, attrs);
}
}
/* Inform Pango to re-layout the text with the new transformation */
pango_cairo_update_layout (cr, layout);
if (width && height)
{
int w, h;
pango_layout_get_pixel_size (layout, &w, &h);
*width = (gdouble)w;
*height = (gdouble)h;
}
else
{
/* FIXME: This feels like a hack but it stops the rendered text */
/* from shifting position depending on the value of 'alignment'. */
if (o->alignment == 1)
cairo_move_to (cr, o->width / 2, 0);
else
{
if (o->alignment == 2)
cairo_move_to (cr, o->width, 0);
}
pango_cairo_show_layout (cr, layout);
}
pango_font_description_free (desc);
pango_attr_list_unref (attrs);
g_object_unref (layout);
}
static gboolean
process (GeglOperation *operation,
GeglBuffer *output,
const GeglRectangle *result,
gint level)
{
GeglChant *self = GEGL_CHANT (operation);
guchar *data = g_new0 (guchar, result->width * result->height * 4);
cairo_t *cr;
cairo_surface_t *surface;
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
result->width,
result->height,
result->width * 4);
cr = cairo_create (surface);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
cairo_translate (cr, -result->x, -result->y);
text_layout_text (self, cr, 0, NULL, NULL);
gegl_buffer_set (output, result, 0, babl_format ("B'aG'aR'aA u8"), data,
GEGL_AUTO_ROWSTRIDE);
cairo_destroy (cr);
cairo_surface_destroy (surface);
g_free (data);
return TRUE;
}
static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
GeglChant *self = GEGL_CHANT (operation);
GeglChantO *o = GEGL_CHANT_PROPERTIES (self);
CachedExtent *extent;
gint status = FALSE;
extent = (CachedExtent*)&self->cex;
/*if (!self->priv)
{
self->priv = g_malloc0 (sizeof (CachedExtent));
extent = (CachedExtent*)self->priv;
extent->string = g_strdup ("");
extent->font = g_strdup ("");
}*/
if ((extent->string && strcmp (extent->string, o->string)) ||
(extent->font && strcmp (extent->font, o->font)) ||
extent->size != o->size ||
extent->wrap != o->wrap ||
extent->alignment != o->alignment)
{ /* get extents */
cairo_t *cr;
gdouble width, height;
cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
1, 1);
cr = cairo_create (surface);
text_layout_text (self, cr, 0, &width, &height);
cairo_destroy (cr);
cairo_surface_destroy (surface);
extent->defined.width = width;
extent->defined.height = height;
if (extent->string)
g_free (extent->string);
extent->string = g_strdup (o->string);
if (extent->font)
g_free (extent->font);
extent->font = g_strdup (o->font);
extent->size = o->size;
extent->wrap = o->wrap;
extent->alignment = o->alignment;
/* store the measured size for later use */
o->width = width;
o->height = height;
gegl_operation_invalidate (operation, NULL, TRUE);
}
if (status)
{
g_warning ("get defined region for text '%s' failed", o->string);
}
return extent->defined;
}
static void
finalize (GObject *object)
{
GeglChant *self = GEGL_CHANT (object);
if (self->cex.string)
g_free (self->cex.string);
if (self->cex.font)
g_free (self->cex.font);
G_OBJECT_CLASS (gegl_chant_parent_class)->finalize (object);
}
static void
prepare (GeglOperation *operation)
{
gegl_operation_set_format (operation, "output", babl_format ("RaGaBaA float"));
}
static void
gegl_chant_class_init (GeglChantClass *klass)
{
GObjectClass *object_class;
GeglOperationClass *operation_class;
GeglOperationSourceClass *operation_source_class;
object_class = G_OBJECT_CLASS (klass);
operation_class = GEGL_OPERATION_CLASS (klass);
operation_source_class = GEGL_OPERATION_SOURCE_CLASS (klass);
object_class->finalize = finalize;
operation_class->prepare = prepare;
operation_class->get_bounding_box = get_bounding_box;
operation_source_class->process = process;
gegl_operation_class_set_keys (operation_class,
"name" , "gegl:text",
"categories" , "render",
"description" , _("Display a string of text using pango and cairo."),
NULL);
}
#endif