2006-12-10 05:33:38 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
1999-10-04 13:51:40 +08:00
|
|
|
* Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1999-10-04 13:51:40 +08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-18 06:28:01 +08:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
1999-10-04 13:51:40 +08:00
|
|
|
* (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
|
2018-07-12 05:27:07 +08:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
1999-10-04 13:51:40 +08:00
|
|
|
*/
|
|
|
|
|
2000-12-17 05:37:03 +08:00
|
|
|
#include "config.h"
|
|
|
|
|
2000-12-29 23:22:01 +08:00
|
|
|
#include <string.h>
|
|
|
|
|
2016-03-08 04:33:42 +08:00
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
2012-03-21 06:14:00 +08:00
|
|
|
#include <gegl.h>
|
2000-12-29 23:22:01 +08:00
|
|
|
|
2008-07-11 18:37:58 +08:00
|
|
|
#include <cairo.h>
|
2002-03-07 04:25:31 +08:00
|
|
|
|
2006-08-29 22:46:32 +08:00
|
|
|
#include "libgimpbase/gimpbase.h"
|
2001-01-24 07:56:18 +08:00
|
|
|
#include "libgimpmath/gimpmath.h"
|
|
|
|
|
2001-05-26 02:10:38 +08:00
|
|
|
#include "core-types.h"
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2016-03-08 04:33:42 +08:00
|
|
|
#include "gimpboundary.h"
|
2011-04-11 01:07:04 +08:00
|
|
|
#include "gimpbezierdesc.h"
|
2001-05-26 02:10:38 +08:00
|
|
|
#include "gimpscanconvert.h"
|
2000-12-29 23:22:01 +08:00
|
|
|
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2001-05-26 02:10:38 +08:00
|
|
|
struct _GimpScanConvert
|
1999-10-04 13:51:40 +08:00
|
|
|
{
|
2005-08-06 22:54:13 +08:00
|
|
|
gdouble ratio_xy;
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2005-08-08 20:05:54 +08:00
|
|
|
gboolean clip;
|
|
|
|
gint clip_x;
|
|
|
|
gint clip_y;
|
|
|
|
gint clip_w;
|
|
|
|
gint clip_h;
|
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
/* stroking options */
|
|
|
|
gboolean do_stroke;
|
|
|
|
gdouble width;
|
|
|
|
GimpJoinStyle join;
|
|
|
|
GimpCapStyle cap;
|
|
|
|
gdouble miter;
|
|
|
|
gdouble dash_offset;
|
|
|
|
GArray *dash_info;
|
2001-12-16 21:33:58 +08:00
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
GArray *path_data;
|
2004-09-16 08:46:02 +08:00
|
|
|
};
|
2003-10-01 02:06:19 +08:00
|
|
|
|
2008-05-09 16:05:01 +08:00
|
|
|
|
2001-05-26 02:10:38 +08:00
|
|
|
/* public functions */
|
2003-10-01 02:06:19 +08:00
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_new:
|
|
|
|
*
|
|
|
|
* Create a new scan conversion context.
|
|
|
|
*
|
2019-08-03 06:10:14 +08:00
|
|
|
* Returns: a newly allocated #GimpScanConvert context.
|
2005-08-23 00:08:08 +08:00
|
|
|
*/
|
2001-05-26 02:10:38 +08:00
|
|
|
GimpScanConvert *
|
2003-10-01 02:06:19 +08:00
|
|
|
gimp_scan_convert_new (void)
|
1999-10-04 13:51:40 +08:00
|
|
|
{
|
2007-05-23 18:22:09 +08:00
|
|
|
GimpScanConvert *sc = g_slice_new0 (GimpScanConvert);
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
sc->path_data = g_array_new (FALSE, FALSE, sizeof (cairo_path_data_t));
|
2005-07-31 19:53:32 +08:00
|
|
|
sc->ratio_xy = 1.0;
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2000-12-29 23:22:01 +08:00
|
|
|
return sc;
|
1999-10-04 13:51:40 +08:00
|
|
|
}
|
|
|
|
|
2016-03-08 04:33:42 +08:00
|
|
|
GimpScanConvert *
|
|
|
|
gimp_scan_convert_new_from_boundary (const GimpBoundSeg *bound_segs,
|
|
|
|
gint n_bound_segs,
|
|
|
|
gint offset_x,
|
|
|
|
gint offset_y)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (bound_segs == NULL || n_bound_segs != 0, NULL);
|
|
|
|
|
|
|
|
if (bound_segs)
|
|
|
|
{
|
|
|
|
GimpBoundSeg *stroke_segs;
|
|
|
|
gint n_stroke_segs;
|
|
|
|
|
|
|
|
stroke_segs = gimp_boundary_sort (bound_segs, n_bound_segs,
|
|
|
|
&n_stroke_segs);
|
|
|
|
|
|
|
|
if (stroke_segs)
|
|
|
|
{
|
|
|
|
GimpBezierDesc *bezier;
|
|
|
|
|
|
|
|
bezier = gimp_bezier_desc_new_from_bound_segs (stroke_segs,
|
|
|
|
n_bound_segs,
|
|
|
|
n_stroke_segs);
|
|
|
|
|
|
|
|
g_free (stroke_segs);
|
|
|
|
|
|
|
|
if (bezier)
|
|
|
|
{
|
|
|
|
GimpScanConvert *scan_convert;
|
|
|
|
|
|
|
|
scan_convert = gimp_scan_convert_new ();
|
|
|
|
|
|
|
|
gimp_bezier_desc_translate (bezier, offset_x, offset_y);
|
|
|
|
gimp_scan_convert_add_bezier (scan_convert, bezier);
|
|
|
|
|
|
|
|
gimp_bezier_desc_free (bezier);
|
|
|
|
|
|
|
|
return scan_convert;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_free:
|
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
*
|
|
|
|
* Frees the resources allocated for @sc.
|
|
|
|
*/
|
2000-12-29 23:22:01 +08:00
|
|
|
void
|
2001-05-26 02:10:38 +08:00
|
|
|
gimp_scan_convert_free (GimpScanConvert *sc)
|
2000-12-29 23:22:01 +08:00
|
|
|
{
|
2003-09-29 04:13:59 +08:00
|
|
|
g_return_if_fail (sc != NULL);
|
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
if (sc->path_data)
|
|
|
|
g_array_free (sc->path_data, TRUE);
|
2003-09-29 04:13:59 +08:00
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
if (sc->dash_info)
|
|
|
|
g_array_free (sc->dash_info, TRUE);
|
|
|
|
|
2007-05-23 18:22:09 +08:00
|
|
|
g_slice_free (GimpScanConvert, sc);
|
2000-12-29 23:22:01 +08:00
|
|
|
}
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_set_pixel_ratio:
|
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @ratio_xy: the aspect ratio of the major coordinate axes
|
|
|
|
*
|
|
|
|
* Sets the pixel aspect ratio.
|
2003-09-30 07:45:06 +08:00
|
|
|
*/
|
|
|
|
void
|
2003-09-30 09:03:25 +08:00
|
|
|
gimp_scan_convert_set_pixel_ratio (GimpScanConvert *sc,
|
|
|
|
gdouble ratio_xy)
|
2003-09-30 07:45:06 +08:00
|
|
|
{
|
|
|
|
g_return_if_fail (sc != NULL);
|
|
|
|
|
|
|
|
/* we only need the relative resolution */
|
2003-09-30 09:03:25 +08:00
|
|
|
sc->ratio_xy = ratio_xy;
|
2003-09-30 07:45:06 +08:00
|
|
|
}
|
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_set_clip_rectangle
|
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @x: horizontal offset of clip rectangle
|
|
|
|
* @y: vertical offset of clip rectangle
|
|
|
|
* @width: width of clip rectangle
|
|
|
|
* @height: height of clip rectangle
|
|
|
|
*
|
|
|
|
* Sets a clip rectangle on @sc. Subsequent render operations will be
|
|
|
|
* restricted to this area.
|
|
|
|
*/
|
2005-08-08 20:05:54 +08:00
|
|
|
void
|
|
|
|
gimp_scan_convert_set_clip_rectangle (GimpScanConvert *sc,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
g_return_if_fail (sc != NULL);
|
|
|
|
|
|
|
|
sc->clip = TRUE;
|
|
|
|
sc->clip_x = x;
|
|
|
|
sc->clip_y = y;
|
|
|
|
sc->clip_w = width;
|
|
|
|
sc->clip_h = height;
|
|
|
|
}
|
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_add_polyline:
|
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @n_points: number of points to add
|
|
|
|
* @points: array of points to add
|
|
|
|
* @closed: whether to close the polyline and make it a polygon
|
|
|
|
*
|
|
|
|
* Add a polyline with @n_points @points that may be open or closed.
|
2003-09-21 03:58:26 +08:00
|
|
|
*
|
2003-09-26 08:41:10 +08:00
|
|
|
* Please note that you should use gimp_scan_convert_stroke() if you
|
2003-09-21 03:58:26 +08:00
|
|
|
* specify open polygons.
|
|
|
|
*/
|
|
|
|
void
|
2008-05-22 03:11:42 +08:00
|
|
|
gimp_scan_convert_add_polyline (GimpScanConvert *sc,
|
|
|
|
guint n_points,
|
|
|
|
const GimpVector2 *points,
|
|
|
|
gboolean closed)
|
2003-09-21 03:58:26 +08:00
|
|
|
{
|
2008-05-09 17:10:22 +08:00
|
|
|
GimpVector2 prev = { 0.0, 0.0, };
|
2008-05-09 07:35:53 +08:00
|
|
|
cairo_path_data_t pd;
|
2008-05-09 17:10:22 +08:00
|
|
|
gint i;
|
2003-09-21 03:58:26 +08:00
|
|
|
|
|
|
|
g_return_if_fail (sc != NULL);
|
|
|
|
g_return_if_fail (points != NULL);
|
|
|
|
g_return_if_fail (n_points > 0);
|
|
|
|
|
|
|
|
for (i = 0; i < n_points; i++)
|
1999-10-04 13:51:40 +08:00
|
|
|
{
|
2003-09-21 03:58:26 +08:00
|
|
|
/* compress multiple identical coordinates */
|
|
|
|
if (i == 0 ||
|
|
|
|
prev.x != points[i].x ||
|
|
|
|
prev.y != points[i].y)
|
2003-05-21 09:01:09 +08:00
|
|
|
{
|
2008-05-09 07:35:53 +08:00
|
|
|
pd.header.type = (i == 0) ? CAIRO_PATH_MOVE_TO : CAIRO_PATH_LINE_TO;
|
|
|
|
pd.header.length = 2;
|
|
|
|
sc->path_data = g_array_append_val (sc->path_data, pd);
|
|
|
|
|
|
|
|
pd.point.x = points[i].x;
|
|
|
|
pd.point.y = points[i].y;
|
|
|
|
sc->path_data = g_array_append_val (sc->path_data, pd);
|
2003-09-21 03:58:26 +08:00
|
|
|
prev = points[i];
|
2003-05-21 09:01:09 +08:00
|
|
|
}
|
2003-09-21 03:58:26 +08:00
|
|
|
}
|
|
|
|
|
2004-01-28 07:38:03 +08:00
|
|
|
/* close the polyline when needed */
|
2008-05-09 07:35:53 +08:00
|
|
|
if (closed)
|
2004-01-21 09:29:00 +08:00
|
|
|
{
|
2008-05-09 07:35:53 +08:00
|
|
|
pd.header.type = CAIRO_PATH_CLOSE_PATH;
|
|
|
|
pd.header.length = 1;
|
|
|
|
sc->path_data = g_array_append_val (sc->path_data, pd);
|
2004-01-21 09:29:00 +08:00
|
|
|
}
|
2003-09-21 03:58:26 +08:00
|
|
|
}
|
|
|
|
|
2011-04-11 01:07:04 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_add_polyline:
|
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @bezier: a #GimpBezierDesc
|
|
|
|
*
|
|
|
|
* Adds a @bezier path to @sc.
|
|
|
|
*
|
|
|
|
* Please note that you should use gimp_scan_convert_stroke() if you
|
|
|
|
* specify open paths.
|
|
|
|
**/
|
|
|
|
void
|
|
|
|
gimp_scan_convert_add_bezier (GimpScanConvert *sc,
|
|
|
|
const GimpBezierDesc *bezier)
|
|
|
|
{
|
|
|
|
g_return_if_fail (sc != NULL);
|
|
|
|
g_return_if_fail (bezier != NULL);
|
|
|
|
|
|
|
|
sc->path_data = g_array_append_vals (sc->path_data,
|
|
|
|
bezier->data, bezier->num_data);
|
|
|
|
}
|
2003-09-21 03:58:26 +08:00
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_stroke:
|
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @width: line width in pixels
|
|
|
|
* @join: how lines should be joined
|
|
|
|
* @cap: how to render the end of lines
|
|
|
|
* @miter: convert a mitered join to a bevelled join if the miter would
|
|
|
|
* extend to a distance of more than @miter times @width from
|
|
|
|
* the actual join point
|
|
|
|
* @dash_offset: offset to apply on the dash pattern
|
2020-05-01 21:33:27 +08:00
|
|
|
* @dash_info: (nullable): dash pattern or %NULL for a solid line
|
2005-08-23 00:08:08 +08:00
|
|
|
*
|
|
|
|
* Stroke the content of a GimpScanConvert. The next
|
|
|
|
* gimp_scan_convert_render() will result in the outline of the
|
|
|
|
* polygon defined with the commands above.
|
2003-09-21 03:58:26 +08:00
|
|
|
*
|
|
|
|
* You cannot add additional polygons after this command.
|
2005-08-23 00:08:08 +08:00
|
|
|
*
|
|
|
|
* Note that if you have nonstandard resolution, "width" gives the
|
|
|
|
* width (in pixels) for a vertical stroke, i.e. use the X resolution
|
|
|
|
* to calculate the width of a stroke when operating with real world
|
|
|
|
* units.
|
2003-09-21 03:58:26 +08:00
|
|
|
*/
|
|
|
|
void
|
2003-09-21 18:45:19 +08:00
|
|
|
gimp_scan_convert_stroke (GimpScanConvert *sc,
|
2003-09-26 08:41:10 +08:00
|
|
|
gdouble width,
|
2003-09-21 18:45:19 +08:00
|
|
|
GimpJoinStyle join,
|
|
|
|
GimpCapStyle cap,
|
2003-09-26 08:41:10 +08:00
|
|
|
gdouble miter,
|
|
|
|
gdouble dash_offset,
|
|
|
|
GArray *dash_info)
|
2003-09-21 03:58:26 +08:00
|
|
|
{
|
2008-05-09 07:35:53 +08:00
|
|
|
sc->do_stroke = TRUE;
|
2014-06-03 02:17:39 +08:00
|
|
|
sc->width = width;
|
|
|
|
sc->join = join;
|
|
|
|
sc->cap = cap;
|
|
|
|
sc->miter = miter;
|
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
if (sc->dash_info)
|
|
|
|
{
|
|
|
|
g_array_free (sc->dash_info, TRUE);
|
|
|
|
sc->dash_info = NULL;
|
|
|
|
}
|
2008-05-09 07:35:53 +08:00
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
if (dash_info && dash_info->len >= 2)
|
2003-09-26 08:41:10 +08:00
|
|
|
{
|
2008-05-09 17:10:22 +08:00
|
|
|
gint n_dashes;
|
2003-09-26 08:41:10 +08:00
|
|
|
gdouble *dashes;
|
|
|
|
gint i;
|
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
dash_offset = dash_offset * MAX (width, 1.0);
|
2003-09-26 08:41:10 +08:00
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
n_dashes = dash_info->len;
|
2003-09-26 08:41:10 +08:00
|
|
|
dashes = g_new (gdouble, dash_info->len);
|
2003-09-29 04:13:59 +08:00
|
|
|
|
2005-07-31 19:53:32 +08:00
|
|
|
for (i = 0; i < dash_info->len ; i++)
|
2003-09-29 06:33:08 +08:00
|
|
|
dashes[i] = MAX (width, 1.0) * g_array_index (dash_info, gdouble, i);
|
2003-09-26 08:41:10 +08:00
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
/* correct 0.0 in the first element (starts with a gap) */
|
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
if (dashes[0] == 0.0)
|
2003-12-28 03:25:19 +08:00
|
|
|
{
|
|
|
|
gdouble first;
|
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
first = dashes[1];
|
2003-12-28 03:25:19 +08:00
|
|
|
|
|
|
|
/* shift the pattern to really starts with a dash and
|
|
|
|
* use the offset to skip into it.
|
|
|
|
*/
|
2005-07-31 19:53:32 +08:00
|
|
|
for (i = 0; i < dash_info->len - 2; i++)
|
2003-12-28 03:25:19 +08:00
|
|
|
{
|
2008-05-09 17:10:22 +08:00
|
|
|
dashes[i] = dashes[i+2];
|
|
|
|
dash_offset += dashes[i];
|
2003-12-28 03:25:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dash_info->len % 2 == 1)
|
|
|
|
{
|
2008-05-09 17:10:22 +08:00
|
|
|
dashes[dash_info->len - 2] = first;
|
|
|
|
n_dashes --;
|
2003-12-28 03:25:19 +08:00
|
|
|
}
|
2008-05-09 17:10:22 +08:00
|
|
|
else if (dash_info->len > 2)
|
|
|
|
{
|
|
|
|
dashes [dash_info->len - 3] += first;
|
|
|
|
n_dashes -= 2;
|
|
|
|
}
|
2003-12-28 03:25:19 +08:00
|
|
|
}
|
2004-09-16 08:46:02 +08:00
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
/* correct odd number of dash specifiers */
|
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
if (n_dashes % 2 == 1)
|
2003-12-28 03:25:19 +08:00
|
|
|
{
|
|
|
|
gdouble last;
|
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
last = dashes[n_dashes - 1];
|
|
|
|
dashes[0] += last;
|
|
|
|
dash_offset += last;
|
|
|
|
n_dashes --;
|
2003-12-28 03:25:19 +08:00
|
|
|
}
|
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
if (n_dashes >= 2)
|
2003-12-28 03:25:19 +08:00
|
|
|
{
|
2008-10-31 15:20:07 +08:00
|
|
|
sc->dash_info = g_array_sized_new (FALSE, FALSE,
|
|
|
|
sizeof (gdouble), n_dashes);
|
2008-05-09 17:10:22 +08:00
|
|
|
sc->dash_info = g_array_append_vals (sc->dash_info, dashes, n_dashes);
|
|
|
|
sc->dash_offset = dash_offset;
|
2003-12-28 03:25:19 +08:00
|
|
|
}
|
2003-09-26 08:41:10 +08:00
|
|
|
|
|
|
|
g_free (dashes);
|
|
|
|
}
|
2003-09-21 03:58:26 +08:00
|
|
|
}
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-05-21 09:01:09 +08:00
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_render:
|
2012-03-21 06:34:11 +08:00
|
|
|
* @sc: a #GimpScanConvert context
|
2018-04-26 02:31:11 +08:00
|
|
|
* @buffer: the #GeglBuffer to render to
|
2012-03-21 06:34:11 +08:00
|
|
|
* @off_x: horizontal offset into the @buffer
|
|
|
|
* @off_y: vertical offset into the @buffer
|
|
|
|
* @antialias: whether to apply antialiasiing
|
2005-08-23 00:08:08 +08:00
|
|
|
*
|
2008-05-09 19:56:58 +08:00
|
|
|
* This is a wrapper around gimp_scan_convert_render_full() that replaces the
|
2012-03-21 06:34:11 +08:00
|
|
|
* content of the @buffer with a rendered form of the path passed in.
|
2003-09-21 03:58:26 +08:00
|
|
|
*
|
|
|
|
* You cannot add additional polygons after this command.
|
|
|
|
*/
|
2003-09-21 18:45:19 +08:00
|
|
|
void
|
2003-09-21 03:58:26 +08:00
|
|
|
gimp_scan_convert_render (GimpScanConvert *sc,
|
2012-03-21 06:34:11 +08:00
|
|
|
GeglBuffer *buffer,
|
2003-10-07 00:43:05 +08:00
|
|
|
gint off_x,
|
|
|
|
gint off_y,
|
2003-10-01 02:06:19 +08:00
|
|
|
gboolean antialias)
|
2003-09-21 03:58:26 +08:00
|
|
|
{
|
2012-03-21 06:34:11 +08:00
|
|
|
gimp_scan_convert_render_full (sc, buffer, off_x, off_y,
|
2012-05-14 05:13:53 +08:00
|
|
|
TRUE, antialias, 1.0);
|
2005-07-29 09:48:56 +08:00
|
|
|
}
|
2003-09-21 18:45:19 +08:00
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_render_value:
|
2012-03-21 06:34:11 +08:00
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @buffer: the #GeglBuffer to render to
|
|
|
|
* @off_x: horizontal offset into the @buffer
|
|
|
|
* @off_y: vertical offset into the @buffer
|
|
|
|
* @value: value to use for covered pixels
|
2005-08-23 00:08:08 +08:00
|
|
|
*
|
2008-10-31 15:20:07 +08:00
|
|
|
* This is a wrapper around gimp_scan_convert_render_full() that
|
|
|
|
* doesn't do antialiasing but gives control over the value that
|
|
|
|
* should be used for pixels covered by the scan conversion. Uncovered
|
|
|
|
* pixels are set to zero.
|
2005-08-23 00:08:08 +08:00
|
|
|
*
|
|
|
|
* You cannot add additional polygons after this command.
|
|
|
|
*/
|
2005-07-29 09:48:56 +08:00
|
|
|
void
|
|
|
|
gimp_scan_convert_render_value (GimpScanConvert *sc,
|
2012-03-21 06:34:11 +08:00
|
|
|
GeglBuffer *buffer,
|
2005-07-29 09:48:56 +08:00
|
|
|
gint off_x,
|
|
|
|
gint off_y,
|
2012-05-14 05:13:53 +08:00
|
|
|
gdouble value)
|
2005-07-29 09:48:56 +08:00
|
|
|
{
|
2012-03-21 06:34:11 +08:00
|
|
|
gimp_scan_convert_render_full (sc, buffer, off_x, off_y,
|
2008-05-09 19:56:58 +08:00
|
|
|
TRUE, FALSE, value);
|
2005-07-29 09:48:56 +08:00
|
|
|
}
|
|
|
|
|
2005-08-23 00:08:08 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_compose:
|
2012-03-21 06:34:11 +08:00
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @buffer: the #GeglBuffer to render to
|
|
|
|
* @off_x: horizontal offset into the @buffer
|
|
|
|
* @off_y: vertical offset into the @buffer
|
2005-08-23 00:08:08 +08:00
|
|
|
*
|
2008-05-09 19:56:58 +08:00
|
|
|
* This is a wrapper around of gimp_scan_convert_render_full() that composes
|
2012-03-21 06:34:11 +08:00
|
|
|
* the (aliased) scan conversion on top of the content of the @buffer.
|
2005-08-23 00:08:08 +08:00
|
|
|
*
|
|
|
|
* You cannot add additional polygons after this command.
|
|
|
|
*/
|
2005-08-06 22:54:13 +08:00
|
|
|
void
|
|
|
|
gimp_scan_convert_compose (GimpScanConvert *sc,
|
2012-03-21 06:34:11 +08:00
|
|
|
GeglBuffer *buffer,
|
2005-08-06 22:54:13 +08:00
|
|
|
gint off_x,
|
|
|
|
gint off_y)
|
|
|
|
{
|
2012-03-21 06:34:11 +08:00
|
|
|
gimp_scan_convert_render_full (sc, buffer, off_x, off_y,
|
2012-05-14 05:13:53 +08:00
|
|
|
FALSE, FALSE, 1.0);
|
2008-05-09 19:18:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_scan_convert_compose_value:
|
2012-03-21 06:34:11 +08:00
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @buffer: the #GeglBuffer to render to
|
|
|
|
* @off_x: horizontal offset into the @buffer
|
|
|
|
* @off_y: vertical offset into the @buffer
|
|
|
|
* @value: value to use for covered pixels
|
2008-05-09 19:18:44 +08:00
|
|
|
*
|
2008-05-09 19:56:58 +08:00
|
|
|
* This is a wrapper around gimp_scan_convert_render_full() that
|
|
|
|
* composes the (aliased) scan conversion with value @value on top of the
|
2012-03-21 06:34:11 +08:00
|
|
|
* content of the @buffer.
|
2008-05-09 19:18:44 +08:00
|
|
|
*
|
|
|
|
* You cannot add additional polygons after this command.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
gimp_scan_convert_compose_value (GimpScanConvert *sc,
|
2012-03-21 06:34:11 +08:00
|
|
|
GeglBuffer *buffer,
|
2008-05-09 19:18:44 +08:00
|
|
|
gint off_x,
|
|
|
|
gint off_y,
|
2012-05-14 05:13:53 +08:00
|
|
|
gdouble value)
|
2008-05-09 19:18:44 +08:00
|
|
|
{
|
2012-03-21 06:34:11 +08:00
|
|
|
gimp_scan_convert_render_full (sc, buffer, off_x, off_y,
|
2008-05-09 19:56:58 +08:00
|
|
|
FALSE, FALSE, value);
|
2005-08-06 22:54:13 +08:00
|
|
|
}
|
|
|
|
|
2008-05-09 19:56:58 +08:00
|
|
|
/**
|
|
|
|
* gimp_scan_convert_render_full:
|
2012-03-21 06:34:11 +08:00
|
|
|
* @sc: a #GimpScanConvert context
|
|
|
|
* @buffer: the #GeglBuffer to render to
|
|
|
|
* @off_x: horizontal offset into the @buffer
|
|
|
|
* @off_y: vertical offset into the @buffer
|
|
|
|
* @replace: if true the original content of the @buffer gets estroyed
|
|
|
|
* @antialias: if true the rendering happens antialiased
|
|
|
|
* @value: value to use for covered pixels
|
2008-05-09 19:56:58 +08:00
|
|
|
*
|
2012-03-21 06:34:11 +08:00
|
|
|
* This function renders the area described by the path to the
|
|
|
|
* @buffer, taking the offset @off_x and @off_y in the buffer into
|
|
|
|
* account. The rendering can happen antialiased and be rendered on
|
|
|
|
* top of existing content or replacing it completely. The @value
|
|
|
|
* specifies the opacity value to be used for the objects in the @sc.
|
2008-05-09 19:56:58 +08:00
|
|
|
*
|
|
|
|
* You cannot add additional polygons after this command.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
gimp_scan_convert_render_full (GimpScanConvert *sc,
|
2012-03-21 06:34:11 +08:00
|
|
|
GeglBuffer *buffer,
|
2008-05-09 19:56:58 +08:00
|
|
|
gint off_x,
|
|
|
|
gint off_y,
|
|
|
|
gboolean replace,
|
|
|
|
gboolean antialias,
|
2012-05-14 05:13:53 +08:00
|
|
|
gdouble value)
|
2005-07-29 09:48:56 +08:00
|
|
|
{
|
2012-03-21 06:14:00 +08:00
|
|
|
const Babl *format;
|
|
|
|
GeglBufferIterator *iter;
|
|
|
|
GeglRectangle *roi;
|
|
|
|
cairo_t *cr;
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
cairo_path_t path;
|
|
|
|
gint bpp;
|
|
|
|
gint x, y;
|
|
|
|
gint width, height;
|
2003-12-28 03:25:19 +08:00
|
|
|
|
2008-05-09 19:56:58 +08:00
|
|
|
g_return_if_fail (sc != NULL);
|
2012-03-21 06:34:11 +08:00
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (buffer));
|
2008-05-09 19:56:58 +08:00
|
|
|
|
2020-02-04 01:59:35 +08:00
|
|
|
x = gegl_buffer_get_x (buffer);
|
|
|
|
y = gegl_buffer_get_y (buffer);
|
2012-03-21 06:34:11 +08:00
|
|
|
width = gegl_buffer_get_width (buffer);
|
|
|
|
height = gegl_buffer_get_height (buffer);
|
2005-08-08 20:05:54 +08:00
|
|
|
|
2009-03-12 04:56:07 +08:00
|
|
|
if (sc->clip && ! gimp_rectangle_intersect (x, y, width, height,
|
|
|
|
sc->clip_x, sc->clip_y,
|
|
|
|
sc->clip_w, sc->clip_h,
|
|
|
|
&x, &y, &width, &height))
|
2005-08-08 20:05:54 +08:00
|
|
|
return;
|
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
path.status = CAIRO_STATUS_SUCCESS;
|
|
|
|
path.data = (cairo_path_data_t *) sc->path_data->data;
|
|
|
|
path.num_data = sc->path_data->len;
|
2004-09-16 08:46:02 +08:00
|
|
|
|
2012-03-21 06:34:11 +08:00
|
|
|
format = babl_format ("Y u8");
|
|
|
|
bpp = babl_format_get_bytes_per_pixel (format);
|
2012-03-21 06:14:00 +08:00
|
|
|
|
2012-03-27 01:08:14 +08:00
|
|
|
iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
|
2018-09-11 06:19:39 +08:00
|
|
|
GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
|
|
|
|
roi = &iter->items[0].roi;
|
2012-03-21 06:14:00 +08:00
|
|
|
|
|
|
|
while (gegl_buffer_iterator_next (iter))
|
1999-10-04 13:51:40 +08:00
|
|
|
{
|
2018-09-11 06:19:39 +08:00
|
|
|
guchar *data = iter->items[0].data;
|
2009-03-12 04:56:07 +08:00
|
|
|
guchar *tmp_buf = NULL;
|
2012-05-14 04:57:58 +08:00
|
|
|
const gint stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8,
|
|
|
|
roi->width);
|
2008-05-09 16:05:01 +08:00
|
|
|
|
2012-03-17 08:12:36 +08:00
|
|
|
/* cairo rowstrides are always multiples of 4, whereas
|
|
|
|
* maskPR.rowstride can be anything, so to be able to create an
|
|
|
|
* image surface, we maybe have to create our own temporary
|
|
|
|
* buffer
|
|
|
|
*/
|
2012-03-21 06:14:00 +08:00
|
|
|
if (roi->width * bpp != stride)
|
2004-09-16 08:46:02 +08:00
|
|
|
{
|
2013-04-30 05:58:44 +08:00
|
|
|
tmp_buf = g_alloca (stride * roi->height);
|
2008-05-09 19:18:44 +08:00
|
|
|
|
2009-03-12 05:04:13 +08:00
|
|
|
if (! replace)
|
2008-05-09 19:18:44 +08:00
|
|
|
{
|
2013-04-30 05:58:44 +08:00
|
|
|
const guchar *src = data;
|
|
|
|
guchar *dest = tmp_buf;
|
|
|
|
gint i;
|
2009-03-12 04:56:07 +08:00
|
|
|
|
2012-03-21 06:14:00 +08:00
|
|
|
for (i = 0; i < roi->height; i++)
|
2008-05-09 19:18:44 +08:00
|
|
|
{
|
2012-03-21 06:14:00 +08:00
|
|
|
memcpy (dest, src, roi->width * bpp);
|
2008-05-09 19:18:44 +08:00
|
|
|
|
2012-03-21 06:14:00 +08:00
|
|
|
src += roi->width * bpp;
|
2008-05-09 19:18:44 +08:00
|
|
|
dest += stride;
|
|
|
|
}
|
|
|
|
}
|
2008-05-09 07:35:53 +08:00
|
|
|
}
|
2004-09-16 19:31:30 +08:00
|
|
|
|
2008-05-09 16:05:01 +08:00
|
|
|
surface = cairo_image_surface_create_for_data (tmp_buf ?
|
2012-03-21 06:14:00 +08:00
|
|
|
tmp_buf : data,
|
2008-05-09 07:35:53 +08:00
|
|
|
CAIRO_FORMAT_A8,
|
2012-03-21 06:14:00 +08:00
|
|
|
roi->width, roi->height,
|
2008-05-09 16:05:01 +08:00
|
|
|
stride);
|
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
cairo_surface_set_device_offset (surface,
|
2012-03-21 06:14:00 +08:00
|
|
|
-off_x - roi->x,
|
|
|
|
-off_y - roi->y);
|
2008-05-09 07:35:53 +08:00
|
|
|
cr = cairo_create (surface);
|
2008-05-09 19:18:44 +08:00
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
2009-03-12 04:56:07 +08:00
|
|
|
|
2008-05-09 19:18:44 +08:00
|
|
|
if (replace)
|
|
|
|
{
|
|
|
|
cairo_set_source_rgba (cr, 0, 0, 0, 0);
|
|
|
|
cairo_paint (cr);
|
|
|
|
}
|
2009-03-12 04:56:07 +08:00
|
|
|
|
2012-05-14 05:13:53 +08:00
|
|
|
cairo_set_source_rgba (cr, 0, 0, 0, value);
|
2008-05-09 07:35:53 +08:00
|
|
|
cairo_append_path (cr, &path);
|
2009-03-12 05:04:13 +08:00
|
|
|
|
2008-10-31 15:20:07 +08:00
|
|
|
cairo_set_antialias (cr, antialias ?
|
|
|
|
CAIRO_ANTIALIAS_GRAY : CAIRO_ANTIALIAS_NONE);
|
2008-05-09 19:56:58 +08:00
|
|
|
cairo_set_miter_limit (cr, sc->miter);
|
2009-03-12 04:56:07 +08:00
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
if (sc->do_stroke)
|
|
|
|
{
|
2009-03-12 04:56:07 +08:00
|
|
|
cairo_set_line_cap (cr,
|
|
|
|
sc->cap == GIMP_CAP_BUTT ? CAIRO_LINE_CAP_BUTT :
|
|
|
|
sc->cap == GIMP_CAP_ROUND ? CAIRO_LINE_CAP_ROUND :
|
|
|
|
CAIRO_LINE_CAP_SQUARE);
|
|
|
|
cairo_set_line_join (cr,
|
|
|
|
sc->join == GIMP_JOIN_MITER ? CAIRO_LINE_JOIN_MITER :
|
|
|
|
sc->join == GIMP_JOIN_ROUND ? CAIRO_LINE_JOIN_ROUND :
|
|
|
|
CAIRO_LINE_JOIN_BEVEL);
|
2008-05-09 16:05:01 +08:00
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
cairo_set_line_width (cr, sc->width);
|
2009-03-12 04:56:07 +08:00
|
|
|
|
2008-05-09 17:10:22 +08:00
|
|
|
if (sc->dash_info)
|
2008-10-31 15:20:07 +08:00
|
|
|
cairo_set_dash (cr,
|
|
|
|
(double *) sc->dash_info->data,
|
|
|
|
sc->dash_info->len,
|
|
|
|
sc->dash_offset);
|
2009-03-12 04:56:07 +08:00
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
cairo_scale (cr, 1.0, sc->ratio_xy);
|
|
|
|
cairo_stroke (cr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
|
|
|
|
cairo_fill (cr);
|
2004-09-16 08:46:02 +08:00
|
|
|
}
|
2008-05-09 16:05:01 +08:00
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
cairo_destroy (cr);
|
|
|
|
cairo_surface_destroy (surface);
|
2004-09-16 08:46:02 +08:00
|
|
|
|
2008-05-09 07:35:53 +08:00
|
|
|
if (tmp_buf)
|
|
|
|
{
|
2008-05-09 16:05:01 +08:00
|
|
|
const guchar *src = tmp_buf;
|
2013-04-30 05:58:44 +08:00
|
|
|
guchar *dest = data;
|
2008-05-09 16:05:01 +08:00
|
|
|
gint i;
|
|
|
|
|
2012-03-21 06:14:00 +08:00
|
|
|
for (i = 0; i < roi->height; i++)
|
2008-05-09 16:05:01 +08:00
|
|
|
{
|
2012-03-21 06:14:00 +08:00
|
|
|
memcpy (dest, src, roi->width * bpp);
|
2004-09-16 19:31:30 +08:00
|
|
|
|
2008-05-09 16:05:01 +08:00
|
|
|
src += stride;
|
2012-03-21 06:14:00 +08:00
|
|
|
dest += roi->width * bpp;
|
2008-05-09 16:05:01 +08:00
|
|
|
}
|
2008-05-09 07:35:53 +08:00
|
|
|
}
|
2004-09-16 08:46:02 +08:00
|
|
|
}
|
|
|
|
}
|