summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMarkus Engel <markus.engel@tum.de>2013-09-19 17:32:15 +0000
committerMarkus Engel <markus.engel@tum.de>2013-09-19 17:32:15 +0000
commit658a60b2ffdb7ed361b3c3b57d62efd419f7ba47 (patch)
tree7f0f76347c27e59a784b4a3af990af7b247c5b41 /src
parentAdded gpl notice (diff)
parentDo not require a new layer for clipping paths in the Cairo renderer. (diff)
downloadinkscape-658a60b2ffdb7ed361b3c3b57d62efd419f7ba47.tar.gz
inkscape-658a60b2ffdb7ed361b3c3b57d62efd419f7ba47.zip
Merged from trunk (r12544).
(bzr r11608.1.126)
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/Makefile.am22
-rw-r--r--src/desktop.cpp1
-rw-r--r--src/display/cairo-utils.cpp539
-rw-r--r--src/display/cairo-utils.h70
-rw-r--r--src/display/drawing-image.cpp64
-rw-r--r--src/display/drawing-image.h6
-rw-r--r--src/display/drawing-item.cpp8
-rw-r--r--src/display/drawing-item.h4
-rw-r--r--src/display/drawing-shape.cpp4
-rw-r--r--src/display/drawing-text.cpp6
-rw-r--r--src/display/nr-filter-image.cpp69
-rw-r--r--src/display/nr-filter-image.h6
-rw-r--r--src/display/nr-filter.cpp27
-rw-r--r--src/display/nr-filter.h6
-rw-r--r--src/doxygen-main.cpp1
-rw-r--r--src/extension/internal/cairo-ps-out.cpp2
-rw-r--r--src/extension/internal/cairo-render-context.cpp8
-rw-r--r--src/extension/internal/cairo-render-context.h6
-rw-r--r--src/extension/internal/cairo-renderer.cpp30
-rw-r--r--src/extension/internal/emf-print.cpp9
-rw-r--r--src/extension/internal/gdkpixbuf-input.cpp20
-rw-r--r--src/extension/internal/metafile-print.cpp2
-rw-r--r--src/extension/internal/metafile-print.h4
-rw-r--r--src/extension/internal/wmf-print.cpp13
-rw-r--r--src/filter-chemistry.cpp18
-rw-r--r--src/filters/image.cpp2
-rw-r--r--src/helper/pixbuf-ops.cpp19
-rw-r--r--src/helper/pixbuf-ops.h3
-rw-r--r--src/interface.cpp15
-rw-r--r--src/interface.h5
-rw-r--r--src/libdepixelize/CMakeLists.txt11
-rw-r--r--src/libdepixelize/Makefile_insert11
-rw-r--r--src/libdepixelize/kopftracer2011.cpp470
-rw-r--r--src/libdepixelize/kopftracer2011.h123
-rw-r--r--src/libdepixelize/makefile.in17
-rw-r--r--src/libdepixelize/priv/branchless.h58
-rw-r--r--src/libdepixelize/priv/colorspace.h111
-rw-r--r--src/libdepixelize/priv/homogeneoussplines.h465
-rw-r--r--src/libdepixelize/priv/iterator.h123
-rw-r--r--src/libdepixelize/priv/pixelgraph.h490
-rw-r--r--src/libdepixelize/priv/point.h75
-rw-r--r--src/libdepixelize/priv/simplifiedvoronoi.h1324
-rw-r--r--src/libdepixelize/priv/splines.h155
-rw-r--r--src/libdepixelize/splines.h120
-rw-r--r--src/libnrtype/Layout-TNG-Compute.cpp2
-rw-r--r--src/livarot/PathStroke.cpp34
-rw-r--r--src/livarot/ShapeSweep.cpp6
-rw-r--r--src/menus-skeleton.h1
-rw-r--r--src/selection-chemistry.cpp4
-rw-r--r--src/sp-image.cpp399
-rw-r--r--src/sp-image.h49
-rw-r--r--src/sp-item-group.cpp2
-rw-r--r--src/sp-item.cpp2
-rw-r--r--src/sp-object.cpp82
-rw-r--r--src/sp-skeleton.cpp198
-rw-r--r--src/sp-skeleton.h48
-rw-r--r--src/sp-string.cpp17
-rw-r--r--src/sp-use.cpp22
-rw-r--r--src/style.cpp5
-rw-r--r--src/trace/imagemap-gdk.cpp4
-rw-r--r--src/trace/trace.cpp25
-rw-r--r--src/ui/CMakeLists.txt1
-rw-r--r--src/ui/dialog/Makefile_insert2
-rw-r--r--src/ui/dialog/dialog-manager.cpp3
-rw-r--r--src/ui/dialog/inkscape-preferences.cpp2
-rw-r--r--src/ui/dialog/pixelartdialog.cpp492
-rw-r--r--src/ui/dialog/pixelartdialog.h58
-rw-r--r--src/verbs.cpp6
-rw-r--r--src/verbs.h1
70 files changed, 5000 insertions, 1010 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 42e091f43..a09bceb06 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -61,7 +61,6 @@ set(sp_SRC
sp-root.cpp
sp-script.cpp
sp-shape.cpp
- # sp-skeleton.cpp
sp-spiral.cpp
sp-star.cpp
sp-stop.cpp
@@ -153,7 +152,6 @@ set(sp_SRC
sp-root.h
sp-script.h
sp-shape.h
- # sp-skeleton.h
sp-spiral.h
sp-star.h
sp-stop.h
@@ -560,6 +558,7 @@ add_subdirectory(libuemf)
add_subdirectory(libvpsc)
add_subdirectory(livarot)
add_subdirectory(libnrtype)
+add_subdirectory(libdepixelize)
get_property(inkscape_global_SRC GLOBAL PROPERTY inkscape_global_SRC)
diff --git a/src/Makefile.am b/src/Makefile.am
index 6fcfa421d..a0c240252 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,15 +23,16 @@ endif
noinst_LIBRARIES = \
- dom/libdom.a \
- libcroco/libcroco.a \
- libavoid/libavoid.a \
- $(internal_GDL) \
- libuemf/libuemf.a \
- libcola/libcola.a \
- libvpsc/libvpsc.a \
- livarot/libvarot.a \
- 2geom/lib2geom.a \
+ dom/libdom.a \
+ libcroco/libcroco.a \
+ libavoid/libavoid.a \
+ $(internal_GDL) \
+ libuemf/libuemf.a \
+ libcola/libcola.a \
+ libvpsc/libvpsc.a \
+ livarot/libvarot.a \
+ 2geom/lib2geom.a \
+ libdepixelize/libdepixelize.a \
libinkversion.a
all_libs = \
@@ -138,6 +139,7 @@ include ui/widget/Makefile_insert
include util/Makefile_insert
include trace/Makefile_insert
include 2geom/Makefile_insert
+include libdepixelize/Makefile_insert
# Extra files not mentioned as sources to include in the source tarball
EXTRA_DIST += \
@@ -181,8 +183,8 @@ EXTRA_DIST += \
io/crystalegg.xml \
io/doc2html.xsl \
show-preview.bmp \
- sp-skeleton.cpp sp-skeleton.h \
winconsole.cpp \
+ libdepixelize/makefile.in \
$(CXXTEST_TEMPLATE)
# Extra files to remove when doing "make distclean"
diff --git a/src/desktop.cpp b/src/desktop.cpp
index cb56669a8..69d83d8da 100644
--- a/src/desktop.cpp
+++ b/src/desktop.cpp
@@ -1923,6 +1923,7 @@ SPDesktop::show_dialogs()
mapVerbPreference.insert(std::make_pair ((int)SP_VERB_DIALOG_DISPLAY, "/dialogs/preferences") );
mapVerbPreference.insert(std::make_pair ((int)SP_VERB_SELECTION_GRIDTILE, "/dialogs/gridtiler") );
mapVerbPreference.insert(std::make_pair ((int)SP_VERB_SELECTION_TRACE, "/dialogs/trace") );
+ mapVerbPreference.insert(std::make_pair ((int)SP_VERB_SELECTION_PIXEL_ART, "/dialogs/pixelart") );
mapVerbPreference.insert(std::make_pair ((int)SP_VERB_DIALOG_TEXT, "/dialogs/textandfont") );
mapVerbPreference.insert(std::make_pair ((int)SP_VERB_DIALOG_EXPORT, "/dialogs/export") );
mapVerbPreference.insert(std::make_pair ((int)SP_VERB_DIALOG_XML_EDITOR, "/dialogs/xml") );
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp
index 755553033..451f0b509 100644
--- a/src/display/cairo-utils.cpp
+++ b/src/display/cairo-utils.cpp
@@ -15,6 +15,8 @@
#include "display/cairo-utils.h"
#include <stdexcept>
+#include <glib/gstdio.h>
+#include <glibmm/fileutils.h>
#include <2geom/pathvector.h>
#include <2geom/bezier-curve.h>
#include <2geom/hvlinesegment.h>
@@ -28,6 +30,8 @@
#include "helper/geom-curves.h"
#include "display/cairo-templates.h"
+static void ink_cairo_pixbuf_cleanup(guchar *, void *);
+
/**
* Key for cairo_surface_t to keep track of current color interpolation value
* Only the address of the structure is used, it is never initialized. See:
@@ -116,6 +120,380 @@ Cairo::RefPtr<CairoContext> CairoContext::create(Cairo::RefPtr<Cairo::Surface> c
return ret;
}
+
+/* The class below implement the following hack:
+ *
+ * The pixels formats of Cairo and GdkPixbuf are different.
+ * GdkPixbuf accesses pixels as bytes, alpha is not premultiplied,
+ * and successive bytes of a single pixel contain R, G, B and A components.
+ * Cairo accesses pixels as 32-bit ints, alpha is premultiplied,
+ * and each int contains as 0xAARRGGBB, accessed with bitwise operations.
+ *
+ * In other words, on a little endian system, a GdkPixbuf will contain:
+ * char *data = "rgbargbargba...."
+ * int *data = { 0xAABBGGRR, 0xAABBGGRR, 0xAABBGGRR, ... }
+ * while a Cairo image surface will contain:
+ * char *data = "bgrabgrabgra...."
+ * int *data = { 0xAARRGGBB, 0xAARRGGBB, 0xAARRGGBB, ... }
+ *
+ * It is possible to convert between these two formats (almost) losslessly.
+ * Some color information from partially transparent regions of the image
+ * is lost, but the result when displaying this image will remain the same.
+ *
+ * The class allows interoperation between GdkPixbuf
+ * and Cairo surfaces without creating a copy of the image.
+ * This is implemented by creating a GdkPixbuf and a Cairo image surface
+ * which share their data. Depending on what is needed at a given time,
+ * the pixels are converted in place to the Cairo or the GdkPixbuf format.
+ */
+
+/** Create a pixbuf from a Cairo surface.
+ * The constructor takes ownership of the passed surface,
+ * so it should not be destroyed. */
+Pixbuf::Pixbuf(cairo_surface_t *s)
+ : _pixbuf(gdk_pixbuf_new_from_data(
+ cairo_image_surface_get_data(s), GDK_COLORSPACE_RGB, TRUE, 8,
+ cairo_image_surface_get_width(s), cairo_image_surface_get_height(s),
+ cairo_image_surface_get_stride(s), NULL, NULL))
+ , _surface(s)
+ , _mod_time(0)
+ , _pixel_format(PF_CAIRO)
+ , _cairo_store(true)
+{}
+
+/** Create a pixbuf from a GdkPixbuf.
+ * The constructor takes ownership of the passed GdkPixbuf reference,
+ * so it should not be unrefed. */
+Pixbuf::Pixbuf(GdkPixbuf *pb)
+ : _pixbuf(pb)
+ , _surface(0)
+ , _mod_time(0)
+ , _pixel_format(PF_GDK)
+ , _cairo_store(false)
+{
+ _forceAlpha();
+ _surface = cairo_image_surface_create_for_data(
+ gdk_pixbuf_get_pixels(_pixbuf), CAIRO_FORMAT_ARGB32,
+ gdk_pixbuf_get_width(_pixbuf), gdk_pixbuf_get_height(_pixbuf), gdk_pixbuf_get_rowstride(_pixbuf));
+}
+
+Pixbuf::Pixbuf(Inkscape::Pixbuf const &other)
+ : _pixbuf(gdk_pixbuf_copy(other._pixbuf))
+ , _surface(cairo_image_surface_create_for_data(
+ gdk_pixbuf_get_pixels(_pixbuf), CAIRO_FORMAT_ARGB32,
+ gdk_pixbuf_get_width(_pixbuf), gdk_pixbuf_get_height(_pixbuf), gdk_pixbuf_get_rowstride(_pixbuf)))
+ , _mod_time(other._mod_time)
+ , _path(other._path)
+ , _pixel_format(other._pixel_format)
+ , _cairo_store(false)
+{}
+
+Pixbuf::~Pixbuf()
+{
+ if (_cairo_store) {
+ g_object_unref(_pixbuf);
+ cairo_surface_destroy(_surface);
+ } else {
+ cairo_surface_destroy(_surface);
+ g_object_unref(_pixbuf);
+ }
+}
+
+Pixbuf *Pixbuf::create_from_data_uri(gchar const *uri_data)
+{
+ Pixbuf *pixbuf = NULL;
+
+ bool data_is_image = false;
+ bool data_is_base64 = false;
+
+ gchar const *data = uri_data;
+
+ while (*data) {
+ if (strncmp(data,"base64",6) == 0) {
+ /* base64-encoding */
+ data_is_base64 = true;
+ data_is_image = true; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what
+ data += 6;
+ }
+ else if (strncmp(data,"image/png",9) == 0) {
+ /* PNG image */
+ data_is_image = true;
+ data += 9;
+ }
+ else if (strncmp(data,"image/jpg",9) == 0) {
+ /* JPEG image */
+ data_is_image = true;
+ data += 9;
+ }
+ else if (strncmp(data,"image/jpeg",10) == 0) {
+ /* JPEG image */
+ data_is_image = true;
+ data += 10;
+ }
+ else if (strncmp(data,"image/jp2",9) == 0) {
+ /* JPEG2000 image */
+ data_is_image = true;
+ data += 9;
+ }
+ else { /* unrecognized option; skip it */
+ while (*data) {
+ if (((*data) == ';') || ((*data) == ',')) {
+ break;
+ }
+ data++;
+ }
+ }
+ if ((*data) == ';') {
+ data++;
+ continue;
+ }
+ if ((*data) == ',') {
+ data++;
+ break;
+ }
+ }
+
+ if ((*data) && data_is_image && data_is_base64) {
+ GdkPixbuf *buf = NULL;
+ GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+
+ if (!loader) return NULL;
+
+ gsize decoded_len = 0;
+ guchar *decoded = g_base64_decode(data, &decoded_len);
+
+ if (gdk_pixbuf_loader_write(loader, decoded, decoded_len, NULL)) {
+ gdk_pixbuf_loader_close(loader, NULL);
+ buf = gdk_pixbuf_loader_get_pixbuf(loader);
+ if (buf) {
+ g_object_ref(buf);
+ pixbuf = new Pixbuf(buf);
+
+ GdkPixbufFormat *fmt = gdk_pixbuf_loader_get_format(loader);
+ gchar *fmt_name = gdk_pixbuf_format_get_name(fmt);
+ pixbuf->_setMimeData(decoded, decoded_len, fmt_name);
+ g_free(fmt_name);
+ } else {
+ g_free(decoded);
+ }
+ } else {
+ g_free(decoded);
+ }
+ g_object_unref(loader);
+ }
+
+ return pixbuf;
+}
+
+Pixbuf *Pixbuf::create_from_file(std::string const &fn)
+{
+ Pixbuf *pb = NULL;
+ // test correctness of filename
+ if (!g_file_test(fn.c_str(), G_FILE_TEST_EXISTS)) {
+ return NULL;
+ }
+ struct stat stdir;
+ int val = g_stat(fn.c_str(), &stdir);
+ if (val == 0 && stdir.st_mode & S_IFDIR){
+ return NULL;
+ }
+
+ // we need to load the entire file into memory,
+ // since we'll store it as MIME data
+ gchar *data = NULL;
+ gsize len = 0;
+ GError *error;
+
+ if (g_file_get_contents(fn.c_str(), &data, &len, &error)) {
+
+ GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+ gdk_pixbuf_loader_write(loader, (guchar *) data, len, NULL);
+ gdk_pixbuf_loader_close(loader, NULL);
+
+ GdkPixbuf *buf = gdk_pixbuf_loader_get_pixbuf(loader);
+ if (buf) {
+ g_object_ref(buf);
+ pb = new Pixbuf(buf);
+ pb->_mod_time = stdir.st_mtime;
+ pb->_path = fn;
+
+ GdkPixbufFormat *fmt = gdk_pixbuf_loader_get_format(loader);
+ gchar *fmt_name = gdk_pixbuf_format_get_name(fmt);
+ pb->_setMimeData((guchar *) data, len, fmt_name);
+ g_free(fmt_name);
+ } else {
+ g_free(data);
+ }
+ g_object_unref(loader);
+
+ // TODO: we could also read DPI, ICC profile, gamma correction, and other information
+ // from the file. This can be done by using format-specific libraries e.g. libpng.
+ } else {
+ return NULL;
+ }
+
+ return pb;
+}
+
+/**
+ * Converts the pixbuf to GdkPixbuf pixel format.
+ * The returned pixbuf can be used e.g. in calls to gdk_pixbuf_save().
+ */
+GdkPixbuf *Pixbuf::getPixbufRaw(bool convert_format)
+{
+ if (convert_format) {
+ ensurePixelFormat(PF_GDK);
+ }
+ return _pixbuf;
+}
+
+/**
+ * Converts the pixbuf to Cairo pixel format and returns an image surface
+ * which can be used as a source.
+ *
+ * The returned surface is owned by the GdkPixbuf and should not be freed.
+ * Calling this function causes the pixbuf to be unsuitable for use
+ * with GTK drawing functions until ensurePixelFormat(Pixbuf::PIXEL_FORMAT_PIXBUF) is called.
+ */
+cairo_surface_t *Pixbuf::getSurfaceRaw(bool convert_format)
+{
+ if (convert_format) {
+ ensurePixelFormat(PF_CAIRO);
+ }
+ return _surface;
+}
+
+/* Declaring this function in the header requires including <gdkmm/pixbuf.h>,
+ * which stupidly includes <glibmm.h> which in turn pulls in <glibmm/threads.h>.
+ * However, since glib 2.32, <glibmm/threads.h> has to be included before <glib.h>
+ * when compiling with G_DISABLE_DEPRECATED, as we do in non-release builds.
+ * This necessitates spamming a lot of files with #include <glibmm/threads.h>
+ * at the top.
+ *
+ * Since we don't really use gdkmm, do not define this function for now. */
+
+/*
+Glib::RefPtr<Gdk::Pixbuf> Pixbuf::getPixbuf(bool convert_format = true)
+{
+ g_object_ref(_pixbuf);
+ Glib::RefPtr<Gdk::Pixbuf> p(getPixbuf(convert_format));
+ return p;
+}
+*/
+
+Cairo::RefPtr<Cairo::Surface> Pixbuf::getSurface(bool convert_format)
+{
+ Cairo::RefPtr<Cairo::Surface> p(new Cairo::Surface(getSurfaceRaw(convert_format), false));
+ return p;
+}
+
+/** Retrieves the original compressed data for the surface, if any.
+ * The returned data belongs to the object and should not be freed. */
+guchar const *Pixbuf::getMimeData(gsize &len, std::string &mimetype) const
+{
+ static gchar const *mimetypes[] = {
+ CAIRO_MIME_TYPE_JPEG, CAIRO_MIME_TYPE_JP2, CAIRO_MIME_TYPE_PNG, NULL };
+ static guint mimetypes_len = g_strv_length(const_cast<gchar**>(mimetypes));
+
+ guchar const *data = NULL;
+
+ for (guint i = 0; i < mimetypes_len; ++i) {
+ unsigned long len_long = 0;
+ cairo_surface_get_mime_data(const_cast<cairo_surface_t*>(_surface), mimetypes[i], &data, &len_long);
+ if (data != NULL) {
+ len = len_long;
+ mimetype = mimetypes[i];
+ break;
+ }
+ }
+
+ return data;
+}
+
+int Pixbuf::width() const {
+ return gdk_pixbuf_get_width(const_cast<GdkPixbuf*>(_pixbuf));
+}
+int Pixbuf::height() const {
+ return gdk_pixbuf_get_height(const_cast<GdkPixbuf*>(_pixbuf));
+}
+int Pixbuf::rowstride() const {
+ return gdk_pixbuf_get_rowstride(const_cast<GdkPixbuf*>(_pixbuf));
+}
+guchar const *Pixbuf::pixels() const {
+ return gdk_pixbuf_get_pixels(const_cast<GdkPixbuf*>(_pixbuf));
+}
+guchar *Pixbuf::pixels() {
+ return gdk_pixbuf_get_pixels(_pixbuf);
+}
+void Pixbuf::markDirty() {
+ cairo_surface_mark_dirty(_surface);
+}
+
+void Pixbuf::_forceAlpha()
+{
+ if (gdk_pixbuf_get_has_alpha(_pixbuf)) return;
+
+ GdkPixbuf *old = _pixbuf;
+ _pixbuf = gdk_pixbuf_add_alpha(old, FALSE, 0, 0, 0);
+ g_object_unref(old);
+}
+
+void Pixbuf::_setMimeData(guchar *data, gsize len, Glib::ustring const &format)
+{
+ gchar const *mimetype = NULL;
+
+ if (format == "jpeg") {
+ mimetype = CAIRO_MIME_TYPE_JPEG;
+ } else if (format == "jpeg2000") {
+ mimetype = CAIRO_MIME_TYPE_JP2;
+ } else if (format == "png") {
+ mimetype = CAIRO_MIME_TYPE_PNG;
+ }
+
+ if (mimetype != NULL) {
+ cairo_surface_set_mime_data(_surface, mimetype, data, len, g_free, data);
+ //g_message("Setting Cairo MIME data: %s", mimetype);
+ } else {
+ g_free(data);
+ //g_message("Not setting Cairo MIME data: unknown format %s", name.c_str());
+ }
+}
+
+void Pixbuf::ensurePixelFormat(PixelFormat fmt)
+{
+ if (_pixel_format == PF_GDK) {
+ if (fmt == PF_GDK) {
+ return;
+ }
+ if (fmt == PF_CAIRO) {
+ convert_pixels_pixbuf_to_argb32(
+ gdk_pixbuf_get_pixels(_pixbuf),
+ gdk_pixbuf_get_width(_pixbuf),
+ gdk_pixbuf_get_height(_pixbuf),
+ gdk_pixbuf_get_rowstride(_pixbuf));
+ _pixel_format = fmt;
+ return;
+ }
+ g_assert_not_reached();
+ }
+ if (_pixel_format == PF_CAIRO) {
+ if (fmt == PF_GDK) {
+ convert_pixels_argb32_to_pixbuf(
+ gdk_pixbuf_get_pixels(_pixbuf),
+ gdk_pixbuf_get_width(_pixbuf),
+ gdk_pixbuf_get_height(_pixbuf),
+ gdk_pixbuf_get_rowstride(_pixbuf));
+ _pixel_format = fmt;
+ return;
+ }
+ if (fmt == PF_CAIRO) {
+ return;
+ }
+ g_assert_not_reached();
+ }
+ g_assert_not_reached();
+}
+
} // namespace Inkscape
/*
@@ -371,129 +749,6 @@ ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m)
cairo_pattern_set_matrix(cp, &cm);
}
-void
-ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y)
-{
- cairo_surface_t *pbs = ink_cairo_surface_get_for_pixbuf(pb);
- cairo_set_source_surface(ct, pbs, x, y);
-}
-
-/* The functions below implement the following hack:
- *
- * The pixels formats of Cairo and GdkPixbuf are different.
- * GdkPixbuf accesses pixels as bytes, alpha is not premultiplied,
- * and successive bytes of a single pixel contain R, G, B and A components.
- * Cairo accesses pixels as 32-bit ints, alpha is premultiplied,
- * and each int contains as 0xAARRGGBB, accessed with bitwise operations.
- *
- * In other words, on a little endian system, a GdkPixbuf will contain:
- * char *data = "rgbargbargba...."
- * int *data = { 0xAABBGGRR, 0xAABBGGRR, 0xAABBGGRR, ... }
- * while a Cairo image surface will contain:
- * char *data = "bgrabgrabgra...."
- * int *data = { 0xAARRGGBB, 0xAARRGGBB, 0xAARRGGBB, ... }
- *
- * It is possible to convert between these two formats (almost) losslessly.
- * Some color information from partially transparent regions of the image
- * is lost, but the result when displaying this image will remain the same.
- *
- * The functions below allow interoperation between GdkPixbuf
- * and Cairo surfaces, allowing pixbufs to be used as Cairo sources,
- * and saving Cairo surfaces using GdkPixbuf APIs.
- * This is implemented by creating a GdkPixbuf and a Cairo image surface
- * which share their data. Depending on what is needed at a given time,
- * the pixels are converted in place to the Cairo or the GdkPixbuf format.
- * In this way, only one copy of the image data is needed.
- *
- * Given either a GdkPixbuf or a Cairo surface, these functions create
- * the other object and convert to its format. The returned object should be
- * freed using cairo_surface_destroy or g_object_unref when it's no longer
- * needed.
- *
- * Memory ownership semantics:
- * Regardless of whether the pixels are stored in memory originally belonging
- * to Cairo surface or to GdkPixbuf, the GdkPixbuf is the master object.
- * To free the memory, unref the GdkPixbuf ONLY.
- */
-
-/**
- * Converts the pixbuf to Cairo pixel format and returns an image surface
- * which can be used as a source.
- *
- * The returned surface is owned by the GdkPixbuf and should not be freed.
- * Calling this function causes the pixbuf to be unsuitable for use
- * with GTK drawing functions until ink_pixbuf_ensure_normal() is called.
- *
- * @bug You have to call g_object_set_data(G_OBJECT(pb), "cairo_surface", NULL)
- * when unrefing the last reference to the pixbuf. Otherwise there will be
- * crashes, because cairo_surface_destroy is called after the pixbuf data
- * is already freed.
- */
-cairo_surface_t *
-ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb)
-{
- cairo_surface_t *pbs =
- reinterpret_cast<cairo_surface_t*>(g_object_get_data(G_OBJECT(pb), "cairo_surface"));
-
- ink_pixbuf_ensure_argb32(pb);
-
- if (pbs == NULL) {
- guchar *data = gdk_pixbuf_get_pixels(pb);
- int w = gdk_pixbuf_get_width(pb);
- int h = gdk_pixbuf_get_height(pb);
- int stride = gdk_pixbuf_get_rowstride(pb);
-
- // create a surface that stores the data
- pbs = cairo_image_surface_create_for_data(
- data, CAIRO_FORMAT_ARGB32, w, h, stride);
-
- g_object_set_data_full(G_OBJECT(pb), "cairo_surface", pbs, (GDestroyNotify) cairo_surface_destroy);
- cairo_surface_set_user_data(pbs, &ink_pixbuf_key, pb, NULL);
- }
-
- return pbs;
-}
-
-/**
- * Converts the Cairo surface to GdkPixbuf pixel format.
- * GdkPixbuf takes ownership of the passed surface reference,
- * so it should NOT be freed after calling this function.
- */
-GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s)
-{
- GdkPixbuf *pb = reinterpret_cast<GdkPixbuf*>(cairo_surface_get_user_data(s, &ink_pixbuf_key));
- if (pb == NULL) {
- pb = gdk_pixbuf_new_from_data(
- cairo_image_surface_get_data(s), GDK_COLORSPACE_RGB, TRUE, 8,
- cairo_image_surface_get_width(s), cairo_image_surface_get_height(s),
- cairo_image_surface_get_stride(s), NULL, NULL);
-
- g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("argb32"), g_free);
- g_object_set_data_full(G_OBJECT(pb), "cairo_surface", s, (GDestroyNotify) cairo_surface_destroy);
-
- cairo_surface_set_user_data(s, &ink_pixbuf_key, pb, NULL);
- } else {
- g_warning("Received surface which is already owned by GdkPixbuf");
- g_object_ref(pb);
- }
-
- ink_pixbuf_ensure_normal(pb);
-
- return pb;
-}
-
-/**
- * Cleanup function for GdkPixbuf.
- * This function should be passed as the GdkPixbufDestroyNotify parameter
- * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by
- * a Cairo surface.
- */
-void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data)
-{
- cairo_surface_t *surface = reinterpret_cast<cairo_surface_t*>(data);
- cairo_surface_destroy(surface);
-}
-
/**
* Create an exact copy of a surface.
* Creates a surface that has the same type, content type, dimensions and contents
@@ -833,6 +1088,44 @@ ink_cairo_pattern_create_checkerboard()
return p;
}
+/**
+ * Converts the Cairo surface to a GdkPixbuf pixel format,
+ * without allocating extra memory.
+ *
+ * This function is intended mainly for creating previews displayed by GTK.
+ * For loading images for display on the canvas, use the Inkscape::Pixbuf object.
+ *
+ * The returned GdkPixbuf takes ownership of the passed surface reference,
+ * so it should NOT be freed after calling this function.
+ */
+GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s)
+{
+ guchar *pixels = cairo_image_surface_get_data(s);
+ int w = cairo_image_surface_get_width(s);
+ int h = cairo_image_surface_get_height(s);
+ int rs = cairo_image_surface_get_stride(s);
+
+ convert_pixels_argb32_to_pixbuf(pixels, w, h, rs);
+
+ GdkPixbuf *pb = gdk_pixbuf_new_from_data(
+ pixels, GDK_COLORSPACE_RGB, TRUE, 8,
+ w, h, rs, ink_cairo_pixbuf_cleanup, s);
+
+ return pb;
+}
+
+/**
+ * Cleanup function for GdkPixbuf.
+ * This function should be passed as the GdkPixbufDestroyNotify parameter
+ * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by
+ * a Cairo surface.
+ */
+static void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data)
+{
+ cairo_surface_t *surface = static_cast<cairo_surface_t*>(data);
+ cairo_surface_destroy(surface);
+}
+
/* The following two functions use "from" instead of "to", because when you write:
val1 = argb32_from_pixbuf(val1);
the name of the format is closer to the value in that format. */
diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h
index 289d4e01f..505e2ca77 100644
--- a/src/display/cairo-utils.h
+++ b/src/display/cairo-utils.h
@@ -12,14 +12,15 @@
#ifndef SEEN_INKSCAPE_DISPLAY_CAIRO_UTILS_H
#define SEEN_INKSCAPE_DISPLAY_CAIRO_UTILS_H
+#include <boost/noncopyable.hpp>
+//#include <glibmm/threads.h> // workaround
#include <glib.h>
#include <cairomm/cairomm.h>
+//#include <gdkmm/pixbuf.h>
#include <2geom/forward.h>
#include "style.h"
struct SPColor;
-struct _GdkPixbuf;
-typedef struct _GdkPixbuf GdkPixbuf;
namespace Inkscape {
@@ -80,15 +81,61 @@ public:
static Cairo::RefPtr<CairoContext> create(Cairo::RefPtr<Cairo::Surface> const &target);
};
-} // namespace Inkscape
+/** Class to hold image data for raster images.
+ * Allows easy interoperation with GdkPixbuf and Cairo. */
+class Pixbuf {
+public:
+ enum PixelFormat {
+ PF_CAIRO = 1,
+ PF_GDK = 2,
+ PF_LAST
+ };
+
+ explicit Pixbuf(cairo_surface_t *s);
+ explicit Pixbuf(GdkPixbuf *pb);
+ Pixbuf(Inkscape::Pixbuf const &other);
+ ~Pixbuf();
+
+ GdkPixbuf *getPixbufRaw(bool convert_format = true);
+ //Glib::RefPtr<Gdk::Pixbuf> getPixbuf(bool convert_format = true);
+
+ cairo_surface_t *getSurfaceRaw(bool convert_format = true);
+ Cairo::RefPtr<Cairo::Surface> getSurface(bool convert_format = true);
+
+ int width() const;
+ int height() const;
+ int rowstride() const;
+ guchar const *pixels() const;
+ guchar *pixels();
+ void markDirty();
+
+ bool hasMimeData() const;
+ guchar const *getMimeData(gsize &len, std::string &mimetype) const;
+ std::string const &originalPath() const { return _path; }
+ time_t modificationTime() const { return _mod_time; }
-enum InkPixelFormat {
- INK_PIXEL_FORMAT_NONE,
- INK_PIXEL_FORMAT_CAIRO,
- INK_PIXEL_FORMAT_PIXBUF,
- INK_PIXEL_FORMAT_LAST
+ PixelFormat pixelFormat() const { return _pixel_format; }
+ void ensurePixelFormat(PixelFormat fmt);
+
+ static Pixbuf *create_from_data_uri(gchar const *uri);
+ static Pixbuf *create_from_file(std::string const &fn);
+
+private:
+ void _ensurePixelsARGB32();
+ void _ensurePixelsPixbuf();
+ void _forceAlpha();
+ void _setMimeData(guchar *data, gsize len, Glib::ustring const &format);
+
+ GdkPixbuf *_pixbuf;
+ cairo_surface_t *_surface;
+ time_t _mod_time;
+ std::string _path;
+ PixelFormat _pixel_format;
+ bool _cairo_store;
};
+} // namespace Inkscape
+
// TODO: these declarations may not be needed in the header
extern cairo_user_data_key_t ink_color_interpolation_key;
extern cairo_user_data_key_t ink_pixbuf_key;
@@ -102,7 +149,6 @@ void ink_cairo_set_source_color(cairo_t *ct, SPColor const &color, double opacit
void ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba);
void ink_cairo_transform(cairo_t *ct, Geom::Affine const &m);
void ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m);
-void ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y);
void ink_matrix_to_2geom(Geom::Affine &, cairo_matrix_t const &);
void ink_matrix_to_cairo(cairo_matrix_t &, Geom::Affine const &);
@@ -125,13 +171,9 @@ int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface);
cairo_pattern_t *ink_cairo_pattern_create_checkerboard();
+GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s);
void convert_pixels_pixbuf_to_argb32(guchar *data, int w, int h, int rs);
void convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int rs);
-void ink_pixbuf_ensure_argb32(GdkPixbuf *);
-void ink_pixbuf_ensure_normal(GdkPixbuf *);
-cairo_surface_t *ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb);
-GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s);
-void ink_cairo_pixbuf_cleanup(guchar *pixels, void *surface);
G_GNUC_CONST guint32 argb32_from_pixbuf(guint32 in);
G_GNUC_CONST guint32 pixbuf_from_argb32(guint32 in);
diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp
index 46f066b8e..0b661a450 100644
--- a/src/display/drawing-image.cpp
+++ b/src/display/drawing-image.cpp
@@ -22,34 +22,23 @@ namespace Inkscape {
DrawingImage::DrawingImage(Drawing &drawing)
: DrawingItem(drawing)
, _pixbuf(NULL)
- , _surface(NULL) // this is owned by _pixbuf!
, _style(NULL)
, _new_surface(NULL)
{}
DrawingImage::~DrawingImage()
{
- if (_style)
+ if (_style) {
sp_style_unref(_style);
- if (_pixbuf) {
- if (_new_surface) cairo_surface_destroy(_new_surface);
- g_object_unref(_pixbuf);
}
+
+ // _pixbuf is owned by SPImage - do not delete it
}
void
-DrawingImage::setARGB32Pixbuf(GdkPixbuf *pb)
+DrawingImage::setPixbuf(Inkscape::Pixbuf *pb)
{
- // when done in this order, it won't break if pb == image->pixbuf and the refcount is 1
- if (pb != NULL) {
- g_object_ref (pb);
- }
- if (_pixbuf != NULL) {
- g_object_unref(_pixbuf);
- // unrefing the pixbuf also destroys surface
- }
_pixbuf = pb;
- _surface = pb ? ink_cairo_surface_get_for_pixbuf(pb) : NULL;
_markForUpdate(STATE_ALL, false);
}
@@ -86,8 +75,8 @@ DrawingImage::bounds() const
{
if (!_pixbuf) return _clipbox;
- double pw = gdk_pixbuf_get_width(_pixbuf);
- double ph = gdk_pixbuf_get_height(_pixbuf);
+ double pw = _pixbuf->width();
+ double ph = _pixbuf->height();
double vw = pw * _scale[Geom::X];
double vh = ph * _scale[Geom::Y];
Geom::Point wh(vw, vh);
@@ -143,14 +132,16 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar
// See https://bugs.launchpad.net/inkscape/+bug/804162
Geom::Scale expansion(_ctm.expansion());
- int orgwidth = cairo_image_surface_get_width(_surface);
- int orgheight = cairo_image_surface_get_height(_surface);
+ int orgwidth = _pixbuf->width();
+ int orgheight = _pixbuf->height();
if (_scale[Geom::X]*expansion[Geom::X]*orgwidth*255.0<1.0 || _scale[Geom::Y]*expansion[Geom::Y]*orgheight*255.0<1.0) {
// Resized image too small to actually see anything
return RENDER_OK;
}
-
+
+ _pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_CAIRO);
+
// Split scale*expansion in a part that is <= 1.0 and a part that is >= 1.0. We only take care of the part <= 1.0.
Geom::Scale scaleExpansionSmall(std::min<Geom::Coord>(fabs(_scale[Geom::X]*expansion[Geom::X]),1),std::min<Geom::Coord>(fabs(_scale[Geom::Y]*expansion[Geom::Y]),1));
Geom::Scale scaleExpansionLarge(_scale[Geom::X]*expansion[Geom::X]/scaleExpansionSmall[Geom::X],_scale[Geom::Y]*expansion[Geom::Y]/scaleExpansionSmall[Geom::Y]);
@@ -161,7 +152,7 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar
ct.scale(expansion.inverse()); // This should not include scale (see derivation above)
ct.translate(_origin*expansion);
ct.scale(scaleExpansionLarge);
- ct.setSource(_surface, 0, 0);
+ ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0);
} else if (!_new_surface || (newSize-_rescaledSize).length()>0.1) {
// Rescaled image is sufficiently different from cached image to recompute
if (_new_surface) cairo_surface_destroy(_new_surface);
@@ -200,13 +191,13 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar
}
}
+ cairo_surface_t *surface = _pixbuf->getSurfaceRaw();
_new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, newwidth,newheight);
- unsigned char * orgdata = cairo_image_surface_get_data(_surface);
+ unsigned char * orgdata = cairo_image_surface_get_data(surface);
unsigned char * newdata = cairo_image_surface_get_data(_new_surface);
- int orgstride = cairo_image_surface_get_stride(_surface);
+ int orgstride = cairo_image_surface_get_stride(surface);
int newstride = cairo_image_surface_get_stride(_new_surface);
-
- //cairo_surface_flush(_surface);
+
cairo_surface_flush(_new_surface);
for(int y=0; y<newheight; y++) {
@@ -245,7 +236,7 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar
// TODO: If Cairo's problems are gone, uncomment the following:
//ct.translate(_origin);
//ct.scale(_scale);
- //ct.setSource(_surface, 0, 0);
+ //ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0);
//ct.paint(_opacity);
ct.paint();
@@ -315,10 +306,10 @@ DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/)
return NULL;
} else {
- unsigned char *const pixels = gdk_pixbuf_get_pixels(_pixbuf);
- int width = gdk_pixbuf_get_width(_pixbuf);
- int height = gdk_pixbuf_get_height(_pixbuf);
- int rowstride = gdk_pixbuf_get_rowstride(_pixbuf);
+ unsigned char *const pixels = _pixbuf->pixels();
+ int width = _pixbuf->width();
+ int height = _pixbuf->height();
+ int rowstride = _pixbuf->rowstride();
Geom::Point tp = p * _ctm.inverse();
Geom::Rect r = bounds();
@@ -336,8 +327,17 @@ DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/)
unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4;
// pick if the image is less than 99% transparent
- float alpha = (pix_ptr[3] / 255.0f) * _opacity;
- return alpha > 0.01 ? this : NULL;
+ guint32 alpha = 0;
+ if (_pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) {
+ guint32 px = *reinterpret_cast<guint32 const *>(pix_ptr);
+ alpha = (px & 0xff000000) >> 24;
+ } else if (_pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_GDK) {
+ alpha = pix_ptr[3];
+ } else {
+ throw std::runtime_error("Unrecognized pixel format");
+ }
+ float alpha_f = (alpha / 255.0f) * _opacity;
+ return alpha_f > 0.01 ? this : NULL;
}
}
diff --git a/src/display/drawing-image.h b/src/display/drawing-image.h
index 593185c97..58e6de72e 100644
--- a/src/display/drawing-image.h
+++ b/src/display/drawing-image.h
@@ -19,6 +19,7 @@
#include "display/drawing-item.h"
namespace Inkscape {
+class Pixbuf;
class DrawingImage
: public DrawingItem
@@ -27,7 +28,7 @@ public:
DrawingImage(Drawing &drawing);
~DrawingImage();
- void setARGB32Pixbuf(GdkPixbuf *pb);
+ void setPixbuf(Inkscape::Pixbuf *pb);
void setStyle(SPStyle *style);
void setScale(double sx, double sy);
void setOrigin(Geom::Point const &o);
@@ -41,8 +42,7 @@ protected:
DrawingItem *stop_at);
virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags);
- GdkPixbuf *_pixbuf;
- cairo_surface_t *_surface;
+ Inkscape::Pixbuf *_pixbuf;
SPStyle *_style;
cairo_surface_t *_new_surface; // Part of hack around Cairo bug
diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp
index 80664d822..a9836a9e3 100644
--- a/src/display/drawing-item.cpp
+++ b/src/display/drawing-item.cpp
@@ -284,7 +284,7 @@ DrawingItem::setZOrder(unsigned z)
void
DrawingItem::setItemBounds(Geom::OptRect const &bounds)
{
- _item_bbox = bounds;
+ if (bounds) _filter_bbox = bounds;
}
/**
@@ -352,8 +352,10 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne
if (to_update & STATE_BBOX) {
// compute drawbox
- if (_filter && render_filters) {
- _drawbox = _filter->compute_drawbox(this, _item_bbox);
+ if (_filter && render_filters && _bbox) {
+ Geom::IntRect newbox(*_bbox);
+ _filter->area_enlarge(newbox, this);
+ _drawbox = Geom::OptIntRect(newbox);
} else {
_drawbox = _bbox;
}
diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h
index 4a516512b..8020659db 100644
--- a/src/display/drawing-item.h
+++ b/src/display/drawing-item.h
@@ -89,7 +89,7 @@ public:
Geom::OptIntRect geometricBounds() const { return _bbox; }
Geom::OptIntRect visualBounds() const { return _drawbox; }
- Geom::OptRect itemBounds() const { return _item_bbox; }
+ Geom::OptRect filterBounds() const { return _filter_bbox; }
Geom::Affine ctm() const { return _ctm; }
Geom::Affine transform() const { return _transform ? *_transform : Geom::identity(); }
Drawing &drawing() const { return _drawing; }
@@ -175,7 +175,7 @@ protected:
Geom::Affine _ctm; ///< Total transform from item coords to display coords
Geom::OptIntRect _bbox; ///< Bounding box in display (pixel) coords including stroke
Geom::OptIntRect _drawbox; ///< Full visual bounding box - enlarged by filters, shrunk by clips and masks
- Geom::OptRect _item_bbox; ///< Geometric bounding box in item coordinates
+ Geom::OptRect _filter_bbox; ///< Used by filters when settings bounds
DrawingItem *_clip;
DrawingItem *_mask;
diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp
index e80f12486..e689d0755 100644
--- a/src/display/drawing-shape.cpp
+++ b/src/display/drawing-shape.cpp
@@ -179,8 +179,8 @@ DrawingShape::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigne
// update fill and stroke paints.
// this cannot be done during nr_arena_shape_update, because we need a Cairo context
// to render svg:pattern
- has_fill = _nrstyle.prepareFill(ct, _item_bbox);
- has_stroke = _nrstyle.prepareStroke(ct, _item_bbox);
+ has_fill = _nrstyle.prepareFill(ct, _bbox);
+ has_stroke = _nrstyle.prepareStroke(ct, _bbox);
has_stroke &= (_nrstyle.stroke_width != 0);
if (has_fill || has_stroke) {
diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp
index 234006983..fa9ce4ff8 100644
--- a/src/display/drawing-text.cpp
+++ b/src/display/drawing-text.cpp
@@ -398,8 +398,8 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are
using Geom::X;
using Geom::Y;
- has_fill = _nrstyle.prepareFill( ct, _item_bbox);
- has_stroke = _nrstyle.prepareStroke(ct, _item_bbox);
+ has_fill = _nrstyle.prepareFill( ct, _bbox);
+ has_stroke = _nrstyle.prepareStroke(ct, _bbox);
if (has_fill || has_stroke) {
Geom::Affine rotinv;
@@ -441,7 +441,7 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are
}
Inkscape::DrawingContext::Save save(ct);
-// ct.transform(_ctm); // Seems to work fine without this line, which was in the original.
+ ct.transform(_ctm); // For one thing, this is needed to scale a fill-pattern when zooming in
if (has_fill) {
_nrstyle.applyFill(ct);
ct.fillPreserve();
diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp
index b9d73f0ad..4ca4cd07c 100644
--- a/src/display/nr-filter-image.cpp
+++ b/src/display/nr-filter-image.cpp
@@ -30,6 +30,7 @@ FilterImage::FilterImage()
: SVGElem(0)
, document(0)
, feImageHref(0)
+ , image(0)
, broken_ref(false)
{ }
@@ -41,7 +42,7 @@ FilterImage::~FilterImage()
{
if (feImageHref)
g_free(feImageHref);
- g_object_set_data(G_OBJECT(image->gobj()), "cairo_surface", NULL);
+ delete image;
}
void FilterImage::render_cairo(FilterSlot &slot)
@@ -131,50 +132,38 @@ void FilterImage::render_cairo(FilterSlot &slot)
// External image, like <image>
if (!image && !broken_ref) {
broken_ref = true;
- try {
- /* TODO: If feImageHref is absolute, then use that (preferably handling the
- * case that it's not a file URI). Otherwise, go up the tree looking
- * for an xml:base attribute, and use that as the base URI for resolving
- * the relative feImageHref URI. Otherwise, if document->base is valid,
- * then use that as the base URI. Otherwise, use feImageHref directly
- * (i.e. interpreting it as relative to our current working directory).
- * (See http://www.w3.org/TR/xmlbase/#resolution .) */
- gchar *fullname = feImageHref;
- if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) {
- // Try to load from relative postion combined with document base
- if( document ) {
- fullname = g_build_filename( document->getBase(), feImageHref, NULL );
- }
- }
- if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) {
- // Should display Broken Image png.
- g_warning("FilterImage::render: Can not find: %s", feImageHref );
- return;
+
+ /* TODO: If feImageHref is absolute, then use that (preferably handling the
+ * case that it's not a file URI). Otherwise, go up the tree looking
+ * for an xml:base attribute, and use that as the base URI for resolving
+ * the relative feImageHref URI. Otherwise, if document->base is valid,
+ * then use that as the base URI. Otherwise, use feImageHref directly
+ * (i.e. interpreting it as relative to our current working directory).
+ * (See http://www.w3.org/TR/xmlbase/#resolution .) */
+ gchar *fullname = feImageHref;
+ if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) {
+ // Try to load from relative postion combined with document base
+ if( document ) {
+ fullname = g_build_filename( document->getBase(), feImageHref, NULL );
}
- image = Gdk::Pixbuf::create_from_file(fullname);
- if( fullname != feImageHref ) g_free( fullname );
}
- catch (const Glib::FileError & e)
- {
- g_warning("caught Glib::FileError in FilterImage::render: %s", e.what().data() );
+ if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) {
+ // Should display Broken Image png.
+ g_warning("FilterImage::render: Can not find: %s", feImageHref );
return;
}
- catch (const Gdk::PixbufError & e)
- {
- g_warning("Gdk::PixbufError in FilterImage::render: %s", e.what().data() );
+ image = Inkscape::Pixbuf::create_from_file(fullname);
+ if( fullname != feImageHref ) g_free( fullname );
+
+ if ( !image ) {
+ g_warning("FilterImage::render: failed to load image: %s", feImageHref);
return;
}
- if ( !image ) return;
broken_ref = false;
-
- bool has_alpha = image->get_has_alpha();
- if (!has_alpha) {
- image = image->add_alpha(false, 0, 0, 0);
- }
}
- cairo_surface_t *image_surface = ink_cairo_surface_get_for_pixbuf(image->gobj());
+ cairo_surface_t *image_surface = image->getSurfaceRaw();
Geom::Rect sa = slot.get_slot_area();
cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
@@ -199,7 +188,7 @@ void FilterImage::render_cairo(FilterSlot &slot)
// Check aspect ratio of image vs. viewport
double feAspect = feImageHeight/feImageWidth;
- double aspect = (double)image->get_height()/(double)image->get_width();
+ double aspect = (double)image->height()/(double)image->width();
bool ratio = (feAspect < aspect);
double ax, ay; // Align side
@@ -274,8 +263,8 @@ void FilterImage::render_cairo(FilterSlot &slot)
}
}
- double scaleX = feImageWidth / image->get_width();
- double scaleY = feImageHeight / image->get_height();
+ double scaleX = feImageWidth / image->width();
+ double scaleY = feImageHeight / image->height();
cairo_translate(ct, feImageX, feImageY);
cairo_scale(ct, scaleX, scaleY);
@@ -302,8 +291,8 @@ void FilterImage::set_href(const gchar *href){
if (feImageHref) g_free (feImageHref);
feImageHref = (href) ? g_strdup (href) : NULL;
- g_object_set_data(G_OBJECT(image->gobj()), "cairo_surface", NULL);
- image.reset();
+ delete image;
+ image = NULL;
broken_ref = false;
}
diff --git a/src/display/nr-filter-image.h b/src/display/nr-filter-image.h
index f45f42265..69691ac99 100644
--- a/src/display/nr-filter-image.h
+++ b/src/display/nr-filter-image.h
@@ -12,14 +12,14 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include <gdkmm/pixbuf.h>
#include "display/nr-filter-primitive.h"
-#include <glibmm/refptr.h>
class SPDocument;
class SPItem;
namespace Inkscape {
+class Pixbuf;
+
namespace Filters {
class FilterSlot;
@@ -43,7 +43,7 @@ public:
private:
SPDocument *document;
gchar *feImageHref;
- Glib::RefPtr<Gdk::Pixbuf> image;
+ Inkscape::Pixbuf *image;
float feImageX, feImageY, feImageWidth, feImageHeight;
unsigned int aspect_align, aspect_clip;
bool broken_ref;
diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp
index f0965c460..a0103cbb0 100644
--- a/src/display/nr-filter.cpp
+++ b/src/display/nr-filter.cpp
@@ -114,13 +114,16 @@ int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, D
Geom::Affine trans = item->ctm();
-// Geom::OptRect filter_area = filter_effect_area(item->itemBounds()); // disabled, already done in visualBounds
- Geom::OptRect filter_area = item->itemBounds(); // see LP Bug 1188336
- if (!filter_area) return 1;
+ // Get filter are, the filter_effect_area is already done in visualBounds
+ Geom::OptRect filter_area = item->filterBounds();
+ // Use the geometricBounds as a backup solution
+ if (!filter_area || (filter_area->hasZeroArea() &&
+ filter_area->min()[Geom::X] == 0 && filter_area->min()[Geom::Y] == 0))
+ filter_area = item->geometricBounds();
FilterUnits units(_filter_units, _primitive_units);
units.set_ctm(trans);
- units.set_item_bbox(item->itemBounds());
+ units.set_item_bbox(filter_area);
units.set_filter_area(*filter_area);
std::pair<double,double> resolution
@@ -200,7 +203,7 @@ void Filter::area_enlarge(Geom::IntRect &bbox, Inkscape::DrawingItem const *item
}
Geom::Rect item_bbox;
- Geom::OptRect maybe_bbox = item->itemBounds();
+ Geom::OptRect maybe_bbox = item->geometricBounds();
if (maybe_bbox.isEmpty()) {
// Code below needs a bounding box
return;
@@ -220,20 +223,6 @@ void Filter::area_enlarge(Geom::IntRect &bbox, Inkscape::DrawingItem const *item
*/
}
-Geom::OptIntRect Filter::compute_drawbox(Inkscape::DrawingItem const *item, Geom::OptRect const &item_bbox) {
-
-// Geom::OptRect enlarged = filter_effect_area(item_bbox); // disabled, already done in visualBounds
- Geom::OptRect enlarged = item_bbox; // see LP Bug 1188336
- if (enlarged) {
- *enlarged *= item->ctm();
-
- Geom::OptIntRect ret(enlarged->roundOutwards());
- return ret;
- } else {
- return Geom::OptIntRect();
- }
-}
-
Geom::OptRect Filter::filter_effect_area(Geom::OptRect const &bbox)
{
Geom::Point minp, maxp;
diff --git a/src/display/nr-filter.h b/src/display/nr-filter.h
index d53005c5d..5df38ffe9 100644
--- a/src/display/nr-filter.h
+++ b/src/display/nr-filter.h
@@ -151,12 +151,6 @@ public:
*/
void area_enlarge(Geom::IntRect &area, Inkscape::DrawingItem const *item) const;
/**
- * Given an item bounding box (in user coords), this function enlarges it
- * to contain the filter effects region and transforms it to screen
- * coordinates
- */
- Geom::OptIntRect compute_drawbox(Inkscape::DrawingItem const *item, Geom::OptRect const &item_bbox);
- /**
* Returns the filter effects area in user coordinate system.
* The given bounding box should be a bounding box as specified in
* SVG standard and in user coordinate system.
diff --git a/src/doxygen-main.cpp b/src/doxygen-main.cpp
index a1d3f3604..e581b8708 100644
--- a/src/doxygen-main.cpp
+++ b/src/doxygen-main.cpp
@@ -245,7 +245,6 @@ namespace XML {}
* - SPLinearGradient
* - SPRadialGradient
* - SPPattern [\ref sp-pattern.cpp, \ref sp-pattern.h]
- * - SPSkeleton [\ref sp-skeleton.cpp, \ref sp-skeleton.h]
* - SPStop [\ref sp-stop.h]
* - SPString [\ref sp-string.cpp, \ref sp-string.h]
* - SPStyleElem [\ref sp-style-elem.cpp, \ref sp-style-elem.h]
diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp
index e06c9f30d..5f535dc64 100644
--- a/src/extension/internal/cairo-ps-out.cpp
+++ b/src/extension/internal/cairo-ps-out.cpp
@@ -340,7 +340,7 @@ CairoPsOutput::init (void)
"<_option value=\"page\">" N_("Use document's page size") "</_option>"
"<_option value=\"drawing\">" N_("Use exported object's size") "</_option>"
"</param>"
- "<param name=\"bleed\" gui-text=\"" N_("Bleed/margin (mm)") "\" type=\"float\" min=\"-10000\" max=\"10000\">0</param>\n"
+ "<param name=\"bleed\" gui-text=\"" N_("Bleed/margin (mm):") "\" type=\"float\" min=\"-10000\" max=\"10000\">0</param>\n"
"<param name=\"exportId\" gui-text=\"" N_("Limit export to the object with ID:") "\" type=\"string\"></param>\n"
"<output>\n"
"<extension>.ps</extension>\n"
diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp
index a950fa177..4f9273cbb 100644
--- a/src/extension/internal/cairo-render-context.cpp
+++ b/src/extension/internal/cairo-render-context.cpp
@@ -1436,7 +1436,7 @@ CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle con
return true;
}
-bool CairoRenderContext::renderImage(GdkPixbuf *pb,
+bool CairoRenderContext::renderImage(Inkscape::Pixbuf *pb,
Geom::Affine const &image_transform, SPStyle const * /*style*/)
{
g_assert( _is_valid );
@@ -1447,13 +1447,13 @@ bool CairoRenderContext::renderImage(GdkPixbuf *pb,
_prepareRenderGraphic();
- int w = gdk_pixbuf_get_width (pb);
- int h = gdk_pixbuf_get_height (pb);
+ int w = pb->width();
+ int h = pb->height();
// TODO: reenable merge_opacity if useful
float opacity = _state->opacity;
- cairo_surface_t *image_surface = ink_cairo_surface_get_for_pixbuf(pb);
+ cairo_surface_t *image_surface = pb->getSurfaceRaw();
if (cairo_surface_status(image_surface)) {
TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
return false;
diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h
index 91e1cdf7d..8d3e63775 100644
--- a/src/extension/internal/cairo-render-context.h
+++ b/src/extension/internal/cairo-render-context.h
@@ -6,7 +6,7 @@
*/
/*
* Authors:
- * Miklos Erdelyi <erdelyim@gmail.com>
+ * Miklos Erdelyi <erdelyim@gmail.com>
*
* Copyright (C) 2006 Miklos Erdelyi
*
@@ -32,6 +32,8 @@ class SPClipPath;
class SPMask;
namespace Inkscape {
+class Pixbuf;
+
namespace Extension {
namespace Internal {
@@ -144,7 +146,7 @@ public:
/* Rendering methods */
bool renderPathVector(Geom::PathVector const &pathv, SPStyle const *style, Geom::OptRect const &pbox);
- bool renderImage(GdkPixbuf *pb,
+ bool renderImage(Inkscape::Pixbuf *pb,
Geom::Affine const &image_transform, SPStyle const *style);
bool renderGlyphtext(PangoFont *font, Geom::Affine const &font_matrix,
std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style);
diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp
index 3463925b6..cace251cf 100644
--- a/src/extension/internal/cairo-renderer.cpp
+++ b/src/extension/internal/cairo-renderer.cpp
@@ -27,6 +27,7 @@
#include <signal.h>
#include <errno.h>
+#include <boost/scoped_ptr.hpp>
#include "libnrtype/Layout-TNG.h"
#include <2geom/transforms.h>
@@ -347,8 +348,8 @@ static void sp_image_render(SPItem *item, CairoRenderContext *ctx)
if (!image->pixbuf) return;
if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return;
- w = gdk_pixbuf_get_width (image->pixbuf);
- h = gdk_pixbuf_get_height (image->pixbuf);
+ w = image->pixbuf->width();
+ h = image->pixbuf->height();
double x = image->x.computed;
double y = image->y.computed;
@@ -497,22 +498,15 @@ static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx)
GSList *items = NULL;
items = g_slist_append(items, item);
- GdkPixbuf *pb = sp_generate_internal_bitmap(document, NULL,
- bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y],
- width, height, res, res, (guint32) 0xffffff00, items );
+ boost::scoped_ptr<Inkscape::Pixbuf> pb(
+ sp_generate_internal_bitmap(document, NULL,
+ bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y],
+ width, height, res, res, (guint32) 0xffffff00, items ));
if (pb) {
- TEST(gdk_pixbuf_save( pb, "bitmap.png", "png", NULL, NULL ));
-
- /* TODO: find a way to avoid a duplicate conversion between
- * Cairo and GdkPixbuf pixel formats here.
- * Internally, generate_internal_bitmap creates a Cairo surface,
- * but then converts it to pixbuf format. In turn, renderImage()
- * below converts back to Cairo format.
- */
- ctx->renderImage(pb, t, item->style);
- g_object_unref(pb);
- pb = 0;
+ //TEST(gdk_pixbuf_save( pb, "bitmap.png", "png", NULL, NULL ));
+
+ ctx->renderImage(pb.get(), t, item->style);
}
g_slist_free (items);
}
@@ -586,9 +580,9 @@ void CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item)
setStateForItem(ctx, item);
CairoRenderState *state = ctx->getCurrentState();
- state->need_layer = ( state->mask || state->clip_path || state->opacity != 1.0 );
+ state->need_layer = ( state->mask || state->opacity != 1.0 );
- // Draw item on a temporary surface so a mask, clip path, or opacity can be applied to it.
+ // Draw item on a temporary surface so a mask or opacity can be applied to it.
if (state->need_layer) {
state->merge_opacity = FALSE;
ctx->pushLayer();
diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp
index 826a52ade..770257978 100644
--- a/src/extension/internal/emf-print.cpp
+++ b/src/extension/internal/emf-print.cpp
@@ -53,6 +53,7 @@
#include "sp-gradient.h"
#include "sp-radial-gradient.h"
#include "sp-linear-gradient.h"
+#include "display/cairo-utils.h"
#include "splivarot.h" // pieces for union on shapes
#include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path
@@ -333,7 +334,7 @@ int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor)
U_LOGBRUSH lb;
uint32_t brush, fmode;
MFDrawMode fill_mode;
- GdkPixbuf *pixbuf;
+ Inkscape::Pixbuf *pixbuf;
uint32_t brushStyle;
int hatchType;
U_COLORREF hatchColor;
@@ -462,7 +463,7 @@ int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor)
int numCt;
U_BITMAPINFOHEADER Bmih;
PU_BITMAPINFO Bmi;
- rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!!
+ rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!!
colortype = U_BCBM_COLOR32;
(void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1);
// Not sure why the next swap is needed because the preceding does it, and the code is identical
@@ -528,7 +529,7 @@ int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform)
int linejoin = 0;
uint32_t pen;
uint32_t brushStyle;
- GdkPixbuf *pixbuf;
+ Inkscape::Pixbuf *pixbuf;
int hatchType;
U_COLORREF hatchColor;
U_COLORREF bkColor;
@@ -565,7 +566,7 @@ int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform)
brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor);
if (pixbuf) {
brushStyle = U_BS_DIBPATTERN;
- rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!!
+ rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!!
colortype = U_BCBM_COLOR32;
(void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1);
// Not sure why the next swap is needed because the preceding does it, and the code is identical
diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp
index 117c2fe39..87cf8a9cc 100644
--- a/src/extension/internal/gdkpixbuf-input.cpp
+++ b/src/extension/internal/gdkpixbuf-input.cpp
@@ -1,6 +1,7 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
+#include <boost/scoped_ptr.hpp>
#include <glib/gprintf.h>
#include <glibmm/i18n.h>
#include "document-private.h"
@@ -14,15 +15,11 @@
#include "document-undo.h"
#include "util/units.h"
#include "image-resolution.h"
+#include "display/cairo-utils.h"
#include <set>
namespace Inkscape {
-namespace IO {
-// this is defined in sp-image.cpp
-GdkPixbuf* pixbuf_new_from_file(char const *filename, time_t &modTime, gchar*& pixPath);
-}
-
namespace Extension {
namespace Internal {
@@ -47,9 +44,7 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
}
SPDocument *doc = NULL;
- gchar *pixpath = NULL;
- time_t dummy;
- GdkPixbuf *pb = Inkscape::IO::pixbuf_new_from_file(uri, dummy, pixpath);
+ boost::scoped_ptr<Inkscape::Pixbuf> pb(Inkscape::Pixbuf::create_from_file(uri));
// TODO: the pixbuf is created again from the base64-encoded attribute in SPImage.
// Find a way to create the pixbuf only once.
@@ -59,8 +54,8 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
bool saved = DocumentUndo::getUndoSensitive(doc);
DocumentUndo::setUndoSensitive(doc, false); // no need to undo in this temporary document
- double width = gdk_pixbuf_get_width(pb);
- double height = gdk_pixbuf_get_height(pb);
+ double width = pb->width();
+ double height = pb->height();
double defaultxdpi = prefs->getDouble("/dialogs/import/defaultxdpi/value", Inkscape::Util::Quantity::convert(1, "in", "px"));
bool forcexdpi = prefs->getBool("/dialogs/import/forcexdpi");
ImageResolution *ir = 0;
@@ -91,7 +86,7 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
sp_repr_set_svg_double(image_node, "height", height);
if (embed) {
- sp_embed_image(image_node, pb);
+ sp_embed_image(image_node, pb.get());
} else {
// convert filename to uri
gchar* _uri = g_filename_to_uri(uri, NULL, NULL);
@@ -103,9 +98,6 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
}
}
- g_object_set_data(G_OBJECT(pb), "cairo_surface", NULL);
- g_object_unref(pb);
-
// Add it to the current layer
doc->getRoot()->appendChildRepr(image_node);
Inkscape::GC::release(image_node);
diff --git a/src/extension/internal/metafile-print.cpp b/src/extension/internal/metafile-print.cpp
index 9d080bd96..1e7735410 100644
--- a/src/extension/internal/metafile-print.cpp
+++ b/src/extension/internal/metafile-print.cpp
@@ -266,7 +266,7 @@ void PrintMetafile::hatch_classify(char *name, int *hatchType, U_COLORREF *hatch
// otherwise hatchType is set to -1 and hatchColor is not defined.
//
-void PrintMetafile::brush_classify(SPObject *parent, int depth, GdkPixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor)
+void PrintMetafile::brush_classify(SPObject *parent, int depth, Inkscape::Pixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor)
{
if (depth == 0) {
*epixbuf = NULL;
diff --git a/src/extension/internal/metafile-print.h b/src/extension/internal/metafile-print.h
index e64ba92f3..cba4d564d 100644
--- a/src/extension/internal/metafile-print.h
+++ b/src/extension/internal/metafile-print.h
@@ -30,6 +30,8 @@ struct SPGradient;
struct SPObject;
namespace Inkscape {
+class Pixbuf;
+
namespace Extension {
namespace Internal {
@@ -93,7 +95,7 @@ protected:
U_COLORREF weight_colors(U_COLORREF c1, U_COLORREF c2, double t);
void hatch_classify(char *name, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor);
- void brush_classify(SPObject *parent, int depth, GdkPixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor);
+ void brush_classify(SPObject *parent, int depth, Inkscape::Pixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor);
static void swapRBinRGBA(char *px, int pixels);
int hold_gradient(void *gr, int mode);
diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp
index e5816073e..99262b109 100644
--- a/src/extension/internal/wmf-print.cpp
+++ b/src/extension/internal/wmf-print.cpp
@@ -56,6 +56,7 @@
#include "sp-gradient.h"
#include "sp-radial-gradient.h"
#include "sp-linear-gradient.h"
+#include "display/cairo-utils.h"
#include "splivarot.h" // pieces for union on shapes
#include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path
@@ -336,7 +337,7 @@ int PrintWmf::create_brush(SPStyle const *style, PU_COLORREF fcolor)
U_WLOGBRUSH lb;
uint32_t brush, fmode;
MFDrawMode fill_mode;
- GdkPixbuf *pixbuf;
+ Inkscape::Pixbuf *pixbuf;
uint32_t brushStyle;
int hatchType;
U_COLORREF hatchColor;
@@ -464,7 +465,7 @@ int PrintWmf::create_brush(SPStyle const *style, PU_COLORREF fcolor)
int numCt;
U_BITMAPINFOHEADER Bmih;
PU_BITMAPINFO Bmi;
- rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!!
+ rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!!
colortype = U_BCBM_COLOR32;
(void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1);
// Not sure why the next swap is needed because the preceding does it, and the code is identical
@@ -1112,10 +1113,10 @@ unsigned int PrintWmf::image(
g_error("Fatal programming error in PrintWmf::image at EMRHEADER");
}
- x1 = atof(style->object->getAttribute("x"));
- y1 = atof(style->object->getAttribute("y"));
- dw = atof(style->object->getAttribute("width"));
- dh = atof(style->object->getAttribute("height"));
+ x1 = g_ascii_strtod(style->object->getAttribute("x"), NULL);
+ y1 = g_ascii_strtod(style->object->getAttribute("y"), NULL);
+ dw = g_ascii_strtod(style->object->getAttribute("width"), NULL);
+ dh = g_ascii_strtod(style->object->getAttribute("height"), NULL);
Geom::Point pLL(x1, y1);
Geom::Point pLL2 = pLL * tf; //location of LL corner in Inkscape coordinates
diff --git a/src/filter-chemistry.cpp b/src/filter-chemistry.cpp
index be030e12f..0f9138560 100644
--- a/src/filter-chemistry.cpp
+++ b/src/filter-chemistry.cpp
@@ -208,6 +208,15 @@ new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion
set_filter_area(repr, radius, expansion, expansionX, expansionY,
width, height);
+ /* Inkscape now supports both sRGB and linear color-interpolation-filters.
+ * But, for the moment, keep sRGB as default value for new filters.
+ * historically set to sRGB and doesn't require conversion between
+ * filter cairo surfaces and other types of cairo surfaces. lp:1127103 */
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "color-interpolation-filters", "sRGB");
+ sp_repr_css_change(repr, css, "style");
+ sp_repr_css_attr_unref(css);
+
//create feGaussianBlur node
Inkscape::XML::Node *b_repr;
b_repr = xml_doc->createElement("svg:feGaussianBlur");
@@ -260,6 +269,15 @@ new_filter_blend_gaussian_blur (SPDocument *document, const char *blendmode, gdo
repr = xml_doc->createElement("svg:filter");
repr->setAttribute("inkscape:collect", "always");
+ /* Inkscape now supports both sRGB and linear color-interpolation-filters.
+ * But, for the moment, keep sRGB as default value for new filters.
+ * historically set to sRGB and doesn't require conversion between
+ * filter cairo surfaces and other types of cairo surfaces. lp:1127103 */
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "color-interpolation-filters", "sRGB");
+ sp_repr_css_change(repr, css, "style");
+ sp_repr_css_attr_unref(css);
+
// Append the new filter node to defs
defs->appendChild(repr);
Inkscape::GC::release(repr);
diff --git a/src/filters/image.cpp b/src/filters/image.cpp
index a454061c9..9a1b85911 100644
--- a/src/filters/image.cpp
+++ b/src/filters/image.cpp
@@ -17,6 +17,8 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
+
+#include <sigc++/bind.h>
#include "display/nr-filter-image.h"
#include "uri.h"
#include "uri-references.h"
diff --git a/src/helper/pixbuf-ops.cpp b/src/helper/pixbuf-ops.cpp
index a51a62f42..8e611d197 100644
--- a/src/helper/pixbuf-ops.cpp
+++ b/src/helper/pixbuf-ops.cpp
@@ -16,6 +16,7 @@
#endif
#include <png.h>
+#include <boost/scoped_ptr.hpp>
#include <2geom/transforms.h>
#include "interface.h"
@@ -67,16 +68,14 @@ bool sp_export_jpg_file(SPDocument *doc, gchar const *filename,
unsigned width, unsigned height, double xdpi, double ydpi,
unsigned long bgcolor, double quality,GSList *items)
{
- GdkPixbuf* pixbuf = 0;
- pixbuf = sp_generate_internal_bitmap(doc, filename, x0, y0, x1, y1,
- width, height, xdpi, ydpi,
- bgcolor, items );
+ boost::scoped_ptr<Inkscape::Pixbuf> pixbuf(
+ sp_generate_internal_bitmap(doc, filename, x0, y0, x1, y1,
+ width, height, xdpi, ydpi, bgcolor, items));
gchar c[32];
g_snprintf(c, 32, "%f", quality);
- gboolean saved = gdk_pixbuf_save (pixbuf, filename, "jpeg", NULL, "quality", c, NULL);
+ gboolean saved = gdk_pixbuf_save(pixbuf->getPixbufRaw(), filename, "jpeg", NULL, "quality", c, NULL);
g_free(c);
- g_object_unref (pixbuf);
return saved;
}
@@ -94,7 +93,7 @@ bool sp_export_jpg_file(SPDocument *doc, gchar const *filename,
@param ydpi
@return the created GdkPixbuf structure or NULL if no memory is allocable
*/
-GdkPixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*/,
+Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*/,
double x0, double y0, double x1, double y1,
unsigned width, unsigned height, double xdpi, double ydpi,
unsigned long /*bgcolor*/,
@@ -103,7 +102,7 @@ GdkPixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*
{
if (width == 0 || height == 0) return NULL;
- GdkPixbuf* pixbuf = NULL;
+ Inkscape::Pixbuf *inkpb = NULL;
/* Create new drawing for offscreen rendering*/
Inkscape::Drawing drawing;
drawing.setExact(true);
@@ -146,7 +145,7 @@ GdkPixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*
// render items
drawing.render(ct, final_bbox, Inkscape::DrawingItem::RENDER_BYPASS_CACHE);
- pixbuf = ink_pixbuf_create_from_cairo_surface(surface);
+ inkpb = new Inkscape::Pixbuf(surface);
}
else
{
@@ -158,7 +157,7 @@ GdkPixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*
// gdk_pixbuf_save (pixbuf, "C:\\temp\\internal.jpg", "jpeg", NULL, "quality","100", NULL);
- return pixbuf;
+ return inkpb;
}
/*
diff --git a/src/helper/pixbuf-ops.h b/src/helper/pixbuf-ops.h
index 44851d388..61a879f9b 100644
--- a/src/helper/pixbuf-ops.h
+++ b/src/helper/pixbuf-ops.h
@@ -15,11 +15,12 @@
#include <glib.h>
class SPDocument;
+namespace Inkscape { class Pixbuf; }
bool sp_export_jpg_file (SPDocument *doc, gchar const *filename, double x0, double y0, double x1, double y1,
unsigned int width, unsigned int height, double xdpi, double ydpi, unsigned long bgcolor, double quality, GSList *items_only = NULL);
-GdkPixbuf* sp_generate_internal_bitmap(SPDocument *doc, gchar const *filename,
+Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const *filename,
double x0, double y0, double x1, double y1,
unsigned width, unsigned height, double xdpi, double ydpi,
unsigned long bgcolor, GSList *items_only = NULL);
diff --git a/src/interface.cpp b/src/interface.cpp
index 63d507f3e..ea5eaf16a 100644
--- a/src/interface.cpp
+++ b/src/interface.cpp
@@ -2009,6 +2009,15 @@ void ContextMenu::MakeImageMenu (void)
mi->set_sensitive(FALSE);
}
+ /* Trace Pixel Art */
+ mi = manage(new Gtk::MenuItem(_("Trace Pixel Art"),1));
+ mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageTracePixelArt));
+ mi->show();
+ insert(*mi,positionOfLastDialog++);
+ if (_desktop->selection->isEmpty()) {
+ mi->set_sensitive(FALSE);
+ }
+
/* Embed image */
if (Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" )) {
mi = manage(new Gtk::MenuItem(C_("Context menu", "Embed Image")));
@@ -2126,6 +2135,12 @@ void ContextMenu::ImageTraceBitmap(void)
_desktop->_dlg_mgr->showDialog("Trace");
}
+void ContextMenu::ImageTracePixelArt(void)
+{
+ inkscape_dialogs_unhide();
+ _desktop->_dlg_mgr->showDialog("PixelArt");
+}
+
void ContextMenu::ImageEmbed(void)
{
if (_desktop->selection->isEmpty()) {
diff --git a/src/interface.h b/src/interface.h
index 13fbaf9ac..215a3bfc9 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -239,6 +239,11 @@ class ContextMenu : public Gtk::Menu
void ImageTraceBitmap(void);
/**
+ * callback, is executed on clicking the "Trace Pixel Art" menu entry
+ */
+ void ImageTracePixelArt(void);
+
+ /**
* callback, is executed on clicking the "Extract Image" menu entry
*/
void ImageExtract(void);
diff --git a/src/libdepixelize/CMakeLists.txt b/src/libdepixelize/CMakeLists.txt
new file mode 100644
index 000000000..64a72f9d9
--- /dev/null
+++ b/src/libdepixelize/CMakeLists.txt
@@ -0,0 +1,11 @@
+
+set(libdepixelize_SRC
+ kopftracer2011.cpp
+
+ # -------
+ # Headers
+ kopftracer2011.h
+ splines.h
+)
+
+add_inkscape_lib(depixelize_LIB "${libdepixelize_SRC}")
diff --git a/src/libdepixelize/Makefile_insert b/src/libdepixelize/Makefile_insert
new file mode 100644
index 000000000..75b19bf5c
--- /dev/null
+++ b/src/libdepixelize/Makefile_insert
@@ -0,0 +1,11 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+libdepixelize/all: libdepixelize/libdepixelize.a
+
+libdepixelize/clean:
+ rm -f libdepixelize/libdepixelize.a $(libdepixelize_libdepixelize_a_OBJECTS)
+
+libdepixelize_libdepixelize_a_SOURCES = \
+ libdepixelize/kopftracer2011.cpp \
+ libdepixelize/kopftracer2011.h \
+ libdepixelize/splines.h
diff --git a/src/libdepixelize/kopftracer2011.cpp b/src/libdepixelize/kopftracer2011.cpp
new file mode 100644
index 000000000..26ad8863b
--- /dev/null
+++ b/src/libdepixelize/kopftracer2011.cpp
@@ -0,0 +1,470 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+// Build fix under Inkscape build tree
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
+#include <utility>
+#include <algorithm>
+#include "kopftracer2011.h"
+#include "priv/colorspace.h"
+#include "priv/homogeneoussplines.h"
+#include "priv/branchless.h"
+#include "priv/splines.h"
+#include "priv/iterator.h"
+
+namespace Tracer {
+namespace Heuristics {
+
+int curves(const PixelGraph &graph, PixelGraph::const_iterator a,
+ PixelGraph::const_iterator b);
+bool islands(PixelGraph::const_iterator a, PixelGraph::const_iterator b);
+
+struct SparsePixels
+{
+ enum Diagonal {
+ /**
+ * From (first) the top left corner to (second) the bottom right.
+ */
+ MAIN_DIAGONAL = 0,
+ /**
+ * From (first) the top right to (second) the bottom left.
+ */
+ SECONDARY_DIAGONAL = 1
+ };
+
+ typedef std::pair<PixelGraph::const_iterator, PixelGraph::const_iterator>
+ Edge;
+ typedef std::pair<Edge, int> EdgeWeight;
+
+ void operator()(const PixelGraph &graph, unsigned radius);
+
+ static bool similar_colors(PixelGraph::const_iterator n,
+ const guint8 (&a)[4], const guint8 (&b)[4]);
+
+ /*
+ * Precondition: Must be filled according to Diagonal enum.
+ */
+ EdgeWeight diagonals[2];
+};
+
+} // namespace Heuristics
+
+Splines Kopf2011::to_voronoi(const std::string &filename,
+ const Options &options)
+{
+ return to_voronoi(Gdk::Pixbuf::create_from_file(filename), options);
+}
+
+Splines Kopf2011::to_voronoi(const Glib::RefPtr<Gdk::Pixbuf const> &buf,
+ const Options &options)
+{
+ return Splines(_voronoi<Precision>(buf, options));
+}
+
+Splines Kopf2011::to_splines(const std::string &filename,
+ const Options &options)
+{
+ return to_splines(Gdk::Pixbuf::create_from_file(filename), options);
+}
+
+Splines Kopf2011::to_splines(const Glib::RefPtr<Gdk::Pixbuf const> &buf,
+ const Options &options)
+{
+ HomogeneousSplines<Precision> splines(_voronoi<Precision>(buf, options));
+ return Splines(splines, options.optimize, options.nthreads);
+}
+
+template<class T>
+SimplifiedVoronoi<T> Kopf2011::_voronoi(const Glib::RefPtr<Gdk::Pixbuf const> &buf,
+ const Options &options)
+{
+ PixelGraph graph(buf);
+
+ /*if ( !graph.width() || !graph.height() )
+ return;*/
+
+#ifndef NDEBUG
+ graph.checkConsistency();
+#endif
+
+ // This step could be part of the initialization of PixelGraph
+ // and decrease the necessary number of passes
+ graph.connectAllNeighbors();
+
+#ifndef NDEBUG
+ graph.checkConsistency();
+#endif
+
+ // This step can't be part of PixelGraph initilization without adding some
+ // cache misses due to random access patterns that might be injected
+ _disconnect_neighbors_with_dissimilar_colors(graph);
+
+#ifndef NDEBUG
+ graph.checkConsistency();
+#endif
+
+ // This and below steps must be executed in separate.
+ // Otherwise, there will be colateral effects due to misassumption about the
+ // data being read.
+ _remove_crossing_edges_safe(graph);
+
+#ifndef NDEBUG
+ graph.checkConsistency();
+#endif
+
+ _remove_crossing_edges_unsafe(graph, options);
+
+#ifndef NDEBUG
+ graph.checkConsistency();
+#endif
+
+ return SimplifiedVoronoi<T>(graph);
+}
+
+// TODO: move this function (plus connectAllNeighbors) to PixelGraph constructor
+inline void
+Kopf2011::_disconnect_neighbors_with_dissimilar_colors(PixelGraph &graph)
+{
+ using colorspace::similar_colors;
+ for ( PixelGraph::iterator it = graph.begin(), end = graph.end() ; it != end
+ ; ++it ) {
+ if ( it->adj.top )
+ it->adj.top = similar_colors(it->rgba, (it - graph.width())->rgba);
+ if ( it->adj.topright ) {
+ it->adj.topright
+ = similar_colors(it->rgba, (it - graph.width() + 1)->rgba);
+ }
+ if ( it->adj.right )
+ it->adj.right = similar_colors(it->rgba, (it + 1)->rgba);
+ if ( it->adj.bottomright ) {
+ it->adj.bottomright
+ = similar_colors(it->rgba, (it + graph.width() + 1)->rgba);
+ }
+ if ( it->adj.bottom ) {
+ it->adj.bottom
+ = similar_colors(it->rgba, (it + graph.width())->rgba);
+ }
+ if ( it->adj.bottomleft ) {
+ it->adj.bottomleft
+ = similar_colors(it->rgba, (it + graph.width() - 1)->rgba);
+ }
+ if ( it->adj.left )
+ it->adj.left = similar_colors(it->rgba, (it - 1)->rgba);
+ if ( it->adj.topleft ) {
+ it->adj.topleft = similar_colors(it->rgba,
+ (it - graph.width() - 1)->rgba);
+ }
+ }
+}
+
+/**
+ * This method removes crossing edges if the 2x2 block is fully connected.
+ *
+ * In this case the two diagonal connections can be safely removed without
+ * affecting the final result.
+ *
+ * \TODO: It should remember/cache who are the unsafe crossing edges?
+ */
+inline void Kopf2011::_remove_crossing_edges_safe(PixelGraph &graph)
+{
+ if ( graph.width() < 2 || graph.height() < 2 )
+ return;
+
+ PixelGraph::iterator it = graph.begin();
+ for ( int i = 0 ; i != graph.height() - 1 ; ++i, ++it ) {
+ for ( int j = 0 ; j != graph.width() - 1 ; ++j, ++it ) {
+ // this <-> right
+ if ( !it->adj.right )
+ continue;
+
+ // this <-> down
+ if ( !it->adj.bottom )
+ continue;
+
+ PixelGraph::iterator down_right = it + graph.width() + 1;
+
+ // down_right <-> right
+ if ( !down_right->adj.top )
+ continue;
+
+ // down_right <-> down
+ if ( !down_right->adj.left )
+ continue;
+
+ // main diagonal
+ // this <-> down_right
+ it->adj.bottomright = 0;
+ down_right->adj.topleft = 0;
+
+ // secondary diagonal
+ // right <-> down
+ (it + 1)->adj.bottomleft = 0;
+ (it + graph.width())->adj.topright = 0;
+ }
+ }
+}
+
+/**
+ * This method removes crossing edges using the heuristics.
+ */
+inline
+void Kopf2011::_remove_crossing_edges_unsafe(PixelGraph &graph,
+ const Options &options)
+{
+ if ( graph.width() < 2 || graph.height() < 2 )
+ return;
+
+ // Iterate over the graph, 2x2 blocks at time
+ PixelGraph::iterator it = graph.begin();
+ for (int i = 0 ; i != graph.height() - 1 ; ++i, ++it ) {
+ for ( int j = 0 ; j != graph.width() - 1 ; ++j, ++it ) {
+ using std::pair;
+ using std::make_pair;
+
+ typedef pair<PixelGraph::iterator, PixelGraph::iterator> Edge;
+ typedef pair<Edge, int> EdgeWeight;
+
+ EdgeWeight diagonals[2] = {
+ make_pair(make_pair(it, graph.nodeBottomRight(it)), 0),
+ make_pair(make_pair(graph.nodeRight(it), graph.nodeBottom(it)),
+ 0)
+ };
+
+ // Check if there are crossing edges
+ if ( !diagonals[0].first.first->adj.bottomright
+ || !diagonals[1].first.first->adj.bottomleft ) {
+ continue;
+ }
+
+ // Compute weights
+ for ( int i = 0 ; i != 2 ; ++i ) {
+ // Curves and islands heuristics
+ PixelGraph::const_iterator a = diagonals[i].first.first;
+ PixelGraph::const_iterator b = diagonals[i].first.second;
+
+ diagonals[i].second += Heuristics::curves(graph, a, b)
+ * options.curvesMultiplier;
+
+ diagonals[i].second += Heuristics::islands(a, b)
+ * options.islandsWeight;
+ }
+
+ {
+ // Sparse pixels heuristic
+ Heuristics::SparsePixels sparse_pixels;
+
+ for ( int i = 0 ; i != 2 ; ++i )
+ sparse_pixels.diagonals[i] = diagonals[i];
+
+ sparse_pixels(graph, options.sparsePixelsRadius);
+
+ for ( int i = 0 ; i != 2 ; ++i ) {
+ diagonals[i].second += sparse_pixels.diagonals[i].second
+ * options.sparsePixelsMultiplier;
+ }
+ }
+
+ // Remove edges with lower weight
+ if ( diagonals[0].second > diagonals[1].second ) {
+ diagonals[1].first.first->adj.bottomleft = 0;
+ diagonals[1].first.second->adj.topright = 0;
+ } else if ( diagonals[0].second < diagonals[1].second ) {
+ diagonals[0].first.first->adj.bottomright = 0;
+ diagonals[0].first.second->adj.topleft = 0;
+ } else {
+ diagonals[0].first.first->adj.bottomright = 0;
+ diagonals[0].first.second->adj.topleft = 0;
+ diagonals[1].first.first->adj.bottomleft = 0;
+ diagonals[1].first.second->adj.topright = 0;
+ }
+ }
+ }
+}
+
+inline int Heuristics::curves(const PixelGraph &graph,
+ PixelGraph::const_iterator a,
+ PixelGraph::const_iterator b)
+{
+ int count = 1;
+ ToPtr<PixelGraph::Node> to_ptr;
+ ToIter<PixelGraph::Node> to_iter(graph.begin());
+
+ // b -> a
+ // and then a -> b
+ for ( int i = 0 ; i != 2 ; ++i ) {
+ PixelGraph::const_iterator it = i ? a : b;
+ PixelGraph::const_iterator prev = i ? b : a;
+ int local_count = 0;
+
+ // Used to avoid inifinite loops in circular-like edges
+ const PixelGraph::const_iterator initial = it;
+
+ while ( it->adjsize() == 2 ) {
+ ++local_count;
+
+ // Iterate to next
+ {
+ // There are only two values that won't be zero'ed
+ // and one of them has the same value of prev
+ guintptr aux = guintptr(to_ptr(it));
+ aux = (it->adj.top
+ * guintptr(to_ptr(graph.nodeTop(it))))
+ + (it->adj.topright
+ * guintptr(to_ptr(graph.nodeTopRight(it))))
+ + (it->adj.right
+ * guintptr(to_ptr(graph.nodeRight(it))))
+ + (it->adj.bottomright
+ * guintptr(to_ptr(graph.nodeBottomRight(it))))
+ + (it->adj.bottom
+ * guintptr(to_ptr(graph.nodeBottom(it))))
+ + (it->adj.bottomleft
+ * guintptr(to_ptr(graph.nodeBottomLeft(it))))
+ + (it->adj.left
+ * guintptr(to_ptr(graph.nodeLeft(it))))
+ + (it->adj.topleft
+ * guintptr(to_ptr(graph.nodeTopLeft(it))))
+ - guintptr(to_ptr(prev));
+ prev = it;
+ it = to_iter(reinterpret_cast<PixelGraph::Node const*>(aux));
+ }
+
+ // Break infinite loops
+ if ( it == initial )
+ return local_count;
+ }
+ count += local_count;
+ }
+
+ return count;
+}
+
+inline void Heuristics::SparsePixels::operator ()(const PixelGraph &graph,
+ unsigned radius)
+{
+ if ( !graph.width() || !graph.height() )
+ return;
+
+ // Clear weights
+ for ( int i = 0 ; i != 2 ; ++i )
+ diagonals[i].second = 0;
+
+ if ( !radius )
+ return;
+
+ // Fix radius/bounds
+ {
+ unsigned x = graph.toX(diagonals[MAIN_DIAGONAL].first.first);
+ unsigned y = graph.toY(diagonals[MAIN_DIAGONAL].first.first);
+ unsigned minor = std::min(x, y);
+ unsigned displace = radius - 1;
+
+ if ( displace > minor ) {
+ displace = minor;
+ radius = displace + 1;
+ }
+
+ displace = radius;
+
+ if ( x + displace >= graph.width() ) {
+ displace = unsigned(graph.width()) - x - 1;
+ radius = displace;
+ }
+
+ if ( y + displace >= graph.height() ) {
+ displace = unsigned(graph.height()) - y - 1;
+ radius = displace;
+ }
+ }
+
+ if ( !radius )
+ return;
+
+ // Iterate over nodes and count them
+ {
+ PixelGraph::const_iterator it = diagonals[MAIN_DIAGONAL].first.first;
+ for ( unsigned i = radius - 1 ; i ; --i )
+ it = graph.nodeTopLeft(it);
+
+ bool invert = false;
+ for ( unsigned i = 0 ; i != 2 * radius ; ++i ) {
+ for ( unsigned j = 0 ; j != 2 * radius ; ++j ) {
+ for ( int k = 0 ; k != 2 ; ++k ) {
+ diagonals[k].second
+ += similar_colors(it, diagonals[k].first.first->rgba,
+ diagonals[k].first.second->rgba);
+ }
+ it = (invert ? graph.nodeLeft(it) : graph.nodeRight(it));
+ }
+ it = (invert ? graph.nodeRight(it) : graph.nodeLeft(it));
+
+
+ invert = !invert;
+ it = graph.nodeBottom(it);
+ }
+ }
+
+ int minor = std::min(diagonals[0].second, diagonals[1].second);
+ for ( int i = 0 ; i != 2 ; ++i )
+ diagonals[i].second -= minor;
+
+ std::swap(diagonals[0].second, diagonals[1].second);
+}
+
+inline bool
+Heuristics::SparsePixels::similar_colors(PixelGraph::const_iterator n,
+ const guint8 (&a)[4],
+ const guint8 (&b)[4])
+{
+ using colorspace::similar_colors;
+ return similar_colors(n->rgba, a) || similar_colors(n->rgba, b);
+}
+
+inline bool Heuristics::islands(PixelGraph::const_iterator a,
+ PixelGraph::const_iterator b)
+{
+ if ( a->adjsize() == 1 || b->adjsize() == 1 )
+ return true;
+
+ return false;
+}
+
+} // namespace Tracer
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/kopftracer2011.h b/src/libdepixelize/kopftracer2011.h
new file mode 100644
index 000000000..73f3b7274
--- /dev/null
+++ b/src/libdepixelize/kopftracer2011.h
@@ -0,0 +1,123 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_KOPFTRACER2011_H
+#define LIBDEPIXELIZE_TRACER_KOPFTRACER2011_H
+
+#include <string>
+
+// Contains exception definitions
+#include <glibmm/fileutils.h>
+#include <gdkmm/pixbuf.h>
+#include "splines.h"
+
+namespace Tracer {
+
+class PixelGraph;
+template<typename T> class SimplifiedVoronoi;
+template<typename T> class HomogeneousSplines;
+
+class Kopf2011
+{
+public:
+ struct Options
+ {
+ enum Defaults {
+ CURVES_MULTIPLIER = 1,
+ ISLANDS_WEIGHT = 5,
+ SPARSE_PIXELS_MULTIPLIER = 1,
+ SPARSE_PIXELS_RADIUS = 4
+ };
+
+ Options() :
+ curvesMultiplier(CURVES_MULTIPLIER),
+ islandsWeight(ISLANDS_WEIGHT),
+ sparsePixelsMultiplier(SPARSE_PIXELS_MULTIPLIER),
+ sparsePixelsRadius(SPARSE_PIXELS_RADIUS),
+ optimize(true),
+ nthreads(1)
+ {}
+
+ // Heuristics
+ double curvesMultiplier;
+ int islandsWeight;
+ double sparsePixelsMultiplier;
+ unsigned sparsePixelsRadius;
+
+ // Other options
+ bool optimize;
+ int nthreads;
+ };
+
+ /**
+ * # Exceptions
+ *
+ * Glib::FileError
+ * Gdk::PixbufError
+ */
+ static Splines to_voronoi(const std::string &filename,
+ const Options &options = Options());
+
+ static Splines to_voronoi(const Glib::RefPtr<Gdk::Pixbuf const> &buf,
+ const Options &options = Options());
+
+ /**
+ * # Exceptions
+ *
+ * Glib::FileError
+ * Gdk::PixbufError
+ */
+ static Splines to_splines(const std::string &filename,
+ const Options &options = Options());
+
+ static Splines to_splines(const Glib::RefPtr<Gdk::Pixbuf const> &buf,
+ const Options &options = Options());
+
+private:
+ typedef double Precision;
+
+ template<class T>
+ static SimplifiedVoronoi<T> _voronoi(const Glib::RefPtr<Gdk::Pixbuf const> &buf,
+ const Options &options);
+
+ static void _disconnect_neighbors_with_dissimilar_colors(PixelGraph &graph);
+ static void _remove_crossing_edges_safe(PixelGraph &graph);
+ static void _remove_crossing_edges_unsafe(PixelGraph &graph,
+ const Options &options);
+};
+
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_KOPFTRACER2011_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/makefile.in b/src/libdepixelize/makefile.in
new file mode 100644
index 000000000..51d020db1
--- /dev/null
+++ b/src/libdepixelize/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+OBJEXT = @OBJEXT@
+
+# Explicit so that it's the default rule.
+all:
+ cd .. && $(MAKE) libdepixelize/all
+
+clean %.a %.$(OBJEXT):
+ cd .. && $(MAKE) libdepixelize/$@
+
+.PHONY: all clean
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/libdepixelize/priv/branchless.h b/src/libdepixelize/priv/branchless.h
new file mode 100644
index 000000000..487a9688d
--- /dev/null
+++ b/src/libdepixelize/priv/branchless.h
@@ -0,0 +1,58 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_BRANCHLESS_H
+#define LIBDEPIXELIZE_TRACER_BRANCHLESS_H
+
+namespace Tracer {
+
+/**
+ * Branch misprediction proof operations
+ */
+namespace branchless {
+
+/*
+ * All modern processors optimize the branch to a conditional move
+ */
+template<class T>
+T first_if(T first, T second, bool cond)
+{
+ return cond ? first : second;
+}
+
+} // namespace branchless
+} // namespace Tracer {
+
+#endif // LIBDEPIXELIZE_TRACER_BRANCHLESS_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/priv/colorspace.h b/src/libdepixelize/priv/colorspace.h
new file mode 100644
index 000000000..4982630ad
--- /dev/null
+++ b/src/libdepixelize/priv/colorspace.h
@@ -0,0 +1,111 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_YUV_H
+#define LIBDEPIXELIZE_TRACER_YUV_H
+
+#include <glib.h>
+
+namespace Tracer {
+namespace colorspace {
+
+/**
+ * The same algorithm used in hqx filter
+ */
+inline void rgb2yuv(guint8 r, guint8 g, guint8 b,
+ guint8 &y, guint8 &u, guint8 &v)
+{
+ y = 0.299 * r + 0.587 * g + 0.114 * b;
+ u = guint16(-0.169 * r - 0.331 * g + 0.5 * b) + 128;
+ v = guint16(0.5 * r - 0.419 * g - 0.081 * b) + 128;
+}
+
+inline void rgb2yuv(const guint8 rgb[], guint8 yuv[])
+{
+ rgb2yuv(rgb[0], rgb[1], rgb[2], yuv[0], yuv[1], yuv[2]);
+}
+
+inline bool same_color(const guint8 (&a)[4], const guint8 (&b)[4])
+{
+ return (a[0] == b[0]
+ && a[1] == b[1]
+ && a[2] == b[2]
+ && a[3] == b[3]);
+}
+
+inline bool dissimilar_colors(const guint8 a[], const guint8 b[])
+{
+ // C uses row-major order, so
+ // A[2][3] = { {1, 2, 3}, {4, 5, 6} } = {1, 2, 3, 4, 5, 6}
+ guint8 yuv[2][3];
+ rgb2yuv(a, yuv[0]);
+ rgb2yuv(b, yuv[1]);
+
+ // Magic numbers taken from hqx algorithm
+ // Only used to describe the level of tolerance
+ return abs(yuv[0][0] - yuv[1][0]) > 0x30
+ || abs(yuv[0][1] - yuv[1][1]) > 7
+ || abs(yuv[0][2] - yuv[1][2]) > 6;
+}
+
+inline bool similar_colors(const guint8 a[], const guint8 b[])
+{
+ return !dissimilar_colors(a, b);
+}
+
+inline bool shading_edge(const guint8 a[], const guint8 b[])
+{
+ // C uses row-major order, so
+ // A[2][3] = { {1, 2, 3}, {4, 5, 6} } = {1, 2, 3, 4, 5, 6}
+ guint8 yuv[2][3];
+ rgb2yuv(a, yuv[0]);
+ rgb2yuv(b, yuv[1]);
+
+ // Magic numbers taken from Kopf-Lischinski algorithm
+ // Only used to describe the level of tolerance
+ return abs(yuv[0][0] - yuv[1][0]) <= 100
+ && abs(yuv[0][1] - yuv[1][1]) <= 100
+ && abs(yuv[0][2] - yuv[1][2]) <= 100;
+}
+
+inline bool contour_edge(const guint8 a[], const guint8 b[])
+{
+ return !shading_edge(a, b);
+}
+
+} // namespace colorspace
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_YUV_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/priv/homogeneoussplines.h b/src/libdepixelize/priv/homogeneoussplines.h
new file mode 100644
index 000000000..57c77a163
--- /dev/null
+++ b/src/libdepixelize/priv/homogeneoussplines.h
@@ -0,0 +1,465 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_HOMOGENEOUSSPLINES_H
+#define LIBDEPIXELIZE_TRACER_HOMOGENEOUSSPLINES_H
+
+#include "simplifiedvoronoi.h"
+#include "point.h"
+#include <algorithm>
+#include <utility>
+
+namespace Tracer {
+
+template<typename T>
+class HomogeneousSplines
+{
+public:
+ struct Polygon
+ {
+ Polygon() {}
+ Polygon(const guint8 (&rgba)[4])
+ {
+ for ( int i = 0 ; i != 4 ; ++i )
+ this->rgba[i] = rgba[i];
+ }
+
+ std::vector< Point<T> > vertices;
+
+ /**
+ * It may be benefited from C++11 move references.
+ */
+ std::vector< std::vector< Point<T> > > holes;
+
+ guint8 rgba[4];
+ };
+
+ typedef typename std::vector<Polygon>::iterator iterator;
+ typedef typename std::vector<Polygon>::const_iterator const_iterator;
+ typedef typename std::vector<Polygon>::size_type size_type;
+
+ HomogeneousSplines(const SimplifiedVoronoi<T> &voronoi);
+
+ // Iterators
+ iterator begin()
+ {
+ return _polygons.begin();
+ }
+
+ const_iterator begin() const
+ {
+ return _polygons.begin();
+ }
+
+ iterator end()
+ {
+ return _polygons.end();
+ }
+
+ const_iterator end() const
+ {
+ return _polygons.end();
+ }
+
+ size_type size() const
+ {
+ return _polygons.size();
+ }
+
+ int width() const
+ {
+ return _width;
+ }
+
+ int height() const
+ {
+ return _height;
+ }
+
+private:
+ typedef typename SimplifiedVoronoi<T>::Cell Cell;
+ typedef std::vector< Point<T> > Points;
+
+ typedef typename SimplifiedVoronoi<T>::iterator voronoi_iter;
+ typedef typename SimplifiedVoronoi<T>::const_iterator voronoi_citer;
+
+ typedef typename Points::iterator points_iter;
+ typedef typename Points::const_iterator points_citer;
+ typedef typename Points::reverse_iterator points_riter;
+ typedef typename Points::const_reverse_iterator points_criter;
+
+ typedef std::pair<points_iter, points_iter> points_range;
+ typedef std::pair<points_citer, points_citer> points_crange;
+
+ struct CommonEdge
+ {
+ bool ok; //< share an edge
+ Points *dst;
+ const Points *src;
+
+ // the interval is closed on both ends
+ // different from [begin, end) STL style
+ points_iter dst_begin, dst_end;
+ points_citer src_begin, src_end;
+ };
+
+ struct SelfCommonEdge
+ {
+ bool ok; //< share an edge
+
+ // Greater range. The one that should be erased from the vector.
+ points_riter grt_begin, grt_end;
+
+ // Smaller range. The one that should be used to create a new vector.
+ points_riter sml_begin, sml_end;
+ };
+
+ /**
+ * Return ok == true if they share an edge (more than one point).
+ */
+ CommonEdge _common_edge(Points &dst, const Points &src);
+
+ /**
+ * Return ok == true if they share an edge (more than one point).
+ *
+ * - [dst_begin, dst_end) will contain the hole polygon
+ * - [src_begin, src_end) will contain the range to be erased
+ *
+ * It's required to do the search in backward order.
+ */
+ SelfCommonEdge _common_edge(Points &points, points_riter it);
+
+ /*!
+ * Add polygon represented by \p common_edge.src to \p common_edge.dst.
+ */
+ void _polygon_union(CommonEdge common_edge);
+
+ /**
+ * Weird recursive function created to solve the complex problem to fill
+ * polygons holes without the need to store temporaries on the heap nor
+ * changing requirements to some data type that don't invalidate iterators
+ * that point before the current element (maybe I'll write some poetry about
+ * the problem someday).
+ */
+ void _fill_holes(std::vector<Points> &holes, points_iter region_begin,
+ points_iter region_end);
+
+ std::vector<Polygon> _polygons;
+ int _width;
+ int _height;
+};
+
+template<class T>
+HomogeneousSplines<T>::HomogeneousSplines(const SimplifiedVoronoi<T> &voronoi) :
+ _width(voronoi.width()),
+ _height(voronoi.height())
+{
+ //if (!voronoi.size())
+ // return;
+ using colorspace::same_color;
+
+ // Identify visible edges (group polygons with the same color)
+ for ( voronoi_citer cell_it = voronoi.begin(), cell_end = voronoi.end()
+ ; cell_it != cell_end ; ++cell_it ) {
+ bool found = false;
+ for ( iterator polygon_it = _polygons.begin(),
+ polygon_end = _polygons.end()
+ ; polygon_it != polygon_end ; ++polygon_it ) {
+ if ( same_color(polygon_it->rgba, cell_it->rgba) ) {
+ CommonEdge common_edge = _common_edge(polygon_it->vertices,
+ cell_it->vertices);
+ if ( common_edge.ok ) {
+ _polygon_union(common_edge);
+ found = true;
+
+ for ( iterator polygon2_it = polygon_it + 1
+ ; polygon2_it != polygon_end ; ++polygon2_it ) {
+ if ( same_color(polygon_it->rgba, polygon2_it->rgba) ) {
+ CommonEdge common_edge2
+ = _common_edge(polygon_it->vertices,
+ polygon2_it->vertices);
+ if ( common_edge2.ok ) {
+ _polygon_union(common_edge2);
+ _polygons.erase(polygon2_it);
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ if ( !found ) {
+ Polygon polygon(cell_it->rgba);
+ polygon.vertices = cell_it->vertices;
+ _polygons.insert(_polygons.end(), polygon);
+ }
+ }
+
+ // Find polygons with holes and fix them
+ // This iteration runs such complex time-consuming algorithm, but each
+ // polygon has an independent result. They wouldn't even need to share/sync
+ // results and the only waste would be a join at the end of the for.
+ for ( typename std::vector<Polygon>::iterator it = _polygons.begin(),
+ end = _polygons.end() ; it != end ; ++it ) {
+ SelfCommonEdge ce = _common_edge(it->vertices, it->vertices.rbegin());
+ while ( ce.ok ) {
+ _fill_holes(it->holes, ce.sml_end.base(), ce.sml_begin.base());
+ it->vertices.erase(ce.grt_end.base() + 1, ce.grt_begin.base());
+ ce = _common_edge(it->vertices, ce.grt_end);
+ }
+ }
+}
+
+// it can infinite loop if points of both entities are equal,
+// but this shouldn't happen if user has only access to Kopf2011 interface
+template<class T>
+typename HomogeneousSplines<T>::CommonEdge
+HomogeneousSplines<T>::_common_edge(Points &dst, const Points &src)
+{
+ // It's an edge, then the points are closer together. After we find the
+ // first point, there is no need for check against all points of the src
+ // a second time
+
+ const points_iter dst_begin = dst.begin();
+ const points_iter dst_end = dst.end();
+
+ const points_citer src_begin = src.begin();
+ const points_citer src_end = src.end();
+
+ for ( points_iter it = dst_begin ; it != dst_end ; ++it ) {
+ points_citer src_it = std::find(src_begin, src_end, *it);
+
+ if ( src_it == src_end )
+ continue;
+
+ points_iter dst_common_edge_begin = it;
+ points_citer src_common_edge_end = src_it;
+
+ // iterate until find the beginning of the common edge range
+ while ( *dst_common_edge_begin == *src_common_edge_end ) {
+ if ( dst_common_edge_begin == dst_begin )
+ dst_common_edge_begin = dst_end - 1;
+ else
+ --dst_common_edge_begin;
+
+ ++src_common_edge_end;
+ if ( src_common_edge_end == src_end )
+ src_common_edge_end = src_begin;
+ }
+
+ // fix {dst_begin, src_end} range
+ ++dst_common_edge_begin;
+ if ( dst_common_edge_begin == dst_end )
+ dst_common_edge_begin = dst_begin;
+
+ if ( src_common_edge_end == src_begin )
+ src_common_edge_end = src_end - 1;
+ else
+ --src_common_edge_end;
+
+ points_iter dst_common_edge_end = it;
+ points_citer src_common_edge_begin = src_it;
+
+ // find the end of the common edge range
+ while ( *dst_common_edge_end == *src_common_edge_begin ) {
+ ++dst_common_edge_end;
+ if ( dst_common_edge_end == dst_end )
+ dst_common_edge_end = dst_begin;
+
+ if ( src_common_edge_begin == src_begin )
+ src_common_edge_begin = src_end - 1;
+ else
+ --src_common_edge_begin;
+ }
+
+ // fix {dst_end, src_begin} range
+ if ( dst_common_edge_end == dst_begin )
+ dst_common_edge_end = dst_end - 1;
+ else
+ --dst_common_edge_end;
+
+ ++src_common_edge_begin;
+ if ( src_common_edge_begin == src_end )
+ src_common_edge_begin = src_begin;
+
+ CommonEdge ret;
+
+ // if only one point in common
+ if ( dst_common_edge_begin == dst_common_edge_end )
+ continue;
+
+ ret.ok = true;
+
+ ret.dst = &dst;
+ ret.dst_begin = dst_common_edge_begin;
+ ret.dst_end = dst_common_edge_end;
+
+ ret.src = &src;
+ ret.src_begin = src_common_edge_begin;
+ ret.src_end = src_common_edge_end;
+
+ return ret;
+ }
+
+ CommonEdge ret;
+ ret.ok = false;
+ return ret;
+}
+
+template<class T>
+typename HomogeneousSplines<T>::SelfCommonEdge
+HomogeneousSplines<T>::_common_edge(Points &points, points_riter it)
+{
+ SelfCommonEdge ret;
+
+ ret.grt_end = points.rend();
+
+ for ( ; it != ret.grt_end ; ++it ) {
+ ret.sml_end = std::find(it + 1, ret.grt_end, *it);
+
+ if ( ret.sml_end == ret.grt_end )
+ continue;
+
+ ret.grt_begin = it;
+ ret.grt_end = ret.sml_end + 1;
+
+ ret.sml_begin = it;
+
+ while ( *ret.sml_begin == *ret.sml_end ) {
+ ++ret.sml_begin;
+ --ret.sml_end;
+ }
+
+ --ret.sml_begin;
+ ++ret.sml_end;
+ ++ret.sml_end;
+
+ ret.ok = true;
+ return ret;
+ }
+
+ ret.ok = false;
+ return ret;
+}
+
+template<class T>
+void HomogeneousSplines<T>::_polygon_union(CommonEdge common_edge)
+{
+ Points &dst = *common_edge.dst;
+ const Points &src = *common_edge.src;
+
+ // the rotated cell must be inserted before (dst.begin() + index)
+ typename Points::difference_type index;
+
+ // first, we remove the common edge in dst
+ if ( common_edge.dst_begin < common_edge.dst_end ) {
+ // common edge is in the middle of dst
+
+ index = dst.erase(common_edge.dst_begin,
+ common_edge.dst_end + 1) - dst.begin();
+ } else {
+ // common edge cross the end of dst
+
+ dst.erase(common_edge.dst_begin, dst.end());
+ dst.erase(dst.begin(), common_edge.dst_end);
+ index = dst.end() - dst.begin();
+ }
+
+ // second, we copy src points to polygon
+ if ( common_edge.src_begin < common_edge.src_end ) {
+ // common edge is in the middle of src
+
+ const typename Points::difference_type nfirstinserted
+ = src.end() - common_edge.src_end;
+ const typename Points::difference_type nsecondinserted
+ = 1 + (common_edge.src_begin - src.begin());
+
+ dst.reserve(dst.size() + nfirstinserted + nsecondinserted);
+
+ dst.insert(dst.begin() + index, common_edge.src_end, src.end());
+
+ dst.insert(dst.begin() + index + nfirstinserted,
+ src.begin(), common_edge.src_begin + 1);
+ } else {
+ // common edge cross the end of src
+
+ dst.reserve(dst.size() + 1
+ + (common_edge.src_begin - common_edge.src_end));
+
+ dst.insert(dst.begin() + index,
+ common_edge.src_end, common_edge.src_begin + 1);
+ }
+}
+
+// The following piece of code is so evil that you could end up invoking an
+// ancient beast if you proceed to read it, but I'll be able to explain it in
+// the form of some video (text is not so representative as an image).
+template<class T>
+void HomogeneousSplines<T>::_fill_holes(std::vector<Points> &holes,
+ points_iter region_begin,
+ points_iter region_end)
+{
+ // the exact location might not always be back and iterators will be
+ // invalidated after some insertions, then the index is required
+ const typename std::vector<Points>::size_type hole_index = holes.size();
+ holes.resize(hole_index + 1);
+
+ for ( points_iter it = region_begin + 1 ; it != region_end ; ++it ) {
+ points_iter res = std::find(it + 1, region_end, *it);
+ if ( res == region_end )
+ continue;
+
+ holes[hole_index].insert(holes[hole_index].end(), region_begin,
+ it);
+ region_begin = res;
+
+ do {
+ ++it;
+ --res;
+ } while ( *it == *res );
+ _fill_holes(holes, it - 1, res + 2);
+
+ it = region_begin;
+ }
+
+ holes[hole_index].insert(holes[hole_index].end(), region_begin,
+ region_end - 1);
+}
+
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_HOMOGENEOUSSPLINES_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/priv/iterator.h b/src/libdepixelize/priv/iterator.h
new file mode 100644
index 000000000..7caa9bfa9
--- /dev/null
+++ b/src/libdepixelize/priv/iterator.h
@@ -0,0 +1,123 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_ITERATOR_H
+#define LIBDEPIXELIZE_TRACER_ITERATOR_H
+
+#include <vector>
+#include <iterator>
+
+namespace Tracer {
+
+template<typename T>
+const T *to_ptr(typename std::vector<T>::const_iterator it)
+{
+ return &*it;
+}
+
+template<typename T>
+T *to_ptr(typename std::vector<T>::iterator it)
+{
+ return &*it;
+}
+
+template<typename T>
+typename std::vector<T>::const_iterator to_iterator(T const *ptr,
+ typename std::vector<T>
+ ::const_iterator begin)
+{
+ typedef typename std::vector<T>::const_iterator It;
+ typedef typename std::iterator_traits<It>::difference_type difference_type;
+ difference_type idx = ptr - to_ptr<T>(begin);
+ return begin + idx;
+}
+
+template<typename T>
+typename std::vector<T>::iterator to_iterator(T *ptr,
+ typename std::vector<T>::iterator
+ begin)
+{
+ typedef typename std::vector<T>::iterator It;
+ typedef typename std::iterator_traits<It>::difference_type difference_type;
+ difference_type idx = ptr - to_ptr<T>(begin);
+ return begin + idx;
+}
+
+template<typename T>
+class ToIter
+{
+public:
+ typedef typename std::vector<T>::const_iterator const_iterator;
+ typedef typename std::vector<T>::iterator iterator;
+
+ ToIter(const_iterator begin) :
+ begin(begin)
+ {}
+
+ const_iterator operator()(T const *ptr) const
+ {
+ return to_iterator<T>(ptr, begin);
+ }
+
+ iterator operator()(T *ptr) const
+ {
+ return to_iterator<T>(ptr, begin);
+ }
+
+private:
+ typename std::vector<T>::const_iterator begin;
+};
+
+template<typename T>
+class ToPtr
+{
+public:
+ typedef typename std::vector<T>::const_iterator const_iterator;
+ typedef typename std::vector<T>::iterator iterator;
+
+ const T *operator()(const_iterator it) const
+ {
+ return to_ptr<T>(it);
+ }
+
+ T *operator()(iterator it) const
+ {
+ return to_ptr<T>(it);
+ }
+};
+
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_ITERATOR_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/priv/pixelgraph.h b/src/libdepixelize/priv/pixelgraph.h
new file mode 100644
index 000000000..32523d8ee
--- /dev/null
+++ b/src/libdepixelize/priv/pixelgraph.h
@@ -0,0 +1,490 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_PIXELGRAPH_H
+#define LIBDEPIXELIZE_TRACER_PIXELGRAPH_H
+
+#include <gdkmm/pixbuf.h>
+#include <vector>
+#include <cassert>
+
+namespace Tracer {
+
+class PixelGraph
+{
+public:
+ class Node
+ {
+ public:
+ /*
+ * Hamming weight of \p adj
+ */
+ unsigned adjsize() const
+ {
+ unsigned all[8] = {
+ adj.top,
+ adj.topright,
+ adj.right,
+ adj.bottomright,
+ adj.bottom,
+ adj.bottomleft,
+ adj.left,
+ adj.topleft
+ };
+ return all[0] + all[1] + all[2] + all[3]
+ + all[4] + all[5] + all[6] + all[7];
+ }
+
+ guint8 rgba[4];
+ // Nodes pointing from this
+ struct Adj
+ {
+ unsigned top: 1;
+ unsigned topright: 1;
+ unsigned right: 1;
+ unsigned bottomright: 1;
+ unsigned bottom: 1;
+ unsigned bottomleft: 1;
+ unsigned left: 1;
+ unsigned topleft: 1;
+ } adj;
+ };
+
+ typedef std::vector<Node>::iterator iterator;
+ typedef std::vector<Node>::const_iterator const_iterator;
+ typedef std::vector<Node>::reverse_iterator reverse_iterator;
+ typedef std::vector<Node>::const_reverse_iterator const_reverse_iterator;
+
+ class ColumnView
+ {
+ public:
+ ColumnView(std::vector<Node> &nodes, int width, int column) :
+ _nodes(nodes), _width(width), _column(column)
+ {}
+
+ Node &operator[](int line);
+
+ private:
+ std::vector<Node> &_nodes;
+ const int _width;
+ const int _column;
+ };
+
+ PixelGraph(Glib::RefPtr<Gdk::Pixbuf const> pixbuf);
+
+ void checkConsistency();
+
+ /**
+ * It'll let you access the nodes using the syntax:
+ *
+ * graph[x][y]
+ *
+ * Where x is the column and y is the line.
+ */
+ ColumnView operator[](int column);
+
+ // Iterators
+ iterator begin()
+ {
+ return _nodes.begin();
+ }
+
+ const_iterator begin() const
+ {
+ return _nodes.begin();
+ }
+
+ iterator end()
+ {
+ return _nodes.end();
+ }
+
+ const_iterator end() const
+ {
+ return _nodes.end();
+ }
+
+ reverse_iterator rbegin()
+ {
+ return _nodes.rbegin();
+ }
+
+ const_reverse_iterator rbegin() const
+ {
+ return _nodes.rbegin();
+ }
+
+ reverse_iterator rend()
+ {
+ return _nodes.rend();
+ }
+
+ const_reverse_iterator rend() const
+ {
+ return _nodes.rend();
+ }
+
+ size_t size() const
+ {
+ return _nodes.size();
+ }
+
+ int width() const
+ {
+ return _width;
+ }
+
+ int height() const
+ {
+ return _height;
+ }
+
+ // Algorithms
+ void connectAllNeighbors();
+
+ int toX(const_iterator n) const
+ {
+ return (&*n - &_nodes[0]) % _width;
+ }
+
+ int toY(const_iterator n) const
+ {
+ return (&*n - &_nodes[0]) / _width;
+ }
+
+ iterator nodeTop(iterator n)
+ {
+ return n - _width;
+ }
+
+ iterator nodeBottom(iterator n)
+ {
+ return n + _width;
+ }
+
+ iterator nodeLeft(iterator n)
+ {
+ return n - 1;
+ }
+
+ iterator nodeRight(iterator n)
+ {
+ return n + 1;
+ }
+
+ iterator nodeTopLeft(iterator n)
+ {
+ return n - _width - 1;
+ }
+
+ iterator nodeTopRight(iterator n)
+ {
+ return n - _width + 1;
+ }
+
+ iterator nodeBottomLeft(iterator n)
+ {
+ return n + _width - 1;
+ }
+
+ iterator nodeBottomRight(iterator n)
+ {
+ return n + _width + 1;
+ }
+
+ const_iterator nodeTop(const_iterator n) const
+ {
+ return n - _width;
+ }
+
+ const_iterator nodeBottom(const_iterator n) const
+ {
+ return n + _width;
+ }
+
+ const_iterator nodeLeft(const_iterator n) const
+ {
+ return n - 1;
+ }
+
+ const_iterator nodeRight(const_iterator n) const
+ {
+ return n + 1;
+ }
+
+ const_iterator nodeTopLeft(const_iterator n) const
+ {
+ return n - _width - 1;
+ }
+
+ const_iterator nodeTopRight(const_iterator n) const
+ {
+ return n - _width + 1;
+ }
+
+ const_iterator nodeBottomLeft(const_iterator n) const
+ {
+ return n + _width - 1;
+ }
+
+ const_iterator nodeBottomRight(const_iterator n) const
+ {
+ return n + _width + 1;
+ }
+
+private:
+ PixelGraph(const PixelGraph&);
+
+ int _width;
+ int _height;
+
+ // The data representation follows the image data pattern from gdk-pixbuf.
+ //
+ // Quoting:
+ // "Image data in a pixbuf is stored in memory in uncompressed, packed
+ // format. Rows in the image are stored top to bottom, and in each row
+ // pixels are stored from left to right. There may be padding at the end of
+ // a row."
+ //
+ // Differently, _nodes don't put padding among rows.
+ std::vector<Node> _nodes;
+};
+
+inline PixelGraph::PixelGraph(Glib::RefPtr<Gdk::Pixbuf const> pixbuf) :
+ _width(pixbuf->get_width()),
+ _height(pixbuf->get_height()),
+ _nodes(size_t(_width) * _height)
+{
+ if ( !_width || !_height )
+ return;
+
+ // Initialize the graph using the pixels' color data
+ guint8 *pixels = pixbuf->get_pixels();
+ Node *dest = &_nodes[0];
+ const int n_channels = pixbuf->get_n_channels();
+ const int rowpadding = pixbuf->get_rowstride() - _width * n_channels;
+
+ if ( n_channels == 4 ) {
+ for ( int i = 0 ; i != _height ; ++i ) {
+ for ( int j = 0 ; j != _width ; ++j ) {
+ for ( int k = 0 ; k != 4 ; ++k )
+ dest->rgba[k] = pixels[k];
+ {
+ dest->adj.top = 0;
+ dest->adj.topright = 0;
+ dest->adj.right = 0;
+ dest->adj.bottomright = 0;
+ dest->adj.bottom = 0;
+ dest->adj.bottomleft = 0;
+ dest->adj.left = 0;
+ dest->adj.topleft = 0;
+ }
+ pixels += n_channels;
+ ++dest;
+ }
+ pixels += rowpadding;
+ }
+ } else {
+ assert(n_channels == 3);
+ for ( int i = 0 ; i != _height ; ++i ) {
+ for ( int j = 0 ; j != _width ; ++j ) {
+ for ( int k = 0 ; k != 3 ; ++k )
+ dest->rgba[k] = pixels[k];
+ dest->rgba[3] = '\xFF';
+ {
+ dest->adj.top = 0;
+ dest->adj.topright = 0;
+ dest->adj.right = 0;
+ dest->adj.bottomright = 0;
+ dest->adj.bottom = 0;
+ dest->adj.bottomleft = 0;
+ dest->adj.left = 0;
+ dest->adj.topleft = 0;
+ }
+ pixels += n_channels;
+ ++dest;
+ }
+ pixels += rowpadding;
+ }
+ }
+}
+
+inline void PixelGraph::checkConsistency()
+{
+ PixelGraph::Node *it = &_nodes.front();
+ for ( int i = 0 ; i != _height ; ++i ) {
+ for ( int j = 0 ; j != _width ; ++j, ++it ) {
+ if ( it->adj.top )
+ assert((it - _width)->adj.bottom);
+ if ( it->adj.topright )
+ assert((it - _width + 1)->adj.bottomleft);
+ if ( it->adj.right )
+ assert((it + 1)->adj.left);
+ if ( it->adj.bottomright )
+ assert((it + _width + 1)->adj.topleft);
+ if ( it->adj.bottom )
+ assert((it + _width)->adj.top);
+ if ( it->adj.bottomleft )
+ assert((it + _width - 1)->adj.topright);
+ if ( it->adj.left )
+ assert((it - 1)->adj.right);
+ if ( it->adj.topleft )
+ assert((it - _width - 1)->adj.bottomright);
+ }
+ }
+}
+
+inline PixelGraph::ColumnView PixelGraph::operator[](int column)
+{
+ return ColumnView(_nodes, _width, column);
+}
+
+inline void PixelGraph::connectAllNeighbors()
+{
+ // ...the "center" nodes first...
+ if ( _width > 2 && _height > 2 ) {
+ iterator it = nodeBottomRight(begin()); // [1][1]
+ for ( int i = 1 ; i != _height - 1 ; ++i ) {
+ for ( int j = 1 ; j != _width - 1 ; ++j ) {
+ it->adj.top = 1;
+ it->adj.topright = 1;
+ it->adj.right = 1;
+ it->adj.bottomright = 1;
+ it->adj.bottom = 1;
+ it->adj.bottomleft = 1;
+ it->adj.left = 1;
+ it->adj.topleft = 1;
+
+ it = nodeRight(it);
+ }
+ // After the previous loop, 'it' is poiting to the last node from
+ // the row.
+ // Go south, then first node in the row (increment 'it' by 1)
+ // Go to the second node in the line (increment 'it' by 1)
+ it += 2;
+ }
+ }
+
+ // ...then the "top" nodes...
+ if ( _width > 2 ) {
+ Node *it = &_nodes[1];
+ for ( int i = 1 ; i != _width - 1 ; ++i ) {
+ it->adj.right = 1;
+ it->adj.bottomright = 1;
+ it->adj.bottom = 1;
+ it->adj.bottomleft = 1;
+ it->adj.left = 1;
+
+ ++it;
+ }
+ }
+
+ // ...then the "bottom" nodes...
+ if ( _width > 2 && _height > 1 ) {
+ Node *it = &((*this)[1][_height - 1]);
+ for ( int i = 1 ; i != _width - 1 ; ++i ) {
+ it->adj.left = 1;
+ it->adj.topleft = 1;
+ it->adj.top = 1;
+ it->adj.topright = 1;
+ it->adj.right = 1;
+
+ ++it;
+ }
+ }
+
+ // ...then the "left" nodes...
+ if ( _height > 2 ) {
+ iterator it = nodeBottom(begin()); // [0][1]
+ for ( int i = 1 ; i != _height - 1 ; ++i ) {
+ it->adj.top = 1;
+ it->adj.topright = 1;
+ it->adj.right = 1;
+ it->adj.bottomright = 1;
+ it->adj.bottom = 1;
+
+ it = nodeBottom(it);
+ }
+ }
+
+ // ...then the "right" nodes...
+ if ( _height > 2 && _width > 1 ) {
+ iterator it = nodeBottom(begin() + _width - 1);// [_width - 1][1]
+ for ( int i = 1 ; i != _height - 1 ; ++i ) {
+ it->adj.bottom = 1;
+ it->adj.bottomleft = 1;
+ it->adj.left = 1;
+ it->adj.topleft = 1;
+ it->adj.top = 1;
+
+ it = nodeBottom(it);
+ }
+ }
+
+ // ...and the 4 corner nodes
+ {
+ Node *const top_left = &(*this)[0][0];
+ top_left->adj.right = 1;
+ top_left->adj.bottomright = 1;
+ top_left->adj.bottom = 1;
+ }
+ if ( _width > 1 ) {
+ Node *const top_right = &(*this)[_width - 1][0];
+ top_right->adj.bottom = 1;
+ top_right->adj.bottomleft = 1;
+ top_right->adj.left = 1;
+ }
+ if ( _height > 1 ) {
+ Node *const down_left = &(*this)[0][_height - 1];
+ down_left->adj.top = 1;
+ down_left->adj.topright = 1;
+ down_left->adj.right = 1;
+ }
+ if ( _width > 1 && _height > 1 ) {
+ Node *const down_right = &(*this)[_width - 1][_height - 1];
+ down_right->adj.left = 1;
+ down_right->adj.topleft = 1;
+ down_right->adj.top = 1;
+ }
+}
+
+inline PixelGraph::Node &PixelGraph::ColumnView::operator[](int line)
+{
+ return _nodes[line * _width + _column];
+}
+
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_PIXELGRAPH_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/priv/point.h b/src/libdepixelize/priv/point.h
new file mode 100644
index 000000000..6bea752ed
--- /dev/null
+++ b/src/libdepixelize/priv/point.h
@@ -0,0 +1,75 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_POINT_H
+#define LIBDEPIXELIZE_TRACER_POINT_H
+
+namespace Tracer {
+
+template<class T>
+struct Point
+{
+ Point() {}
+ Point(T x, T y) : x(x), y(y) {}
+ Point(T x, T y, bool smooth) : smooth(smooth), x(x), y(y) {}
+
+ Point operator+(const Point &rhs) const
+ {
+ return Point(x + rhs.x, y + rhs.y);
+ }
+
+ Point operator/(T foo) const
+ {
+ return Point(x / foo, y / foo);
+ }
+
+ bool smooth;
+
+ T x, y;
+};
+
+template<class T>
+inline bool operator==(const Point<T> &lhs, const Point<T> &rhs)
+{
+ return
+#ifndef LIBDEPIXELIZE_IS_VERY_WELL_TESTED
+ lhs.smooth == rhs.smooth &&
+#endif // LIBDEPIXELIZE_IS_VERY_WELL_TESTED
+ lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_POINT_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/priv/simplifiedvoronoi.h b/src/libdepixelize/priv/simplifiedvoronoi.h
new file mode 100644
index 000000000..a33695ff7
--- /dev/null
+++ b/src/libdepixelize/priv/simplifiedvoronoi.h
@@ -0,0 +1,1324 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_SIMPLIFIEDVORONOI_H
+#define LIBDEPIXELIZE_TRACER_SIMPLIFIEDVORONOI_H
+
+#include "pixelgraph.h"
+#include "colorspace.h"
+#include "point.h"
+#include "branchless.h"
+
+namespace Tracer {
+
+template<typename T>
+class SimplifiedVoronoi
+{
+public:
+ /**
+ * The "smooth" attribute of each vertex is only accurate if edge is
+ * visible. This decision was made because invisible edges will disappear
+ * during polygon-union, the next phase of Kopf-Lischinski.
+ */
+ struct Cell
+ {
+ // There may not exist more than 8 vertices per cell and a
+ // "small vector optimization" could improve the performance
+ // by avoiding memory fragmentation. Serious testing is needed.
+
+ // The vertices are filled in clockwise order
+ std::vector< Point<T> > vertices;
+ guint8 rgba[4];
+ };
+
+ typedef typename std::vector<Cell>::iterator iterator;
+ typedef typename std::vector<Cell>::const_iterator const_iterator;
+ typedef typename std::vector<Cell>::reverse_iterator reverse_iterator;
+
+ typedef typename std::vector<Cell>::const_reverse_iterator
+ const_reverse_iterator;
+
+ /*
+ It will work correctly if no crossing-edges are present.
+ */
+ SimplifiedVoronoi(const PixelGraph &graph);
+
+ // Iterators
+ iterator begin()
+ {
+ return _cells.begin();
+ }
+
+ const_iterator begin() const
+ {
+ return _cells.begin();
+ }
+
+ iterator end()
+ {
+ return _cells.end();
+ }
+
+ const_iterator end() const
+ {
+ return _cells.end();
+ }
+
+ reverse_iterator rbegin()
+ {
+ return _cells.rbegin();
+ }
+
+ const_reverse_iterator rbegin() const
+ {
+ return _cells.rbegin();
+ }
+
+ reverse_iterator rend()
+ {
+ return _cells.rend();
+ }
+
+ const_reverse_iterator rend() const
+ {
+ return _cells.rend();
+ }
+
+ size_t size() const
+ {
+ return _cells.size();
+ }
+
+ int width() const
+ {
+ return _width;
+ }
+
+ int height() const
+ {
+ return _height;
+ }
+
+private:
+ typedef void (*PointTransform)(Point<T> &p, T dx, T dy);
+ typedef bool (*NodeTransform)(PixelGraph::const_iterator);
+
+ static Point<T> _midpoint(const Point<T> &p1, const Point<T> p2)
+ {
+ return Point<T>((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
+ }
+
+ /**
+ * Output is translated by -.5 in each axis. This function fixes this error.
+ */
+ static Point<T> _adjust(Point<T> p)
+ {
+ return Point<T>(p.x + .5, p.y + .5);
+ }
+
+ /**
+ * Output is translated by -.5 in each axis. This function fixes this error.
+ */
+ static Point<T> _adjust(Point<T> p, bool smooth)
+ {
+ return Point<T>(p.x + .5, p.y + .5, smooth);
+ }
+
+ void _complexTopLeft(const PixelGraph &graph,
+ PixelGraph::const_iterator graph_it,
+ Cell *const cells_it, int x, int y);
+ void _complexTopRight(const PixelGraph &graph,
+ PixelGraph::const_iterator graph_it,
+ Cell *const cells_it, int x, int y);
+ void _complexBottomRight(const PixelGraph &graph,
+ PixelGraph::const_iterator graph_it,
+ Cell *const cells_it, int x, int y);
+ void _complexBottomLeft(const PixelGraph &graph,
+ PixelGraph::const_iterator graph_it,
+ Cell *const cells_it, int x, int y);
+
+ static void _complexTopLeftTransform(Point<T> &p, T dx, T dy);
+ static void _complexTopRightTransform(Point<T> &p, T dx, T dy);
+ static void _complexBottomRightTransform(Point<T> &p, T dx, T dy);
+ static void _complexBottomLeftTransform(Point<T> &p, T dx, T dy);
+
+ static bool _complexTopLeftTransformTop(PixelGraph::const_iterator graph_it);
+ static bool _complexTopLeftTransformTopRight(PixelGraph::const_iterator graph_it);
+ static bool _complexTopLeftTransformRight(PixelGraph::const_iterator graph_it);
+ static bool _complexTopLeftTransformBottomRight(PixelGraph::const_iterator graph_it);
+ static bool _complexTopLeftTransformBottom(PixelGraph::const_iterator graph_it);
+ static bool _complexTopLeftTransformBottomLeft(PixelGraph::const_iterator graph_it);
+ static bool _complexTopLeftTransformLeft(PixelGraph::const_iterator graph_it);
+ static bool _complexTopLeftTransformTopLeft(PixelGraph::const_iterator graph_it);
+
+ static bool _complexTopRightTransformTop(PixelGraph::const_iterator graph_it);
+ static bool _complexTopRightTransformTopRight(PixelGraph::const_iterator graph_it);
+ static bool _complexTopRightTransformRight(PixelGraph::const_iterator graph_it);
+ static bool _complexTopRightTransformBottomRight(PixelGraph::const_iterator graph_it);
+ static bool _complexTopRightTransformBottom(PixelGraph::const_iterator graph_it);
+ static bool _complexTopRightTransformBottomLeft(PixelGraph::const_iterator graph_it);
+ static bool _complexTopRightTransformLeft(PixelGraph::const_iterator graph_it);
+ static bool _complexTopRightTransformTopLeft(PixelGraph::const_iterator graph_it);
+
+ static bool _complexBottomRightTransformTop(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomRightTransformTopRight(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomRightTransformRight(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomRightTransformBottomRight(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomRightTransformBottom(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomRightTransformBottomLeft(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomRightTransformLeft(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomRightTransformTopLeft(PixelGraph::const_iterator graph_it);
+
+ static bool _complexBottomLeftTransformTop(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomLeftTransformTopRight(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomLeftTransformRight(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomLeftTransformBottomRight(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomLeftTransformBottom(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomLeftTransformBottomLeft(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomLeftTransformLeft(PixelGraph::const_iterator graph_it);
+ static bool _complexBottomLeftTransformTopLeft(PixelGraph::const_iterator graph_it);
+
+ /*
+ * The memory layout assumed goes like this (with a_it being the current
+ * iterated element):
+ *
+ * (a_it) | (b_it)
+ * -------+-------
+ * (c_it) | (d_it)
+ *
+ * If you want to use it with another directions (topleft, topright, ...)
+ * **DO NOT** invert x or y axis, because the insertion order will go mad.
+ *
+ * The idea behind this abstraction is to rotate the iterators, then the
+ * insertion order will be preserved.
+ *
+ * The initial value of all nodes that will be inserted is {x, y}. All
+ * changes to this node **MUST** occur through \p transform or _adjust.
+ *
+ * Some maintainers may like this function because they will handle a
+ * code base 4 times smaller and bugs will be MUCH MUCH difficult to hide.
+ *
+ * Some maintainers may go mad because the level extra level of
+ * abstracation.
+ *
+ * "All problems in computer science can be solved by another level of
+ * indirection, except for the problem of too many layers of indirection."
+ * -- David J. Wheeler
+ */
+ void _genericComplexBottomRight(PixelGraph::const_iterator a_it,
+ PixelGraph::const_iterator b_it,
+ PixelGraph::const_iterator c_it,
+ PixelGraph::const_iterator d_it,
+ Cell *const cells_it, int x, int y,
+ PointTransform transform,
+ NodeTransform top,
+ NodeTransform topright,
+ NodeTransform right,
+ NodeTransform bottomright,
+ NodeTransform bottom,
+ NodeTransform bottomleft,
+ NodeTransform left,
+ NodeTransform topleft);
+
+ int _width;
+ int _height;
+ std::vector<Cell> _cells;
+};
+
+template<class T>
+SimplifiedVoronoi<T>::SimplifiedVoronoi(const PixelGraph &graph) :
+ _width(graph.width()),
+ _height(graph.height()),
+ _cells(graph.size())
+{
+ if (!graph.size())
+ return;
+
+ // ...the "center" cells first...
+ if ( _width > 2 && _height > 2 ) {
+ PixelGraph::const_iterator graph_it = graph.begin() + _width + 1;
+ Cell *cells_it = &_cells.front() + _width + 1;
+
+ for ( int i = 1 ; i != _height - 1 ; ++i ) {
+ for ( int j = 1 ; j != _width - 1 ; ++j, ++graph_it, ++cells_it ) {
+ for ( int k = 0 ; k != 4 ; ++k )
+ cells_it->rgba[k] = graph_it->rgba[k];
+ // Top-left
+ _complexTopLeft(graph, graph_it, cells_it, j, i);
+
+ // Top-right
+ _complexTopRight(graph, graph_it, cells_it, j, i);
+
+ // Bottom-right
+ _complexBottomRight(graph, graph_it, cells_it, j, i);
+
+ // Bottom-left
+ _complexBottomLeft(graph, graph_it, cells_it, j, i);
+ }
+ // After the previous loop, 'it' is poiting to the last cell from
+ // the row.
+ // Go south, then first node in the row (increment 'it' by 1)
+ // Go to the second node in the line (increment 'it' by 1)
+ graph_it += 2;
+ cells_it += 2;
+ }
+ }
+
+ // ...then the "top" cells...
+ if ( _width > 2 ) {
+ PixelGraph::const_iterator graph_it = graph.begin() + 1;
+ Cell *cells_it = &_cells.front() + 1;
+
+ for ( int i = 1 ; i != _width - 1 ; ++i, ++graph_it, ++cells_it ) {
+ for ( int j = 0 ; j != 4 ; ++j )
+ cells_it->rgba[j] = graph_it->rgba[j];
+
+ // Top-left
+ cells_it->vertices.push_back(Point<T>(i, 0, false));
+
+ // Top-right
+ cells_it->vertices.push_back(Point<T>(i + 1, 0, false));
+
+ // Bottom-right
+ _complexBottomRight(graph, graph_it, cells_it, i, 0);
+
+ // Bottom-left
+ _complexBottomLeft(graph, graph_it, cells_it, i, 0);
+ }
+ }
+
+ // ...then the "bottom" cells...
+ if ( _width > 2 && _height > 1 ) {
+ // Node *it = &((*this)[1][_height - 1]);
+ PixelGraph::const_iterator graph_it
+ = graph.begin() + (_height - 1) * _width + 1;
+ Cell *cells_it = &_cells.front() + (_height - 1) * _width + 1;
+
+ for ( int i = 1 ; i != _width - 1 ; ++i, ++graph_it, ++cells_it ) {
+ for ( int j = 0 ; j != 4 ; ++j )
+ cells_it->rgba[j] = graph_it->rgba[j];
+
+ // Top-left
+ _complexTopLeft(graph, graph_it, cells_it, i, _height - 1);
+
+ // Top-right
+ _complexTopRight(graph, graph_it, cells_it, i, _height - 1);
+
+ // Bottom-right
+ cells_it->vertices.push_back(Point<T>(i + 1, _height, false));
+
+ // Bottom-left
+ cells_it->vertices.push_back(Point<T>(i, _height, false));
+ }
+ }
+
+ // ...then the "left" cells...
+ if ( _height > 2 ) {
+ PixelGraph::const_iterator graph_it = graph.begin() + _width;
+ Cell *cells_it = &_cells.front() + _width;
+
+ for ( int i = 1 ; i != _height - 1 ; ++i) {
+ for ( int j = 0 ; j != 4 ; ++j )
+ cells_it->rgba[j] = graph_it->rgba[j];
+
+ // Top-left
+ cells_it->vertices.push_back(Point<T>(0, i, false));
+
+ // Top-right
+ _complexTopRight(graph, graph_it, cells_it, 0, i);
+
+ // Bottom-right
+ _complexBottomRight(graph, graph_it, cells_it, 0, i);
+
+ // Bottom-left
+ cells_it->vertices.push_back(Point<T>(0, i + 1, false));
+
+ graph_it += _width;
+ cells_it += _width;
+ }
+ }
+
+ // ...then the "right" cells...
+ if ( _height > 2 && _width > 1 ) {
+ PixelGraph::const_iterator graph_it = graph.begin() + 2 * _width - 1;
+ Cell *cells_it = &_cells.front() + 2 * _width - 1;
+
+ for ( int i = 1 ; i != _height - 1 ; ++i ) {
+ for ( int j = 0 ; j != 4 ; ++j )
+ cells_it->rgba[j] = graph_it->rgba[j];
+
+ // Top-left
+ _complexTopLeft(graph, graph_it, cells_it, _width - 1, i);
+
+ // Top-right
+ cells_it->vertices.push_back(Point<T>(_width, i, false));
+
+ // Bottom-right
+ cells_it->vertices.push_back(Point<T>(_width, i + 1, false));
+
+ // Bottom-left
+ _complexBottomLeft(graph, graph_it, cells_it, _width - 1, i);
+
+ graph_it += _width;
+ cells_it += _width;
+ }
+ }
+
+ // ...and the 4 corner nodes
+ // top-left
+ {
+ PixelGraph::const_iterator graph_it = graph.begin();
+ Cell *cells_it = &_cells.front();
+
+ for ( int i = 0 ; i != 4 ; ++i )
+ cells_it->rgba[i] = graph_it->rgba[i];
+
+ // Top-left
+ cells_it->vertices.push_back(Point<T>(0, 0, false));
+
+ // Top-right
+ cells_it->vertices.push_back(Point<T>(1, 0, false));
+
+ // Bottom-right
+ if ( _width > 1 && _height > 1 )
+ _complexBottomRight(graph, graph_it, cells_it, 0, 0);
+ else
+ cells_it->vertices.push_back(Point<T>(1, 1, false));
+
+ // Bottom-left
+ cells_it->vertices.push_back(Point<T>(0, 1, false));
+ }
+
+ // top-right
+ if ( _width > 1 ) {
+ PixelGraph::const_iterator graph_it = graph.begin() + _width - 1;
+ Cell *cells_it = &_cells.front() + _width - 1;
+
+ for ( int i = 0 ; i != 4 ; ++i )
+ cells_it->rgba[i] = graph_it->rgba[i];
+
+ // Top-left
+ cells_it->vertices.push_back(Point<T>(_width - 1, 0, false));
+
+ // Top-right
+ cells_it->vertices.push_back(Point<T>(_width, 0, false));
+
+ // Bottom-right
+ cells_it->vertices.push_back(Point<T>(_width, 1, false));
+
+ // Bottom-left
+ if ( _height > 1 )
+ _complexBottomLeft(graph, graph_it, cells_it, _width - 1, 0);
+ else
+ cells_it->vertices.push_back(Point<T>(_width - 1, 1, false));
+ }
+
+ // bottom-left
+ if ( _height > 1 ) {
+ PixelGraph::const_iterator graph_it
+ = graph.begin() + (_height - 1) * _width;
+ Cell *cells_it = &_cells.front() + (_height - 1) * _width;
+
+ for ( int i = 0 ; i != 4 ; ++i )
+ cells_it->rgba[i] = graph_it->rgba[i];
+
+ // Top-left
+ cells_it->vertices.push_back(Point<T>(0, _height - 1, false));
+
+ // Top-right
+ if ( _width > 1)
+ _complexTopRight(graph, graph_it, cells_it, 0, _height - 1);
+ else
+ cells_it->vertices.push_back(Point<T>(1, _height - 1, false));
+
+ // Bottom-right
+ cells_it->vertices.push_back(Point<T>(1, _height, false));
+
+ // Bottom-left
+ cells_it->vertices.push_back(Point<T>(0, _height, false));
+ }
+
+ // bottom-right
+ if ( _width > 1 && _height > 1 ) {
+ PixelGraph::const_iterator graph_it = --graph.end();
+ Cell *cells_it = &_cells.back();
+
+ for ( int i = 0 ; i != 4 ; ++i )
+ cells_it->rgba[i] = graph_it->rgba[i];
+
+ // Top-left
+ _complexTopLeft(graph, graph_it, cells_it, _width - 1, _height - 1);
+
+ // Top-right
+ cells_it->vertices.push_back(Point<T>(_width, _height - 1, false));
+
+ // Bottom-right
+ cells_it->vertices.push_back(Point<T>(_width, _height, false));
+
+ // Bottom-left
+ cells_it->vertices.push_back(Point<T>(_width - 1, _height, false));
+ }
+}
+
+template<class T> void
+SimplifiedVoronoi<T>::_complexTopLeft(const PixelGraph &graph,
+ PixelGraph::const_iterator graph_it,
+ Cell *const cells_it, int x, int y)
+{
+ _genericComplexBottomRight(graph_it,
+ graph.nodeLeft(graph_it),
+ graph.nodeTop(graph_it),
+ graph.nodeTopLeft(graph_it),
+ cells_it, x, y,
+ &SimplifiedVoronoi::_complexTopLeftTransform,
+ &SimplifiedVoronoi::_complexTopLeftTransformTop,
+ &SimplifiedVoronoi::_complexTopLeftTransformTopRight,
+ &SimplifiedVoronoi::_complexTopLeftTransformRight,
+ &SimplifiedVoronoi::_complexTopLeftTransformBottomRight,
+ &SimplifiedVoronoi::_complexTopLeftTransformBottom,
+ &SimplifiedVoronoi::_complexTopLeftTransformBottomLeft,
+ &SimplifiedVoronoi::_complexTopLeftTransformLeft,
+ &SimplifiedVoronoi::_complexTopLeftTransformTopLeft);
+}
+
+template<class T> void
+SimplifiedVoronoi<T>::_complexTopRight(const PixelGraph &graph,
+ PixelGraph::const_iterator graph_it,
+ Cell *const cells_it, int x, int y)
+{
+ _genericComplexBottomRight(graph_it,
+ graph.nodeTop(graph_it),
+ graph.nodeRight(graph_it),
+ graph.nodeTopRight(graph_it),
+ cells_it, x, y,
+ &SimplifiedVoronoi::_complexTopRightTransform,
+ &SimplifiedVoronoi::_complexTopRightTransformTop,
+ &SimplifiedVoronoi::_complexTopRightTransformTopRight,
+ &SimplifiedVoronoi::_complexTopRightTransformRight,
+ &SimplifiedVoronoi::_complexTopRightTransformBottomRight,
+ &SimplifiedVoronoi::_complexTopRightTransformBottom,
+ &SimplifiedVoronoi::_complexTopRightTransformBottomLeft,
+ &SimplifiedVoronoi::_complexTopRightTransformLeft,
+ &SimplifiedVoronoi::_complexTopRightTransformTopLeft);
+}
+
+template<class T> void
+SimplifiedVoronoi<T>::_complexBottomRight(const PixelGraph &graph,
+ PixelGraph::const_iterator graph_it,
+ Cell *const cells_it, int x, int y)
+{
+ _genericComplexBottomRight(graph_it,
+ graph.nodeRight(graph_it),
+ graph.nodeBottom(graph_it),
+ graph.nodeBottomRight(graph_it),
+ cells_it, x, y,
+ &SimplifiedVoronoi::_complexBottomRightTransform,
+ &SimplifiedVoronoi::_complexBottomRightTransformTop,
+ &SimplifiedVoronoi::_complexBottomRightTransformTopRight,
+ &SimplifiedVoronoi::_complexBottomRightTransformRight,
+ &SimplifiedVoronoi::_complexBottomRightTransformBottomRight,
+ &SimplifiedVoronoi::_complexBottomRightTransformBottom,
+ &SimplifiedVoronoi::_complexBottomRightTransformBottomLeft,
+ &SimplifiedVoronoi::_complexBottomRightTransformLeft,
+ &SimplifiedVoronoi::_complexBottomRightTransformTopLeft);
+}
+
+template<class T> void
+SimplifiedVoronoi<T>::_complexBottomLeft(const PixelGraph &graph,
+ PixelGraph::const_iterator graph_it,
+ Cell *const cells_it, int x, int y)
+{
+ _genericComplexBottomRight(graph_it,
+ graph.nodeBottom(graph_it),
+ graph.nodeLeft(graph_it),
+ graph.nodeBottomLeft(graph_it),
+ cells_it, x, y,
+ &SimplifiedVoronoi::_complexBottomLeftTransform,
+ &SimplifiedVoronoi::_complexBottomLeftTransformTop,
+ &SimplifiedVoronoi::_complexBottomLeftTransformTopRight,
+ &SimplifiedVoronoi::_complexBottomLeftTransformRight,
+ &SimplifiedVoronoi::_complexBottomLeftTransformBottomRight,
+ &SimplifiedVoronoi::_complexBottomLeftTransformBottom,
+ &SimplifiedVoronoi::_complexBottomLeftTransformBottomLeft,
+ &SimplifiedVoronoi::_complexBottomLeftTransformLeft,
+ &SimplifiedVoronoi::_complexBottomLeftTransformTopLeft);
+}
+
+template<class T> void
+SimplifiedVoronoi<T>::_complexTopLeftTransform(Point<T> &p, T dx, T dy)
+{
+ p.x -= dx;
+ p.y -= dy;
+}
+
+template<class T> void
+SimplifiedVoronoi<T>::_complexTopRightTransform(Point<T> &p, T dx, T dy)
+{
+ p.x += dy;
+ p.y -= dx;
+}
+
+template<class T> void
+SimplifiedVoronoi<T>::_complexBottomRightTransform(Point<T> &p, T dx, T dy)
+{
+ p.x += dx;
+ p.y += dy;
+}
+
+template<class T> void
+SimplifiedVoronoi<T>::_complexBottomLeftTransform(Point<T> &p, T dx, T dy)
+{
+ p.x -= dy;
+ p.y += dx;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopLeftTransformTop(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottom;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopLeftTransformTopRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottomleft;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopLeftTransformRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.left;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopLeftTransformBottomRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.topleft;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopLeftTransformBottom(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.top;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopLeftTransformBottomLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.topright;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopLeftTransformLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.right;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopLeftTransformTopLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottomright;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopRightTransformTop(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.left;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopRightTransformTopRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.topleft;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopRightTransformRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.top;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopRightTransformBottomRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.topright;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopRightTransformBottom(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.right;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopRightTransformBottomLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottomright;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopRightTransformLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottom;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexTopRightTransformTopLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottomleft;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomRightTransformTop(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.top;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomRightTransformTopRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.topright;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomRightTransformRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.right;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomRightTransformBottomRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottomright;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomRightTransformBottom(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottom;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomRightTransformBottomLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottomleft;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomRightTransformLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.left;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomRightTransformTopLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.topleft;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomLeftTransformTop(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.right;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomLeftTransformTopRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottomright;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomLeftTransformRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottom;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomLeftTransformBottomRight(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.bottomleft;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomLeftTransformBottom(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.left;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomLeftTransformBottomLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.topleft;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomLeftTransformLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.top;
+}
+
+template<class T>
+bool SimplifiedVoronoi<T>::_complexBottomLeftTransformTopLeft(PixelGraph::const_iterator graph_it)
+{
+ return graph_it->adj.topright;
+}
+
+template<class T>
+void
+SimplifiedVoronoi<T>
+::_genericComplexBottomRight(PixelGraph::const_iterator a_it,
+ PixelGraph::const_iterator b_it,
+ PixelGraph::const_iterator c_it,
+ PixelGraph::const_iterator d_it,
+ Cell *const cells_it, int x, int y,
+ PointTransform transform,
+ NodeTransform top,
+ NodeTransform topright,
+ NodeTransform right,
+ NodeTransform bottomright,
+ NodeTransform bottom,
+ NodeTransform bottomleft,
+ NodeTransform left,
+ NodeTransform topleft)
+{
+ using colorspace::contour_edge;
+ using colorspace::same_color;
+
+ const Point<T> initial(x, y);
+
+ if ( bottomright(a_it) ) {
+ // this and bottom-right are connected
+
+ bool smooth[2] = {
+ same_color(a_it->rgba, d_it->rgba) + right(a_it),
+ same_color(a_it->rgba, d_it->rgba) + bottom(a_it)
+ };
+
+ Point<T> borderMid = initial;
+ {
+ transform(borderMid, 1, 1);
+ borderMid = _midpoint(initial, borderMid);
+ }
+
+ Point<T> vertices[2] = {initial, initial};
+ {
+ transform(vertices[0], 1, 0);
+ vertices[0] = _adjust(_midpoint(borderMid, vertices[0]), smooth[0]);
+
+ transform(vertices[1], 0, 1);
+ vertices[1] = _adjust(_midpoint(borderMid, vertices[1]), smooth[1]);
+ }
+
+ if ( !smooth[0] ) {
+ cells_it->vertices.push_back(vertices[0]);
+ {
+ Point<T> another = vertices[0];
+ transform(another,
+ - ( 0.1875
+ - ( topright(a_it) - topleft(b_it) ) * 0.1875 ),
+ // y
+ - ( 0.5625
+ - ( topright(a_it) + topleft(b_it) ) * 0.1875 ));
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertices[0];
+ transform(another,
+ - ( 0.0625
+ - ( topright(a_it) - topleft(b_it) ) * 0.0625 ),
+ // y
+ - ( 0.1875
+ - ( topright(a_it) + topleft(b_it) ) * 0.0625) );
+ another.smooth = true;
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertices[0];
+ transform(another,
+ 0.1875
+ - ( bottomright(b_it) + topright(d_it) ) * 0.0625,
+ // y
+ 0.0625
+ + ( bottomright(b_it) - topright(d_it) ) * 0.0625);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ transform(vertices[0],
+ 0.0625
+ + ( topright(a_it) - topright(d_it) - topleft(b_it)
+ - bottomright(b_it) ) * 0.03125,
+ // y
+ - ( 0.0625
+ + ( topright(d_it) - topright(a_it)
+ - topleft(b_it) - bottomright(b_it) )
+ * 0.03125 ));
+ }
+ }
+
+ cells_it->vertices.push_back(vertices[0]);
+
+ if ( !smooth[1] ) {
+ {
+ Point<T> another = vertices[1];
+ transform(another,
+ - ( 0.0625
+ + ( bottomleft(d_it) - bottomleft(a_it)
+ - topleft(c_it) - bottomright(c_it) )
+ * 0.03125 ),
+ // y
+ 0.0625
+ + ( bottomleft(a_it) - bottomleft(d_it)
+ - topleft(c_it) - bottomright(c_it) ) * 0.03125);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertices[1];
+ transform(another,
+ 0.0625
+ + ( bottomright(c_it) - bottomleft(d_it) ) * 0.0625,
+ // y
+ 0.1875
+ - ( bottomright(c_it) + bottomleft(d_it) ) * 0.0625);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertices[1];
+ transform(another,
+ - ( 0.1875
+ - ( bottomleft(a_it) + topleft(c_it) )
+ * 0.0625 ),
+ // y
+ - ( 0.0625
+ - ( bottomleft(a_it) - topleft(c_it) )
+ * 0.0625 ));
+ another.smooth = true;
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertices[1];
+ transform(another,
+ - ( 0.5625
+ - ( bottomleft(a_it) + topleft(c_it) )
+ * 0.1875 ),
+ // y
+ - ( 0.1875
+ - ( bottomleft(a_it) - topleft(c_it) )
+ * 0.1875 ));
+ cells_it->vertices.push_back(another);
+ }
+ }
+
+ cells_it->vertices.push_back(vertices[1]);
+ } else if ( bottomleft(b_it) ) {
+ // right and bottom are connected
+
+ Point<T> vertex = initial;
+ transform(vertex, 1, 1);
+ vertex = _adjust(_midpoint(_midpoint(initial, vertex), initial), true);
+ cells_it->vertices.push_back(vertex);
+ } else {
+ // Connections don't affect the shape of this squared-like
+ // pixel
+
+ Point<T> vertex = initial;
+ transform(vertex, 1, 1);
+ vertex = _adjust(_midpoint(initial, vertex));
+
+ // compute smoothness
+ if ( right(a_it) ) {
+ // this and right are connected
+
+ if ( !right(c_it) && !( bottom(a_it) && bottom(b_it) ) ) {
+ // bottom and bottom-right are disconnected
+
+ bool foreign_is_contour = contour_edge(c_it->rgba, d_it->rgba);
+ bool twin_is_contour = contour_edge(b_it->rgba, d_it->rgba);
+ bool another_is_contour = contour_edge(a_it->rgba, c_it->rgba);
+
+ if ( another_is_contour + twin_is_contour
+ + foreign_is_contour == 2 ) {
+ vertex.smooth = !foreign_is_contour;
+
+ if ( !vertex.smooth ) {
+ if ( another_is_contour ) {
+ {
+ Point<T> another = vertex;
+ T amount = 0.125
+ - ( ( bottomright(c_it) + topleft(c_it) )
+ * 0.03125 );
+ transform(another, - amount, amount);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625 * bottomright(c_it);
+ transform(another, amount, 0.25 - amount);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625 * topleft(c_it);
+ transform(another, - ( 0.25 - amount ),
+ - amount);
+ another.smooth = true;
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.1875 * topleft(c_it);
+ transform(another, - ( 0.75 - amount ),
+ - amount);
+ cells_it->vertices.push_back(another);
+ }
+ } else if ( twin_is_contour ) {
+ T amount = 0.125
+ - ( ( bottomleft(d_it) + topright(d_it) )
+ * 0.03125 );
+ transform(vertex, amount, amount);
+ }
+ }
+ } else {
+ // {this, right} is the pair with the angle
+ // closest to 180 degrees
+ vertex.smooth = true;
+ }
+ } else {
+ // there might be 2-color, then vertex.smooth = true
+
+ // or it might be 1-color and doesn't matter,
+ // because the current node will disappear
+ vertex.smooth
+ = !( bottom(a_it) ^ bottom(b_it) );
+
+ if ( vertex.smooth ) {
+ vertex.smooth
+ = same_color(a_it->rgba, b_it->rgba)
+ + same_color(a_it->rgba, c_it->rgba)
+ + same_color(d_it->rgba, b_it->rgba)
+ + same_color(d_it->rgba, c_it->rgba) == 2;
+ }
+ }
+ } else if ( bottom(a_it) ) {
+ // this and bottom are connected
+
+ if ( !bottom(b_it) && !( right(a_it) && right(c_it) ) ) {
+ // right and bottom-right are disconnected
+
+ bool foreign_is_contour = contour_edge(b_it->rgba, d_it->rgba);
+ bool twin_is_contour = contour_edge(c_it->rgba, d_it->rgba);
+ bool another_is_contour = contour_edge(a_it->rgba, b_it->rgba);
+
+ if ( another_is_contour + twin_is_contour
+ + foreign_is_contour == 2 ) {
+ vertex.smooth = !foreign_is_contour;
+
+ if ( !vertex.smooth ) {
+ if ( another_is_contour ) {
+ cells_it->vertices.push_back(vertex);
+ {
+ Point<T> another = vertex;
+ T amount = 0.1875 * topleft(b_it);
+ transform(another, - amount,
+ - ( 0.75 - amount ));
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625 * topleft(b_it);
+ transform(another, - amount,
+ - ( 0.25 - amount ));
+ another.smooth = true;
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625 * bottomright(b_it);
+ transform(another, 0.25 - amount, amount);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ T amount = 0.125
+ - (bottomright(b_it) + topleft(b_it))
+ * 0.03125;
+ transform(vertex, amount, - amount);
+ }
+ } else if ( twin_is_contour ) {
+ T amount = 0.125
+ - ( ( topright(d_it) + bottomleft(d_it) )
+ * 0.03125 );
+ transform(vertex, amount, amount);
+ }
+ }
+ } else {
+ // {this, bottom} is the pair with the angle
+ // closest to 180 degrees
+ vertex.smooth = true;
+ }
+ } else {
+ // there might be 2-color, then vertex.smooth = true
+
+ // or it might be 1-color and doesn't matter,
+ // because the current node will disappear
+ vertex.smooth = !( right(a_it) ^ right(c_it) );
+
+ if ( vertex.smooth ) {
+ vertex.smooth
+ = same_color(a_it->rgba, c_it->rgba)
+ + same_color(a_it->rgba, b_it->rgba)
+ + same_color(d_it->rgba, b_it->rgba)
+ + same_color(d_it->rgba, c_it->rgba) == 2;
+ }
+ }
+ } else if ( bottom(b_it) ) {
+ // right and bottom-right are connected
+
+ bool special = false;
+
+ bool foreign_is_contour = contour_edge(c_it->rgba, d_it->rgba);
+
+ // the neighbor similar in 90° feature
+ bool similar_neighbor_is_contour
+ = contour_edge(a_it->rgba, c_it->rgba);
+
+ if ( contour_edge(a_it->rgba, b_it->rgba)
+ + similar_neighbor_is_contour
+ + foreign_is_contour == 2 ) {
+ vertex.smooth = !foreign_is_contour;
+
+ if ( !vertex.smooth ) {
+ if ( similar_neighbor_is_contour ) {
+ {
+ Point<T> another = vertex;
+ T amount = 0.125
+ - ( topleft(c_it) + bottomright(c_it) )
+ * 0.03125;
+ transform(another, - amount, amount);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625 * bottomright(c_it);
+ transform(another, amount, 0.25 - amount);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625 * topleft(c_it);
+ transform(another, - ( 0.25 - amount ), - amount);
+ another.smooth = true;
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.1875 * topleft(c_it);
+ transform(another, - ( 0.75 - amount ), - amount);
+ cells_it->vertices.push_back(another);
+ }
+ } else {
+ special = true;
+ }
+ }
+ } else {
+ // {right, bottom-right} is the pair with the
+ // angle closest to 180 degrees
+ vertex.smooth = false;
+
+ special = true;
+ }
+
+ if ( special ) {
+ cells_it->vertices.push_back(vertex);
+ {
+ Point<T> another = vertex;
+ T amount = 0.1875;
+ transform(another,
+ - ( topleft(b_it) - topright(a_it) ) * amount,
+ // y
+ - ( 0.75
+ - ( topleft(b_it) + topright(a_it) )
+ * amount ));
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625;
+ transform(another,
+ - ( topleft(b_it) - topright(a_it) ) * amount,
+ // y
+ - ( 0.25
+ - ( topleft(b_it) + topright(a_it) )
+ * amount ));
+ another.smooth = true;
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625;
+ transform(another, - amount
+ * ( bottomleft(d_it) - bottomright(c_it) ),
+ // y
+ 0.25 - amount
+ * ( bottomleft(d_it) + bottomright(c_it) ));
+ cells_it->vertices.push_back(another);
+ }
+ {
+ transform(vertex,
+ - ( topleft(b_it) + bottomleft(d_it)
+ - topright(a_it) - bottomright(c_it) )
+ * 0.03125,
+ // y
+ ( topleft(b_it) - bottomleft(d_it)
+ + topright(a_it) - bottomright(c_it) )
+ * 0.03125);
+ }
+ }
+ } else if ( right(c_it) ) {
+ // bottom and bottom-right are connected
+
+ bool special = false;
+
+ bool foreign_is_contour = contour_edge(b_it->rgba, d_it->rgba);
+
+ // the neighbor similar in 90° feature
+ bool similar_neighbor_is_contour
+ = contour_edge(a_it->rgba, b_it->rgba);
+
+ if ( contour_edge(a_it->rgba, c_it->rgba)
+ + similar_neighbor_is_contour
+ + foreign_is_contour == 2 ) {
+ vertex.smooth = !foreign_is_contour;
+
+ if ( !vertex.smooth ) {
+ if ( similar_neighbor_is_contour ) {
+ cells_it->vertices.push_back(vertex);
+ {
+ Point<T> another = vertex;
+ T amount = 0.1875 * topleft(b_it);
+ transform(another, - amount, - ( 0.75 - amount ));
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625 * topleft(b_it);
+ transform(another, - amount, - ( 0.25 - amount ));
+ another.smooth = true;
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625 * bottomright(b_it);
+ transform(another, 0.25 - amount, amount);
+ cells_it->vertices.push_back(another);
+ }
+ {
+ T amount = 0.125
+ - 0.03125 * (topleft(b_it) + bottomright(b_it));
+ transform(vertex, amount, - amount);
+ }
+ } else {
+ special = true;
+ }
+ }
+ } else {
+ // {bottom, bottom-right} is the pair with the
+ // angle closest to 180 degrees
+ vertex.smooth = false;
+
+ special = true;
+ }
+
+ if ( special ) {
+ {
+ Point<T> another = vertex;
+ T amount = 0.03125;
+ transform(another,
+ amount
+ * ( topleft(c_it) - topright(d_it)
+ + bottomleft(a_it) - bottomright(b_it) ),
+ // y
+ - amount
+ * ( topleft(c_it) + topright(d_it)
+ - bottomleft(a_it) - bottomright(b_it) ));
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625;
+ transform(another,
+ 0.25 - amount
+ * ( topright(d_it) + bottomright(b_it) ),
+ // y
+ - amount
+ * ( topright(d_it) - bottomright(b_it) ));
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.0625;
+ transform(another,
+ - ( 0.25 - amount
+ * ( topleft(c_it) + bottomleft(a_it) ) ),
+ // y
+ - amount
+ * ( topleft(c_it) - bottomleft(a_it) ));
+ another.smooth = true;
+ cells_it->vertices.push_back(another);
+ }
+ {
+ Point<T> another = vertex;
+ T amount = 0.1875;
+ transform(another,
+ - ( 0.75 - amount
+ * ( topleft(c_it) + bottomleft(a_it) ) ),
+ // y
+ - amount
+ * ( topleft(c_it) - bottomleft(a_it) ));
+ cells_it->vertices.push_back(another);
+ }
+ }
+ } else {
+ // there is a 4-color pattern, where the current node
+ // won't be smooth
+ vertex.smooth = false;
+ }
+
+ cells_it->vertices.push_back(vertex);
+ }
+}
+
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_SIMPLIFIEDVORONOI_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/priv/splines.h b/src/libdepixelize/priv/splines.h
new file mode 100644
index 000000000..c7ef2b492
--- /dev/null
+++ b/src/libdepixelize/priv/splines.h
@@ -0,0 +1,155 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_SPLINES_PRIV_H
+#define LIBDEPIXELIZE_TRACER_SPLINES_PRIV_H
+
+#include "../splines.h"
+#include "homogeneoussplines.h"
+
+namespace Tracer {
+
+template<class T>
+Geom::Point to_geom_point(Point<T> p)
+{
+ return Geom::Point(p.x, p.y);
+}
+
+template<class T>
+Geom::Path worker_helper(const std::vector< Point<T> > &source, bool optimize)
+{
+ typedef Geom::LineSegment Line;
+ typedef Geom::QuadraticBezier Quad;
+ typedef typename std::vector< Point<T> >::const_iterator iterator;
+
+ iterator it = source.begin();
+ Geom::Path ret(to_geom_point((source.back() + *it) / 2));
+
+ for ( iterator end = --source.end() ; it != end ; ++it ) {
+ Point<T> next = *(it + 1);
+ Point<T> middle = (*it + next) / 2;
+
+ if ( !it->smooth ) {
+ // TODO: remove redundant colinear points
+ ret.appendNew<Line>(Geom::Point(it->x, it->y));
+ ret.appendNew<Line>(Geom::Point(middle.x, middle.y));
+ } else {
+ ret.appendNew<Quad>(Geom::Point(it->x, it->y),
+ Geom::Point(middle.x, middle.y));
+ }
+ }
+
+ {
+ Point<T> next = source.front();
+ Point<T> middle = (*it + next) / 2;
+
+ if ( !it->smooth ) {
+ ret.appendNew<Line>(Geom::Point(it->x, it->y));
+ ret.appendNew<Line>(Geom::Point(middle.x, middle.y));
+ } else {
+ ret.appendNew<Quad>(Geom::Point(it->x, it->y),
+ Geom::Point(middle.x, middle.y));
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * It should be used by worker threads. Convert only one object.
+ */
+template<class T>
+void worker(const typename HomogeneousSplines<T>::Polygon &source,
+ Splines::Path &dest, bool optimize)
+{
+ dest.pathVector.reserve(source.holes.size() + 1);
+
+ for ( int i = 0 ; i != 4 ; ++i )
+ dest.rgba[i] = source.rgba[i];
+
+ dest.pathVector.push_back(worker_helper(source.vertices, optimize));
+
+ for ( typename std::vector< std::vector< Point<T> > >::const_iterator
+ it = source.holes.begin(), end = source.holes.end()
+ ; it != end ; ++it ) {
+ dest.pathVector.push_back(worker_helper(*it, optimize));
+ }
+}
+
+template<typename T>
+Splines::Splines(const SimplifiedVoronoi<T> &diagram)
+{
+ _paths.reserve(diagram.size());
+
+ for ( typename SimplifiedVoronoi<T>::const_iterator it = diagram.begin()
+ , end = diagram.end() ; it != end ; ++it ) {
+ Path path;
+
+ path.pathVector
+ .push_back(Geom::Path(to_geom_point(it->vertices.front())));
+
+ for ( typename std::vector< Point<T> >::const_iterator
+ it2 = it->vertices.begin(), end2 = it->vertices.end()
+ ; it2 != end2 ; ++it2 ) {
+ path.pathVector.back()
+ .appendNew<Geom::LineSegment>(Geom::Point(it2->x, it2->y));
+ }
+
+ for ( int i = 0 ; i != 4 ; ++i )
+ path.rgba[i] = it->rgba[i];
+
+ _paths.push_back(path);
+ }
+}
+
+template<class T>
+Splines::Splines(const HomogeneousSplines<T> &homogeneousSplines,
+ bool optimize, int nthreads) :
+ _paths(homogeneousSplines.size()),
+ _width(homogeneousSplines.width()),
+ _height(homogeneousSplines.height())
+{
+ // TODO: It should be threaded
+ iterator paths_it = begin();
+ for ( typename HomogeneousSplines<T>::const_iterator
+ it = homogeneousSplines.begin(), end = homogeneousSplines.end()
+ ; it != end ; ++it, ++paths_it ) {
+ worker<T>(*it, *paths_it, optimize);
+ }
+}
+
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_SPLINES_PRIV_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libdepixelize/splines.h b/src/libdepixelize/splines.h
new file mode 100644
index 000000000..c4b455aae
--- /dev/null
+++ b/src/libdepixelize/splines.h
@@ -0,0 +1,120 @@
+/* This file is part of the libdepixelize project
+ Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+
+ GNU Lesser General Public License Usage
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2.1 of the License, or (at your
+ option) any later version.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ GNU General Public License Usage
+ Alternatively, this library may be used 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.
+ You should have received a copy of the GNU General Public License along with
+ this library. If not, see <http://www.gnu.org/licenses/>.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBDEPIXELIZE_TRACER_SPLINES_H
+#define LIBDEPIXELIZE_TRACER_SPLINES_H
+
+#include <2geom/pathvector.h>
+#include <glib.h>
+
+namespace Tracer {
+
+template<typename T>
+class SimplifiedVoronoi;
+
+template<typename T>
+class HomogeneousSplines;
+
+class Splines
+{
+public:
+ struct Path
+ {
+ /**
+ * It may be benefited from C++11 move references.
+ */
+ Geom::PathVector pathVector;
+
+ guint8 rgba[4];
+ };
+
+ typedef std::vector<Path>::iterator iterator;
+ typedef std::vector<Path>::const_iterator const_iterator;
+
+ Splines() /* = default */ {}
+
+ template<typename T>
+ Splines(const SimplifiedVoronoi<T> &simplifiedVoronoi);
+
+ /**
+ * There are two levels of optimization. The first level only removes
+ * redundant points of colinear points. The second level uses the
+ * Kopf-Lischinski optimization. The first level is always enabled.
+ * The second level is enabled using \p optimize.
+ */
+ template<typename T>
+ Splines(const HomogeneousSplines<T> &homogeneousSplines, bool optimize,
+ int nthreads);
+
+ // Iterators
+ iterator begin()
+ {
+ return _paths.begin();
+ }
+
+ const_iterator begin() const
+ {
+ return _paths.begin();
+ }
+
+ iterator end()
+ {
+ return _paths.end();
+ }
+
+ const_iterator end() const
+ {
+ return _paths.end();
+ }
+
+ int width() const
+ {
+ return _width;
+ }
+
+ int height() const
+ {
+ return _height;
+ }
+
+private:
+ std::vector<Path> _paths;
+ int _width;
+ int _height;
+};
+
+} // namespace Tracer
+
+#endif // LIBDEPIXELIZE_TRACER_SPLINES_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp
index 1b2704a7e..7ea089c93 100644
--- a/src/libnrtype/Layout-TNG-Compute.cpp
+++ b/src/libnrtype/Layout-TNG-Compute.cpp
@@ -706,7 +706,7 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){
if (newcluster){
// find where the text ends for this log_cluster
end_byte = it_span->start.iter_span->text_bytes; // Upper limit
- for(unsigned next_glyph_index = glyph_index+1; next_glyph_index < it_span->end_glyph_index; next_glyph_index++){
+ for(int next_glyph_index = glyph_index+1; next_glyph_index < unbroken_span.glyph_string->num_glyphs; next_glyph_index++){
if(unbroken_span.glyph_string->glyphs[next_glyph_index].attr.is_cluster_start){
end_byte = unbroken_span.glyph_string->log_clusters[next_glyph_index];
break;
diff --git a/src/livarot/PathStroke.cpp b/src/livarot/PathStroke.cpp
index cdd5cae6d..50c335176 100644
--- a/src/livarot/PathStroke.cpp
+++ b/src/livarot/PathStroke.cpp
@@ -456,19 +456,20 @@ Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
} else {*/
leftStNo = dest->AddPoint (pos + width * pnor);
leftEnNo = dest->AddPoint (pos + width * nnor);
- int midNo = dest->AddPoint (pos);
- int nEdge=dest->AddEdge (leftEnNo, midNo);
- if ( dest->hasBackData() ) {
- dest->ebData[nEdge].pathID=pathID;
- dest->ebData[nEdge].pieceID=pieceID;
- dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
- }
- nEdge=dest->AddEdge (midNo, leftStNo);
+// int midNo = dest->AddPoint (pos);
+// int nEdge=dest->AddEdge (leftEnNo, midNo);
+ int nEdge=dest->AddEdge (leftEnNo, leftStNo);
if ( dest->hasBackData() ) {
dest->ebData[nEdge].pathID=pathID;
dest->ebData[nEdge].pieceID=pieceID;
dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
}
+// nEdge=dest->AddEdge (midNo, leftStNo);
+// if ( dest->hasBackData() ) {
+// dest->ebData[nEdge].pathID=pathID;
+// dest->ebData[nEdge].pieceID=pieceID;
+// dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+// }
// }
}
else
@@ -678,19 +679,20 @@ Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
} else {*/
rightStNo = dest->AddPoint (pos - width*pnor);
rightEnNo = dest->AddPoint (pos - width*nnor);
- int midNo = dest->AddPoint (pos);
- int nEdge=dest->AddEdge (rightStNo, midNo);
+// int midNo = dest->AddPoint (pos);
+// int nEdge=dest->AddEdge (rightStNo, midNo);
+ int nEdge=dest->AddEdge (rightStNo, rightEnNo);
if ( dest->hasBackData() ) {
dest->ebData[nEdge].pathID=pathID;
dest->ebData[nEdge].pieceID=pieceID;
dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
}
- nEdge=dest->AddEdge (midNo, rightEnNo);
- if ( dest->hasBackData() ) {
- dest->ebData[nEdge].pathID=pathID;
- dest->ebData[nEdge].pieceID=pieceID;
- dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
- }
+// nEdge=dest->AddEdge (midNo, rightEnNo);
+// if ( dest->hasBackData() ) {
+// dest->ebData[nEdge].pathID=pathID;
+// dest->ebData[nEdge].pieceID=pieceID;
+// dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+// }
// }
}
}
diff --git a/src/livarot/ShapeSweep.cpp b/src/livarot/ShapeSweep.cpp
index ff58b4a71..b04b36bfd 100644
--- a/src/livarot/ShapeSweep.cpp
+++ b/src/livarot/ShapeSweep.cpp
@@ -2740,8 +2740,7 @@ Shape::CheckAdjacencies (int lastPointNo, int lastChgtPt, Shape * /*shapeHead*/,
if (TesteAdjacency (lS, lB, getPoint(n).x, n, false) ==
false)
break;
- if (getPoint(lS->swsData[lB].leftRnd).x[0] > getPoint(n).x[0] + HalfRound (1)) // LP Bug 614577
- lS->swsData[lB].leftRnd = n;
+ lS->swsData[lB].leftRnd = n;
}
for (int n = rgtN + 1; n < lastPointNo; n++)
{
@@ -2767,8 +2766,7 @@ Shape::CheckAdjacencies (int lastPointNo, int lastChgtPt, Shape * /*shapeHead*/,
if (TesteAdjacency (rS, rB, getPoint(n).x, n, false) ==
false)
break;
- if (getPoint(rS->swsData[rB].leftRnd).x[0] > getPoint(n).x[0] + HalfRound (1)) // LP Bug 614577
- rS->swsData[rB].leftRnd = n;
+ rS->swsData[rB].leftRnd = n;
}
for (int n = rgtN + 1; n < lastPointNo; n++)
{
diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h
index 5b141902b..2334a08c1 100644
--- a/src/menus-skeleton.h
+++ b/src/menus-skeleton.h
@@ -222,6 +222,7 @@ static char const menus_skeleton[] =
" <verb verb-id=\"ObjectToPath\" />\n"
" <verb verb-id=\"StrokeToPath\" />\n"
" <verb verb-id=\"SelectionTrace\" />\n"
+" <verb verb-id=\"SelectionPixelArt\" />\n"
" <separator/>\n"
" <verb verb-id=\"SelectionUnion\" />\n"
" <verb verb-id=\"SelectionDiff\" />\n"
diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp
index 6aacc3f15..91b99e3f4 100644
--- a/src/selection-chemistry.cpp
+++ b/src/selection-chemistry.cpp
@@ -96,6 +96,7 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS;
#include "uri-references.h"
#include "display/curve.h"
#include "display/canvas-bpath.h"
+#include "display/cairo-utils.h"
#include "inkscape-private.h"
#include "path-chemistry.h"
#include "ui/tool/control-point-selection.h"
@@ -3480,9 +3481,10 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop)
}
// Import the image back
- GdkPixbuf *pb = gdk_pixbuf_new_from_file(filepath, NULL);
+ Inkscape::Pixbuf *pb = Inkscape::Pixbuf::create_from_file(filepath);
if (pb) {
// Create the repr for the image
+ // TODO: avoid unnecessary roundtrip between data URI and decoded pixbuf
Inkscape::XML::Node * repr = xml_doc->createElement("svg:image");
sp_embed_image(repr, pb);
if (res == Inkscape::Util::Quantity::convert(1, "in", "px")) { // for default 90 dpi, snap it to pixel grid
diff --git a/src/sp-image.cpp b/src/sp-image.cpp
index b0a094950..80daf33c3 100644
--- a/src/sp-image.cpp
+++ b/src/sp-image.cpp
@@ -17,9 +17,6 @@
# include "config.h"
#endif
-// This has to be included prior to anything that includes setjmp.h, it croaks otherwise
-#include <png.h>
-
#include <cstring>
#include <algorithm>
#include <string>
@@ -43,8 +40,9 @@
#include "xml/repr.h"
#include "snap-candidate.h"
#include "preferences.h"
-
#include "io/sys.h"
+#include "sp-factory.h"
+
#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
#include "cms-system.h"
#include "color-profile.h"
@@ -77,8 +75,7 @@
static void sp_image_set_curve(SPImage *image);
-static GdkPixbuf *sp_image_repr_read_image( time_t& modTime, gchar*& pixPath, const gchar *href, const gchar *absref, const gchar *base );
-static GdkPixbuf *sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf);
+static Inkscape::Pixbuf *sp_image_repr_read_image(gchar const *href, gchar const *absref, gchar const *base );
static void sp_image_update_arenaitem (SPImage *img, Inkscape::DrawingImage *ai);
static void sp_image_update_canvas_image (SPImage *image);
static GdkPixbuf * sp_image_repr_read_dataURI (const gchar * uri_data);
@@ -117,78 +114,16 @@ extern guint update_in_progress;
#define DEBUG_MESSAGE_SCISLAC(key, ...)
#endif // DEBUG_LCMS
-namespace Inkscape {
-namespace IO {
-
-GdkPixbuf* pixbuf_new_from_file(const char *filename, time_t &modTime, gchar*& pixPath)
-{
- GdkPixbuf* buf = NULL;
- modTime = 0;
- if ( pixPath ) {
- g_free(pixPath);
- pixPath = NULL;
- }
-
- //test correctness of filename
- if (!g_file_test (filename, G_FILE_TEST_EXISTS)){
- return NULL;
- }
- struct stat stdir;
- int val = g_stat(filename, &stdir);
- if (stdir.st_mode & S_IFDIR){
- g_warning("Linked image file %s is a directory", filename);
- return NULL;
- }
-
- // we need to load the entire pixbuf into memory
- gchar *data = NULL;
- gsize len = 0;
-
- if (g_file_get_contents(filename, &data, &len, NULL)) {
- if (!val) {
- modTime = stdir.st_mtime;
- pixPath = g_strdup(filename);
- }
-
- GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
- gdk_pixbuf_loader_write(loader, (guchar *) data, len, NULL);
- gdk_pixbuf_loader_close(loader, NULL);
-
- buf = gdk_pixbuf_loader_get_pixbuf(loader);
- if (buf) {
- g_object_ref(buf);
- buf = sp_image_pixbuf_force_rgba(buf);
- pixbuf_set_mime_data(buf, (guchar *) data, len, gdk_pixbuf_loader_get_format(loader));
- } else {
- g_free(data);
- g_warning("Error loading pixbuf");
- }
-
- // TODO: we could also read DPI, ICC profile, gamma correction, and other information
- // from the file. This can be done by using format-specific libraries e.g. libpng.
- } else {
- g_warning("Unable to open linked file: %s", filename);
- }
-
- return buf;
-}
-
-}
-}
-
-
-#include "sp-factory.h"
-
namespace {
- SPObject* createImage() {
- return new SPImage();
- }
+SPObject* createImage() {
+ return new SPImage();
+}
- bool imageRegistered = SPFactory::instance().registerObject("svg:image", createImage);
+bool imageRegistered = SPFactory::instance().registerObject("svg:image", createImage);
}
SPImage::SPImage() : SPItem() {
- this->aspect_clip = 0;
+ this->aspect_clip = 0;
this->x.unset();
this->y.unset();
@@ -206,15 +141,13 @@ SPImage::SPImage() : SPItem() {
this->color_profile = 0;
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
this->pixbuf = 0;
- this->pixPath = 0;
- this->lastMod = 0;
}
SPImage::~SPImage() {
}
void SPImage::build(SPDocument *document, Inkscape::XML::Node *repr) {
- SPItem::build(document, repr);
+ SPItem::build(document, repr);
this->readAttr( "xlink:href" );
this->readAttr( "x" );
@@ -231,7 +164,7 @@ void SPImage::build(SPDocument *document, Inkscape::XML::Node *repr) {
void SPImage::release() {
if (this->document) {
// Unregister ourselves
- this->document->removeResource("image", this);
+ this->document->removeResource("image", this);
}
if (this->href) {
@@ -239,11 +172,8 @@ void SPImage::release() {
this->href = NULL;
}
- if (this->pixbuf) {
- g_object_set_data(G_OBJECT(this->pixbuf), "cairo_surface", NULL);
- g_object_unref (this->pixbuf);
- this->pixbuf = NULL;
- }
+ delete this->pixbuf;
+ this->pixbuf = NULL;
#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
if (this->color_profile) {
@@ -252,11 +182,6 @@ void SPImage::release() {
}
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
- if (this->pixPath) {
- g_free(this->pixPath);
- this->pixPath = 0;
- }
-
if (this->curve) {
this->curve = this->curve->unref();
}
@@ -275,7 +200,7 @@ void SPImage::set(unsigned int key, const gchar* value) {
case SP_ATTR_X:
if (!this->x.readAbsolute(value)) {
/* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
- this->x.unset();
+ this->x.unset();
}
this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
@@ -284,7 +209,7 @@ void SPImage::set(unsigned int key, const gchar* value) {
case SP_ATTR_Y:
if (!this->y.readAbsolute(value)) {
/* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
- this->y.unset();
+ this->y.unset();
}
this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
@@ -293,7 +218,7 @@ void SPImage::set(unsigned int key, const gchar* value) {
case SP_ATTR_WIDTH:
if (!this->width.readAbsolute(value)) {
/* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
- this->width.unset();
+ this->width.unset();
}
this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
@@ -302,7 +227,7 @@ void SPImage::set(unsigned int key, const gchar* value) {
case SP_ATTR_HEIGHT:
if (!this->height.readAbsolute(value)) {
/* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
- this->height.unset();
+ this->height.unset();
}
this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
@@ -310,7 +235,7 @@ void SPImage::set(unsigned int key, const gchar* value) {
case SP_ATTR_PRESERVEASPECTRATIO:
/* Do setup before, so we can use break to escape */
- this->aspect_align = SP_ASPECT_NONE;
+ this->aspect_align = SP_ASPECT_NONE;
this->aspect_clip = SP_ASPECT_MEET;
this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
@@ -406,28 +331,13 @@ void SPImage::update(SPCtx *ctx, unsigned int flags) {
SPItem::update(ctx, flags);
if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
- if (this->pixbuf) {
- g_object_unref (this->pixbuf);
- this->pixbuf = NULL;
- }
-
- if ( this->pixPath ) {
- g_free(this->pixPath);
- this->pixPath = 0;
- }
-
- this->lastMod = 0;
-
+ delete this->pixbuf;
+ this->pixbuf = NULL;
+
if (this->href) {
- GdkPixbuf *pixbuf;
+ Inkscape::Pixbuf *pixbuf = NULL;
pixbuf = sp_image_repr_read_image (
- this->lastMod,
- this->pixPath,
-
- //XML Tree being used directly while it shouldn't be.
this->getRepr()->attribute("xlink:href"),
-
- //XML Tree being used directly while it shouldn't be.
this->getRepr()->attribute("sodipodi:absref"),
doc->getBase());
@@ -436,10 +346,13 @@ void SPImage::update(SPCtx *ctx, unsigned int flags) {
#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
if ( this->color_profile )
{
- int imagewidth = gdk_pixbuf_get_width( pixbuf );
- int imageheight = gdk_pixbuf_get_height( pixbuf );
- int rowstride = gdk_pixbuf_get_rowstride( pixbuf );
- guchar* px = gdk_pixbuf_get_pixels( pixbuf );
+ // TODO: this will prevent using MIME data when exporting.
+ // Integrate color correction into loading.
+ pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_GDK);
+ int imagewidth = pixbuf->width();
+ int imageheight = pixbuf->height();
+ int rowstride = pixbuf->rowstride();;
+ guchar* px = pixbuf->pixels();
if ( px ) {
DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" );
@@ -508,23 +421,23 @@ void SPImage::update(SPCtx *ctx, unsigned int flags) {
if (this->pixbuf) {
/* fixme: We are slightly violating spec here (Lauris) */
if (!this->width._set) {
- this->width.computed = gdk_pixbuf_get_width(this->pixbuf);
+ this->width.computed = this->pixbuf->width();
}
-
+
if (!this->height._set) {
- this->height.computed = gdk_pixbuf_get_height(this->pixbuf);
+ this->height.computed = this->pixbuf->height();
}
}
- Geom::Point p(this->x.computed, this->y.computed);
- Geom::Point wh(this->width.computed, this->height.computed);
- this->clipbox = Geom::Rect(p, p + wh);
+ this->clipbox = Geom::Rect::from_xywh(
+ this->x.computed, this->y.computed,
+ this->width.computed, this->height.computed);
this->ox = this->x.computed;
this->oy = this->y.computed;
- int pixwidth = gdk_pixbuf_get_width (this->pixbuf);
- int pixheight = gdk_pixbuf_get_height (this->pixbuf);
+ int pixwidth = this->pixbuf->width();
+ int pixheight = this->pixbuf->height();
this->sx = this->width.computed / pixwidth;
this->sy = this->height.computed / pixheight;
@@ -656,16 +569,15 @@ Geom::OptRect SPImage::bbox(Geom::Affine const &transform, SPItem::BBoxType type
void SPImage::print(SPPrintContext *ctx) {
if (this->pixbuf && (this->width.computed > 0.0) && (this->height.computed > 0.0) ) {
- GdkPixbuf *pb = gdk_pixbuf_copy(this->pixbuf);
- // GObject data is not copied, so we have to set the pixel format explicitly
- g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("argb32"), g_free);
- ink_pixbuf_ensure_normal(pb);
+ Inkscape::Pixbuf *pb = new Inkscape::Pixbuf(*this->pixbuf);
+ pb->ensurePixelFormat(Inkscape::Pixbuf::PF_GDK);
- guchar *px = gdk_pixbuf_get_pixels(pb);
- int w = gdk_pixbuf_get_width(pb);
- int h = gdk_pixbuf_get_height(pb);
- int rs = gdk_pixbuf_get_rowstride(pb);
- int pixskip = gdk_pixbuf_get_n_channels(pb) * gdk_pixbuf_get_bits_per_sample(pb) / 8;
+ guchar *px = pb->pixels();
+ int w = pb->width();
+ int h = pb->height();
+ int rs = pb->rowstride();
+ //int pixskip = gdk_pixbuf_get_n_channels(pb) * gdk_pixbuf_get_bits_per_sample(pb) / 8;
+ int pixskip = 4;
if (this->aspect_align == SP_ASPECT_NONE) {
Geom::Affine t;
@@ -697,7 +609,7 @@ void SPImage::print(SPPrintContext *ctx) {
t = ti * t;
sp_print_image_R8G8B8A8_N(ctx, px + trimx*pixskip + trimy*rs, trimwidth, trimheight, rs, t, this->style);
}
- g_object_unref(pb);
+ delete pb;
}
}
@@ -716,8 +628,8 @@ gchar* SPImage::description() {
char *ret = ( this->pixbuf == NULL
? g_strdup_printf(_("<b>Image with bad reference</b>: %s"), href_desc)
: g_strdup_printf(_("<b>Image</b> %d &#215; %d: %s"),
- gdk_pixbuf_get_width(this->pixbuf),
- gdk_pixbuf_get_height(this->pixbuf),
+ this->pixbuf->width(),
+ this->pixbuf->height(),
href_desc) );
g_free(href_desc);
return ret;
@@ -731,22 +643,9 @@ Inkscape::DrawingItem* SPImage::show(Inkscape::Drawing &drawing, unsigned int ke
return ai;
}
-/*
- * utility function to try loading image from href
- *
- * docbase/relative_src
- * absolute_src
- *
- */
-
-GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gchar *href, const gchar *absref, const gchar *base )
+Inkscape::Pixbuf *sp_image_repr_read_image(gchar const *href, gchar const *absref, gchar const *base)
{
- GdkPixbuf *pixbuf = 0;
- modTime = 0;
- if ( pixPath ) {
- g_free(pixPath);
- pixPath = 0;
- }
+ Inkscape::Pixbuf *inkpb = 0;
gchar const *filename = href;
@@ -754,18 +653,18 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha
if (strncmp (filename,"file:",5) == 0) {
gchar *fullname = g_filename_from_uri(filename, NULL, NULL);
if (fullname) {
- pixbuf = Inkscape::IO::pixbuf_new_from_file(fullname, modTime, pixPath);
+ inkpb = Inkscape::Pixbuf::create_from_file(fullname);
g_free(fullname);
- if (pixbuf != NULL) {
- return pixbuf;
+ if (inkpb != NULL) {
+ return inkpb;
}
}
} else if (strncmp (filename,"data:",5) == 0) {
/* data URI - embedded image */
filename += 5;
- pixbuf = sp_image_repr_read_dataURI (filename);
- if (pixbuf != NULL) {
- return pixbuf;
+ inkpb = Inkscape::Pixbuf::create_from_data_uri(filename);
+ if (inkpb != NULL) {
+ return inkpb;
}
} else {
@@ -781,19 +680,19 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha
// different dir) or unset (when doc is not saved yet), so we check for base+href existence first,
// and if it fails, we also try to use bare href regardless of its g_path_is_absolute
if (g_file_test (fullname, G_FILE_TEST_EXISTS) && !g_file_test (fullname, G_FILE_TEST_IS_DIR)) {
- pixbuf = Inkscape::IO::pixbuf_new_from_file(fullname, modTime, pixPath);
+ inkpb = Inkscape::Pixbuf::create_from_file(fullname);
g_free (fullname);
- if (pixbuf != NULL) {
- return pixbuf;
+ if (inkpb != NULL) {
+ return inkpb;
}
}
}
/* try filename as absolute */
if (g_file_test (filename, G_FILE_TEST_EXISTS) && !g_file_test (filename, G_FILE_TEST_IS_DIR)) {
- pixbuf = Inkscape::IO::pixbuf_new_from_file(filename, modTime, pixPath);
- if (pixbuf != NULL) {
- return pixbuf;
+ inkpb = Inkscape::Pixbuf::create_from_file(filename);
+ if (inkpb != NULL) {
+ return inkpb;
}
}
}
@@ -809,31 +708,20 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha
g_warning ("xlink:href did not resolve to a valid image file, now trying sodipodi:absref=\"%s\"", absref);
}
- pixbuf = Inkscape::IO::pixbuf_new_from_file(filename, modTime, pixPath);
- if (pixbuf != NULL) {
- return pixbuf;
+ inkpb = Inkscape::Pixbuf::create_from_file(filename);
+ if (inkpb != NULL) {
+ return inkpb;
}
}
/* Nope: We do not find any valid pixmap file :-( */
- pixbuf = gdk_pixbuf_new_from_xpm_data((const gchar **) brokenimage_xpm);
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_xpm_data((const gchar **) brokenimage_xpm);
+ inkpb = new Inkscape::Pixbuf(pixbuf);
/* It should be included xpm, so if it still does not does load, */
/* our libraries are broken */
- g_assert (pixbuf != NULL);
+ g_assert (inkpb != NULL);
- return pixbuf;
-}
-
-static GdkPixbuf *sp_image_pixbuf_force_rgba( GdkPixbuf * pixbuf )
-{
- GdkPixbuf* result;
- if (gdk_pixbuf_get_has_alpha(pixbuf)) {
- result = pixbuf;
- } else {
- result = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
- g_object_unref(pixbuf);
- }
- return result;
+ return inkpb;
}
/* We assert that realpixbuf is either NULL or identical size to pixbuf */
@@ -841,7 +729,7 @@ static void
sp_image_update_arenaitem (SPImage *image, Inkscape::DrawingImage *ai)
{
ai->setStyle(SP_OBJECT(image)->style);
- ai->setARGB32Pixbuf(image->pixbuf);
+ ai->setPixbuf(image->pixbuf);
ai->setOrigin(Geom::Point(image->ox, image->oy));
ai->setScale(image->sx, image->sy);
ai->setClipbox(image->clipbox);
@@ -928,113 +816,6 @@ Geom::Affine SPImage::set_transform(Geom::Affine const &xform) {
return ret;
}
-static GdkPixbuf *sp_image_repr_read_dataURI( const gchar * uri_data )
-{
- GdkPixbuf * pixbuf = NULL;
-
- gint data_is_image = 0;
- gint data_is_base64 = 0;
-
- const gchar * data = uri_data;
-
- while (*data) {
- if (strncmp(data,"base64",6) == 0) {
- /* base64-encoding */
- data_is_base64 = 1;
- data_is_image = 1; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what
- data += 6;
- }
- else if (strncmp(data,"image/png",9) == 0) {
- /* PNG image */
- data_is_image = 1;
- data += 9;
- }
- else if (strncmp(data,"image/jpg",9) == 0) {
- /* JPEG image */
- data_is_image = 1;
- data += 9;
- }
- else if (strncmp(data,"image/jpeg",10) == 0) {
- /* JPEG image */
- data_is_image = 1;
- data += 10;
- }
- else { /* unrecognized option; skip it */
- while (*data) {
- if (((*data) == ';') || ((*data) == ',')) {
- break;
- }
- data++;
- }
- }
- if ((*data) == ';') {
- data++;
- continue;
- }
- if ((*data) == ',') {
- data++;
- break;
- }
- }
-
- if ((*data) && data_is_image && data_is_base64) {
- pixbuf = sp_image_repr_read_b64(data);
- }
-
- return pixbuf;
-}
-
-static GdkPixbuf *sp_image_repr_read_b64(gchar const *uri_data)
-{
- GdkPixbuf *pixbuf = NULL;
- GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
-
- if (!loader) return NULL;
-
- gsize decoded_len = 0;
- guchar *decoded = g_base64_decode(uri_data, &decoded_len);
-
- if (gdk_pixbuf_loader_write(loader, decoded, decoded_len, NULL)) {
- gdk_pixbuf_loader_close(loader, NULL);
- pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
- g_object_ref(pixbuf);
- pixbuf = sp_image_pixbuf_force_rgba(pixbuf);
- pixbuf_set_mime_data(pixbuf, decoded, decoded_len, gdk_pixbuf_loader_get_format(loader));
- } else {
- g_free(decoded);
- }
- g_object_unref(loader);
-
- return pixbuf;
-}
-
-// takes ownership of passed data
-static void pixbuf_set_mime_data(GdkPixbuf *pb, guchar *data, gsize len, GdkPixbufFormat *fmt)
-{
- cairo_surface_t *s = ink_cairo_surface_get_for_pixbuf(pb);
-
- gchar const *mimetype = NULL;
- gchar *fmt_name = gdk_pixbuf_format_get_name(fmt);
- Glib::ustring name = fmt_name;
- g_free(fmt_name);
-
- if (name == "jpeg") {
- mimetype = CAIRO_MIME_TYPE_JPEG;
- } else if (name == "jpeg2000") {
- mimetype = CAIRO_MIME_TYPE_JP2;
- } else if (name == "png") {
- mimetype = CAIRO_MIME_TYPE_PNG;
- }
-
- if (mimetype != NULL) {
- cairo_surface_set_mime_data(s, mimetype, data, len, g_free, data);
- //g_message("Setting Cairo MIME data: %s", mimetype);
- } else {
- g_free(data);
- //g_message("Not setting Cairo MIME data: unknown format %s", name.c_str());
- }
-}
-
static void sp_image_set_curve( SPImage *image )
{
//create a curve at the image's boundary for snapping
@@ -1070,52 +851,32 @@ SPCurve *sp_image_get_curve( SPImage *image )
return result;
}
-void sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb)
+void sp_embed_image(Inkscape::XML::Node *image_node, Inkscape::Pixbuf *pb)
{
- static gchar const *mimetypes[] = {
- CAIRO_MIME_TYPE_JPEG, CAIRO_MIME_TYPE_JP2, CAIRO_MIME_TYPE_PNG, NULL };
- static guint mimetypes_len = g_strv_length(const_cast<gchar**>(mimetypes));
-
bool free_data = false;
// check whether the pixbuf has MIME data
guchar *data = NULL;
gsize len = 0;
- gchar const *data_mimetype = NULL;
-
- cairo_surface_t *s = reinterpret_cast<cairo_surface_t*>(g_object_get_data(G_OBJECT(pb), "cairo_surface"));
- if (s) {
- for (guint i = 0; i < mimetypes_len; ++i) {
- unsigned long len_long = 0;
- cairo_surface_get_mime_data(s, mimetypes[i], const_cast<unsigned char const **>(&data), &len_long);
- len = len_long; // this assumes that the added range of long is not needed. the code below assumes gsize range of values is sufficient.
- if (data != NULL) {
- data_mimetype = mimetypes[i];
- break;
- }
- }
- }
+ std::string data_mimetype;
- if (data == NULL) {
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- Glib::ustring quality = Glib::ustring::format(prefs->getInt("/dialogs/import/quality", 100));
+ data = const_cast<guchar *>(pb->getMimeData(len, data_mimetype));
+ if (data == NULL) {
// if there is no supported MIME data, embed as PNG
data_mimetype = "image/png";
- ink_pixbuf_ensure_normal(pb);
- gdk_pixbuf_save_to_buffer(pb, reinterpret_cast<gchar**>(&data), &len, "png", NULL,
- "quality", quality.c_str(), NULL);
+ gdk_pixbuf_save_to_buffer(pb->getPixbufRaw(), reinterpret_cast<gchar**>(&data), &len, "png", NULL, NULL);
free_data = true;
}
// Save base64 encoded data in image node
// this formula taken from Glib docs
gsize needed_size = len * 4 / 3 + len * 4 / (3 * 72) + 7;
- needed_size += 5 + 8 + strlen(data_mimetype); // 5 bytes for data: + 8 for ;base64,
+ needed_size += 5 + 8 + data_mimetype.size(); // 5 bytes for data: + 8 for ;base64,
gchar *buffer = (gchar *) g_malloc(needed_size);
gchar *buf_work = buffer;
- buf_work += g_sprintf(buffer, "data:%s;base64,", data_mimetype);
+ buf_work += g_sprintf(buffer, "data:%s;base64,", data_mimetype.c_str());
gint state = 0;
gint save = 0;
@@ -1135,18 +896,18 @@ void sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb)
void sp_image_refresh_if_outdated( SPImage* image )
{
- if ( image->href && image->lastMod ) {
+ if ( image->href && image->pixbuf && image->pixbuf->modificationTime()) {
// It *might* change
struct stat st;
memset(&st, 0, sizeof(st));
int val = 0;
- if (g_file_test (image->pixPath, G_FILE_TEST_EXISTS)){
- val = g_stat(image->pixPath, &st);
+ if (g_file_test (image->pixbuf->originalPath().c_str(), G_FILE_TEST_EXISTS)){
+ val = g_stat(image->pixbuf->originalPath().c_str(), &st);
}
if ( !val ) {
// stat call worked. Check time now
- if ( st.st_mtime != image->lastMod ) {
+ if ( st.st_mtime != image->pixbuf->modificationTime() ) {
SPCtx *ctx = 0;
unsigned int flags = SP_IMAGE_HREF_MODIFIED_FLAG;
image->update(ctx, flags);
diff --git a/src/sp-image.h b/src/sp-image.h
index 9a229e5f5..bfc10e7f2 100644
--- a/src/sp-image.h
+++ b/src/sp-image.h
@@ -1,9 +1,6 @@
-#ifndef __SP_IMAGE_H__
-#define __SP_IMAGE_H__
-
-/*
+/** @file
* SVG <image> implementation
- *
+ *//*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* Edward Flick (EAF)
@@ -14,22 +11,24 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#define SP_IMAGE(obj) (dynamic_cast<SPImage*>((SPObject*)obj))
-#define SP_IS_IMAGE(obj) (dynamic_cast<const SPImage*>((SPObject*)obj) != NULL)
-
-/* SPImage */
+#ifndef SEEN_INKSCAPE_SP_IMAGE_H
+#define SEEN_INKSCAPE_SP_IMAGE_H
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <glibmm/ustring.h>
#include "svg/svg-length.h"
-#include "sp-item.h"
+#include "sp-shape.h"
+
+#define SP_IMAGE(obj) (dynamic_cast<SPImage*>((SPObject*)obj))
+#define SP_IS_IMAGE(obj) (dynamic_cast<const SPImage*>((SPObject*)obj) != NULL)
#define SP_IMAGE_HREF_MODIFIED_FLAG SP_OBJECT_USER_MODIFIED_FLAG_A
+namespace Inkscape { class Pixbuf; }
class SPImage : public SPItem {
public:
- SPImage();
- virtual ~SPImage();
+ SPImage();
+ virtual ~SPImage();
SVGLength x;
SVGLength y;
@@ -54,28 +53,26 @@ public:
gchar *color_profile;
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
- GdkPixbuf *pixbuf;
- gchar *pixPath;
- time_t lastMod;
+ Inkscape::Pixbuf *pixbuf;
- virtual void build(SPDocument *document, Inkscape::XML::Node *repr);
- virtual void release();
- virtual void set(unsigned int key, gchar const* value);
- virtual void update(SPCtx *ctx, guint flags);
- virtual Inkscape::XML::Node* write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
- virtual void modified(unsigned int flags);
+ virtual void build(SPDocument *document, Inkscape::XML::Node *repr);
+ virtual void release();
+ virtual void set(unsigned int key, gchar const* value);
+ virtual void update(SPCtx *ctx, guint flags);
+ virtual Inkscape::XML::Node* write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
+ virtual void modified(unsigned int flags);
- virtual Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type);
- virtual void print(SPPrintContext *ctx);
- virtual gchar* description();
- virtual Inkscape::DrawingItem* show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags);
+ virtual Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type);
+ virtual void print(SPPrintContext *ctx);
+ virtual gchar* description();
+ virtual Inkscape::DrawingItem* show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags);
virtual void snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs);
virtual Geom::Affine set_transform(Geom::Affine const &transform);
};
/* Return duplicate of curve or NULL */
SPCurve *sp_image_get_curve (SPImage *image);
-void sp_embed_image(Inkscape::XML::Node *imgnode, GdkPixbuf *pb);
+void sp_embed_image(Inkscape::XML::Node *imgnode, Inkscape::Pixbuf *pb);
void sp_image_refresh_if_outdated( SPImage* image );
#endif
diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp
index 36a42f704..010cc5449 100644
--- a/src/sp-item-group.cpp
+++ b/src/sp-item-group.cpp
@@ -720,7 +720,9 @@ sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write)
// only run LPEs when the shape has a curve defined
if (c) {
+ c->transform(i2anc_affine(subitem, topgroup));
sp_lpe_item_perform_path_effect(SP_LPE_ITEM(topgroup), c);
+ c->transform(i2anc_affine(subitem, topgroup).inverse());
SP_SHAPE(subitem)->setCurve(c, TRUE);
if (write) {
diff --git a/src/sp-item.cpp b/src/sp-item.cpp
index abff398e6..e6991a1fa 100644
--- a/src/sp-item.cpp
+++ b/src/sp-item.cpp
@@ -601,11 +601,9 @@ void SPItem::update(SPCtx *ctx, guint flags) {
}
}
}
-
/* Update bounding box data used by filters */
if (item->style->filter.set && item->display) {
Geom::OptRect item_bbox = item->visualBounds();
-
SPItemView *itemview = item->display;
do {
if (itemview->arenaitem)
diff --git a/src/sp-object.cpp b/src/sp-object.cpp
index b622d14e9..895b36e1c 100644
--- a/src/sp-object.cpp
+++ b/src/sp-object.cpp
@@ -140,15 +140,15 @@ SPObject::~SPObject() {
// CPPIFY: make pure virtual
void SPObject::read_content() {
- //throw;
+ //throw;
}
void SPObject::update(SPCtx* ctx, unsigned int flags) {
- //throw;
+ //throw;
}
void SPObject::modified(unsigned int flags) {
- //throw;
+ //throw;
}
namespace {
@@ -221,7 +221,7 @@ SPObject *sp_object_unref(SPObject *object, SPObject *owner)
object->refCount--;
if (object->refCount < 0) {
- delete object;
+ delete object;
}
return NULL;
@@ -575,25 +575,27 @@ SPObject *SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
}
void SPObject::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) {
- SPObject* object = this;
+ SPObject* object = this;
- try {
- const std::string typeString = NodeTraits::get_type_string(*child);
+ try {
+ const std::string typeString = NodeTraits::get_type_string(*child);
- SPObject* ochild = SPFactory::instance().createObject(typeString);
+ SPObject* ochild = SPFactory::instance().createObject(typeString);
- SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
- object->attach(ochild, prev);
- sp_object_unref(ochild, NULL);
+ SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
+ object->attach(ochild, prev);
+ sp_object_unref(ochild, NULL);
- ochild->invoke_build(object->document, child, object->cloned);
- } catch (const FactoryExceptions::TypeNotRegistered& e) {
- g_warning("TypeNotRegistered exception: %s", e.what());
- }
+ ochild->invoke_build(object->document, child, object->cloned);
+ } catch (const FactoryExceptions::TypeNotRegistered& e) {
+ if (std::string(e.what()) != "rdf:RDF") { // temporary special case
+ g_warning("TypeNotRegistered exception: %s", e.what());
+ }
+ }
}
void SPObject::release() {
- SPObject* object = this;
+ SPObject* object = this;
debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
while (object->children) {
@@ -602,7 +604,7 @@ void SPObject::release() {
}
void SPObject::remove_child(Inkscape::XML::Node* child) {
- SPObject* object = this;
+ SPObject* object = this;
debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
SPObject *ochild = object->get_child_by_repr(child);
@@ -613,7 +615,7 @@ void SPObject::remove_child(Inkscape::XML::Node* child) {
}
void SPObject::order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node * old_ref, Inkscape::XML::Node *new_ref) {
- SPObject* object = this;
+ SPObject* object = this;
SPObject *ochild = object->get_child_by_repr(child);
g_return_if_fail(ochild != NULL);
@@ -623,7 +625,7 @@ void SPObject::order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node * o
}
void SPObject::build(SPDocument *document, Inkscape::XML::Node *repr) {
- SPObject* object = this;
+ SPObject* object = this;
/* Nothing specific here */
debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
@@ -639,22 +641,22 @@ void SPObject::build(SPDocument *document, Inkscape::XML::Node *repr) {
// }
// SPObject *child = SP_OBJECT(g_object_new(type, 0));
-// SPObject* child = SPFactory::instance().createObject(*rchild);
-// if (!child) {
-// continue;
-// }
+// SPObject* child = SPFactory::instance().createObject(*rchild);
+// if (!child) {
+// continue;
+// }
- try {
- const std::string typeString = NodeTraits::get_type_string(*rchild);
+ try {
+ const std::string typeString = NodeTraits::get_type_string(*rchild);
- SPObject* child = SPFactory::instance().createObject(typeString);
+ SPObject* child = SPFactory::instance().createObject(typeString);
- object->attach(child, object->lastChild());
- sp_object_unref(child, NULL);
- child->invoke_build(document, rchild, object->cloned);
- } catch (const FactoryExceptions::TypeNotRegistered& e) {
- //g_warning("TypeNotRegistered exception: %s", e.what());
- }
+ object->attach(child, object->lastChild());
+ sp_object_unref(child, NULL);
+ child->invoke_build(document, rchild, object->cloned);
+ } catch (const FactoryExceptions::TypeNotRegistered& e) {
+ //g_warning("TypeNotRegistered exception: %s", e.what());
+ }
}
}
@@ -723,7 +725,7 @@ void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, uns
int SPObject::getIntAttribute(char const *key, int def)
{
sp_repr_get_int(getRepr(),key,&def);
- return def;
+ return def;
}
unsigned SPObject::getPosition(){
@@ -907,7 +909,7 @@ void SPObject::setKeyValue(unsigned int key, gchar const *value)
//g_assert(object != NULL);
//g_assert(SP_IS_OBJECT(object));
- this->set(key, value);
+ this->set(key, value);
}
void SPObject::readAttr(gchar const *key)
@@ -964,7 +966,7 @@ static gchar const *sp_xml_get_space_string(unsigned int space)
}
Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags) {
- SPObject* object = this;
+ SPObject* object = this;
if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
repr = object->getRepr()->duplicate(doc);
@@ -1066,10 +1068,10 @@ Inkscape::XML::Node * SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscap
return NULL;
}
- if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
- repr = getRepr();
- }
- return this->write(doc, repr, flags);
+ if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ repr = getRepr();
+ }
+ return this->write(doc, repr, flags);
}
@@ -1136,7 +1138,7 @@ void SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
try
{
- this->update(ctx, flags);
+ this->update(ctx, flags);
}
catch(...)
{
diff --git a/src/sp-skeleton.cpp b/src/sp-skeleton.cpp
deleted file mode 100644
index 83f2bc20d..000000000
--- a/src/sp-skeleton.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-/** \file
- * SVG <skeleton> implementation, used as an example for a base starting class
- * when implementing new sp-objects.
- *
- * In vi, three global search-and-replaces will let you rename everything
- * in this and the .h file:
- *
- * :%s/SKELETON/YOURNAME/g
- * :%s/Skeleton/Yourname/g
- * :%s/skeleton/yourname/g
- */
-/*
- * Authors:
- * Kees Cook <kees@outflux.net>
- * Abhishek Sharma
- *
- * Copyright (C) 2004 Kees Cook
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "attributes.h"
-#include "sp-skeleton.h"
-#include "xml/repr.h"
-
-#define DEBUG_SKELETON
-#ifdef DEBUG_SKELETON
-# define debug(f, a...) { g_print("%s(%d) %s:", \
- __FILE__,__LINE__,__FUNCTION__); \
- g_print(f, ## a); \
- g_print("\n"); \
- }
-#else
-# define debug(f, a...) /**/
-#endif
-
-/* Skeleton base class */
-static void sp_skeleton_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
-static void sp_skeleton_release(SPObject *object);
-static void sp_skeleton_set(SPObject *object, unsigned int key, gchar const *value);
-static void sp_skeleton_update(SPObject *object, SPCtx *ctx, guint flags);
-static Inkscape::XML::Node *sp_skeleton_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
-
-G_DEFINE_TYPE(SPSkeleton, sp_skeleton, SP_TYPE_OBJECT);
-
-static void
-sp_skeleton_class_init(SPSkeletonClass *klass)
-{
- SPObjectClass *sp_object_class = (SPObjectClass *)klass;
-
-<<<<<<< TREE
- sp_object_class->build = sp_skeleton_build;
-=======
- skeleton_parent_class = (SPObjectClass*)g_type_class_peek_parent(klass);
-
- //sp_object_class->build = sp_skeleton_build;
->>>>>>> MERGE-SOURCE
- sp_object_class->release = sp_skeleton_release;
- sp_object_class->write = sp_skeleton_write;
- sp_object_class->set = sp_skeleton_set;
- sp_object_class->update = sp_skeleton_update;
-}
-
-static void
-sp_skeleton_init(SPSkeleton *skeleton)
-{
- debug("0x%p",skeleton);
-}
-
-/**
- * Reads the Inkscape::XML::Node, and initializes SPSkeleton variables. For this to get called,
- * our name must be associated with a repr via "sp_object_type_register". Best done through
- * sp-object-repr.cpp's repr_name_entries array.
- */
-static void
-sp_skeleton_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
-{
- debug("0x%p",object);
-<<<<<<< TREE
- if (((SPObjectClass *) sp_skeleton_parent_class)->build) {
- ((SPObjectClass *) sp_skeleton_parent_class)->build(object, document, repr);
- }
-=======
-// if (((SPObjectClass *) skeleton_parent_class)->build) {
-// ((SPObjectClass *) skeleton_parent_class)->build(object, document, repr);
-// }
->>>>>>> MERGE-SOURCE
-
- /*
- Pay attention to certain settings here
-
- object->readAttr( "xlink:href" );
- object->readAttr( "attributeName" );
- object->readAttr( "attributeType" );
- object->readAttr( "begin" );
- object->readAttr( "dur" );
- object->readAttr( "end" );
- object->readAttr( "min" );
- object->readAttr( "max" );
- object->readAttr( "restart" );
- object->readAttr( "repeatCount" );
- object->readAttr( "repeatDur" );
- object->readAttr( "fill" );
- */
-}
-
-/**
- * Drops any allocated memory.
- */
-static void
-sp_skeleton_release(SPObject *object)
-{
- debug("0x%p",object);
-
- /* deal with our children and our selves here */
-
- if (((SPObjectClass *) sp_skeleton_parent_class)->release)
- ((SPObjectClass *) sp_skeleton_parent_class)->release(object);
-}
-
-/**
- * Sets a specific value in the SPSkeleton.
- */
-static void
-sp_skeleton_set(SPObject *object, unsigned int key, gchar const *value)
-{
- debug("0x%p %s(%u): '%s'",object,
- sp_attribute_name(key),key,value ? value : "<no value>");
- //SPSkeleton *skeleton = SP_SKELETON(object);
-
- /* See if any parents need this value. */
- if (((SPObjectClass *) sp_skeleton_parent_class)->set) {
- ((SPObjectClass *) sp_skeleton_parent_class)->set(object, key, value);
- }
-}
-
-/**
- * Receives update notifications.
- */
-static void
-sp_skeleton_update(SPObject *object, SPCtx *ctx, guint flags)
-{
- debug("0x%p",object);
- //SPSkeleton *skeleton = SP_SKELETON(object);
-
- if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
- SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
-
- /* do something to trigger redisplay, updates? */
-
- }
-
- if (((SPObjectClass *) sp_skeleton_parent_class)->update) {
- ((SPObjectClass *) sp_skeleton_parent_class)->update(object, ctx, flags);
- }
-}
-
-/**
- * Writes its settings to an incoming repr object, if any.
- */
-static Inkscape::XML::Node *
-sp_skeleton_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
-{
- debug("0x%p",object);
- //SPSkeleton *skeleton = SP_SKELETON(object);
-
- // Inkscape-only object, not copied during an "plain SVG" dump:
- if (flags & SP_OBJECT_WRITE_EXT) {
- if (repr) {
- // is this sane?
- repr->mergeFrom(object->getRepr(), "id");
- } else {
- repr = object->getRepr()->duplicate(doc);
- }
- }
-
- if (((SPObjectClass *) sp_skeleton_parent_class)->write) {
- ((SPObjectClass *) sp_skeleton_parent_class)->write(object, doc, repr, flags);
- }
-
- return repr;
-}
-
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-skeleton.h b/src/sp-skeleton.h
deleted file mode 100644
index d01cbcada..000000000
--- a/src/sp-skeleton.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef SP_SKELETON_H_SEEN
-#define SP_SKELETON_H_SEEN
-
-/** \file
- * SVG <skeleton> implementation, see sp-skeleton.cpp.
- */
-/*
- * Authors:
- * Kees Cook <kees@outflux.net>
- *
- * Copyright (C) 2004 Kees Cook
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include "sp-object.h"
-
-/* Skeleton base class */
-
-#define SP_TYPE_SKELETON (sp_skeleton_get_type())
-#define SP_SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_SKELETON, SPSkeleton))
-#define SP_IS_SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_SKELETON))
-
-class SPSkeleton;
-class SPSkeletonClass;
-
-struct SPSkeleton : public SPObject {
-};
-
-struct SPSkeletonClass {
- SPObjectClass parent_class;
-};
-
-GType sp_skeleton_get_type();
-
-
-#endif /* !SP_SKELETON_H_SEEN */
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-string.cpp b/src/sp-string.cpp
index a826d182d..be450b248 100644
--- a/src/sp-string.cpp
+++ b/src/sp-string.cpp
@@ -35,11 +35,11 @@
#include "sp-factory.h"
namespace {
- SPObject* createString() {
- return new SPString();
- }
+ SPObject* createString() {
+ return new SPString();
+ }
- bool stringRegistered = SPFactory::instance().registerObject("string", createString);
+ bool stringRegistered = SPFactory::instance().registerObject("string", createString);
}
/*#####################################################
@@ -54,24 +54,19 @@ SPString::~SPString() {
}
void SPString::build(SPDocument *doc, Inkscape::XML::Node *repr) {
- SPString* object = this;
+ SPString* object = this;
object->read_content();
SPObject::build(doc, repr);
}
void SPString::release() {
- SPString* object = this;
- SPString *string = SP_STRING(object);
-
- //string->string.~ustring();
-
SPObject::release();
}
void SPString::read_content() {
- SPString* object = this;
+ SPString* object = this;
SPString *string = SP_STRING(object);
diff --git a/src/sp-use.cpp b/src/sp-use.cpp
index 0887ab50e..44935e61d 100644
--- a/src/sp-use.cpp
+++ b/src/sp-use.cpp
@@ -44,15 +44,15 @@ static void sp_use_delete_self(SPObject *deleted, SPUse *self);
#include "sp-factory.h"
namespace {
- SPObject* createUse() {
- return new SPUse();
- }
+ SPObject* createUse() {
+ return new SPUse();
+ }
- bool useRegistered = SPFactory::instance().registerObject("svg:use", createUse);
+ bool useRegistered = SPFactory::instance().registerObject("svg:use", createUse);
}
SPUse::SPUse() : SPItem() {
- this->child = NULL;
+ this->child = NULL;
this->x.unset();
this->y.unset();
@@ -102,7 +102,7 @@ void SPUse::build(SPDocument *document, Inkscape::XML::Node *repr) {
void SPUse::release() {
if (this->child) {
- this->detach(this->child);
+ this->detach(this->child);
this->child = NULL;
}
@@ -227,8 +227,6 @@ void SPUse::print(SPPrintContext* ctx) {
}
gchar* SPUse::description() {
- char *ret;
-
if (this->child) {
if( SP_IS_SYMBOL( this->child ) ) {
char *symbol_desc = SP_ITEM(this->child)->title();
@@ -455,9 +453,9 @@ sp_use_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, SPUse *use)
SPObject* obj = SPFactory::instance().createObject(NodeTraits::get_type_string(*childrepr));
if (SP_IS_ITEM(obj)) {
- use->child = obj;
+ use->child = obj;
- use->attach(use->child, use->lastChild());
+ use->attach(use->child, use->lastChild());
sp_object_unref(use->child, use);
(use->child)->invoke_build(use->document, childrepr, TRUE);
@@ -469,7 +467,7 @@ sp_use_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, SPUse *use)
}
}
} else {
- delete obj;
+ delete obj;
}
use->_delete_connection = refobj->connectDelete(sigc::bind(sigc::ptr_fun(&sp_use_delete_self), use));
@@ -523,7 +521,7 @@ void SPUse::update(SPCtx *ctx, unsigned flags) {
}
if (this->y.unit == SVGLength::PERCENT) {
- this->y.computed = this->y.value * ictx->viewport.height();
+ this->y.computed = this->y.value * ictx->viewport.height();
}
if (this->width.unit == SVGLength::PERCENT) {
diff --git a/src/style.cpp b/src/style.cpp
index db05a748f..e9cf22891 100644
--- a/src/style.cpp
+++ b/src/style.cpp
@@ -3170,7 +3170,10 @@ sp_style_clear(SPStyle *style)
style->color_interpolation.value = style->color_interpolation.computed = SP_CSS_COLOR_INTERPOLATION_SRGB;
style->color_interpolation_filters.set = FALSE;
style->color_interpolation_filters.inherit = FALSE;
- style->color_interpolation_filters.value = style->color_interpolation_filters.computed = SP_CSS_COLOR_INTERPOLATION_LINEARRGB;
+ style->color_interpolation_filters.value = style->color_interpolation_filters.computed = SP_CSS_COLOR_INTERPOLATION_SRGB;
+ //this line changed because rendering issues: Bug lp:1127103
+ //style->color_interpolation_filters.value = style->color_interpolation_filters.computed = SP_CSS_COLOR_INTERPOLATION_LINEARRGB;
+
style->fill.clear();
style->fill.setColor(0.0, 0.0, 0.0);
diff --git a/src/trace/imagemap-gdk.cpp b/src/trace/imagemap-gdk.cpp
index 7c7139002..298414074 100644
--- a/src/trace/imagemap-gdk.cpp
+++ b/src/trace/imagemap-gdk.cpp
@@ -152,9 +152,9 @@ RgbMap *gdkPixbufToRgbMap(GdkPixbuf *buf)
{
int alpha = (int)p[3];
int white = 255 - alpha;
- int r = (int)p[2]; r = r * alpha / 256 + white;
+ int r = (int)p[0]; r = r * alpha / 256 + white;
int g = (int)p[1]; g = g * alpha / 256 + white;
- int b = (int)p[0]; b = b * alpha / 256 + white;
+ int b = (int)p[2]; b = b * alpha / 256 + white;
rgbMap->setPixel(rgbMap, x, y, r, g, b);
p += n_channels;
diff --git a/src/trace/trace.cpp b/src/trace/trace.cpp
index cad8ea9be..e2cda6247 100644
--- a/src/trace/trace.cpp
+++ b/src/trace/trace.cpp
@@ -31,6 +31,7 @@
#include <2geom/transforms.h>
#include "verbs.h"
+#include "display/cairo-utils.h"
#include "display/drawing.h"
#include "display/drawing-shape.h"
@@ -336,8 +337,17 @@ Glib::RefPtr<Gdk::Pixbuf> Tracer::getSelectedImage()
if (!img->pixbuf)
return Glib::RefPtr<Gdk::Pixbuf>(NULL);
- Glib::RefPtr<Gdk::Pixbuf> pixbuf =
- Glib::wrap(img->pixbuf, true);
+ GdkPixbuf *raw_pb = img->pixbuf->getPixbufRaw(false);
+ GdkPixbuf *trace_pb = gdk_pixbuf_copy(raw_pb);
+ if (img->pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) {
+ convert_pixels_argb32_to_pixbuf(
+ gdk_pixbuf_get_pixels(trace_pb),
+ gdk_pixbuf_get_width(trace_pb),
+ gdk_pixbuf_get_height(trace_pb),
+ gdk_pixbuf_get_rowstride(trace_pb));
+ }
+
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(trace_pb, false);
if (sioxEnabled)
{
@@ -410,7 +420,16 @@ void Tracer::traceThread()
return;
}
- Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(img->pixbuf, true);
+ GdkPixbuf *trace_pb = gdk_pixbuf_copy(img->pixbuf->getPixbufRaw(false));
+ if (img->pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) {
+ convert_pixels_argb32_to_pixbuf(
+ gdk_pixbuf_get_pixels(trace_pb),
+ gdk_pixbuf_get_width(trace_pb),
+ gdk_pixbuf_get_height(trace_pb),
+ gdk_pixbuf_get_rowstride(trace_pb));
+ }
+
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(trace_pb, false);
pixbuf = sioxProcessImage(img, pixbuf);
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index c95dd35cc..233e01862 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -71,6 +71,7 @@ set(ui_SRC
dialog/text-edit.cpp
dialog/tile.cpp
dialog/tracedialog.cpp
+ dialog/pixelartdialog.cpp
dialog/transformation.cpp
dialog/undo-history.cpp
# dialog/whiteboard-connect.cpp
diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert
index 09a7ef573..c37767a08 100644
--- a/src/ui/dialog/Makefile_insert
+++ b/src/ui/dialog/Makefile_insert
@@ -101,6 +101,8 @@ ink_common_sources += \
ui/dialog/tile.h \
ui/dialog/tracedialog.cpp \
ui/dialog/tracedialog.h \
+ ui/dialog/pixelartdialog.cpp \
+ ui/dialog/pixelartdialog.h \
ui/dialog/transformation.cpp \
ui/dialog/transformation.h \
ui/dialog/undo-history.cpp \
diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp
index 0ce74f54e..17f6ae74d 100644
--- a/src/ui/dialog/dialog-manager.cpp
+++ b/src/ui/dialog/dialog-manager.cpp
@@ -35,6 +35,7 @@
#include "ui/dialog/symbols.h"
#include "ui/dialog/tile.h"
#include "ui/dialog/tracedialog.h"
+#include "ui/dialog/pixelartdialog.h"
#include "ui/dialog/transformation.h"
#include "ui/dialog/undo-history.h"
#include "ui/dialog/panel-dialog.h"
@@ -118,6 +119,7 @@ DialogManager::DialogManager() {
registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>);
registerFactory("TileDialog", &create<TileDialog, FloatingBehavior>);
registerFactory("Trace", &create<TraceDialog, FloatingBehavior>);
+ registerFactory("PixelArt", &create<PixelArtDialog, FloatingBehavior>);
registerFactory("Transformation", &create<Transformation, FloatingBehavior>);
registerFactory("UndoHistory", &create<UndoHistory, FloatingBehavior>);
registerFactory("InputDevices", &create<InputDialog, FloatingBehavior>);
@@ -151,6 +153,7 @@ DialogManager::DialogManager() {
registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>);
registerFactory("TileDialog", &create<TileDialog, DockBehavior>);
registerFactory("Trace", &create<TraceDialog, DockBehavior>);
+ registerFactory("PixelArt", &create<PixelArtDialog, DockBehavior>);
registerFactory("Transformation", &create<Transformation, DockBehavior>);
registerFactory("UndoHistory", &create<UndoHistory, DockBehavior>);
registerFactory("InputDevices", &create<InputDialog, DockBehavior>);
diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp
index b06c1fd1f..e9cf2e753 100644
--- a/src/ui/dialog/inkscape-preferences.cpp
+++ b/src/ui/dialog/inkscape-preferences.cpp
@@ -884,7 +884,7 @@ void InkscapePreferences::initPageIO()
int pathstringFormatValues[numPathstringFormat] = {0, 1, 2};
_svgoutput_pathformat.init("/options/svgoutput/pathstring_format", pathstringFormatLabels, pathstringFormatValues, numPathstringFormat, 2);
- _page_svgoutput.add_line( true, _("Path string format"), _svgoutput_pathformat, "", _("Path data should be written: only with absolute coordinates, only with relative coordinates, or optimized for string length (mixed absolute and relative coordinates)"), false);
+ _page_svgoutput.add_line( true, _("Path string format:"), _svgoutput_pathformat, "", _("Path data should be written: only with absolute coordinates, only with relative coordinates, or optimized for string length (mixed absolute and relative coordinates)"), false);
_svgoutput_forcerepeatcommands.init( _("Force repeat commands"), "/options/svgoutput/forcerepeatcommands", false);
_page_svgoutput.add_line( true, "", _svgoutput_forcerepeatcommands, "", _("Force repeating of the same path command (for example, 'L 1,2 L 3,4' instead of 'L 1,2 3,4')"), false);
diff --git a/src/ui/dialog/pixelartdialog.cpp b/src/ui/dialog/pixelartdialog.cpp
new file mode 100644
index 000000000..ef181b357
--- /dev/null
+++ b/src/ui/dialog/pixelartdialog.cpp
@@ -0,0 +1,492 @@
+/**
+ * @file
+ * Pixel art tracing settings dialog - implementation.
+ */
+/* Authors:
+ * Bob Jamison <rjamison@titan.com>
+ * Stéphane Gimenez <dev@gim.name>
+ * Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+ * Other dudes from The Inkscape Organization
+ *
+ * Copyright (C) 2004-2013 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "pixelartdialog.h"
+#include <gtkmm/radiobutton.h>
+#include <gtkmm/stock.h>
+
+#include <gtk/gtk.h> //for GTK_RESPONSE* types
+#include <glibmm/i18n.h>
+
+#include "ui/widget/spinbutton.h"
+#include "ui/widget/frame.h"
+
+#include "desktop.h"
+#include "desktop-tracker.h"
+#include "message-stack.h"
+#include "selection.h"
+#include "preferences.h"
+
+#include "sp-image.h"
+#include "display/cairo-utils.h"
+#include "libdepixelize/kopftracer2011.h"
+#include <algorithm>
+#include "document.h"
+#include "xml/repr.h"
+#include "xml/document.h"
+#include "svg/svg.h"
+#include "svg/svg-color.h"
+#include "color.h"
+#include "svg/css-ostringstream.h"
+#include "document-undo.h"
+
+#ifdef HAVE_OPENMP
+#include <omp.h>
+#endif // HAVE_OPENMP
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+template<class T>
+T move(T &obj)
+{
+#ifdef LIBDEPIXELIZE_ENABLE_CPP11
+ return std::move(obj);
+#else
+ T ret;
+ std::swap(obj, ret);
+ return ret;
+#endif // LIBDEPIXELIZE_ENABLE_CPP11
+}
+
+/**
+ * A dialog for adjusting pixel art -> vector tracing parameters
+ */
+class PixelArtDialogImpl : public PixelArtDialog
+{
+public:
+ PixelArtDialogImpl();
+
+ ~PixelArtDialogImpl();
+
+private:
+ void setDesktop(SPDesktop *desktop);
+ void setTargetDesktop(SPDesktop *desktop);
+
+ //############ Events
+
+ void responseCallback(int response_id);
+
+ //############ UI Logic
+
+ Tracer::Kopf2011::Options options();
+
+ void vectorize();
+ void processLibdepixelize(SPImage *img);
+ void setDefaults();
+ void updatePreview();
+
+ bool ignorePreview;
+ bool pendingPreview;
+
+ //############ UI
+
+ Gtk::HBox buttonsHBox;
+
+ Gtk::Button *mainOkButton;
+ Gtk::Button *mainCancelButton;
+ Gtk::Button *mainResetButton;
+
+ Gtk::VBox heuristicsVBox;
+ UI::Widget::Frame heuristicsFrame;
+
+ Gtk::HBox curvesMultiplierHBox;
+ Gtk::Label curvesMultiplierLabel;
+ Inkscape::UI::Widget::SpinButton curvesMultiplierSpinner;
+
+ Gtk::HBox islandsWeightHBox;
+ Gtk::Label islandsWeightLabel;
+ Inkscape::UI::Widget::SpinButton islandsWeightSpinner;
+
+ Gtk::HBox sparsePixelsMultiplierHBox;
+ Gtk::Label sparsePixelsMultiplierLabel;
+ Inkscape::UI::Widget::SpinButton sparsePixelsMultiplierSpinner;
+
+ Gtk::HBox sparsePixelsRadiusHBox;
+ Gtk::Label sparsePixelsRadiusLabel;
+ Inkscape::UI::Widget::SpinButton sparsePixelsRadiusSpinner;
+
+ Gtk::VBox outputVBox;
+ UI::Widget::Frame outputFrame;
+ Gtk::RadioButtonGroup outputGroup;
+
+ Gtk::RadioButton voronoiRadioButton;
+ Gtk::RadioButton noOptimizeRadioButton;
+ Gtk::RadioButton optimizeRadioButton;
+
+ SPDesktop *desktop;
+ DesktopTracker deskTrack;
+ sigc::connection desktopChangeConn;
+};
+
+void PixelArtDialogImpl::setDesktop(SPDesktop *desktop)
+{
+ Panel::setDesktop(desktop);
+ deskTrack.setBase(desktop);
+}
+
+void PixelArtDialogImpl::setTargetDesktop(SPDesktop *desktop)
+{
+ this->desktop = desktop;
+}
+
+PixelArtDialogImpl::PixelArtDialogImpl() :
+ PixelArtDialog(),
+ ignorePreview(false),
+ pendingPreview(false)
+{
+
+ Gtk::Box *contents = _getContents();
+
+ // Heuristics
+ {
+ curvesMultiplierLabel.set_label(_("_Curves (multiplier)"));
+ curvesMultiplierLabel.set_use_underline(true);
+ curvesMultiplierLabel.set_mnemonic_widget(curvesMultiplierSpinner);
+ curvesMultiplierLabel.set_tooltip_text(_("Favors connections that are part of a long curve"));
+ curvesMultiplierSpinner.set_increments(0.125, 0);
+ curvesMultiplierSpinner.set_digits(3);
+ curvesMultiplierSpinner.set_range(-10, 10);
+ curvesMultiplierSpinner.get_adjustment()->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &PixelArtDialogImpl::updatePreview));
+
+ curvesMultiplierHBox.pack_start(curvesMultiplierLabel, false, false);
+ curvesMultiplierHBox.pack_end(curvesMultiplierSpinner, false, false);
+ heuristicsVBox.pack_start(curvesMultiplierHBox, false, false);
+
+ islandsWeightLabel.set_label(_("_Islands (weight)"));
+ islandsWeightLabel.set_use_underline(true);
+ islandsWeightLabel.set_mnemonic_widget(islandsWeightSpinner);
+ islandsWeightLabel.set_tooltip_text(_("Avoid single disconnected pixels"));
+
+ islandsWeightSpinner.set_tooltip_text(_("A constant vote value"));
+ islandsWeightSpinner.set_increments(1, 0);
+ islandsWeightSpinner.set_range(-20, 20);
+ islandsWeightSpinner.get_adjustment()->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &PixelArtDialogImpl::updatePreview));
+
+ islandsWeightHBox.pack_start(islandsWeightLabel, false, false);
+ islandsWeightHBox.pack_end(islandsWeightSpinner, false, false);
+ heuristicsVBox.pack_start(islandsWeightHBox, false, false);
+
+ sparsePixelsRadiusLabel.set_label(_("Sparse pixels (window _radius)"));
+ sparsePixelsRadiusLabel.set_use_underline(true);
+ sparsePixelsRadiusLabel.set_mnemonic_widget(sparsePixelsRadiusSpinner);
+
+ sparsePixelsRadiusSpinner.set_increments(1, 0);
+ sparsePixelsRadiusSpinner.set_range(2, 8);
+ sparsePixelsRadiusSpinner.get_adjustment()->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &PixelArtDialogImpl::updatePreview));
+
+ sparsePixelsRadiusSpinner.set_tooltip_text(_("The radius of the window analyzed"));
+ sparsePixelsMultiplierLabel.set_label(_("Sparse pixels (_multiplier)"));
+ sparsePixelsMultiplierLabel.set_use_underline(true);
+ sparsePixelsMultiplierLabel.set_mnemonic_widget(sparsePixelsMultiplierSpinner);
+
+ sparsePixelsMultiplierSpinner.set_increments(0.125, 0);
+ sparsePixelsMultiplierSpinner.set_digits(3);
+ sparsePixelsMultiplierSpinner.set_range(-10, 10);
+ sparsePixelsMultiplierSpinner.get_adjustment()->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &PixelArtDialogImpl::updatePreview));
+
+ {
+ char const *str = _("Favors connections that are part of foreground color");
+ sparsePixelsRadiusLabel.set_tooltip_text(str);
+ sparsePixelsMultiplierLabel.set_tooltip_text(str);
+ }
+
+ {
+ char const *str = _("The heuristic computed vote will be multiplied by this value");
+ curvesMultiplierSpinner.set_tooltip_text(str);
+ sparsePixelsMultiplierSpinner.set_tooltip_text(str);
+ }
+
+ sparsePixelsRadiusHBox.pack_start(sparsePixelsRadiusLabel, false, false);
+ sparsePixelsRadiusHBox.pack_end(sparsePixelsRadiusSpinner, false, false);
+ heuristicsVBox.pack_start(sparsePixelsRadiusHBox, false, false);
+
+ sparsePixelsMultiplierHBox.pack_start(sparsePixelsMultiplierLabel, false, false);
+ sparsePixelsMultiplierHBox.pack_end(sparsePixelsMultiplierSpinner, false, false);
+ heuristicsVBox.pack_start(sparsePixelsMultiplierHBox, false, false);
+
+ heuristicsFrame.set_label(_("Heuristics"));
+ heuristicsFrame.add(heuristicsVBox);
+ contents->pack_start(heuristicsFrame, false, false);
+ }
+
+ // Output
+ {
+ voronoiRadioButton.set_label(_("_Voronoi diagram"));
+ voronoiRadioButton.set_tooltip_text(_("Output composed of straight lines"));
+ voronoiRadioButton.set_use_underline(true);
+ outputGroup = voronoiRadioButton.get_group();
+
+ outputVBox.pack_start(voronoiRadioButton, false, false);
+
+ noOptimizeRadioButton.set_label(_("Convert to _B-spline curves"));
+ noOptimizeRadioButton.set_tooltip_text(_("Preserve staircasing artifacts"));
+ noOptimizeRadioButton.set_use_underline(true);
+ noOptimizeRadioButton.set_group(outputGroup);
+
+ outputVBox.pack_start(noOptimizeRadioButton, false, false);
+
+ optimizeRadioButton.set_label(_("_Smooth curves"));
+ optimizeRadioButton.set_tooltip_text(_("The Kopf-Lischinski algorithm"));
+ optimizeRadioButton.set_use_underline(true);
+ optimizeRadioButton.set_group(outputGroup);
+
+ outputVBox.pack_start(optimizeRadioButton, false, false);
+
+ outputFrame.set_label(_("Output"));
+ outputFrame.add(outputVBox);
+ contents->pack_start(outputFrame, true, false);
+ }
+
+ // Buttons
+ {
+ mainResetButton = addResponseButton(_("Reset"), GTK_RESPONSE_HELP, true);
+ mainResetButton ->set_tooltip_text(_("Reset all settings to defaults"));
+
+ //## The OK button
+ mainCancelButton = addResponseButton(Gtk::Stock::STOP, GTK_RESPONSE_CANCEL);
+ if (mainCancelButton) {
+ mainCancelButton->set_tooltip_text(_("Abort a trace in progress"));
+ mainCancelButton->set_sensitive(false);
+ }
+ mainOkButton = addResponseButton(Gtk::Stock::OK, GTK_RESPONSE_OK);
+ mainOkButton->set_tooltip_text(_("Execute the trace"));
+
+ contents->pack_start(buttonsHBox);
+ }
+
+ setDefaults();
+
+ show_all_children();
+
+ desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &PixelArtDialogImpl::setTargetDesktop) );
+ deskTrack.connect(GTK_WIDGET(gobj()));
+
+ signalResponse().connect(sigc::mem_fun(*this, &PixelArtDialogImpl::responseCallback));
+}
+
+void PixelArtDialogImpl::responseCallback(int response_id)
+{
+ if (response_id == GTK_RESPONSE_OK) {
+ vectorize();
+ } else if (response_id == GTK_RESPONSE_CANCEL) {
+ // TODO
+ } else if (response_id == GTK_RESPONSE_HELP) {
+ setDefaults();
+ } else {
+ hide();
+ return;
+ }
+}
+
+Tracer::Kopf2011::Options PixelArtDialogImpl::options()
+{
+ Tracer::Kopf2011::Options options;
+
+ options.curvesMultiplier = curvesMultiplierSpinner.get_value();
+ options.islandsWeight = islandsWeightSpinner.get_value_as_int();
+ options.sparsePixelsMultiplier = sparsePixelsMultiplierSpinner.get_value();
+ options.sparsePixelsRadius = sparsePixelsRadiusSpinner.get_value_as_int();
+ options.optimize = optimizeRadioButton.get_active();
+
+ options.nthreads = Inkscape::Preferences::get()
+ ->getIntLimited("/options/threading/numthreads",
+#ifdef HAVE_OPENMP
+ omp_get_num_procs(),
+#else
+ 1,
+#endif // HAVE_OPENMP
+ 1, 256);
+
+ return options;
+}
+
+void PixelArtDialogImpl::vectorize()
+{
+ Inkscape::MessageStack *msgStack = desktop->messageStack();
+
+ if ( !desktop->selection ) {
+ char *msg = _("Select an <b>image</b> to trace");
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
+ return;
+ }
+
+ bool found = false;
+
+ for ( GSList const *list = desktop->selection->itemList() ; list
+ ; list = list->next ) {
+ if ( !SP_IS_IMAGE(list->data) )
+ continue;
+
+ found = true;
+
+ processLibdepixelize(SP_IMAGE(list->data));
+ }
+
+ if ( !found ) {
+ char *msg = _("Select an <b>image</b> to trace");
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
+ return;
+ }
+
+ DocumentUndo::done(desktop->doc(), SP_VERB_SELECTION_PIXEL_ART,
+ _("Trace pixel art"));
+
+ // Flush pending updates
+ desktop->doc()->ensureUpToDate();
+}
+
+void PixelArtDialogImpl::processLibdepixelize(SPImage *img)
+{
+ Tracer::Splines out;
+
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf
+ = Glib::wrap(img->pixbuf->getPixbufRaw(), true);
+
+ if ( voronoiRadioButton.get_active() ) {
+ out = Tracer::Kopf2011::to_voronoi(pixbuf, options());
+ } else {
+ out = Tracer::Kopf2011::to_splines(pixbuf, options());
+ }
+
+ Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
+ Inkscape::XML::Node *group = xml_doc->createElement("svg:g");
+
+ for ( Tracer::Splines::iterator it = out.begin(), end = out.end()
+ ; it != end ; ++it ) {
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
+
+ {
+ SPCSSAttr *css = sp_repr_css_attr_new();
+
+ {
+ gchar b[64];
+ sp_svg_write_color(b, sizeof(b),
+ SP_RGBA32_U_COMPOSE(unsigned(it->rgba[0]),
+ unsigned(it->rgba[1]),
+ unsigned(it->rgba[2]),
+ unsigned(it->rgba[3])));
+
+ sp_repr_css_set_property(css, "fill", b);
+ }
+
+ {
+ Inkscape::CSSOStringStream osalpha;
+ osalpha << float(it->rgba[3]) / 255.;
+ sp_repr_css_set_property(css, "fill-opacity",
+ osalpha.str().c_str());
+ }
+
+ sp_repr_css_set(repr, css, "style");
+ sp_repr_css_attr_unref(css);
+ }
+
+ gchar *str = sp_svg_write_path(move(it->pathVector));
+ repr->setAttribute("d", str);
+ g_free(str);
+
+ group->appendChild(repr);
+
+ Inkscape::GC::release(repr);
+ }
+
+ {
+ group->setAttribute("transform",
+ (std::string("translate(")
+ + sp_svg_length_write_with_units(img->x)
+ + ' ' + sp_svg_length_write_with_units(img->y)
+ + ')').c_str());
+ }
+
+ desktop->currentLayer()->appendChildRepr(group);
+
+ Inkscape::GC::release(group);
+}
+
+void PixelArtDialogImpl::setDefaults()
+{
+ ignorePreview = true;
+
+ curvesMultiplierSpinner.set_value(Tracer::Kopf2011::Options
+ ::CURVES_MULTIPLIER);
+
+ islandsWeightSpinner.set_value(Tracer::Kopf2011::Options::ISLANDS_WEIGHT);
+
+ sparsePixelsRadiusSpinner.set_value(Tracer::Kopf2011::Options
+ ::SPARSE_PIXELS_RADIUS);
+
+ sparsePixelsMultiplierSpinner.set_value(Tracer::Kopf2011::Options
+ ::SPARSE_PIXELS_MULTIPLIER);
+
+ optimizeRadioButton.set_active();
+
+ ignorePreview = false;
+
+ if ( pendingPreview )
+ updatePreview();
+}
+
+void PixelArtDialogImpl::updatePreview()
+{
+ if ( ignorePreview ) {
+ pendingPreview = true;
+ return;
+ }
+
+ // TODO: update preview
+ pendingPreview = false;
+}
+
+/**
+ * Factory method. Use this to create a new PixelArtDialog
+ */
+PixelArtDialog &PixelArtDialog::getInstance()
+{
+ PixelArtDialog *dialog = new PixelArtDialogImpl();
+ return *dialog;
+}
+
+PixelArtDialogImpl::~PixelArtDialogImpl()
+{
+ desktopChangeConn.disconnect();
+}
+
+
+} //namespace Dialog
+} //namespace UI
+} //namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/pixelartdialog.h b/src/ui/dialog/pixelartdialog.h
new file mode 100644
index 000000000..165cb8699
--- /dev/null
+++ b/src/ui/dialog/pixelartdialog.h
@@ -0,0 +1,58 @@
+/** @file
+ * @brief Bitmap tracing settings dialog
+ */
+/* Authors:
+ * Bob Jamison
+ * Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+ * Other dudes from The Inkscape Organization
+ *
+ * Copyright (C) 2004, 2005 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef __PIXELARTDIALOG_H__
+#define __PIXELARTDIALOG_H__
+
+#include "ui/widget/panel.h"
+#include "verbs.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+
+/**
+ * A dialog that displays log messages
+ */
+class PixelArtDialog : public UI::Widget::Panel
+{
+
+public:
+
+ PixelArtDialog() :
+ UI::Widget::Panel("", "/dialogs/pixelart", SP_VERB_SELECTION_PIXEL_ART)
+ {}
+
+
+ static PixelArtDialog &getInstance();
+
+ virtual ~PixelArtDialog() {};
+};
+
+
+} //namespace Dialog
+} //namespace UI
+} //namespace Inkscape
+
+#endif /* __PIXELARTDIALOGDIALOG_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/verbs.cpp b/src/verbs.cpp
index 737d9e150..23a560423 100644
--- a/src/verbs.cpp
+++ b/src/verbs.cpp
@@ -1173,6 +1173,10 @@ void SelectionVerb::perform(SPAction *action, void *data)
inkscape_dialogs_unhide();
dt->_dlg_mgr->showDialog("Trace");
break;
+ case SP_VERB_SELECTION_PIXEL_ART:
+ inkscape_dialogs_unhide();
+ dt->_dlg_mgr->showDialog("PixelArt");
+ break;
case SP_VERB_SELECTION_CREATE_BITMAP:
sp_selection_create_bitmap_copy(dt);
break;
@@ -2545,6 +2549,8 @@ Verb *Verb::_base_verbs[] = {
// TRANSLATORS: "to trace" means "to convert a bitmap to vector graphics" (to vectorize)
new SelectionVerb(SP_VERB_SELECTION_TRACE, "SelectionTrace", N_("_Trace Bitmap..."),
N_("Create one or more paths from a bitmap by tracing it"), INKSCAPE_ICON("bitmap-trace")),
+ new SelectionVerb(SP_VERB_SELECTION_PIXEL_ART, "SelectionPixelArt", N_("Trace Pixel Art..."),
+ N_("Create paths using Kopf-Lischinski algorithm to vectorize pixel art"), INKSCAPE_ICON("pixelart-trace")),
new SelectionVerb(SP_VERB_SELECTION_CREATE_BITMAP, "SelectionCreateBitmap", N_("Make a _Bitmap Copy"),
N_("Export selection to a bitmap and insert it into document"), INKSCAPE_ICON("selection-make-bitmap-copy") ),
new SelectionVerb(SP_VERB_SELECTION_COMBINE, "SelectionCombine", N_("_Combine"),
diff --git a/src/verbs.h b/src/verbs.h
index c4b01dfbf..40292745a 100644
--- a/src/verbs.h
+++ b/src/verbs.h
@@ -134,6 +134,7 @@ enum {
SP_VERB_SELECTION_SIMPLIFY,
SP_VERB_SELECTION_REVERSE,
SP_VERB_SELECTION_TRACE,
+ SP_VERB_SELECTION_PIXEL_ART,
SP_VERB_SELECTION_CREATE_BITMAP,
SP_VERB_SELECTION_COMBINE,
SP_VERB_SELECTION_BREAK_APART,