1999-10-04 13:51:40 +08:00
|
|
|
/* The GIMP -- an image manipulation program
|
|
|
|
* Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
|
|
|
* 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 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, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2000-12-17 05:37:03 +08:00
|
|
|
#include "config.h"
|
|
|
|
|
2000-12-29 23:22:01 +08:00
|
|
|
#include <string.h>
|
|
|
|
|
2001-08-14 22:53:55 +08:00
|
|
|
#include <glib-object.h>
|
2000-12-29 23:22:01 +08:00
|
|
|
|
2002-04-09 19:39:04 +08:00
|
|
|
#include <libart_lgpl/libart.h>
|
2004-01-27 05:19:17 +08:00
|
|
|
#include <libart_lgpl/art_svp_intersect.h>
|
2002-03-07 04:25:31 +08:00
|
|
|
|
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
|
|
|
|
2001-05-15 19:25:25 +08:00
|
|
|
#include "base/pixel-region.h"
|
2003-09-21 03:58:26 +08:00
|
|
|
#include "base/tile-manager.h"
|
2001-05-15 19:25:25 +08:00
|
|
|
|
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
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
/* stuff necessary for the _add_polygons API... :-/ */
|
2005-08-06 22:54:13 +08:00
|
|
|
gboolean got_first;
|
|
|
|
gboolean need_closing;
|
|
|
|
GimpVector2 first;
|
|
|
|
GimpVector2 prev;
|
2001-12-16 21:33:58 +08:00
|
|
|
|
2005-08-06 22:54:13 +08:00
|
|
|
gboolean have_open;
|
|
|
|
guint num_nodes;
|
|
|
|
ArtVpath *vpath;
|
2003-09-21 03:58:26 +08:00
|
|
|
|
2005-08-06 22:54:13 +08:00
|
|
|
ArtSVP *svp; /* Sorted vector path
|
2003-09-21 03:58:26 +08:00
|
|
|
(extension no longer possible) */
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2004-09-16 08:46:02 +08:00
|
|
|
/* stuff necessary for the rendering callback */
|
2005-08-06 22:54:13 +08:00
|
|
|
GimpChannelOps op;
|
|
|
|
guchar *buf;
|
|
|
|
gint rowstride;
|
|
|
|
gint x0, x1;
|
|
|
|
gboolean antialias;
|
|
|
|
gboolean value;
|
2004-09-16 08:46:02 +08:00
|
|
|
};
|
2003-10-01 02:06:19 +08:00
|
|
|
|
2005-07-29 09:48:56 +08:00
|
|
|
|
2003-10-01 02:06:19 +08:00
|
|
|
/* private functions */
|
|
|
|
|
2005-07-29 09:48:56 +08:00
|
|
|
static void gimp_scan_convert_render_internal (GimpScanConvert *sc,
|
2005-08-06 22:54:13 +08:00
|
|
|
GimpChannelOps op,
|
2005-07-29 09:48:56 +08:00
|
|
|
TileManager *tile_manager,
|
|
|
|
gint off_x,
|
|
|
|
gint off_y,
|
|
|
|
gboolean antialias,
|
|
|
|
guchar value);
|
2003-10-01 02:06:19 +08:00
|
|
|
static void gimp_scan_convert_finish (GimpScanConvert *sc);
|
|
|
|
static void gimp_scan_convert_close_add_points (GimpScanConvert *sc);
|
|
|
|
|
2005-08-06 22:54:13 +08:00
|
|
|
static void gimp_scan_convert_render_callback (gpointer user_data,
|
|
|
|
gint y,
|
|
|
|
gint start_value,
|
|
|
|
ArtSVPRenderAAStep *steps,
|
|
|
|
gint n_steps);
|
|
|
|
static void gimp_scan_convert_compose_callback (gpointer user_data,
|
|
|
|
gint y,
|
|
|
|
gint start_value,
|
|
|
|
ArtSVPRenderAAStep *steps,
|
|
|
|
gint n_steps);
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2001-05-26 02:10:38 +08:00
|
|
|
/* public functions */
|
2003-10-01 02:06:19 +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
|
|
|
{
|
2001-05-26 02:10:38 +08:00
|
|
|
GimpScanConvert *sc;
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2001-05-26 02:10:38 +08:00
|
|
|
sc = g_new0 (GimpScanConvert, 1);
|
1999-10-04 13:51:40 +08:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (sc->vpath)
|
|
|
|
art_free (sc->vpath);
|
|
|
|
if (sc->svp)
|
|
|
|
art_svp_free (sc->svp);
|
|
|
|
|
2000-12-29 23:22:01 +08:00
|
|
|
g_free (sc);
|
|
|
|
}
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-09-30 09:03:25 +08:00
|
|
|
/* set the Pixel-Ratio (width / height) for the pixels.
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-05-26 02:10:38 +08:00
|
|
|
/* Add "n_points" from "points" to the polygon currently being
|
2003-09-21 03:58:26 +08:00
|
|
|
* described by "scan_converter". DEPRECATED.
|
2001-05-26 02:10:38 +08:00
|
|
|
*/
|
1999-10-04 13:51:40 +08:00
|
|
|
void
|
2001-05-26 02:10:38 +08:00
|
|
|
gimp_scan_convert_add_points (GimpScanConvert *sc,
|
2003-05-21 09:01:09 +08:00
|
|
|
guint n_points,
|
|
|
|
GimpVector2 *points,
|
|
|
|
gboolean new_polygon)
|
1999-10-04 13:51:40 +08:00
|
|
|
{
|
2000-12-29 23:22:01 +08:00
|
|
|
gint i;
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2000-12-29 23:22:01 +08:00
|
|
|
g_return_if_fail (sc != NULL);
|
2001-05-26 02:10:38 +08:00
|
|
|
g_return_if_fail (points != NULL);
|
2003-09-21 03:58:26 +08:00
|
|
|
g_return_if_fail (n_points > 0);
|
|
|
|
g_return_if_fail (sc->svp == NULL);
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
/* We need an extra nodes to end the path */
|
2003-09-22 03:07:48 +08:00
|
|
|
sc->vpath = art_renew (sc->vpath, ArtVpath, sc->num_nodes + n_points + 1);
|
2003-05-21 09:01:09 +08:00
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
if (sc->num_nodes == 0 || new_polygon)
|
|
|
|
{
|
|
|
|
if (sc->need_closing)
|
|
|
|
gimp_scan_convert_close_add_points (sc);
|
2003-09-21 03:58:26 +08:00
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
sc->got_first = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We have to compress multiple identical coordinates */
|
2003-09-29 04:13:59 +08:00
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
for (i = 0; i < n_points; i++)
|
2003-05-21 09:01:09 +08:00
|
|
|
{
|
2003-09-29 04:13:59 +08:00
|
|
|
if (sc->got_first == FALSE ||
|
2003-09-22 03:07:48 +08:00
|
|
|
sc->prev.x != points[i].x || sc->prev.y != points[i].y)
|
2003-05-21 09:01:09 +08:00
|
|
|
{
|
2003-09-22 03:07:48 +08:00
|
|
|
sc->vpath[sc->num_nodes].code = ((! sc->got_first) || new_polygon) ?
|
|
|
|
ART_MOVETO : ART_LINETO;
|
2003-09-21 03:58:26 +08:00
|
|
|
sc->vpath[sc->num_nodes].x = points[i].x;
|
|
|
|
sc->vpath[sc->num_nodes].y = points[i].y;
|
2003-05-21 09:01:09 +08:00
|
|
|
sc->num_nodes++;
|
2003-09-22 03:07:48 +08:00
|
|
|
sc->prev = points[i];
|
|
|
|
|
|
|
|
if (!sc->got_first)
|
|
|
|
{
|
|
|
|
sc->got_first = TRUE;
|
|
|
|
sc->first = points[i];
|
|
|
|
}
|
2003-05-21 09:01:09 +08:00
|
|
|
}
|
|
|
|
}
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
sc->need_closing = TRUE;
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
sc->vpath[sc->num_nodes].code = ART_END;
|
2004-01-27 05:19:17 +08:00
|
|
|
sc->vpath[sc->num_nodes].x = 0.0;
|
|
|
|
sc->vpath[sc->num_nodes].y = 0.0;
|
2003-09-21 03:58:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
static void
|
|
|
|
gimp_scan_convert_close_add_points (GimpScanConvert *sc)
|
|
|
|
{
|
2003-09-29 04:13:59 +08:00
|
|
|
if (sc->need_closing &&
|
2003-09-22 03:07:48 +08:00
|
|
|
(sc->prev.x != sc->first.x || sc->prev.y != sc->first.y))
|
|
|
|
{
|
|
|
|
sc->vpath = art_renew (sc->vpath, ArtVpath, sc->num_nodes + 2);
|
|
|
|
sc->vpath[sc->num_nodes].code = ART_LINETO;
|
|
|
|
sc->vpath[sc->num_nodes].x = sc->first.x;
|
|
|
|
sc->vpath[sc->num_nodes].y = sc->first.y;
|
|
|
|
sc->num_nodes++;
|
|
|
|
sc->vpath[sc->num_nodes].code = ART_END;
|
2004-01-27 05:19:17 +08:00
|
|
|
sc->vpath[sc->num_nodes].x = 0.0;
|
|
|
|
sc->vpath[sc->num_nodes].y = 0.0;
|
2003-09-22 03:07:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
sc->need_closing = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
/* Add a polygon with "npoints" "points" that may be open or closed.
|
|
|
|
* It is not recommended to mix gimp_scan_convert_add_polyline with
|
|
|
|
* gimp_scan_convert_add_points.
|
|
|
|
*
|
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
|
|
|
|
gimp_scan_convert_add_polyline (GimpScanConvert *sc,
|
|
|
|
guint n_points,
|
|
|
|
GimpVector2 *points,
|
|
|
|
gboolean closed)
|
|
|
|
{
|
2005-07-29 09:48:56 +08:00
|
|
|
GimpVector2 prev = { 0.0, 0.0, };
|
|
|
|
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);
|
|
|
|
g_return_if_fail (sc->svp == NULL);
|
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
if (sc->need_closing)
|
|
|
|
gimp_scan_convert_close_add_points (sc);
|
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
if (!closed)
|
|
|
|
sc->have_open = TRUE;
|
|
|
|
|
2004-01-21 09:29:00 +08:00
|
|
|
/* make sure that we have enough space for the nodes */
|
2003-09-21 03:58:26 +08:00
|
|
|
sc->vpath = art_renew (sc->vpath, ArtVpath,
|
|
|
|
sc->num_nodes + n_points + 2);
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2003-09-21 03:58:26 +08:00
|
|
|
sc->vpath[sc->num_nodes].code = (i == 0 ? (closed ?
|
|
|
|
ART_MOVETO :
|
|
|
|
ART_MOVETO_OPEN) :
|
|
|
|
ART_LINETO);
|
2003-05-21 09:01:09 +08:00
|
|
|
sc->vpath[sc->num_nodes].x = points[i].x;
|
|
|
|
sc->vpath[sc->num_nodes].y = points[i].y;
|
|
|
|
sc->num_nodes++;
|
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 */
|
|
|
|
if (closed && (prev.x != points[0].x ||
|
|
|
|
prev.y != points[0].y))
|
2004-01-21 09:29:00 +08:00
|
|
|
{
|
2004-01-28 07:38:03 +08:00
|
|
|
sc->vpath[sc->num_nodes].x = points[0].x;
|
|
|
|
sc->vpath[sc->num_nodes].y = points[0].y;
|
2004-01-21 09:29:00 +08:00
|
|
|
sc->vpath[sc->num_nodes].code = ART_LINETO;
|
|
|
|
sc->num_nodes++;
|
|
|
|
}
|
2004-01-28 07:38:03 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
sc->vpath[sc->num_nodes].code = ART_END;
|
2004-01-27 05:19:17 +08:00
|
|
|
sc->vpath[sc->num_nodes].x = 0.0;
|
|
|
|
sc->vpath[sc->num_nodes].y = 0.0;
|
2003-09-21 03:58:26 +08:00
|
|
|
|
|
|
|
/* If someone wants to mix this function with _add_points ()
|
|
|
|
* try to do something reasonable...
|
|
|
|
*/
|
|
|
|
|
|
|
|
sc->got_first = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Stroke the content of a GimpScanConvert. The next
|
2003-10-01 02:06:19 +08:00
|
|
|
* gimp_scan_convert_render() will result in the outline of the polygon
|
2003-09-21 03:58:26 +08:00
|
|
|
* defined with the commands above.
|
|
|
|
*
|
|
|
|
* You cannot add additional polygons after this command.
|
|
|
|
*/
|
|
|
|
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
|
|
|
{
|
|
|
|
ArtSVP *stroke;
|
2003-09-21 18:45:19 +08:00
|
|
|
ArtPathStrokeJoinType artjoin = 0;
|
|
|
|
ArtPathStrokeCapType artcap = 0;
|
2003-05-21 09:01:09 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
g_return_if_fail (sc->svp == NULL);
|
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
if (sc->need_closing)
|
|
|
|
gimp_scan_convert_close_add_points (sc);
|
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
switch (join)
|
|
|
|
{
|
|
|
|
case GIMP_JOIN_MITER:
|
2003-10-01 02:06:19 +08:00
|
|
|
artjoin = ART_PATH_STROKE_JOIN_MITER;
|
|
|
|
break;
|
2003-09-21 03:58:26 +08:00
|
|
|
case GIMP_JOIN_ROUND:
|
2003-10-01 02:06:19 +08:00
|
|
|
artjoin = ART_PATH_STROKE_JOIN_ROUND;
|
|
|
|
break;
|
2003-09-21 03:58:26 +08:00
|
|
|
case GIMP_JOIN_BEVEL:
|
2003-10-01 02:06:19 +08:00
|
|
|
artjoin = ART_PATH_STROKE_JOIN_BEVEL;
|
|
|
|
break;
|
1999-10-04 13:51:40 +08:00
|
|
|
}
|
1999-10-06 02:05:34 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
switch (cap)
|
1999-10-04 13:51:40 +08:00
|
|
|
{
|
2003-09-21 03:58:26 +08:00
|
|
|
case GIMP_CAP_BUTT:
|
2003-10-01 02:06:19 +08:00
|
|
|
artcap = ART_PATH_STROKE_CAP_BUTT;
|
|
|
|
break;
|
2003-09-21 03:58:26 +08:00
|
|
|
case GIMP_CAP_ROUND:
|
2003-10-01 02:06:19 +08:00
|
|
|
artcap = ART_PATH_STROKE_CAP_ROUND;
|
|
|
|
break;
|
2003-09-21 03:58:26 +08:00
|
|
|
case GIMP_CAP_SQUARE:
|
2003-10-01 02:06:19 +08:00
|
|
|
artcap = ART_PATH_STROKE_CAP_SQUARE;
|
|
|
|
break;
|
1999-10-04 13:51:40 +08:00
|
|
|
}
|
|
|
|
|
2003-09-30 09:03:25 +08:00
|
|
|
if (sc->ratio_xy != 1.0)
|
2003-09-30 07:45:06 +08:00
|
|
|
{
|
|
|
|
gint i;
|
2003-10-01 02:06:19 +08:00
|
|
|
|
2003-09-30 07:45:06 +08:00
|
|
|
for (i = 0; i < sc->num_nodes; i++)
|
2003-10-01 02:06:19 +08:00
|
|
|
sc->vpath[i].x *= sc->ratio_xy;
|
2003-09-30 07:45:06 +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
|
|
|
{
|
|
|
|
ArtVpath *dash_vpath;
|
|
|
|
ArtVpathDash dash;
|
|
|
|
gdouble *dashes;
|
|
|
|
gint i;
|
|
|
|
|
2003-09-29 06:33:08 +08:00
|
|
|
dash.offset = dash_offset * MAX (width, 1.0);
|
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
|
|
|
|
|
|
|
dash.n_dash = dash_info->len;
|
|
|
|
dash.dash = dashes;
|
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
/* correct 0.0 in the first element (starts with a gap) */
|
|
|
|
|
|
|
|
if (dash.dash[0] == 0.0)
|
|
|
|
{
|
|
|
|
gdouble first;
|
|
|
|
|
|
|
|
first = dash.dash[1];
|
|
|
|
|
|
|
|
/* 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
|
|
|
{
|
|
|
|
dash.dash[i] = dash.dash[i+2];
|
|
|
|
dash.offset += dash.dash[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dash_info->len % 2 == 1)
|
|
|
|
{
|
|
|
|
dash.dash[dash_info->len - 2] = first;
|
|
|
|
dash.n_dash --;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (dash_info->len < 3)
|
|
|
|
{
|
|
|
|
/* empty stroke */
|
|
|
|
art_free (sc->vpath);
|
|
|
|
sc->vpath = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dash.dash [dash_info->len - 3] += first;
|
|
|
|
dash.n_dash -= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-09-16 08:46:02 +08:00
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
/* correct odd number of dash specifiers */
|
|
|
|
|
|
|
|
if (dash.n_dash % 2 == 1)
|
|
|
|
{
|
|
|
|
gdouble last;
|
|
|
|
|
|
|
|
last = dash.dash[dash.n_dash - 1];
|
|
|
|
dash.dash[0] += last;
|
|
|
|
dash.offset += last;
|
|
|
|
dash.n_dash --;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (sc->vpath)
|
|
|
|
{
|
|
|
|
dash_vpath = art_vpath_dash (sc->vpath, &dash);
|
|
|
|
art_free (sc->vpath);
|
|
|
|
sc->vpath = dash_vpath;
|
|
|
|
}
|
2003-09-26 08:41:10 +08:00
|
|
|
|
|
|
|
g_free (dashes);
|
|
|
|
}
|
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
if (sc->vpath)
|
2003-09-30 07:45:06 +08:00
|
|
|
{
|
2003-12-28 03:25:19 +08:00
|
|
|
stroke = art_svp_vpath_stroke (sc->vpath, artjoin, artcap,
|
|
|
|
width, miter, 0.2);
|
2003-10-01 02:06:19 +08:00
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
if (sc->ratio_xy != 1.0)
|
2003-09-30 07:45:06 +08:00
|
|
|
{
|
2003-12-28 03:25:19 +08:00
|
|
|
ArtSVPSeg *segment;
|
|
|
|
ArtPoint *point;
|
|
|
|
gint i, j;
|
2003-09-30 07:45:06 +08:00
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
for (i = 0; i < stroke->n_segs; i++)
|
2003-09-30 07:45:06 +08:00
|
|
|
{
|
2003-12-28 03:25:19 +08:00
|
|
|
segment = stroke->segs + i;
|
|
|
|
segment->bbox.x0 /= sc->ratio_xy;
|
|
|
|
segment->bbox.x1 /= sc->ratio_xy;
|
|
|
|
|
2005-07-31 19:53:32 +08:00
|
|
|
for (j = 0; j < segment->n_points; j++)
|
2003-12-28 03:25:19 +08:00
|
|
|
{
|
|
|
|
point = segment->points + j;
|
|
|
|
point->x /= sc->ratio_xy;
|
|
|
|
}
|
2003-09-30 07:45:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
sc->svp = stroke;
|
|
|
|
}
|
2003-09-21 03:58:26 +08:00
|
|
|
}
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-05-21 09:01:09 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
/* This is a more low level version. Expects a tile manager of depth 1.
|
|
|
|
*
|
|
|
|
* 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,
|
2003-10-01 02:06:19 +08:00
|
|
|
TileManager *tile_manager,
|
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
|
|
|
{
|
2005-07-29 09:48:56 +08:00
|
|
|
g_return_if_fail (sc != NULL);
|
|
|
|
g_return_if_fail (tile_manager != NULL);
|
|
|
|
|
2005-08-06 22:54:13 +08:00
|
|
|
gimp_scan_convert_render_internal (sc, GIMP_CHANNEL_OP_REPLACE,
|
2005-07-31 22:27:46 +08:00
|
|
|
tile_manager, off_x, off_y,
|
|
|
|
antialias, 255);
|
2005-07-29 09:48:56 +08:00
|
|
|
}
|
2003-09-21 18:45:19 +08:00
|
|
|
|
2005-07-29 09:48:56 +08:00
|
|
|
void
|
|
|
|
gimp_scan_convert_render_value (GimpScanConvert *sc,
|
|
|
|
TileManager *tile_manager,
|
|
|
|
gint off_x,
|
|
|
|
gint off_y,
|
|
|
|
guchar value)
|
|
|
|
{
|
2003-09-21 03:58:26 +08:00
|
|
|
g_return_if_fail (sc != NULL);
|
|
|
|
g_return_if_fail (tile_manager != NULL);
|
2003-09-21 18:45:19 +08:00
|
|
|
|
2005-08-06 22:54:13 +08:00
|
|
|
gimp_scan_convert_render_internal (sc, GIMP_CHANNEL_OP_REPLACE,
|
2005-07-31 22:27:46 +08:00
|
|
|
tile_manager, off_x, off_y,
|
|
|
|
FALSE, value);
|
2005-07-29 09:48:56 +08:00
|
|
|
}
|
|
|
|
|
2005-08-06 22:54:13 +08:00
|
|
|
void
|
|
|
|
gimp_scan_convert_compose (GimpScanConvert *sc,
|
|
|
|
GimpChannelOps op,
|
|
|
|
TileManager *tile_manager,
|
|
|
|
gint off_x,
|
|
|
|
gint off_y)
|
|
|
|
{
|
|
|
|
g_return_if_fail (sc != NULL);
|
|
|
|
g_return_if_fail (tile_manager != NULL);
|
|
|
|
|
|
|
|
gimp_scan_convert_render_internal (sc, op,
|
|
|
|
tile_manager, off_x, off_y,
|
|
|
|
FALSE, 255);
|
|
|
|
}
|
|
|
|
|
2005-07-29 09:48:56 +08:00
|
|
|
static void
|
|
|
|
gimp_scan_convert_render_internal (GimpScanConvert *sc,
|
2005-08-06 22:54:13 +08:00
|
|
|
GimpChannelOps op,
|
2005-07-29 09:48:56 +08:00
|
|
|
TileManager *tile_manager,
|
|
|
|
gint off_x,
|
|
|
|
gint off_y,
|
|
|
|
gboolean antialias,
|
|
|
|
guchar value)
|
|
|
|
{
|
|
|
|
PixelRegion maskPR;
|
|
|
|
gpointer pr;
|
2005-08-06 22:54:13 +08:00
|
|
|
gpointer callback;
|
2005-07-29 09:48:56 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
gimp_scan_convert_finish (sc);
|
2002-03-07 04:25:31 +08:00
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
if (!sc->svp)
|
|
|
|
return;
|
|
|
|
|
2003-09-21 18:45:19 +08:00
|
|
|
pixel_region_init (&maskPR, tile_manager, 0, 0,
|
2003-09-21 03:58:26 +08:00
|
|
|
tile_manager_width (tile_manager),
|
|
|
|
tile_manager_height (tile_manager),
|
2003-05-08 21:12:46 +08:00
|
|
|
TRUE);
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
g_return_if_fail (maskPR.bytes == 1);
|
2002-03-07 04:25:31 +08:00
|
|
|
|
2004-09-16 08:46:02 +08:00
|
|
|
sc->antialias = antialias;
|
2005-07-29 09:48:56 +08:00
|
|
|
sc->value = value;
|
2005-08-06 22:54:13 +08:00
|
|
|
sc->op = op;
|
|
|
|
|
|
|
|
callback = (op == GIMP_CHANNEL_OP_REPLACE ?
|
|
|
|
gimp_scan_convert_render_callback :
|
|
|
|
gimp_scan_convert_compose_callback);
|
2004-09-16 08:46:02 +08:00
|
|
|
|
2002-03-07 04:25:31 +08:00
|
|
|
for (pr = pixel_regions_register (1, &maskPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
1999-10-04 13:51:40 +08:00
|
|
|
{
|
2005-07-29 09:48:56 +08:00
|
|
|
sc->buf = maskPR.data;
|
2004-09-16 08:46:02 +08:00
|
|
|
sc->rowstride = maskPR.rowstride;
|
2005-07-29 09:48:56 +08:00
|
|
|
sc->x0 = off_x + maskPR.x;
|
|
|
|
sc->x1 = off_x + maskPR.x + maskPR.w;
|
2004-09-16 08:46:02 +08:00
|
|
|
|
|
|
|
art_svp_render_aa (sc->svp,
|
|
|
|
sc->x0,
|
|
|
|
off_y + maskPR.y,
|
|
|
|
sc->x1,
|
|
|
|
off_y + maskPR.y + maskPR.h,
|
2005-08-06 22:54:13 +08:00
|
|
|
callback, sc);
|
1999-10-04 13:51:40 +08:00
|
|
|
}
|
2003-09-21 03:58:26 +08:00
|
|
|
}
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
/* private function to convert the vpath to a svp when not using
|
|
|
|
* gimp_scan_convert_stroke
|
|
|
|
*/
|
2003-09-21 18:45:19 +08:00
|
|
|
static void
|
2003-09-21 03:58:26 +08:00
|
|
|
gimp_scan_convert_finish (GimpScanConvert *sc)
|
|
|
|
{
|
2004-01-27 05:19:17 +08:00
|
|
|
ArtSVP *svp, *svp2;
|
|
|
|
ArtSvpWriter *swr;
|
2003-09-21 03:58:26 +08:00
|
|
|
|
2003-12-28 03:25:19 +08:00
|
|
|
/* return gracefully on empty path */
|
|
|
|
if (!sc->vpath)
|
|
|
|
return;
|
2003-09-21 03:58:26 +08:00
|
|
|
|
2003-09-22 03:07:48 +08:00
|
|
|
if (sc->need_closing)
|
|
|
|
gimp_scan_convert_close_add_points (sc);
|
2003-09-21 18:45:19 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
if (sc->svp)
|
|
|
|
return; /* We already have a valid SVP */
|
|
|
|
|
|
|
|
/* Debug output of libart path */
|
|
|
|
/* {
|
|
|
|
* gint i;
|
2005-07-31 19:53:32 +08:00
|
|
|
* for (i = 0; i < sc->num_nodes + 1; i++)
|
2003-09-21 03:58:26 +08:00
|
|
|
* {
|
|
|
|
* g_printerr ("X: %f, Y: %f, Type: %d\n", sc->vpath[i].x,
|
|
|
|
* sc->vpath[i].y,
|
|
|
|
* sc->vpath[i].code );
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*/
|
2004-09-16 08:46:02 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
if (sc->have_open)
|
|
|
|
{
|
|
|
|
gint i;
|
2003-10-01 02:06:19 +08:00
|
|
|
|
2003-09-21 03:58:26 +08:00
|
|
|
for (i = 0; i < sc->num_nodes; i++)
|
|
|
|
if (sc->vpath[i].code == ART_MOVETO_OPEN)
|
|
|
|
{
|
|
|
|
g_printerr ("Fixing ART_MOVETO_OPEN - result might be incorrect\n");
|
|
|
|
sc->vpath[i].code = ART_MOVETO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-01-27 05:19:17 +08:00
|
|
|
svp = art_svp_from_vpath (sc->vpath);
|
2004-01-21 09:29:00 +08:00
|
|
|
|
2004-01-27 05:19:17 +08:00
|
|
|
swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN);
|
|
|
|
art_svp_intersector (svp, swr);
|
2003-09-21 03:58:26 +08:00
|
|
|
|
2004-01-27 05:19:17 +08:00
|
|
|
svp2 = art_svp_writer_rewind_reap (swr); /* this also frees swr */
|
1999-10-04 13:51:40 +08:00
|
|
|
|
2003-09-29 04:13:59 +08:00
|
|
|
art_svp_free (svp);
|
2003-09-21 18:45:19 +08:00
|
|
|
|
2004-01-27 05:19:17 +08:00
|
|
|
sc->svp = svp2;
|
1999-10-04 13:51:40 +08:00
|
|
|
}
|
2004-09-16 08:46:02 +08:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* private function to render libart SVPRenderAASteps into the pixel region
|
|
|
|
*
|
|
|
|
* A function pretty similiar to this could be used to implement a
|
|
|
|
* lookup table for the values (just change VALUE_TO_PIXEL).
|
|
|
|
*
|
|
|
|
* from the libart documentation:
|
|
|
|
*
|
|
|
|
* The value 0x8000 represents 0% coverage by the polygon, while
|
|
|
|
* 0xff8000 represents 100% coverage. This format is designed so that
|
|
|
|
* >> 16 results in a standard 0x00..0xff value range, with nice
|
|
|
|
* rounding.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_scan_convert_render_callback (gpointer user_data,
|
|
|
|
gint y,
|
|
|
|
gint start_value,
|
|
|
|
ArtSVPRenderAAStep *steps,
|
|
|
|
gint n_steps)
|
|
|
|
{
|
2004-09-16 19:31:30 +08:00
|
|
|
GimpScanConvert *sc = user_data;
|
|
|
|
gint cur_value = start_value;
|
|
|
|
gint k, run_x0, run_x1;
|
2004-09-16 08:46:02 +08:00
|
|
|
|
2004-09-16 19:31:30 +08:00
|
|
|
#define VALUE_TO_PIXEL(x) (sc->antialias ? \
|
|
|
|
((x) >> 16) : \
|
2005-07-29 09:48:56 +08:00
|
|
|
(((x) & (1 << 23) ? sc->value : 0)))
|
2004-09-16 08:46:02 +08:00
|
|
|
|
|
|
|
if (n_steps > 0)
|
|
|
|
{
|
|
|
|
run_x1 = steps[0].x;
|
2004-09-16 19:31:30 +08:00
|
|
|
|
2004-09-16 08:46:02 +08:00
|
|
|
if (run_x1 > sc->x0)
|
|
|
|
memset (sc->buf,
|
|
|
|
VALUE_TO_PIXEL (cur_value),
|
|
|
|
run_x1 - sc->x0);
|
|
|
|
|
|
|
|
for (k = 0; k < n_steps - 1; k++)
|
|
|
|
{
|
|
|
|
cur_value += steps[k].delta;
|
2004-09-16 19:31:30 +08:00
|
|
|
|
2004-09-16 08:46:02 +08:00
|
|
|
run_x0 = run_x1;
|
|
|
|
run_x1 = steps[k + 1].x;
|
2004-09-16 19:31:30 +08:00
|
|
|
|
2004-09-16 08:46:02 +08:00
|
|
|
if (run_x1 > run_x0)
|
|
|
|
memset (sc->buf + run_x0 - sc->x0,
|
|
|
|
VALUE_TO_PIXEL (cur_value),
|
|
|
|
run_x1 - run_x0);
|
|
|
|
}
|
|
|
|
|
|
|
|
cur_value += steps[k].delta;
|
2004-09-16 19:31:30 +08:00
|
|
|
|
2004-09-16 08:46:02 +08:00
|
|
|
if (sc->x1 > run_x1)
|
|
|
|
memset (sc->buf + run_x1 - sc->x0,
|
|
|
|
VALUE_TO_PIXEL (cur_value),
|
|
|
|
sc->x1 - run_x1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memset (sc->buf,
|
|
|
|
VALUE_TO_PIXEL (cur_value),
|
|
|
|
sc->x1 - sc->x0);
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->buf += sc->rowstride;
|
|
|
|
|
|
|
|
#undef VALUE_TO_PIXEL
|
|
|
|
}
|
|
|
|
|
2005-08-06 22:54:13 +08:00
|
|
|
static inline void
|
|
|
|
compose (GimpChannelOps op,
|
|
|
|
guchar *buf,
|
|
|
|
guchar value,
|
|
|
|
gint len)
|
|
|
|
{
|
|
|
|
switch (op)
|
|
|
|
{
|
|
|
|
case GIMP_CHANNEL_OP_ADD:
|
|
|
|
if (value)
|
|
|
|
memset (buf, value, len);
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_SUBTRACT:
|
|
|
|
if (value)
|
|
|
|
memset (buf, 0, len);
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_REPLACE:
|
|
|
|
memset (buf, value, len);
|
|
|
|
break;
|
|
|
|
case GIMP_CHANNEL_OP_INTERSECT:
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (*buf)
|
|
|
|
*buf = value;
|
|
|
|
buf++;
|
|
|
|
}
|
|
|
|
while (--len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_scan_convert_compose_callback (gpointer user_data,
|
|
|
|
gint y,
|
|
|
|
gint start_value,
|
|
|
|
ArtSVPRenderAAStep *steps,
|
|
|
|
gint n_steps)
|
|
|
|
{
|
|
|
|
GimpScanConvert *sc = user_data;
|
|
|
|
gint cur_value = start_value;
|
|
|
|
gint k, run_x0, run_x1;
|
|
|
|
|
|
|
|
#define VALUE_TO_PIXEL(x) (((x) & (1 << 23) ? 255 : 0))
|
|
|
|
|
|
|
|
if (n_steps > 0)
|
|
|
|
{
|
|
|
|
run_x1 = steps[0].x;
|
|
|
|
|
|
|
|
if (run_x1 > sc->x0)
|
|
|
|
compose (sc->op, sc->buf,
|
|
|
|
VALUE_TO_PIXEL (cur_value),
|
|
|
|
run_x1 - sc->x0);
|
|
|
|
|
|
|
|
for (k = 0; k < n_steps - 1; k++)
|
|
|
|
{
|
|
|
|
cur_value += steps[k].delta;
|
|
|
|
|
|
|
|
run_x0 = run_x1;
|
|
|
|
run_x1 = steps[k + 1].x;
|
|
|
|
|
|
|
|
if (run_x1 > run_x0)
|
|
|
|
compose (sc->op, sc->buf + run_x0 - sc->x0,
|
|
|
|
VALUE_TO_PIXEL (cur_value),
|
|
|
|
run_x1 - run_x0);
|
|
|
|
}
|
|
|
|
|
|
|
|
cur_value += steps[k].delta;
|
|
|
|
|
|
|
|
if (sc->x1 > run_x1)
|
|
|
|
compose (sc->op, sc->buf + run_x1 - sc->x0,
|
|
|
|
VALUE_TO_PIXEL (cur_value),
|
|
|
|
sc->x1 - run_x1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
compose (sc->op, sc->buf,
|
|
|
|
VALUE_TO_PIXEL (cur_value),
|
|
|
|
sc->x1 - sc->x0);
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->buf += sc->rowstride;
|
|
|
|
|
|
|
|
#undef VALUE_TO_PIXEL
|
|
|
|
}
|