added Raph's scalable vector icons machinery

added Raph's scalable vector icons machinery
This commit is contained in:
Andy Hertzfeld 2000-04-14 01:33:35 +00:00
parent 74748cdc3a
commit c599d12496
30 changed files with 2497 additions and 18 deletions

View file

@ -1,3 +1,21 @@
2000-04-13 Andy Hertzfeld <andy@eazel.com>
added Raph's scalable icon machinery. Right now the only way to see it
is to use the eazel theme and drag a file over a folder; the open folder
is a vector icon. Soon we'll add lots more.
* librsvg/*
Raph's new library
* libnautilus/nautilus-icon-factory.c:
integrate vector icons with the icon factory. Right now, it prefers
them to bitmapped ones, which is probably wrong.
* Makefile.am: added librsvg
* configure.in: added librsvg
* src/Makefile.am: link with librsvg
* components/*/Makefile.am: link with librsvg
* icons/eazel/Makefile.am: added i-directory-accept.svg
* icons/eazel/i-directory-accept.svg: our sole vector icon
2000-04-13 Darin Adler <darin@eazel.com>
* components/html/ntl-web-browser.c: (browser_vfs_read_callback):

View file

@ -1,6 +1,7 @@
NULL=
SUBDIRS =\
librsvg \
libnautilus \
nautilus-widgets \
src \

View file

@ -12,6 +12,7 @@ INCLUDES=-I$(top_srcdir) -I$(top_builddir) \
LDADD =\
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
$(XML_LIBS)

View file

@ -5,6 +5,7 @@ INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GNOMEUI_CFLAGS) $(BONOBO_CFLAGS) $(
LDADD=\
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS)

View file

@ -31,6 +31,7 @@ ntl_web_browser_SOURCES = \
ntl_web_browser_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(GTKHTML_LIBS) \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \

View file

@ -25,6 +25,7 @@ nautilus_music_view_SOURCES = \
nautilus_music_view_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
$(VFS_LIBS) \

View file

@ -5,6 +5,7 @@ INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GNOMEUI_CFLAGS) $(BONOBO_CFLAGS) $(
LDADD=\
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS)

View file

@ -26,6 +26,7 @@ nautilus_rpm_view_SOURCES = \
nautilus_rpm_view_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
-lrpm \

View file

@ -24,6 +24,7 @@ nautilus_sample_content_view_SOURCES = \
nautilus_sample_content_view_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
$(VFS_LIBS) \

View file

@ -28,6 +28,7 @@ nautilus_service_startup_view_SOURCES = \
nautilus_service_startup_view_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
$(VFS_LIBS) \

View file

