PIKApp/app/text/pikatext-vectors.c

260 lines
6.1 KiB
C

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* PikaText-vectors
* Copyright (C) 2003 Sven Neumann <sven@gimp.org>
*
* 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 3 of the License, 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, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <pango/pangocairo.h>
#include <gegl.h>
#include "text-types.h"
#include "core/pika.h"
#include "core/pikaimage.h"
#include "vectors/pikabezierstroke.h"
#include "vectors/pikavectors.h"
#include "vectors/pikaanchor.h"
#include "pikatext.h"
#include "pikatext-vectors.h"
#include "pikatextlayout.h"
#include "pikatextlayout-render.h"
typedef struct
{
PikaVectors *vectors;
PikaStroke *stroke;
PikaAnchor *anchor;
} RenderContext;
static void pika_text_render_vectors (cairo_t *cr,
RenderContext *context);
PikaVectors *
pika_text_vectors_new (PikaImage *image,
PikaText *text)
{
PikaVectors *vectors;
RenderContext context = { NULL, };
g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL);
g_return_val_if_fail (PIKA_IS_TEXT (text), NULL);
vectors = pika_vectors_new (image, NULL);
if (text->text || text->markup)
{
PikaTextLayout *layout;
cairo_surface_t *surface;
cairo_t *cr;
gdouble xres;
gdouble yres;
GError *error = NULL;
if (text->text)
pika_object_set_name_safe (PIKA_OBJECT (vectors), text->text);
context.vectors = vectors;
surface = cairo_recording_surface_create (CAIRO_CONTENT_ALPHA, NULL);
cr = cairo_create (surface);
pika_image_get_resolution (image, &xres, &yres);
layout = pika_text_layout_new (text, xres, yres, &error);
if (error)
{
pika_message_literal (image->pika, NULL, PIKA_MESSAGE_ERROR, error->message);
g_error_free (error);
}
pika_text_layout_render (layout, cr, text->base_dir, TRUE);
g_object_unref (layout);
pika_text_render_vectors (cr, &context);
cairo_destroy (cr);
cairo_surface_destroy (surface);
if (context.stroke)
pika_stroke_close (context.stroke);
}
return vectors;
}
static inline void
pika_text_vector_coords (const double x,
const double y,
PikaCoords *coords)
{
const PikaCoords default_values = PIKA_COORDS_DEFAULT_VALUES;
*coords = default_values;
coords->x = x;
coords->y = y;
}
static gint
moveto (RenderContext *context,
const double x,
const double y)
{
PikaCoords start;
#if PIKA_TEXT_DEBUG
g_printerr ("moveto %f, %f\n", x, y);
#endif
pika_text_vector_coords (x, y, &start);
if (context->stroke)
pika_stroke_close (context->stroke);
context->stroke = pika_bezier_stroke_new_moveto (&start);
pika_vectors_stroke_add (context->vectors, context->stroke);
g_object_unref (context->stroke);
return 0;
}
static gint
lineto (RenderContext *context,
const double x,
const double y)
{
PikaCoords end;
#if PIKA_TEXT_DEBUG
g_printerr ("lineto %f, %f\n", x, y);
#endif
if (! context->stroke)
return 0;
pika_text_vector_coords (x, y, &end);
pika_bezier_stroke_lineto (context->stroke, &end);
return 0;
}
static gint
cubicto (RenderContext *context,
const double x1,
const double y1,
const double x2,
const double y2,
const double x3,
const double y3)
{
PikaCoords control1;
PikaCoords control2;
PikaCoords end;
#if PIKA_TEXT_DEBUG
g_printerr ("cubicto %f, %f\n", x3, y3);
#endif
if (! context->stroke)
return 0;
pika_text_vector_coords (x1, y1, &control1);
pika_text_vector_coords (x2, y2, &control2);
pika_text_vector_coords (x3, y3, &end);
pika_bezier_stroke_cubicto (context->stroke, &control1, &control2, &end);
return 0;
}
static gint
closepath (RenderContext *context)
{
#if PIKA_TEXT_DEBUG
g_printerr ("moveto\n");
#endif
if (! context->stroke)
return 0;
pika_stroke_close (context->stroke);
context->stroke = NULL;
return 0;
}
static void
pika_text_render_vectors (cairo_t *cr,
RenderContext *context)
{
cairo_path_t *path;
gint i;
path = cairo_copy_path (cr);
for (i = 0; i < path->num_data; i += path->data[i].header.length)
{
cairo_path_data_t *data = &path->data[i];
/* if the drawing operation is the final moveto of the glyph,
* break to avoid creating an empty point. This is because cairo
* always adds a moveto after each closepath.
*/
if (i + data->header.length >= path->num_data)
break;
switch (data->header.type)
{
case CAIRO_PATH_MOVE_TO:
moveto (context, data[1].point.x, data[1].point.y);
break;
case CAIRO_PATH_LINE_TO:
lineto (context, data[1].point.x, data[1].point.y);
break;
case CAIRO_PATH_CURVE_TO:
cubicto (context,
data[1].point.x, data[1].point.y,
data[2].point.x, data[2].point.y,
data[3].point.x, data[3].point.y);
break;
case CAIRO_PATH_CLOSE_PATH:
closepath (context);
break;
}
}
cairo_path_destroy (path);
}