@ -5,6 +5,7 @@ INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GNOMEUI_CFLAGS) $(BONOBO_CFLAGS) $(
LDADD=\
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
$(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS)

View file

@ -152,6 +152,7 @@ data/Makefile
data/mime/Makefile
data/top/Makefile
idl/Makefile
librsvg/Makefile
libnautilus/Makefile
nautilus-widgets/Makefile
src/Makefile

View file

@ -9,6 +9,7 @@ eazel_DATA = \
i-directory-accept.png \
i-directory-accept-36.png \
i-directory-accept-72.png \
i-directory-accept.svg \
i-regular.png \
i-regular.xml \
i-regular-36.png \

View file

@ -0,0 +1,13 @@
<svg width="70" height="42">
<!-- Open folder 1.eps converted by ill2svg.pl -->
<g style="fill: #000000">
<path d="M9.54 41.4L60.9 41.4C60.9 41.4 69.156 12.216 69.348 11.544C69.54 10.872 69.924 9.816 68.916 9.048C68.366 8.628 66.852 8.568 65.316 8.664C63.78 8.76 52.26 8.664 52.26 8.664C52.26 8.664 52.452 6.552 51.684 5.88C50.916 5.208 50.244 4.728 46.884 4.824C43.524 4.92 22.884 4.824 22.884 4.824C22.884 4.824 22.404 5.4 21.54 4.056C20.676 2.712 19.908 0.792 18.948 0.696C17.988 0.6 6.468 0.696 6.468 0.696C6.468 0.696 5.06 0.696 4.772 2.04C4.484 3.384 4.459 4.253 4.164 4.632C3.716 5.208 3.14 4.984 2.468 5.08C1.796 5.176 0.196 4.92 0.132 6.552C0.076 7.994 2.148 14.616 2.244 15.288C2.34 15.96 9.54 41.4 9.54 41.4z"/>
</g>
<g style="fill: #ffffc0; stroke:#000000; stroke-width:0.6">
<path d="M10.5 40.056L17.86 13.88C17.86 13.88 18.228 12.504 19.588 12.6C20.61 12.672 37.38 12.664 37.892 12.664C38.404 12.664 38.9 12.081 40.068 10.936C41.652 9.384 42.308 9.848 43.396 9.784C44.355 9.728 67.012 9.848 67.012 9.848C67.012 9.848 68.292 10.104 68.036 11.064C67.78 12.024 59.716 40.184 59.716 40.184L10.5 40.056z"/>
</g>
<g style="fill: #ffc040; stroke:#000000; stroke-width:0.6">
<path d="M9.732 37.08C9.732 37.08 16.26 13.656 16.452 13.272C16.644 12.888 17.268 11.352 18.468 11.256C19.138 11.202 37.476 11.256 37.476 11.256C37.476 11.256 37.956 11.544 39.3 10.008C40.644 8.472 40.932 8.472 45.348 8.472C49.764 8.472 51.012 8.568 51.012 8.568C51.012 8.568 51.108 7.608 50.82 7.032C50.532 6.456 50.244 6.072 48.228 6.072C46.212 6.072 23.076 6.072 22.5 6.072C21.924 6.072 20.868 6.264 20.1 4.92C19.332 3.576 19.236 1.944 17.412 1.944C15.588 1.944 6.756 1.944 6.756 1.944C6.756 1.944 6.116 1.976 5.924 2.552C5.732 3.128 5.732 4.728 5.156 5.4C4.58 6.072 4.563 5.978 3.62 6.072C2.66 6.168 1.476 6.04 1.476 7.224C1.476 8.664 9.732 37.08 9.732 37.08z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -41,6 +41,8 @@
#include <parser.h>
#include <xmlmemory.h>
#include "librsvg/rsvg.h"
#include "nautilus-string.h"
#include "nautilus-default-file-icon.h"
#include "nautilus-metadata.h"
@ -54,6 +56,8 @@
static const char *icon_file_name_suffixes[] =
{
"",
".svg",
".SVG",
".png",
".PNG",
".gif",
@ -507,6 +511,18 @@ make_full_icon_path (const char *path, const char *suffix)
return full_path;
}
/* Return true if the given suffix is a scalable image. */
static gboolean
suffix_is_scalable (const char *path)
{
const char *suffix;
suffix = (const char *)strrchr (path, '.');
if (suffix == NULL)
return FALSE;
return (!strcmp (suffix, ".svg") || !strcmp (suffix, ".SVG"));
}
/* Pick a particular icon to use, trying all the various suffixes.
* Return the path of the icon or NULL if no icon is found.
*/
@ -535,11 +551,15 @@ get_themed_icon_file_path (const char *theme_name,
/* Try each suffix. */
for (i = 0; i < NAUTILUS_N_ELEMENTS (icon_file_name_suffixes); i++) {
/* Build a path for this icon. */
partial_path = g_strdup_printf ("%s%s%.0u",
themed_icon_name,
include_size ? "-" : "",
include_size ? icon_size : 0);
if (include_size &&
!suffix_is_scalable (icon_file_name_suffixes[i])) {
/* Build a path for this icon. */
partial_path = g_strdup_printf ("%s-%u",
themed_icon_name,
icon_size);
} else {
partial_path = g_strdup (themed_icon_name);
}
path = make_full_icon_path (partial_path,
icon_file_name_suffixes[i]);
@ -1011,6 +1031,24 @@ get_next_icon_size_to_try (guint target_size, guint *current_size)
return FALSE;
}
/* This loads an SVG image, scaling it to the appropriate size. */
static GdkPixbuf *
load_specific_image_svg (const char *path, guint size_in_pixels)
{
FILE *f;
GdkPixbuf *result;
f = fopen (path, "r");
if (f == NULL) {
return NULL;
}
result = rsvg_render_file (f, size_in_pixels *
(1.0 / NAUTILUS_ICON_SIZE_STANDARD));
fclose (f);
return result;
}
/* This load function returns NULL if the icon is not available at this size. */
static GdkPixbuf *
load_specific_image (NautilusScalableIcon *scalable_icon,
@ -1046,7 +1084,10 @@ load_specific_image (NautilusScalableIcon *scalable_icon,
if (path == NULL) {
return NULL;
}
image = gdk_pixbuf_new_from_file (path);
if (suffix_is_scalable (path))
image = load_specific_image_svg (path, size_in_pixels);
else
image = gdk_pixbuf_new_from_file (path);
g_free (path);
return image;
}

View file

@ -41,6 +41,8 @@
#include <parser.h>
#include <xmlmemory.h>
#include "librsvg/rsvg.h"
#include "nautilus-string.h"
#include "nautilus-default-file-icon.h"
#include "nautilus-metadata.h"
@ -54,6 +56,8 @@
static const char *icon_file_name_suffixes[] =
{
"",
".svg",
".SVG",
".png",
".PNG",
".gif",
@ -507,6 +511,18 @@ make_full_icon_path (const char *path, const char *suffix)
return full_path;
}
/* Return true if the given suffix is a scalable image. */
static gboolean
suffix_is_scalable (const char *path)
{
const char *suffix;
suffix = (const char *)strrchr (path, '.');
if (suffix == NULL)
return FALSE;
return (!strcmp (suffix, ".svg") || !strcmp (suffix, ".SVG"));
}
/* Pick a particular icon to use, trying all the various suffixes.
* Return the path of the icon or NULL if no icon is found.
*/
@ -535,11 +551,15 @@ get_themed_icon_file_path (const char *theme_name,
/* Try each suffix. */
for (i = 0; i < NAUTILUS_N_ELEMENTS (icon_file_name_suffixes); i++) {
/* Build a path for this icon. */
partial_path = g_strdup_printf ("%s%s%.0u",
themed_icon_name,
include_size ? "-" : "",
include_size ? icon_size : 0);
if (include_size &&
!suffix_is_scalable (icon_file_name_suffixes[i])) {
/* Build a path for this icon. */
partial_path = g_strdup_printf ("%s-%u",
themed_icon_name,
icon_size);
} else {
partial_path = g_strdup (themed_icon_name);
}
path = make_full_icon_path (partial_path,
icon_file_name_suffixes[i]);
@ -1011,6 +1031,24 @@ get_next_icon_size_to_try (guint target_size, guint *current_size)
return FALSE;
}
/* This loads an SVG image, scaling it to the appropriate size. */
static GdkPixbuf *
load_specific_image_svg (const char *path, guint size_in_pixels)
{
FILE *f;
GdkPixbuf *result;
f = fopen (path, "r");
if (f == NULL) {
return NULL;
}
result = rsvg_render_file (f, size_in_pixels *
(1.0 / NAUTILUS_ICON_SIZE_STANDARD));
fclose (f);
return result;
}
/* This load function returns NULL if the icon is not available at this size. */
static GdkPixbuf *
load_specific_image (NautilusScalableIcon *scalable_icon,
@ -1046,7 +1084,10 @@ load_specific_image (NautilusScalableIcon *scalable_icon,
if (path == NULL) {
return NULL;
}
image = gdk_pixbuf_new_from_file (path);
if (suffix_is_scalable (path))
image = load_specific_image_svg (path, size_in_pixels);
else
image = gdk_pixbuf_new_from_file (path);
g_free (path);
return image;
}

View file

@ -41,6 +41,8 @@
#include <parser.h>
#include <xmlmemory.h>
#include "librsvg/rsvg.h"
#include "nautilus-string.h"
#include "nautilus-default-file-icon.h"
#include "nautilus-metadata.h"
@ -54,6 +56,8 @@
static const char *icon_file_name_suffixes[] =
{
"",
".svg",
".SVG",
".png",
".PNG",
".gif",
@ -507,6 +511,18 @@ make_full_icon_path (const char *path, const char *suffix)
return full_path;
}
/* Return true if the given suffix is a scalable image. */
static gboolean
suffix_is_scalable (const char *path)
{
const char *suffix;
suffix = (const char *)strrchr (path, '.');
if (suffix == NULL)
return FALSE;
return (!strcmp (suffix, ".svg") || !strcmp (suffix, ".SVG"));
}
/* Pick a particular icon to use, trying all the various suffixes.
* Return the path of the icon or NULL if no icon is found.
*/
@ -535,11 +551,15 @@ get_themed_icon_file_path (const char *theme_name,
/* Try each suffix. */
for (i = 0; i < NAUTILUS_N_ELEMENTS (icon_file_name_suffixes); i++) {
/* Build a path for this icon. */
partial_path = g_strdup_printf ("%s%s%.0u",
themed_icon_name,
include_size ? "-" : "",
include_size ? icon_size : 0);
if (include_size &&
!suffix_is_scalable (icon_file_name_suffixes[i])) {
/* Build a path for this icon. */
partial_path = g_strdup_printf ("%s-%u",
themed_icon_name,
icon_size);
} else {
partial_path = g_strdup (themed_icon_name);
}
path = make_full_icon_path (partial_path,
icon_file_name_suffixes[i]);
@ -1011,6 +1031,24 @@ get_next_icon_size_to_try (guint target_size, guint *current_size)
return FALSE;
}
/* This loads an SVG image, scaling it to the appropriate size. */
static GdkPixbuf *
load_specific_image_svg (const char *path, guint size_in_pixels)
{
FILE *f;
GdkPixbuf *result;
f = fopen (path, "r");
if (f == NULL) {
return NULL;
}
result = rsvg_render_file (f, size_in_pixels *
(1.0 / NAUTILUS_ICON_SIZE_STANDARD));
fclose (f);
return result;
}
/* This load function returns NULL if the icon is not available at this size. */
static GdkPixbuf *
load_specific_image (NautilusScalableIcon *scalable_icon,
@ -1046,7 +1084,10 @@ load_specific_image (NautilusScalableIcon *scalable_icon,
if (path == NULL) {
return NULL;
}
image = gdk_pixbuf_new_from_file (path);
if (suffix_is_scalable (path))
image = load_specific_image_svg (path, size_in_pixels);
else
image = gdk_pixbuf_new_from_file (path);
g_free (path);
return image;
}

30
librsvg/Makefile.am Normal file
View file

@ -0,0 +1,30 @@
NULL=
lib_LTLIBRARIES=librsvg.la
INCLUDES=-I$(top_srcdir) -I$(top_builddir) \
$(GNOME_CFLAGS) \
$(GNOMECANVASPIXBUF_INCLUDEDIR) \
$(XML_CFLAGS) \
$(WERROR) \
-D_REENTRANT
librsvg_la_LDFLAGS=\
$(GNOME_LIBS) \
$(GNOMECANVASPIXBUF_LIBS) \
$(XML_LIBS) \
$(LIBPNG)
librsvgincludedir=$(includedir)/librsvg
librsvginclude_HEADERS= \
rsvg.h \
$(NULL)
librsvg_la_SOURCES= \
rsvg.c \
rsvg-path.c \
rsvg-bpath-util.c \
art_rgba.c \
art_rgba_svp.c \
$(NULL)

236
librsvg/art_rgba.c Normal file
View file

@ -0,0 +1,236 @@
#include <libart_lgpl/art_misc.h>
#include "art_rgba.h"
#define ART_OPTIMIZE_SPACE
#ifndef ART_OPTIMIZE_SPACE
#include "art_rgba_table.c"
#endif
/**
* art_rgba_rgba_composite: Composite RGBA image over RGBA buffer.
* @dst: Destination RGBA buffer.
* @src: Source RGBA buffer.
* @n: Number of RGBA pixels to composite.
*
* Composites the RGBA pixels in @dst over the @src buffer.
**/
void
art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n)
{
int i;
#ifdef WORDS_BIGENDIAN
art_u32 src_rgba, dst_rgba;
#else
art_u32 src_abgr, dst_abgr;
#endif
art_u8 src_alpha, dst_alpha;
for (i = 0; i < n; i++)
{
#ifdef WORDS_BIGENDIAN
src_rgba = ((art_u32 *)src)[i];
src_alpha = src_rgba & 0xff;
#else
src_abgr = ((art_u32 *)src)[i];
src_alpha = (src_abgr >> 24) & 0xff;
#endif
if (src_alpha)
{
if (src_alpha == 0xff ||
(
#ifdef WORDS_BIGENDIAN
dst_rgba = ((art_u32 *)dst)[i],
dst_alpha = dst_rgba & 0xff,
#else
dst_abgr = ((art_u32 *)dst)[i],
dst_alpha = (dst_abgr >> 24),
#endif
dst_alpha == 0))
#ifdef WORDS_BIGENDIAN
((art_u32 *)dst)[i] = src_rgba;
#else
((art_u32 *)dst)[i] = src_abgr;
#endif
else
{
int r, g, b, a;
int src_r, src_g, src_b;
int dst_r, dst_g, dst_b;
int tmp;
int c;
#ifdef ART_OPTIMIZE_SPACE
tmp = (255 - src_alpha) * (255 - dst_alpha) + 0x80;
a = 255 - ((tmp + (tmp >> 8)) >> 8);
c = ((src_alpha << 16) + (a >> 1)) / a;
#else
tmp = art_rgba_composite_table[(src_alpha << 8) + dst_alpha];
c = tmp & 0x1ffff;
a = tmp >> 24;
#endif
#ifdef WORDS_BIGENDIAN
src_r = (src_rgba >> 24) & 0xff;
src_g = (src_rgba >> 16) & 0xff;
src_b = (src_rgba >> 8) & 0xff;
dst_r = (dst_rgba >> 24) & 0xff;
dst_g = (dst_rgba >> 16) & 0xff;
dst_b = (dst_rgba >> 8) & 0xff;
#else
src_r = src_abgr & 0xff;
src_g = (src_abgr >> 8) & 0xff;
src_b = (src_abgr >> 16) & 0xff;
dst_r = dst_abgr & 0xff;
dst_g = (dst_abgr >> 8) & 0xff;
dst_b = (dst_abgr >> 16) & 0xff;
#endif
r = dst_r + (((src_r - dst_r) * c + 0x8000) >> 16);
g = dst_g + (((src_g - dst_g) * c + 0x8000) >> 16);
b = dst_b + (((src_b - dst_b) * c + 0x8000) >> 16);
#ifdef WORDS_BIGENDIAN
((art_u32 *)dst)[i] = (r << 24) | (g << 16) | (b << 8) | a;
#else
((art_u32 *)dst)[i] = (a << 24) | (b << 16) | (g << 8) | r;
#endif
}
}
#if 0
/* it's not clear to me this optimization really wins */
else
{
/* skip over run of transparent pixels */
for (; i < n - 1; i++)
{
#ifdef WORDS_BIGENDIAN
src_rgba = ((art_u32 *)src)[i + 1];
if (src_rgba & 0xff)
break;
#else
src_abgr = ((art_u32 *)src)[i + 1];
if (src_abgr & 0xff000000)
break;
#endif
}
}
#endif
}
}
/**
* art_rgba_fill_run: fill an RGBA buffer a solid RGB color.
* @buf: Buffer to fill.
* @r: Red, range 0..255.
* @g: Green, range 0..255.
* @b: Blue, range 0..255.
* @n: Number of RGB triples to fill.
*
* Fills a buffer with @n copies of the (@r, @g, @b) triple, solid
* alpha. Thus, locations @buf (inclusive) through @buf + 4 * @n
* (exclusive) are written.
**/
void
art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n)
{
int i;
#ifdef WORDS_BIGENDIAN
art_u32 src_rgba;
#else
art_u32 src_abgr;
#endif
#ifdef WORDS_BIGENDIAN
src_rgba = (r << 24) | (g << 16) | (b << 8) | 255;
#else
src_abgr = (255 << 24) | (b << 16) | (g << 8) | r;
#endif
for (i = 0; i < n; i++)
{
#ifdef WORDS_BIGENDIAN
((art_u32 *)buf)[i] = src_rgba;
#else
((art_u32 *)buf)[i] = src_abgr;
#endif
}
}
/**
* art_rgba_run_alpha: Render semitransparent color over RGBA buffer.
* @buf: Buffer for rendering.
* @r: Red, range 0..255.
* @g: Green, range 0..255.
* @b: Blue, range 0..255.
* @alpha: Alpha, range 0..255.
* @n: Number of RGB triples to render.
*
* Renders a sequential run of solid (@r, @g, @b) color over @buf with
* opacity @alpha. Note that the range of @alpha is 0..255, in contrast
* to art_rgb_run_alpha, which has a range of 0..256.
**/
void
art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
{
int i;
#ifdef WORDS_BIGENDIAN
art_u32 src_rgba, dst_rgba;
#else
art_u32 src_abgr, dst_abgr;
#endif
art_u8 dst_alpha;
int a;
int dst_r, dst_g, dst_b;
int tmp;
int c;
#ifdef WORDS_BIGENDIAN
src_rgba = (r << 24) | (g << 16) | (b << 8) | alpha;
#else
src_abgr = (alpha << 24) | (b << 16) | (g << 8) | r;
#endif
for (i = 0; i < n; i++)
{
#ifdef WORDS_BIGENDIAN
dst_rgba = ((art_u32 *)buf)[i];
dst_alpha = dst_rgba & 0xff;
#else
dst_abgr = ((art_u32 *)buf)[i];
dst_alpha = (dst_abgr >> 24) & 0xff;
#endif
if (dst_alpha)
{
#ifdef ART_OPTIMIZE_SPACE
tmp = (255 - alpha) * (255 - dst_alpha) + 0x80;
a = 255 - ((tmp + (tmp >> 8)) >> 8);
c = ((alpha << 16) + (a >> 1)) / a;
#else
tmp = art_rgba_composite_table[(alpha << 8) + dst_alpha];
c = tmp & 0x1ffff;
a = tmp >> 24;
#endif
#ifdef WORDS_BIGENDIAN
dst_r = (dst_rgba >> 24) & 0xff;
dst_g = (dst_rgba >> 16) & 0xff;
dst_b = (dst_rgba >> 8) & 0xff;
#else
dst_r = dst_abgr & 0xff;
dst_g = (dst_abgr >> 8) & 0xff;
dst_b = (dst_abgr >> 16) & 0xff;
#endif
dst_r += (((r - dst_r) * c + 0x8000) >> 16);
dst_g += (((g - dst_g) * c + 0x8000) >> 16);
dst_b += (((b - dst_b) * c + 0x8000) >> 16);
#ifdef WORDS_BIGENDIAN
((art_u32 *)buf)[i] = (dst_r << 24) | (dst_g << 16) | (dst_b << 8) | a;
#else
((art_u32 *)buf)[i] = (a << 24) | (dst_b << 16) | (dst_g << 8) | dst_r;
#endif
}
else
{
#ifdef WORDS_BIGENDIAN
((art_u32 *)buf)[i] = src_rgba;
#else
((art_u32 *)buf)[i] = src_abgr;
#endif
}
}
}

13
librsvg/art_rgba.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef __ART_RGBA_H__
#define __ART_RGBA_H__
void
art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n);
void
art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n);
void
art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n);
#endif

257
librsvg/art_rgba_svp.c Normal file
View file

@ -0,0 +1,257 @@
#include <libart_lgpl/art_misc.h>
#include <libart_lgpl/art_svp.h>
#include <libart_lgpl/art_svp_render_aa.h>
#include "art_rgba.h"
#include "art_rgba_svp.h"
typedef struct _ArtRgbaSVPAlphaData ArtRgbaSVPAlphaData;
struct _ArtRgbaSVPAlphaData {
int alphatab[256];
art_u8 r, g, b, alpha;
art_u8 *buf;
int rowstride;
int x0, x1;
};
static void
art_rgba_svp_alpha_callback (void *callback_data, int y,
int start, ArtSVPRenderAAStep *steps, int n_steps)
{
ArtRgbaSVPAlphaData *data = callback_data;
art_u8 *linebuf;
int run_x0, run_x1;
art_u32 running_sum = start;
int x0, x1;
int k;
art_u8 r, g, b;
int *alphatab;
int alpha;
linebuf = data->buf;
x0 = data->x0;
x1 = data->x1;
r = data->r;
g = data->g;
b = data->b;
alphatab = data->alphatab;
if (n_steps > 0)
{
run_x1 = steps[0].x;
if (run_x1 > x0)
{
alpha = (running_sum >> 16) & 0xff;
if (alpha)
art_rgba_run_alpha (linebuf,
r, g, b, alphatab[alpha],
run_x1 - x0);
}
/* render the steps into tmpbuf */
for (k = 0; k < n_steps - 1; k++)
{
running_sum += steps[k].delta;
run_x0 = run_x1;
run_x1 = steps[k + 1].x;
if (run_x1 > run_x0)
{
alpha = (running_sum >> 16) & 0xff;
if (alpha)
art_rgba_run_alpha (linebuf + ((run_x0 - x0) << 2),
r, g, b, alphatab[alpha],
run_x1 - run_x0);
}
}
running_sum += steps[k].delta;
if (x1 > run_x1)
{
alpha = (running_sum >> 16) & 0xff;
if (alpha)
art_rgba_run_alpha (linebuf + ((run_x1 - x0) << 2),
r, g, b, alphatab[alpha],
x1 - run_x1);
}
}
else
{
alpha = (running_sum >> 16) & 0xff;
if (alpha)
art_rgba_run_alpha (linebuf,
r, g, b, alphatab[alpha],
x1 - x0);
}
data->buf += data->rowstride;
}
static void
art_rgba_svp_alpha_opaque_callback (void *callback_data, int y,
int start,
ArtSVPRenderAAStep *steps, int n_steps)
{
ArtRgbaSVPAlphaData *data = callback_data;
art_u8 *linebuf;
int run_x0, run_x1;
art_u32 running_sum = start;
int x0, x1;
int k;
art_u8 r, g, b;
int *alphatab;
int alpha;
linebuf = data->buf;
x0 = data->x0;
x1 = data->x1;
r = data->r;
g = data->g;
b = data->b;
alphatab = data->alphatab;
if (n_steps > 0)
{
run_x1 = steps[0].x;
if (run_x1 > x0)
{
alpha = running_sum >> 16;
if (alpha)
{
if (alpha >= 255)
art_rgba_fill_run (linebuf,
r, g, b,
run_x1 - x0);
else
art_rgba_run_alpha (linebuf,
r, g, b, alphatab[alpha],
run_x1 - x0);
}
}
/* render the steps into tmpbuf */
for (k = 0; k < n_steps - 1; k++)
{
running_sum += steps[k].delta;
run_x0 = run_x1;
run_x1 = steps[k + 1].x;
if (run_x1 > run_x0)
{
alpha = running_sum >> 16;
if (alpha)
{
if (alpha >= 255)
art_rgba_fill_run (linebuf + ((run_x0 - x0) << 2),
r, g, b,
run_x1 - run_x0);
else
art_rgba_run_alpha (linebuf + ((run_x0 - x0) << 2),
r, g, b, alphatab[alpha],
run_x1 - run_x0);
}
}
}
running_sum += steps[k].delta;
if (x1 > run_x1)
{
alpha = running_sum >> 16;
if (alpha)
{
if (alpha >= 255)
art_rgba_fill_run (linebuf + ((run_x1 - x0) << 2),
r, g, b,
x1 - run_x1);
else
art_rgba_run_alpha (linebuf + ((run_x1 - x0) << 2),
r, g, b, alphatab[alpha],
x1 - run_x1);
}
}
}
else
{
alpha = running_sum >> 16;
if (alpha)
{
if (alpha >= 255)
art_rgba_fill_run (linebuf,
r, g, b,
x1 - x0);
else
art_rgba_run_alpha (linebuf,
r, g, b, alphatab[alpha],
x1 - x0);
}
}
data->buf += data->rowstride;
}
/**
* art_rgba_svp_alpha: Alpha-composite sorted vector path over RGBA buffer.
* @svp: The source sorted vector path.
* @x0: Left coordinate of destination rectangle.
* @y0: Top coordinate of destination rectangle.
* @x1: Right coordinate of destination rectangle.
* @y1: Bottom coordinate of destination rectangle.
* @rgba: Color in 0xRRGGBBAA format.
* @buf: Destination RGBA buffer.
* @rowstride: Rowstride of @buf buffer.
* @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
*
* Renders the shape specified with @svp over the @buf RGBA buffer.
* @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
* of the rectangle rendered. The new pixels are stored starting at
* the first byte of @buf. Thus, the @x0 and @y0 parameters specify
* an offset within @svp, and may be tweaked as a way of doing
* integer-pixel translations without fiddling with @svp itself.
*
* The @rgba argument specifies the color for the rendering. Pixels of
* entirely 0 winding number are left untouched. Pixels of entirely
* 1 winding number have the color @rgba composited over them (ie,
* are replaced by the red, green, blue components of @rgba if the alpha
* component is 0xff). Pixels of intermediate coverage are interpolated
* according to the rule in @alphagamma, or default to linear if
* @alphagamma is NULL.
**/
void
art_rgba_svp_alpha (const ArtSVP *svp,
int x0, int y0, int x1, int y1,
art_u32 rgba,
art_u8 *buf, int rowstride,
ArtAlphaGamma *alphagamma)
{
ArtRgbaSVPAlphaData data;
int r, g, b, alpha;
int i;
int a, da;
r = rgba >> 24;
g = (rgba >> 16) & 0xff;
b = (rgba >> 8) & 0xff;
alpha = rgba & 0xff;
data.r = r;
data.g = g;
data.b = b;
data.alpha = alpha;
a = 0x8000;
da = (alpha * 65793 + 0x80) >> 8; /* 65793 equals 2 ^ 24 / 255 */
for (i = 0; i < 256; i++)
{
data.alphatab[i] = a >> 16;
a += da;
}
data.buf = buf;
data.rowstride = rowstride;
data.x0 = x0;
data.x1 = x1;
if (alpha == 255)
art_svp_render_aa (svp, x0, y0, x1, y1, art_rgba_svp_alpha_opaque_callback,
&data);
else
art_svp_render_aa (svp, x0, y0, x1, y1, art_rgba_svp_alpha_callback, &data);
}

13
librsvg/art_rgba_svp.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef __ART_RGBA_SVP_H__
#define __ART_RGBA_SVP_H__
#include <libart_lgpl/art_alphagamma.h>
void
art_rgba_svp_alpha (const ArtSVP *svp,
int x0, int y0, int x1, int y1,
art_u32 rgba,
art_u8 *buf, int rowstride,
ArtAlphaGamma *alphagamma);
#endif

172
librsvg/rsvg-bpath-util.c Normal file
View file

@ -0,0 +1,172 @@
#include <glib.h>
#include <math.h>
#include "rsvg-bpath-util.h"
/* This is adapted from gnome-canvas-bpath-util in libgnomeprint
(originally developed as part of Gill). */
RsvgBpathDef *
rsvg_bpath_def_new (void)
{
RsvgBpathDef *bpd;
bpd = g_new (RsvgBpathDef, 1);
bpd->n_bpath = 0;
bpd->n_bpath_max = 16;
bpd->moveto_idx = -1;
bpd->bpath = g_new (ArtBpath, bpd->n_bpath_max);
bpd->ref_count = 1;
return bpd;
}
RsvgBpathDef *
rsvg_bpath_def_new_from (ArtBpath *path)
{
RsvgBpathDef *bpd;
int i;
g_return_val_if_fail (path != NULL, NULL);
for (i = 0; path[i].code != ART_END; i++)
;
if (i <= 0)
return rsvg_bpath_def_new ();
bpd = g_new (RsvgBpathDef, 1);
bpd->n_bpath = i;
bpd->n_bpath_max = i;
bpd->moveto_idx = -1;
bpd->ref_count = 1;
bpd->bpath = g_new (ArtBpath, i);
memcpy (bpd->bpath, path, i * sizeof (ArtBpath));
return bpd;
}
RsvgBpathDef *
rsvg_bpath_def_ref (RsvgBpathDef *bpd)
{
g_return_val_if_fail (bpd != NULL, NULL);
bpd->ref_count += 1;
return bpd;
}
void
rsvg_bpath_def_free (RsvgBpathDef *bpd)
{
g_return_if_fail (bpd != NULL);
bpd->ref_count -= 1;
if (bpd->ref_count == 0)
{
g_free (bpd->bpath);
g_free (bpd);
}
}
void
rsvg_bpath_def_moveto (RsvgBpathDef *bpd, double x, double y)
{
ArtBpath *bpath;
int n_bpath;
g_return_if_fail (bpd != NULL);
n_bpath = bpd->n_bpath++;
if (n_bpath == bpd->n_bpath_max)
bpd->bpath = g_realloc (bpd->bpath,
(bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
bpath = bpd->bpath;
bpath[n_bpath].code = ART_MOVETO_OPEN;
bpath[n_bpath].x3 = x;
bpath[n_bpath].y3 = y;
bpd->moveto_idx = n_bpath;
}
void
rsvg_bpath_def_lineto (RsvgBpathDef *bpd, double x, double y)
{
ArtBpath *bpath;
int n_bpath;
g_return_if_fail (bpd != NULL);
g_return_if_fail (bpd->moveto_idx >= 0);
n_bpath = bpd->n_bpath++;
if (n_bpath == bpd->n_bpath_max)
bpd->bpath = g_realloc (bpd->bpath,
(bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
bpath = bpd->bpath;
bpath[n_bpath].code = ART_LINETO;
bpath[n_bpath].x3 = x;
bpath[n_bpath].y3 = y;
}
void
rsvg_bpath_def_curveto (RsvgBpathDef *bpd, double x1, double y1, double x2, double y2, double x3, double y3)
{
ArtBpath *bpath;
int n_bpath;
g_return_if_fail (bpd != NULL);
g_return_if_fail (bpd->moveto_idx >= 0);
n_bpath = bpd->n_bpath++;
if (n_bpath == bpd->n_bpath_max)
bpd->bpath = g_realloc (bpd->bpath,
(bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
bpath = bpd->bpath;
bpath[n_bpath].code = ART_CURVETO;
bpath[n_bpath].x1 = x1;
bpath[n_bpath].y1 = y1;
bpath[n_bpath].x2 = x2;
bpath[n_bpath].y2 = y2;
bpath[n_bpath].x3 = x3;
bpath[n_bpath].y3 = y3;
}
void
rsvg_bpath_def_closepath (RsvgBpathDef *bpd)
{
ArtBpath *bpath;
int n_bpath;
g_return_if_fail (bpd != NULL);
g_return_if_fail (bpd->moveto_idx >= 0);
g_return_if_fail (bpd->n_bpath > 0);
bpath = bpd->bpath;
n_bpath = bpd->n_bpath;
/* Add closing vector if we need it. */
if (bpath[n_bpath - 1].x3 != bpath[bpd->moveto_idx].x3 ||
bpath[n_bpath - 1].y3 != bpath[bpd->moveto_idx].y3)
{
rsvg_bpath_def_lineto (bpd, bpath[bpd->moveto_idx].x3,
bpath[bpd->moveto_idx].y3);
bpath = bpd->bpath;
}
bpath[bpd->moveto_idx].code = ART_MOVETO;
bpd->moveto_idx = -1;
}
void
rsvg_bpath_def_art_finish (RsvgBpathDef *bpd)
{
int n_bpath;
g_return_if_fail (bpd != NULL);
n_bpath = bpd->n_bpath++;
if (n_bpath == bpd->n_bpath_max)
bpd->bpath = g_realloc (bpd->bpath,
(bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
bpd->bpath[n_bpath].code = ART_END;
}

37
librsvg/rsvg-bpath-util.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef RSVG_BPATH_UTIL_H
#define RSVG_BPATH_UTIL_H
#include <libart_lgpl/art_bpath.h>
typedef struct _RsvgBpathDef RsvgBpathDef;
struct _RsvgBpathDef {
int ref_count;
ArtBpath *bpath;
int n_bpath;
int n_bpath_max;
int moveto_idx;
};
RsvgBpathDef *rsvg_bpath_def_new (void);
RsvgBpathDef *rsvg_bpath_def_new_from (ArtBpath *bpath);
RsvgBpathDef *rsvg_bpath_def_ref (RsvgBpathDef *bpd);
#define rsvg_bpath_def_unref rsvg_bpath_def_free
void rsvg_bpath_def_free (RsvgBpathDef *bpd);
void rsvg_bpath_def_moveto (RsvgBpathDef *bpd,
double x, double y);
void rsvg_bpath_def_lineto (RsvgBpathDef *bpd,
double x, double y);
void rsvg_bpath_def_curveto (RsvgBpathDef *bpd,
double x1, double y1,
double x2, double y2,
double x3, double y3);
void rsvg_bpath_def_closepath (RsvgBpathDef *bpd);
void rsvg_bpath_def_art_finish (RsvgBpathDef *bpd);
#endif

168
librsvg/rsvg-bpath.c Normal file
View file

@ -0,0 +1,168 @@
#include <glib.h>
#include <math.h>
#include "rsvg-bpath-util.h"
GnomeCanvasBpathDef *
gnome_canvas_bpath_def_new (void)
{
GnomeCanvasBpathDef *bpd;
bpd = g_new (GnomeCanvasBpathDef, 1);
bpd->n_bpath = 0;
bpd->n_bpath_max = 16;
bpd->moveto_idx = -1;
bpd->bpath = g_new (ArtBpath, bpd->n_bpath_max);
bpd->ref_count = 1;
return bpd;
}
GnomeCanvasBpathDef *
gnome_canvas_bpath_def_new_from (ArtBpath *path)
{
GnomeCanvasBpathDef *bpd;
int n, i;
g_return_val_if_fail (path != NULL, NULL);
for (i = 0; path [i].code != ART_END; i++)
;
if (i <= 0)
return gnome_canvas_bpath_def_new ();
bpd = g_new (GnomeCanvasBpathDef, 1);
bpd->n_bpath = i;
bpd->n_bpath_max = i;
bpd->moveto_idx = -1;
bpd->ref_count = 1;
bpd->bpath = g_new (ArtBpath, i);
memcpy (bpd->bpath, path, i * sizeof (ArtBpath));
return bpd;
}
GnomeCanvasBpathDef *
gnome_canvas_bpath_def_ref (GnomeCanvasBpathDef *bpd)
{
g_return_val_if_fail (bpd != NULL, NULL);
bpd->ref_count += 1;
return bpd;
}
void
gnome_canvas_bpath_def_free (GnomeCanvasBpathDef *bpd)
{
g_return_if_fail (bpd != NULL);
bpd->ref_count -= 1;
if (bpd->ref_count == 0) {
g_free (bpd->bpath);
g_free (bpd);
}
}
void
gnome_canvas_bpath_def_moveto (GnomeCanvasBpathDef *bpd, double x, double y)
{
ArtBpath *bpath;
int n_bpath;
g_return_if_fail (bpd != NULL);
n_bpath = bpd->n_bpath++;
if (n_bpath == bpd->n_bpath_max)
bpd->bpath = g_realloc (bpd->bpath,
(bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
bpath = bpd->bpath;
bpath[n_bpath].code = ART_MOVETO_OPEN;
bpath[n_bpath].x3 = x;
bpath[n_bpath].y3 = y;
bpd->moveto_idx = n_bpath;
}
void
gnome_canvas_bpath_def_lineto (GnomeCanvasBpathDef *bpd, double x, double y)
{
ArtBpath *bpath;
int n_bpath;
g_return_if_fail (bpd != NULL);
g_return_if_fail (bpd->moveto_idx >= 0);
n_bpath = bpd->n_bpath++;
if (n_bpath == bpd->n_bpath_max)
bpd->bpath = g_realloc (bpd->bpath,
(bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
bpath = bpd->bpath;
bpath[n_bpath].code = ART_LINETO;
bpath[n_bpath].x3 = x;
bpath[n_bpath].y3 = y;
}
void
gnome_canvas_bpath_def_curveto (GnomeCanvasBpathDef *bpd, double x1, double y1, double x2, double y2, double x3, double y3)
{
ArtBpath *bpath;
int n_bpath;
g_return_if_fail (bpd != NULL);
g_return_if_fail (bpd->moveto_idx >= 0);
n_bpath = bpd->n_bpath++;
if (n_bpath == bpd->n_bpath_max)
bpd->bpath = g_realloc (bpd->bpath,
(bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
bpath = bpd->bpath;
bpath[n_bpath].code = ART_CURVETO;
bpath[n_bpath].x1 = x1;
bpath[n_bpath].y1 = y1;
bpath[n_bpath].x2 = x2;
bpath[n_bpath].y2 = y2;
bpath[n_bpath].x3 = x3;
bpath[n_bpath].y3 = y3;
}
void
gnome_canvas_bpath_def_closepath (GnomeCanvasBpathDef *bpd)
{
ArtBpath *bpath;
int n_bpath;
g_return_if_fail (bpd != NULL);
g_return_if_fail (bpd->moveto_idx >= 0);
g_return_if_fail (bpd->n_bpath > 0);
bpath = bpd->bpath;
n_bpath = bpd->n_bpath;
/* Add closing vector if we need it. */
if (bpath[n_bpath - 1].x3 != bpath[bpd->moveto_idx].x3 ||
bpath[n_bpath - 1].y3 != bpath[bpd->moveto_idx].y3) {
gnome_canvas_bpath_def_lineto (bpd, bpath[bpd->moveto_idx].x3,
bpath[bpd->moveto_idx].y3);
bpath = bpd->bpath;
}
bpath[bpd->moveto_idx].code = ART_MOVETO;
bpd->moveto_idx = -1;
}
void
gnome_canvas_bpath_def_art_finish (GnomeCanvasBpathDef *bpd)
{
int n_bpath;
g_return_if_fail (bpd != NULL);
n_bpath = bpd->n_bpath++;
if (n_bpath == bpd->n_bpath_max)
bpd->bpath = g_realloc (bpd->bpath,
(bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
bpd->bpath [n_bpath].code = ART_END;
}

611
librsvg/rsvg-path.c Normal file
View file

@ -0,0 +1,611 @@
/* rsvg-path.c
Copyright (C) 1999,2000 Raph Levien <raph@acm.org>
Gill 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.
Gill 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 Gill; see the file COPYING. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Author: Raph Levien <raph@acm.org>
*/
/* This is adapted from svg-path in Gill. */
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <glib.h>
#include "rsvg-bpath-util.h"
#include "rsvg-path.h"
/* This module parses an SVG path element into an RsvgBpathDef.
At present, there is no support for <marker> or any other contextual
information from the SVG file. The API will need to change rather
significantly to support these.
Reference: SVG working draft 3 March 2000, section 8.
*/
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif /* M_PI */
typedef struct _RSVGParsePathCtx RSVGParsePathCtx;
struct _RSVGParsePathCtx {
RsvgBpathDef *bpath;
double cpx, cpy; /* current point */
double rpx, rpy; /* reflection point (for 's' and 't' commands) */
char cmd; /* current command (lowercase) */
int param; /* parameter number */
gboolean rel; /* true if relative coords */
double params[7]; /* parameters that have been parsed */
};
static void
rsvg_path_arc_segment (RSVGParsePathCtx *ctx,
double xc, double yc,
double th0, double th1,
double rx, double ry, double x_axis_rotation)
{
double sin_th, cos_th;
double a00, a01, a10, a11;
double x1, y1, x2, y2, x3, y3;
double t;
double th_half;
sin_th = sin (x_axis_rotation * (M_PI / 180.0));
cos_th = cos (x_axis_rotation * (M_PI / 180.0));
/* inverse transform compared with rsvg_path_arc */
a00 = cos_th * rx;
a01 = -sin_th * ry;
a10 = sin_th * rx;
a11 = cos_th * ry;
th_half = 0.5 * (th1 - th0);
t = (8.0 / 3.0) * sin (th_half * 0.5) * sin (th_half * 0.5) / sin (th_half);
x1 = xc + cos (th0) - t * sin (th0);
y1 = yc + sin (th0) + t * cos (th0);
x3 = xc + cos (th1);
y3 = yc + sin (th1);
x2 = x3 + t * sin (th1);
y2 = y3 - t * cos (th1);
rsvg_bpath_def_curveto (ctx->bpath,
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
}
/**
* rsvg_path_arc: Add an RSVG arc to the path context.
* @ctx: Path context.
* @rx: Radius in x direction (before rotation).
* @ry: Radius in y direction (before rotation).
* @x_axis_rotation: Rotation angle for axes.
* @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
* @sweep: 0 for "negative angle", 1 for "positive angle".
* @x: New x coordinate.
* @y: New y coordinate.
*
**/
static void
rsvg_path_arc (RSVGParsePathCtx *ctx,
double rx, double ry, double x_axis_rotation,
int large_arc_flag, int sweep_flag,
double x, double y)
{
double sin_th, cos_th;
double a00, a01, a10, a11;
double x0, y0, x1, y1, xc, yc;
double d, sfactor, sfactor_sq;
double th0, th1, th_arc;
int i, n_segs;
sin_th = sin (x_axis_rotation * (M_PI / 180.0));
cos_th = cos (x_axis_rotation * (M_PI / 180.0));
a00 = cos_th / rx;
a01 = sin_th / rx;
a10 = -sin_th / ry;
a11 = cos_th / ry;
x0 = a00 * ctx->cpx + a01 * ctx->cpy;
y0 = a10 * ctx->cpx + a11 * ctx->cpy;
x1 = a00 * x + a01 * y;
y1 = a10 * x + a11 * y;
/* (x0, y0) is current point in transformed coordinate space.
(x1, y1) is new point in transformed coordinate space.
The arc fits a unit-radius circle in this space.
*/
d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
sfactor_sq = 1.0 / d - 0.25;
if (sfactor_sq < 0) sfactor_sq = 0;
sfactor = sqrt (sfactor_sq);
if (sweep_flag == large_arc_flag) sfactor = -sfactor;
xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
/* (xc, yc) is center of the circle. */
th0 = atan2 (y0 - yc, x0 - xc);
th1 = atan2 (y1 - yc, x1 - xc);
th_arc = th1 - th0;
if (th_arc < 0 && sweep_flag)
th_arc += 2 * M_PI;
else if (th_arc > 0 && !sweep_flag)
th_arc -= 2 * M_PI;
n_segs = ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
for (i = 0; i < n_segs; i++)
rsvg_path_arc_segment (ctx, xc, yc,
th0 + i * th_arc / n_segs,
th0 + (i + 1) * th_arc / n_segs,
rx, ry, x_axis_rotation);
ctx->cpx = x;
ctx->cpy = y;
}
/* supply defaults for missing parameters, assuming relative coordinates
are to be interpreted as x,y */
static void
rsvg_parse_path_default_xy (RSVGParsePathCtx *ctx, int n_params)
{
int i;
if (ctx->rel)
{
for (i = ctx->param; i < n_params; i++)
{
if (i > 2)
ctx->params[i] = ctx->params[i - 2];
else if (i == 1)
ctx->params[i] = ctx->cpy;
else if (i == 0)
/* we shouldn't get here (usually ctx->param > 0 as
precondition) */
ctx->params[i] = ctx->cpx;
}
}
else
{
for (i = ctx->param; i < n_params; i++)
ctx->params[i] = 0.0;
}
}
static void
rsvg_parse_path_do_cmd (RSVGParsePathCtx *ctx, gboolean final)
{
double x1, y1, x2, y2, x3, y3;
#ifdef VERBOSE
int i;
g_print ("parse_path %c:", ctx->cmd);
for (i = 0; i < ctx->param; i++)
g_print (" %f", ctx->params[i]);
g_print (final ? ".\n" : "\n");
#endif
switch (ctx->cmd)
{
case 'm':
/* moveto */
if (ctx->param == 2 || final)
{
rsvg_parse_path_default_xy (ctx, 2);
#ifdef VERBOSE
g_print ("'m' moveto %g,%g\n",
ctx->params[0], ctx->params[1]);
#endif
rsvg_bpath_def_moveto (ctx->bpath,
ctx->params[0], ctx->params[1]);
ctx->cpx = ctx->rpx = ctx->params[0];
ctx->cpy = ctx->rpy = ctx->params[1];
ctx->param = 0;
}
break;
case 'l':
/* lineto */
if (ctx->param == 2 || final)
{
rsvg_parse_path_default_xy (ctx, 2);
#ifdef VERBOSE
g_print ("'l' lineto %g,%g\n",
ctx->params[0], ctx->params[1]);
#endif
rsvg_bpath_def_lineto (ctx->bpath,
ctx->params[0], ctx->params[1]);
ctx->cpx = ctx->rpx = ctx->params[0];
ctx->cpy = ctx->rpy = ctx->params[1];
ctx->param = 0;
}
break;
case 'c':
/* curveto */
if (ctx->param == 6 || final)
{
rsvg_parse_path_default_xy (ctx, 6);
x1 = ctx->params[0];
y1 = ctx->params[1];
x2 = ctx->params[2];
y2 = ctx->params[3];
x3 = ctx->params[4];
y3 = ctx->params[5];
#ifdef VERBOSE
g_print ("'c' curveto %g,%g %g,%g, %g,%g\n",
x1, y1, x2, y2, x3, y3);
#endif
rsvg_bpath_def_curveto (ctx->bpath,
x1, y1, x2, y2, x3, y3);
ctx->rpx = x2;
ctx->rpy = y2;
ctx->cpx = x3;
ctx->cpy = y3;
ctx->param = 0;
}
break;
case 's':
/* smooth curveto */
if (ctx->param == 4 || final)
{
rsvg_parse_path_default_xy (ctx, 4);
x1 = 2 * ctx->cpx - ctx->rpx;
y1 = 2 * ctx->cpy - ctx->rpy;
x2 = ctx->params[0];
y2 = ctx->params[1];
x3 = ctx->params[2];
y3 = ctx->params[3];
#ifdef VERBOSE
g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
x1, y1, x2, y2, x3, y3);
#endif
rsvg_bpath_def_curveto (ctx->bpath,
x1, y1, x2, y2, x3, y3);
ctx->rpx = x2;
ctx->rpy = y2;
ctx->cpx = x3;
ctx->cpy = y3;
ctx->param = 0;
}
break;
case 'h':
/* horizontal lineto */
if (ctx->param == 1)
{
#ifdef VERBOSE
g_print ("'h' lineto %g,%g\n",
ctx->params[0], ctx->cpy);
#endif
rsvg_bpath_def_lineto (ctx->bpath,
ctx->params[0], ctx->cpy);
ctx->cpx = ctx->rpx = ctx->params[0];
ctx->param = 0;
}
break;
case 'v':
/* vertical lineto */
if (ctx->param == 1)
{
#ifdef VERBOSE
g_print ("'v' lineto %g,%g\n",
ctx->cpx, ctx->params[0]);
#endif
rsvg_bpath_def_lineto (ctx->bpath,
ctx->cpx, ctx->params[0]);
ctx->cpy = ctx->rpy = ctx->params[0];
ctx->param = 0;
}
break;
case 'q':
/* quadratic bezier curveto */
/* non-normative reference:
http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
*/
if (ctx->param == 4 || final)
{
rsvg_parse_path_default_xy (ctx, 4);
/* raise quadratic bezier to cubic */
x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
x3 = ctx->params[2];
y3 = ctx->params[3];
x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
#ifdef VERBOSE
g_print ("'q' curveto %g,%g %g,%g, %g,%g\n",
x1, y1, x2, y2, x3, y3);
#endif
rsvg_bpath_def_curveto (ctx->bpath,
x1, y1, x2, y2, x3, y3);
ctx->rpx = x2;
ctx->rpy = y2;
ctx->cpx = x3;
ctx->cpy = y3;
ctx->param = 0;
}
break;
case 't':
/* Truetype quadratic bezier curveto */
if (ctx->param == 2 || final)
{
double xc, yc; /* quadratic control point */
xc = 2 * ctx->cpx - ctx->rpx;
yc = 2 * ctx->cpy - ctx->rpy;
/* generate a quadratic bezier with control point = xc, yc */
x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
x3 = ctx->params[0];
y3 = ctx->params[1];
x2 = (x3 + 2 * xc) * (1.0 / 3.0);
y2 = (y3 + 2 * yc) * (1.0 / 3.0);
#ifdef VERBOSE
g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
x1, y1, x2, y2, x3, y3);
#endif
rsvg_bpath_def_curveto (ctx->bpath,
x1, y1, x2, y2, x3, y3);
ctx->rpx = xc;
ctx->rpy = yc;
ctx->cpx = x3;
ctx->cpy = y3;
ctx->param = 0;
}
else if (final)
{
if (ctx->param > 2)
{
rsvg_parse_path_default_xy (ctx, 4);
/* raise quadratic bezier to cubic */
x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
x3 = ctx->params[2];
y3 = ctx->params[3];
x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
#ifdef VERBOSE
g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
x1, y1, x2, y2, x3, y3);
#endif
rsvg_bpath_def_curveto (ctx->bpath,
x1, y1, x2, y2, x3, y3);
ctx->rpx = x2;
ctx->rpy = y2;
ctx->cpx = x3;
ctx->cpy = y3;
}
else
{
rsvg_parse_path_default_xy (ctx, 2);
#ifdef VERBOSE
g_print ("'t' lineto %g,%g\n",
ctx->params[0], ctx->params[1]);
#endif
rsvg_bpath_def_lineto (ctx->bpath,
ctx->params[0], ctx->params[1]);
ctx->cpx = ctx->rpx = ctx->params[0];
ctx->cpy = ctx->rpy = ctx->params[1];
}
ctx->param = 0;
}
break;
case 'a':
if (ctx->param == 7 || final)
{
rsvg_path_arc (ctx,
ctx->params[0], ctx->params[1], ctx->params[2],
ctx->params[3], ctx->params[4],
ctx->params[5], ctx->params[6]);
ctx->param = 0;
}
break;
default:
ctx->param = 0;
}
}
static void
rsvg_parse_path_data (RSVGParsePathCtx *ctx, const char *data)
{
int i = 0;
double val = 0;
char c = 0;
gboolean in_num = FALSE;
gboolean in_frac = FALSE;
gboolean in_exp = FALSE;
gboolean exp_wait_sign = FALSE;
int sign = 0;
int exp = 0;
int exp_sign = 0;
double frac = 0.0;
in_num = FALSE;
for (i = 0; ; i++)
{
c = data[i];
if (c >= '0' && c <= '9')
{
/* digit */
if (in_num)
{
if (in_exp)
{
exp = (exp * 10) + c - '0';
exp_wait_sign = FALSE;
}
else if (in_frac)
val += (frac *= 0.1) * (c - '0');
else
val = (val * 10) + c - '0';
}
else
{
in_num = TRUE;
in_frac = FALSE;
in_exp = FALSE;
exp = 0;
exp_sign = 1;
exp_wait_sign = FALSE;
val = c - '0';
sign = 1;
}
}
else if (c == '.')
{
if (!in_num)
{
in_num = TRUE;
val = 0;
}
in_frac = TRUE;
frac = 1;
}
else if ((c == 'E' || c == 'e') && in_num)
{
in_exp = TRUE;
exp_wait_sign = TRUE;
exp = 0;
exp_sign = 1;
}
else if ((c == '+' || c == '-') && in_exp)
{
exp_sign = c == '+' ? 1 : -1;
}
else if (in_num)
{
/* end of number */
val *= sign * pow (10, exp_sign * exp);
if (ctx->rel)
{
/* Handle relative coordinates. This switch statement attempts
to determine _what_ the coords are relative to. This is
underspecified in the 12 Apr working draft. */
switch (ctx->cmd)
{
case 'l':
case 'm':
case 'c':
case 's':
case 'q':
case 't':
#ifndef RSVGV_RELATIVE
/* rule: even-numbered params are x-relative, odd-numbered
are y-relative */
if (ctx->param == 0)
val += ctx->cpx;
else if (ctx->param == 1)
val += ctx->cpy;
else
val += ctx->params[ctx->param - 2];
break;
#else
/* rule: even-numbered params are x-relative, odd-numbered
are y-relative */
if (ctx->param == 0 || (ctx->param % 2 ==0))
val += ctx->cpx;
else
val += ctx->cpy;
break;
#endif
case 'a':
/* rule: sixth and seventh are x and y, rest are not
relative */
if (ctx->param == 5)
val += ctx->cpx;
else if (ctx->param == 6)
val += ctx->cpy;
break;
case 'h':
/* rule: x-relative */
val += ctx->cpx;
break;
case 'v':
/* rule: y-relative */
val += ctx->cpy;
break;
}
}
ctx->params[ctx->param++] = val;
rsvg_parse_path_do_cmd (ctx, FALSE);
in_num = FALSE;
}
if (c == '\0')
break;
else if ((c == '+' || c == '-') && !exp_wait_sign)
{
sign = c == '+' ? 1 : -1;;
val = 0;
in_num = TRUE;
in_frac = FALSE;
in_exp = FALSE;
exp = 0;
exp_sign = 1;
exp_wait_sign = FALSE;
}
else if (c == 'z' || c == 'Z')
{
if (ctx->param)
rsvg_parse_path_do_cmd (ctx, TRUE);
#ifdef VERBOSE
g_print ("'z' closepath\n");
#endif
rsvg_bpath_def_closepath (ctx->bpath);
}
else if (c >= 'A' && c <= 'Z' && c != 'E')
{
if (ctx->param)
rsvg_parse_path_do_cmd (ctx, TRUE);
ctx->cmd = c + 'a' - 'A';
ctx->rel = FALSE;
}
else if (c >= 'a' && c <= 'z' && c != 'e')
{
if (ctx->param)
rsvg_parse_path_do_cmd (ctx, TRUE);
ctx->cmd = c;
ctx->rel = TRUE;
}
/* else c _should_ be whitespace or , */
}
}
RsvgBpathDef *
rsvg_parse_path (const char *path_str)
{
RSVGParsePathCtx ctx;
ctx.bpath = rsvg_bpath_def_new ();
ctx.cpx = 0.0;
ctx.cpy = 0.0;
ctx.cmd = 0;
ctx.param = 0;
rsvg_parse_path_data (&ctx, path_str);
if (ctx.param)
rsvg_parse_path_do_cmd (&ctx, TRUE);
return ctx.bpath;
}

2
librsvg/rsvg-path.h Normal file
View file

@ -0,0 +1,2 @@
RsvgBpathDef *
rsvg_parse_path (const char *path_str);

771
librsvg/rsvg.c Normal file
View file

@ -0,0 +1,771 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <glib.h>
#include <libart_lgpl/art_misc.h>
#include <libart_lgpl/art_affine.h>
#include <libart_lgpl/art_svp.h>
#include <libart_lgpl/art_bpath.h>
#include <libart_lgpl/art_vpath.h>
#include <libart_lgpl/art_vpath_bpath.h>
#include <libart_lgpl/art_rgb_svp.h>
#include <libart_lgpl/art_svp_vpath_stroke.h>
#include <libart_lgpl/art_svp_vpath.h>
#include <libart_lgpl/art_svp_wind.h>
#include "art_rgba_svp.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "SAX.h"
#include "rsvg-bpath-util.h"
#include "rsvg-path.h"
#include "rsvg.h"
#define noVERBOSE
typedef struct _RsvgCtx RsvgCtx;
typedef struct _RsvgState RsvgState;
struct _RsvgCtx {
GdkPixbuf *pixbuf;
double zoom;
/* stack; there is a state for each element */
RsvgState *state;
int n_state;
int n_state_max;
};
struct _RsvgState {
double affine[6];
gboolean fill;
guint32 fill_color; /* rgb */
gint fill_opacity; /* 0...255 */
gboolean stroke;
guint32 stroke_color; /* rgb */
gint stroke_opacity; /* 0..255 */
double stroke_width;
ArtPathStrokeCapType cap;
ArtPathStrokeJoinType join;
double font_size;
};
static RsvgCtx *
rsvg_ctx_new (void)
{
RsvgCtx *result;
result = g_new (RsvgCtx, 1);
result->pixbuf = NULL;
result->zoom = 1.0;
result->n_state = 0;
result->n_state_max = 16;
result->state = g_new (RsvgState, result->n_state_max);
return result;
}
/* does not destroy the pixbuf */
static void
rsvg_ctx_free (RsvgCtx *ctx)
{
free (ctx->state);
free (ctx);
}
static void
rsvg_state_init (RsvgState *state)
{
art_affine_identity (state->affine);
state->fill = 0;
state->fill_color = 0;
state->fill_opacity = 0xff;
state->stroke = 0;
state->stroke_color = 0;
state->stroke_opacity = 0xff;
state->stroke_width = 1;
state->cap = ART_PATH_STROKE_CAP_BUTT;
state->join = ART_PATH_STROKE_JOIN_MITER;
}
/**
* rsvg_css_parse_length: Parse CSS2 length to a pixel value.
* @str: Original string.
* @fixed: Where to store boolean value of whether length is fixed.
*
* Parses a CSS2 length into a pixel value.
*
* Returns: returns the length.
**/
static double
rsvg_css_parse_length (const char *str, gint *fixed)
{
char *p;
/*
* The supported CSS length unit specifiers are:
* em, ex, px, pt, pc, cm, mm, in, and percentages.
*/
*fixed = FALSE;
p = strstr (str, "px");
if (p != NULL)
{
*fixed = TRUE;
return atof (str);
}
p = strstr (str, "in");
if (p != NULL)
{
*fixed = TRUE;
/* return svg->pixels_per_inch * atof (str); */
}
return atof (str);
}
static void
rsvg_pixmap_destroy (guchar *pixels, gpointer data)
{
g_free (pixels);
}
static void
rsvg_start_svg (RsvgCtx *ctx, const xmlChar **atts)
{
int i;
int width = -1, height = -1;
int rowstride;
art_u8 *pixels;
gint fixed;
RsvgState *state;
gboolean has_alpha = 1;
if (atts != NULL)
{
for (i = 0; atts[i] != NULL; i += 2)
{
if (!strcmp ((char *)atts[i], "width"))
width = rsvg_css_parse_length ((char *)atts[i + 1], &fixed);
else if (!strcmp ((char *)atts[i], "height"))
height = rsvg_css_parse_length ((char *)atts[i + 1], &fixed);
}
#ifdef VERBOSE
fprintf (stdout, "rsvg_start_svg: width = %d, height = %d\n",
width, height);
#endif
/* Scale size of target pixbuf */
width = ceil (width * ctx->zoom);
height = ceil (height * ctx->zoom);
state = &ctx->state[ctx->n_state - 1];
art_affine_scale (state->affine, ctx->zoom, ctx->zoom);
rowstride = (width * (has_alpha ? 4 : 3) + 3) & -4;
pixels = g_new (art_u8, rowstride * height);
memset (pixels, has_alpha ? 0 : 255, rowstride * height);
ctx->pixbuf = gdk_pixbuf_new_from_data (pixels,
GDK_COLORSPACE_RGB,
has_alpha, 8,
width, height,
rowstride,
rsvg_pixmap_destroy,
NULL);
}
}
static gboolean
rsvg_css_param_match (const char *str, const char *param_name)
{
int i;
for (i = 0; str[i] != '\0' && str[i] != ':'; i++)
if (param_name[i] != str[i])
return FALSE;
return str[i] == ':' && param_name[i] == '\0';
}
static int
rsvg_css_param_arg_offset (const char *str)
{
int i;
for (i = 0; str[i] != '\0' && str[i] != ':'; i++);
if (str[i] != '\0') i++;
for (; str[i] == ' '; i++);
return i;
}
/* Parse a CSS2 color, returning rgb */
static guint32
rsvg_css_parse_color (const char *str)
{
gint val = 0;
static GHashTable *colors = NULL;
/*
* todo: handle the rgb (r, g, b) and rgb ( r%, g%, b%), syntax
* defined in http://www.w3.org/TR/REC-CSS2/syndata.html#color-units
*/
#ifdef VERBOSE
g_print ("color = %s\n", str);
#endif
if (str[0] == '#')
{
int i;
for (i = 1; str[i]; i++)
{
int hexval;
if (str[i] >= '0' && str[i] <= '9')
hexval = str[i] - '0';
else if (str[i] >= 'A' && str[i] <= 'F')
hexval = str[i] - 'A' + 10;
else if (str[i] >= 'a' && str[i] <= 'f')
hexval = str[i] - 'a' + 10;
else
break;
val = (val << 4) + hexval;
}
/* handle #rgb case */
if (i == 4)
{
val = ((val & 0xf00) << 8) |
((val & 0x0f0) << 4) |
(val & 0x00f);
val |= val << 4;
}
#ifdef VERBOSE
printf ("val = %x\n", val);
#endif
}
else
{
GString * string;
if (!colors)
{
colors = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (colors, "black", GINT_TO_POINTER (0x000000));
g_hash_table_insert (colors, "silver", GINT_TO_POINTER (0xc0c0c0));
g_hash_table_insert (colors, "gray", GINT_TO_POINTER (0x808080));
g_hash_table_insert (colors, "white", GINT_TO_POINTER (0xFFFFFF));
g_hash_table_insert (colors, "maroon", GINT_TO_POINTER (0x800000));
g_hash_table_insert (colors, "red", GINT_TO_POINTER (0xFF0000));
g_hash_table_insert (colors, "purple", GINT_TO_POINTER (0x800080));
g_hash_table_insert (colors, "fuchsia", GINT_TO_POINTER (0xFF00FF));
g_hash_table_insert (colors, "green", GINT_TO_POINTER (0x008000));
g_hash_table_insert (colors, "lime", GINT_TO_POINTER (0x00FF00));
g_hash_table_insert (colors, "olive", GINT_TO_POINTER (0x808000));
g_hash_table_insert (colors, "yellow", GINT_TO_POINTER (0xFFFF00));
g_hash_table_insert (colors, "navy", GINT_TO_POINTER (0x000080));
g_hash_table_insert (colors, "blue", GINT_TO_POINTER (0x0000FF));
g_hash_table_insert (colors, "teal", GINT_TO_POINTER (0x008080));
g_hash_table_insert (colors, "aqua", GINT_TO_POINTER (0x00FFFF));
}
string = g_string_down (g_string_new (str));
/* this will default to black on a failed lookup */
val = GPOINTER_TO_INT (g_hash_table_lookup (colors, string->str));
}
return val;
}
static guint
rsvg_css_parse_opacity (const char *str)
{
char *end_ptr;
double opacity;
opacity = strtod (str, &end_ptr);
if (end_ptr[0] == '%')
opacity *= 0.01;
return floor (opacity * 255 + 0.5);
}
static double
rsvg_css_parse_fontsize (RsvgState *state, const char *str)
{
char *end_ptr;
double size;
/* todo: handle absolute-size and relative-size tags and proper units */
size = strtod (str, &end_ptr);
if (end_ptr[0] == '%')
size = (state->font_size * size * 0.01);
return size;
}
/* Parse a CSS2 style argument, setting the SVG context attributes. */
static void
rsvg_parse_style_arg (RsvgState *state, const char *str)
{
int arg_off;
if (rsvg_css_param_match (str, "opacity"))
{
arg_off = rsvg_css_param_arg_offset (str);
/* state->opacity = rsvg_css_parse_opacity (str + arg_off); */
}
else if (rsvg_css_param_match (str, "fill"))
{
arg_off = rsvg_css_param_arg_offset (str);
if (!strcmp (str + arg_off, "none"))
state->fill = 0;
else
{
state->fill = 1;
state->fill_color = rsvg_css_parse_color (str + arg_off);
}
}
else if (rsvg_css_param_match (str, "fill-opacity"))
{
arg_off = rsvg_css_param_arg_offset (str);
state->fill_opacity = rsvg_css_parse_opacity (str + arg_off);
}
else if (rsvg_css_param_match (str, "stroke"))
{
arg_off = rsvg_css_param_arg_offset (str);
if (!strcmp (str + arg_off, "none"))
state->stroke = 0;
else
{
state->stroke = 1;
state->stroke_color = rsvg_css_parse_color (str + arg_off);
}
}
else if (rsvg_css_param_match (str, "stroke-width"))
{
int fixed;
arg_off = rsvg_css_param_arg_offset (str);
state->stroke_width = rsvg_css_parse_length (str + arg_off, &fixed);
}
else if (rsvg_css_param_match (str, "stroke-linecap"))
{
arg_off = rsvg_css_param_arg_offset (str);
if (!strcmp (str + arg_off, "butt"))
state->cap = ART_PATH_STROKE_CAP_BUTT;
else if (!strcmp (str + arg_off, "round"))
state->cap = ART_PATH_STROKE_CAP_ROUND;
else if (!strcmp (str + arg_off, "square"))
state->cap = ART_PATH_STROKE_CAP_SQUARE;
else
g_warning ("unknown line cap style %s", str + arg_off);
}
else if (rsvg_css_param_match (str, "stroke-opacity"))
{
arg_off = rsvg_css_param_arg_offset (str);
state->stroke_opacity = rsvg_css_parse_opacity (str + arg_off);
}
else if (rsvg_css_param_match (str, "stroke-linejoin"))
{
arg_off = rsvg_css_param_arg_offset (str);
if (!strcmp (str + arg_off, "miter"))
state->join = ART_PATH_STROKE_JOIN_MITER;
else if (!strcmp (str + arg_off, "round"))
state->join = ART_PATH_STROKE_JOIN_ROUND;
else if (!strcmp (str + arg_off, "bevel"))
state->join = ART_PATH_STROKE_JOIN_BEVEL;
else
g_warning ("unknown line join style %s", str + arg_off);
}
else if (rsvg_css_param_match (str, "font-size"))
{
arg_off = rsvg_css_param_arg_offset (str);
state->font_size = rsvg_css_parse_fontsize (state, str + arg_off);
}
else if (rsvg_css_param_match (str, "font-family"))
{
arg_off = rsvg_css_param_arg_offset (str);
/* state->font_family = g_strdup (str + arg_off); */
}
}
/* Split a CSS2 style into individual style arguments, setting attributes
in the SVG context.
It's known that this is _way_ out of spec. A more complete CSS2
implementation will happen later.
*/
static void
rsvg_parse_style (RsvgState *state, const char *str)
{
int start, end;
char *arg;
start = 0;
while (str[start] != '\0')
{
for (end = start; str[end] != '\0' && str[end] != ';'; end++);
arg = g_new (char, 1 + end - start);
memcpy (arg, str + start, end - start);
arg[end - start] = '\0';
rsvg_parse_style_arg (state, arg);
g_free (arg);
start = end;
if (str[start] == ';') start++;
while (str[start] == ' ') start++;
}
}
/**
* rsvg_parse_style_attrs: Parse style attribute.
* @ctx: Rsvg context.
* @atts: Attributes in SAX style.
*
* Parses style attribute and modifies state at top of stack.
*
* Note: this routine will also be responsible for parsing transforms.
**/
static void
rsvg_parse_style_attrs (RsvgCtx *ctx, const xmlChar **atts)
{
int i;
if (atts != NULL)
{
for (i = 0; atts[i] != NULL; i += 2)
{
if (!strcmp ((char *)atts[i], "style"))
rsvg_parse_style (&ctx->state[ctx->n_state - 1],
(char *)atts[i + 1]);
}
}
}
static void
rsvg_start_g (RsvgCtx *ctx, const xmlChar **atts)
{
rsvg_parse_style_attrs (ctx, atts);
}
/**
* rsvg_close_vpath: Close a vector path.
* @src: Source vector path.
*
* Closes any open subpaths in the vector path.
*
* Return value: Closed vector path, allocated with g_new.
**/
static ArtVpath *
rsvg_close_vpath (const ArtVpath *src)
{
ArtVpath *result;
int n_result, n_result_max;
int src_ix;
double beg_x, beg_y;
gboolean open;
n_result = 0;
n_result_max = 16;
result = g_new (ArtVpath, n_result_max);
beg_x = 0;
beg_y = 0;
open = FALSE;
for (src_ix = 0; src[src_ix].code != ART_END; src_ix++)
{
if (n_result == n_result_max)
result = g_renew (ArtVpath, result, n_result_max <<= 1);
result[n_result].code = src[src_ix].code == ART_MOVETO_OPEN ?
ART_MOVETO : src[src_ix].code;
result[n_result].x = src[src_ix].x;
result[n_result].y = src[src_ix].y;
n_result++;
if (src[src_ix].code == ART_MOVETO_OPEN)
{
beg_x = src[src_ix].x;
beg_y = src[src_ix].y;
open = TRUE;
}
else if (src[src_ix + 1].code != ART_LINETO)
{
if (open && (beg_x != src[src_ix].x || beg_y != src[src_ix].y))
{
if (n_result == n_result_max)
result = g_renew (ArtVpath, result, n_result_max <<= 1);
result[n_result].code = ART_LINETO;
result[n_result].x = beg_x;
result[n_result].y = beg_y;
n_result++;
}
open = FALSE;
}
}
if (n_result == n_result_max)
result = g_renew (ArtVpath, result, n_result_max <<= 1);
result[n_result].code = ART_END;
result[n_result].x = 0.0;
result[n_result].y = 0.0;
return result;
}
/**
* rsvg_render_svp: Render an SVP.
* @ctx: Context in which to render.
* @svp: SVP to render.
* @rgba: Color.
*
* Renders the SVP over the pixbuf in @ctx.
**/
static void
rsvg_render_svp (RsvgCtx *ctx, const ArtSVP *svp, guint32 rgba)
{
GdkPixbuf *pixbuf;
pixbuf = ctx->pixbuf;
if (gdk_pixbuf_get_has_alpha (pixbuf))
art_rgba_svp_alpha (svp, 0, 0, gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
rgba,
gdk_pixbuf_get_pixels (pixbuf),
gdk_pixbuf_get_rowstride (pixbuf),
NULL);
else
art_rgb_svp_alpha (svp, 0, 0, gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
rgba,
gdk_pixbuf_get_pixels (pixbuf),
gdk_pixbuf_get_rowstride (pixbuf),
NULL);
}
static void
rsvg_render_bpath (RsvgCtx *ctx, const ArtBpath *bpath)
{
RsvgState *state;
ArtBpath *affine_bpath;
ArtVpath *vpath;
ArtSVP *svp;
GdkPixbuf *pixbuf;
state = &ctx->state[ctx->n_state - 1];
pixbuf = ctx->pixbuf;
affine_bpath = art_bpath_affine_transform (bpath,
state->affine);
vpath = art_bez_path_to_vec (affine_bpath, 0.25);
art_free (affine_bpath);
if (state->fill)
{
ArtVpath *closed_vpath;
ArtVpath *perturbed_vpath;
ArtSVP *tmp_svp;
ArtWindRule art_wind;
closed_vpath = rsvg_close_vpath (vpath);
perturbed_vpath = art_vpath_perturb (closed_vpath);
g_free (closed_vpath);
svp = art_svp_from_vpath (perturbed_vpath);
art_free (perturbed_vpath);
tmp_svp = art_svp_uncross (svp);
art_svp_free (svp);
art_wind = ART_WIND_RULE_NONZERO; /* todo - get from state */
svp = art_svp_rewind_uncrossed (tmp_svp, art_wind);
art_svp_free (tmp_svp);
rsvg_render_svp (ctx, svp,
(state->fill_color << 8) | state->fill_opacity);
art_svp_free (svp);
}
if (state->stroke)
{
svp = art_svp_vpath_stroke (vpath, state->join, state->cap,
state->stroke_width, 4, 0.25);
rsvg_render_svp (ctx, svp,
(state->stroke_color << 8) | state->stroke_opacity);
art_svp_free (svp);
}
art_free (vpath);
}
static void
rsvg_start_path (RsvgCtx *ctx, const xmlChar **atts)
{
int i;
char *d = NULL;
if (atts != NULL)
{
for (i = 0; atts[i] != NULL; i += 2)
{
if (!strcmp ((char *)atts[i], "d"))
d = (char *)atts[i + 1];
}
}
if (d != NULL)
{
RsvgBpathDef *bpath_def;
bpath_def = rsvg_parse_path (d);
rsvg_bpath_def_art_finish (bpath_def);
rsvg_render_bpath (ctx, bpath_def->bpath);
rsvg_bpath_def_free (bpath_def);
}
}
static void
rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts)
{
RsvgCtx *ctx = (RsvgCtx *)data;
#ifdef VERBOSE
int i;
#endif
#ifdef VERBOSE
fprintf (stdout, "SAX.startElement(%s", (char *) name);
if (atts != NULL) {
for (i = 0;(atts[i] != NULL);i++) {
fprintf (stdout, ", %s='", atts[i++]);
fprintf (stdout, "%s'", atts[i]);
}
}
fprintf (stdout, ")\n");
#endif
/* push the state stack */
if (ctx->n_state == ctx->n_state_max)
ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1);
if (ctx->n_state)
ctx->state[ctx->n_state] = ctx->state[ctx->n_state - 1];
else
rsvg_state_init (ctx->state);
ctx->n_state++;
if (!strcmp ((char *)name, "svg"))
rsvg_start_svg (ctx, atts);
else if (!strcmp ((char *)name, "g"))
rsvg_start_g (ctx, atts);
else if (!strcmp ((char *)name, "path"))
rsvg_start_path (ctx, atts);
}
static void
rsvg_end_element (void *data, const xmlChar *name)
{
RsvgCtx *ctx = (RsvgCtx *)data;
/* pop the state stack */
ctx->n_state--;
#ifdef VERBOSE
fprintf (stdout, "SAX.endElement(%s)\n", (char *) name);
#endif
}
xmlSAXHandler emptySAXHandlerStruct = {
NULL, /* internalSubset */
NULL, /* isStandalone */
NULL, /* hasInternalSubset */
NULL, /* hasExternalSubset */
NULL, /* resolveEntity */
NULL, /* getEntity */
NULL, /* entityDecl */
NULL, /* notationDecl */
NULL, /* attributeDecl */
NULL, /* elementDecl */
NULL, /* unparsedEntityDecl */
NULL, /* setDocumentLocator */
NULL, /* startDocument */
NULL, /* endDocument */
rsvg_start_element, /* startElement */
rsvg_end_element, /* endElement */
NULL, /* reference */
NULL, /* characters */
NULL, /* ignorableWhitespace */
NULL, /* processingInstruction */
NULL, /* comment */
NULL, /* xmlParserWarning */
NULL, /* xmlParserError */
NULL, /* xmlParserError */
NULL, /* getParameterEntity */
};
xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct;
GdkPixbuf *
rsvg_render_file (FILE *f, double zoom)
{
int res;
char chars[10];
xmlParserCtxtPtr ctxt;
RsvgCtx *ctx;
GdkPixbuf *result;
ctx = rsvg_ctx_new ();
ctx->zoom = zoom;
res = fread(chars, 1, 4, f);
if (res > 0) {
ctxt = xmlCreatePushParserCtxt(emptySAXHandler, ctx,
chars, res, "filename XXX");
while ((res = fread(chars, 1, 3, f)) > 0) {
xmlParseChunk(ctxt, chars, res, 0);
}
xmlParseChunk(ctxt, chars, 0, 1);
xmlFreeParserCtxt(ctxt);
}
result = ctx->pixbuf;
rsvg_ctx_free (ctx);
return result;
}
#ifdef RSVG_MAIN
static void
write_pixbuf (ArtPixBuf *pixbuf)
{
int y;
printf ("P6\n%d %d\n255\n", pixbuf->width, pixbuf->height);
for (y = 0; y < pixbuf->height; y++)
fwrite (pixbuf->pixels + y * pixbuf->rowstride, 1, pixbuf->width * 3,
stdout);
}
int
main (int argc, char **argv)
{
FILE *f;
ArtPixBuf *pixbuf;
if (argc == 1)
f = stdin;
else
{
f = fopen (argv[1], "r");
if (f == NULL)
{
fprintf (stderr, "Error opening source file %s\n", argv[1]);
}
}
pixbuf = rsvg_render_file (f);
if (f != stdin)
fclose (f);
write_pixbuf (pixbuf);
return 0;
}
#endif

2
librsvg/rsvg.h Normal file
View file

@ -0,0 +1,2 @@
GdkPixbuf *
rsvg_render_file (FILE *f, double zoom);

View file

@ -18,6 +18,7 @@ LDADD =\
file-manager/libntl-file-manager.la \
../nautilus-widgets/libnautilus-widgets.la \
../libnautilus/libnautilus.la \
../librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNORBA_LIBS) \
$(GNOMEUI_LIBS) \