From 2ddabce031c6c408688477544f9b16a28b7b9cb1 Mon Sep 17 00:00:00 2001 From: Fridrich Strba Date: Tue, 27 May 2014 12:35:54 +0200 Subject: Port inkscape to librevenge framework for WPG, CDR and VSD imports (bzr r13398.1.1) --- src/extension/internal/cdr-input.cpp | 20 ++++++++++-------- src/extension/internal/vsd-input.cpp | 20 ++++++++++-------- src/extension/internal/wpg-input.cpp | 41 ++++++++++++------------------------ src/ui/dialog/symbols.cpp | 10 +++++---- 4 files changed, 41 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp index 0111ed626..c4f251361 100644 --- a/src/extension/internal/cdr-input.cpp +++ b/src/extension/internal/cdr-input.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -60,7 +60,7 @@ namespace Internal { class CdrImportDialog : public Gtk::Dialog { public: - CdrImportDialog(const std::vector &vec); + CdrImportDialog(const std::vector &vec); virtual ~CdrImportDialog(); bool showDialog(); @@ -86,12 +86,12 @@ private: class Gtk::VBox * vbox2; class Gtk::Widget * _previewArea; - const std::vector &_vec; // Document to be imported + const std::vector &_vec; // Document to be imported unsigned _current_page; // Current selected page int _preview_width, _preview_height; // Size of the preview area }; -CdrImportDialog::CdrImportDialog(const std::vector &vec) +CdrImportDialog::CdrImportDialog(const std::vector &vec) : _vec(vec), _current_page(1) { int num_pages = _vec.size(); @@ -210,14 +210,16 @@ void CdrImportDialog::_setPreviewPage(unsigned page) SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - WPXFileStream input(uri); + librevenge::RVNGFileStream input(uri); if (!libcdr::CDRDocument::isSupported(&input)) { return NULL; } - libcdr::CDRStringVector output; - if (!libcdr::CDRDocument::generateSVG(&input, output)) { + librevenge::RVNGStringVector output; + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libcdr::CDRDocument::parse(&input, &generator)) { return NULL; } @@ -225,9 +227,9 @@ SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - std::vector tmpSVGOutput; + std::vector tmpSVGOutput; for (unsigned i=0; i\n\n"); + librevenge::RVNGString tmpString("\n\n"); tmpString.append(output[i]); tmpSVGOutput.push_back(tmpString); } diff --git a/src/extension/internal/vsd-input.cpp b/src/extension/internal/vsd-input.cpp index 6fc79237b..b7e8669b8 100644 --- a/src/extension/internal/vsd-input.cpp +++ b/src/extension/internal/vsd-input.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -59,7 +59,7 @@ namespace Internal { class VsdImportDialog : public Gtk::Dialog { public: - VsdImportDialog(const std::vector &vec); + VsdImportDialog(const std::vector &vec); virtual ~VsdImportDialog(); bool showDialog(); @@ -85,12 +85,12 @@ private: class Gtk::VBox * vbox2; class Gtk::Widget * _previewArea; - const std::vector &_vec; // Document to be imported + const std::vector &_vec; // Document to be imported unsigned _current_page; // Current selected page int _preview_width, _preview_height; // Size of the preview area }; -VsdImportDialog::VsdImportDialog(const std::vector &vec) +VsdImportDialog::VsdImportDialog(const std::vector &vec) : _vec(vec), _current_page(1) { int num_pages = _vec.size(); @@ -209,14 +209,16 @@ void VsdImportDialog::_setPreviewPage(unsigned page) SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - WPXFileStream input(uri); + librevenge::RVNGFileStream input(uri); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } - libvisio::VSDStringVector output; - if (!libvisio::VisioDocument::generateSVG(&input, output)) { + librevenge::RVNGStringVector output; + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libvisio::VisioDocument::parse(&input, &generator)) { return NULL; } @@ -224,9 +226,9 @@ SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - std::vector tmpSVGOutput; + std::vector tmpSVGOutput; for (unsigned i=0; i\n\n"); + librevenge::RVNGString tmpString("\n\n"); tmpString.append(output[i]); tmpSVGOutput.push_back(tmpString); } diff --git a/src/extension/internal/wpg-input.cpp b/src/extension/internal/wpg-input.cpp index 14ff3ec77..c10926361 100644 --- a/src/extension/internal/wpg-input.cpp +++ b/src/extension/internal/wpg-input.cpp @@ -52,17 +52,8 @@ #include "util/units.h" #include -// Take a guess and fallback to 0.1.x if no configure has run -#if !defined(WITH_LIBWPG01) && !defined(WITH_LIBWPG02) -#define WITH_LIBWPG01 1 -#endif - #include "libwpg/libwpg.h" -#if WITH_LIBWPG01 -#include "libwpg/WPGStreamImplementation.h" -#elif WITH_LIBWPG02 -#include "libwpd-stream/libwpd-stream.h" -#endif +#include "librevenge-stream/librevenge-stream.h" using namespace libwpg; @@ -73,17 +64,9 @@ namespace Internal { SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { -#if WITH_LIBWPG01 - WPXInputStream* input = new libwpg::WPGFileStream(uri); -#elif WITH_LIBWPG02 - WPXInputStream* input = new WPXFileStream(uri); -#endif - if (input->isOLEStream()) { -#if WITH_LIBWPG01 - WPXInputStream* olestream = input->getDocumentOLEStream(); -#elif WITH_LIBWPG02 - WPXInputStream* olestream = input->getDocumentOLEStream("PerfectOffice_MAIN"); -#endif + librevenge::RVNGInputStream* input = new librevenge::RVNGFileStream(uri); + if (input->isStructured()) { + librevenge::RVNGInputStream* olestream = input->getSubStreamByName("PerfectOffice_MAIN"); if (olestream) { delete input; input = olestream; @@ -98,15 +81,17 @@ SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } -#if WITH_LIBWPG01 - libwpg::WPGString output; -#elif WITH_LIBWPG02 - WPXString output; -#endif - if (!libwpg::WPGraphics::generateSVG(input, output)) { + librevenge::RVNGStringVector vec; + librevenge::RVNGSVGDrawingGenerator generator(vec, ""); + + if (!libwpg::WPGraphics::parse(input, &generator) || vec.empty() || vec[0].empty()) + { delete input; return NULL; - } + } + + librevenge::RVNGString output("\n\n"); + output.append(vec[0]); //printf("I've got a doc: \n%s", painter.document.c_str()); diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp index 8e0d085a4..0b048ed82 100644 --- a/src/ui/dialog/symbols.cpp +++ b/src/ui/dialog/symbols.cpp @@ -63,7 +63,7 @@ #ifdef WITH_LIBVISIO #include -#include +#include #endif #include "verbs.h" @@ -449,14 +449,16 @@ void SymbolsDialog::iconChanged() { // Read Visio stencil files SPDocument* read_vss( gchar* fullname, gchar* filename ) { - WPXFileStream input(fullname); + librevenge::RVNGFileStream input(fullname); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } - libvisio::VSDStringVector output; - if (!libvisio::VisioDocument::generateSVGStencils(&input, output)) { + librevenge::RVNGStringVector output; + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libvisio::VisioDocument::parseStencils(&input, &generator)) { return NULL; } -- cgit v1.2.3 From bec0a3b7e0a10473bdb3290b6e763969edc0b160 Mon Sep 17 00:00:00 2001 From: su_v Date: Wed, 20 Aug 2014 15:57:00 +0200 Subject: Reorg Resources folder: move inkscape's shared resources into Resources/share/inkscape (bzr r13506.1.26) --- src/path-prefix.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/path-prefix.h b/src/path-prefix.h index be57ae354..4c31b629c 100644 --- a/src/path-prefix.h +++ b/src/path-prefix.h @@ -66,23 +66,23 @@ extern "C" { # define CREATE_PALETTESDIR WIN32_DATADIR("create\\swatches") # define CREATE_PATTERNSDIR WIN32_DATADIR("create\\patterns\\vector") # elif defined ENABLE_OSX_APP_LOCATIONS -# define INKSCAPE_APPICONDIR "Contents/Resources/pixmaps" -# define INKSCAPE_ATTRRELDIR "Contents/Resources/attributes" -# define INKSCAPE_BINDDIR "Contents/Resources/bind" -# define INKSCAPE_EXAMPLESDIR "Contents/Resources/examples" -# define INKSCAPE_EXTENSIONDIR "Contents/Resources/extensions" -# define INKSCAPE_FILTERDIR "Contents/Resources/filters" -# define INKSCAPE_GRADIENTSDIR "Contents/Resources/gradients" -# define INKSCAPE_KEYSDIR "Contents/Resources/keys" -# define INKSCAPE_PIXMAPDIR "Contents/Resources/icons" -# define INKSCAPE_MARKERSDIR "Contents/Resources/markers" -# define INKSCAPE_PALETTESDIR "Contents/Resources/palettes" -# define INKSCAPE_PATTERNSDIR "Contents/Resources/patterns" -# define INKSCAPE_SCREENSDIR "Contents/Resources/screens" -# define INKSCAPE_SYMBOLSDIR "Contents/Resources/symbols" -# define INKSCAPE_TUTORIALSDIR "Contents/Resources/tutorials" -# define INKSCAPE_TEMPLATESDIR "Contents/Resources/templates" -# define INKSCAPE_UIDIR "Contents/Resources/ui" +# define INKSCAPE_APPICONDIR "Contents/Resources/share/pixmaps" +# define INKSCAPE_ATTRRELDIR "Contents/Resources/share/inkscape/attributes" +# define INKSCAPE_BINDDIR "Contents/Resources/share/inkscape/bind" +# define INKSCAPE_EXAMPLESDIR "Contents/Resources/share/inkscape/examples" +# define INKSCAPE_EXTENSIONDIR "Contents/Resources/share/inkscape/extensions" +# define INKSCAPE_FILTERDIR "Contents/Resources/share/inkscape/filters" +# define INKSCAPE_GRADIENTSDIR "Contents/Resources/share/inkscape/gradients" +# define INKSCAPE_KEYSDIR "Contents/Resources/share/inkscape/keys" +# define INKSCAPE_PIXMAPDIR "Contents/Resources/share/inkscape/icons" +# define INKSCAPE_MARKERSDIR "Contents/Resources/share/inkscape/markers" +# define INKSCAPE_PALETTESDIR "Contents/Resources/share/inkscape/palettes" +# define INKSCAPE_PATTERNSDIR "Contents/Resources/share/inkscape/patterns" +# define INKSCAPE_SCREENSDIR "Contents/Resources/share/inkscape/screens" +# define INKSCAPE_SYMBOLSDIR "Contents/Resources/share/inkscape/symbols" +# define INKSCAPE_TUTORIALSDIR "Contents/Resources/share/inkscape/tutorials" +# define INKSCAPE_TEMPLATESDIR "Contents/Resources/share/inkscape/templates" +# define INKSCAPE_UIDIR "Contents/Resources/share/inkscape/ui" //CREATE V0.1 support # define CREATE_GRADIENTSDIR "/Library/Application Support/create/gradients/gimp" # define CREATE_PALETTESDIR "/Library/Application Support/create/swatches" -- cgit v1.2.3 From 963a90531a042d793f0f2e0fddbe9df0de35bf6d Mon Sep 17 00:00:00 2001 From: su_v Date: Thu, 21 Aug 2014 19:18:39 +0200 Subject: add poppler data to bundle, fix relocation support (see bug #956282) (bzr r13506.1.40) --- src/extension/internal/pdfinput/pdf-input.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src') diff --git a/src/extension/internal/pdfinput/pdf-input.cpp b/src/extension/internal/pdfinput/pdf-input.cpp index 63581bd8a..cbdaf9d20 100644 --- a/src/extension/internal/pdfinput/pdf-input.cpp +++ b/src/extension/internal/pdfinput/pdf-input.cpp @@ -647,7 +647,31 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { // Initialize the globalParams variable for poppler if (!globalParams) { +#ifdef ENABLE_OSX_APP_LOCATIONS + // + // data files for poppler are not relocatable (loaded from + // path defined at build time). This fails to work with relocatable + // application bundles for OS X. + // + // Workaround: + // 1. define $POPPLER_DATADIR env variable in app launcher script + // 2. pass custom $POPPLER_DATADIR via poppler's GlobalParams() + // + // relevant poppler commit: + // + // + // FIXES: Inkscape bug #956282, #1264793 + // TODO: report RFE upstream (full relocation support for OS X packaging) + // + gchar const *poppler_datadir = g_getenv("POPPLER_DATADIR"); + if (poppler_datadir != NULL) { + globalParams = new GlobalParams(poppler_datadir); + } else { + globalParams = new GlobalParams(); + } +#else globalParams = new GlobalParams(); +#endif // ENABLE_OSX_APP_LOCATIONS } // poppler does not use glib g_open. So on win32 we must use unicode call. code was copied from glib gstdio.c #ifndef WIN32 -- cgit v1.2.3 From 4a6dfa29131adb8c7470ab66a839d144b02542e5 Mon Sep 17 00:00:00 2001 From: su_v Date: Tue, 26 Aug 2014 10:41:57 +0200 Subject: librevenge: update to latest patch from bug #1323592 (support old and new versions of libwpg, libcdr and libvisio ) (bzr r13398.1.7) --- src/extension/internal/cdr-input.cpp | 34 ++++++++++++++++++------ src/extension/internal/vsd-input.cpp | 35 +++++++++++++++++++------ src/extension/internal/wpg-input.cpp | 50 ++++++++++++++++++++++++++++-------- src/ui/dialog/symbols.cpp | 24 ++++++++++++++--- 4 files changed, 113 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp index c4f251361..748bf4477 100644 --- a/src/extension/internal/cdr-input.cpp +++ b/src/extension/internal/cdr-input.cpp @@ -24,7 +24,21 @@ #include #include -#include + +// TODO: Drop this check when librevenge is widespread. +#if WITH_LIBCDR01 + #include + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; +#else + #include + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef libcdr::CDRStringVector RVNGStringVector; +#endif #include #include @@ -60,7 +74,7 @@ namespace Internal { class CdrImportDialog : public Gtk::Dialog { public: - CdrImportDialog(const std::vector &vec); + CdrImportDialog(const std::vector &vec); virtual ~CdrImportDialog(); bool showDialog(); @@ -86,12 +100,12 @@ private: class Gtk::VBox * vbox2; class Gtk::Widget * _previewArea; - const std::vector &_vec; // Document to be imported + const std::vector &_vec; // Document to be imported unsigned _current_page; // Current selected page int _preview_width, _preview_height; // Size of the preview area }; -CdrImportDialog::CdrImportDialog(const std::vector &vec) +CdrImportDialog::CdrImportDialog(const std::vector &vec) : _vec(vec), _current_page(1) { int num_pages = _vec.size(); @@ -210,16 +224,20 @@ void CdrImportDialog::_setPreviewPage(unsigned page) SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - librevenge::RVNGFileStream input(uri); + RVNGFileStream input(uri); if (!libcdr::CDRDocument::isSupported(&input)) { return NULL; } - librevenge::RVNGStringVector output; + RVNGStringVector output; +#if WITH_LIBCDR01 librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); if (!libcdr::CDRDocument::parse(&input, &generator)) { +#else + if (!libcdr::CDRDocument::generateSVG(&input, output)) { +#endif return NULL; } @@ -227,9 +245,9 @@ SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - std::vector tmpSVGOutput; + std::vector tmpSVGOutput; for (unsigned i=0; i\n\n"); + RVNGString tmpString("\n\n"); tmpString.append(output[i]); tmpSVGOutput.push_back(tmpString); } diff --git a/src/extension/internal/vsd-input.cpp b/src/extension/internal/vsd-input.cpp index b7e8669b8..674997d54 100644 --- a/src/extension/internal/vsd-input.cpp +++ b/src/extension/internal/vsd-input.cpp @@ -24,7 +24,22 @@ #include #include -#include + +// TODO: Drop this check when librevenge is widespread. +#if WITH_LIBVISIO01 + #include + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; +#else + #include + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef libvisio::VSDStringVector RVNGStringVector; +#endif + #include #include @@ -59,7 +74,7 @@ namespace Internal { class VsdImportDialog : public Gtk::Dialog { public: - VsdImportDialog(const std::vector &vec); + VsdImportDialog(const std::vector &vec); virtual ~VsdImportDialog(); bool showDialog(); @@ -85,12 +100,12 @@ private: class Gtk::VBox * vbox2; class Gtk::Widget * _previewArea; - const std::vector &_vec; // Document to be imported + const std::vector &_vec; // Document to be imported unsigned _current_page; // Current selected page int _preview_width, _preview_height; // Size of the preview area }; -VsdImportDialog::VsdImportDialog(const std::vector &vec) +VsdImportDialog::VsdImportDialog(const std::vector &vec) : _vec(vec), _current_page(1) { int num_pages = _vec.size(); @@ -209,16 +224,20 @@ void VsdImportDialog::_setPreviewPage(unsigned page) SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - librevenge::RVNGFileStream input(uri); + RVNGFileStream input(uri); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } - librevenge::RVNGStringVector output; + RVNGStringVector output; +#if WITH_LIBVISIO01 librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); if (!libvisio::VisioDocument::parse(&input, &generator)) { +#else + if (!libvisio::VisioDocument::generateSVG(&input, output)) { +#endif return NULL; } @@ -226,9 +245,9 @@ SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - std::vector tmpSVGOutput; + std::vector tmpSVGOutput; for (unsigned i=0; i\n\n"); + RVNGString tmpString("\n\n"); tmpString.append(output[i]); tmpSVGOutput.push_back(tmpString); } diff --git a/src/extension/internal/wpg-input.cpp b/src/extension/internal/wpg-input.cpp index c10926361..12d86a99a 100644 --- a/src/extension/internal/wpg-input.cpp +++ b/src/extension/internal/wpg-input.cpp @@ -52,8 +52,25 @@ #include "util/units.h" #include +// Take a guess and fallback to 0.2.x if no configure has run +#if !defined(WITH_LIBWPG03) && !defined(WITH_LIBWPG02) +#define WITH_LIBWPG02 1 +#endif + #include "libwpg/libwpg.h" -#include "librevenge-stream/librevenge-stream.h" +#if WITH_LIBWPG03 + #include + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGInputStream; +#else + #include "libwpd-stream/libwpd-stream.h" + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef WPXInputStream RVNGInputStream; +#endif using namespace libwpg; @@ -64,9 +81,15 @@ namespace Internal { SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - librevenge::RVNGInputStream* input = new librevenge::RVNGFileStream(uri); + RVNGInputStream* input = new RVNGFileStream(uri); +#if WITH_LIBWPG03 if (input->isStructured()) { - librevenge::RVNGInputStream* olestream = input->getSubStreamByName("PerfectOffice_MAIN"); + RVNGInputStream* olestream = input->getSubStreamByName("PerfectOffice_MAIN"); +#else + if (input->isOLEStream()) { + RVNGInputStream* olestream = input->getDocumentOLEStream("PerfectOffice_MAIN"); +#endif + if (olestream) { delete input; input = olestream; @@ -81,17 +104,24 @@ SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - librevenge::RVNGStringVector vec; - librevenge::RVNGSVGDrawingGenerator generator(vec, ""); +#if WITH_LIBWPG03 + librevenge::RVNGStringVector vec; + librevenge::RVNGSVGDrawingGenerator generator(vec, ""); - if (!libwpg::WPGraphics::parse(input, &generator) || vec.empty() || vec[0].empty()) - { + if (!libwpg::WPGraphics::parse(input, &generator) || vec.empty() || vec[0].empty()) { delete input; return NULL; - } + } - librevenge::RVNGString output("\n\n"); - output.append(vec[0]); + RVNGString output("\n\n"); + output.append(vec[0]); +#else + RVNGString output; + if (!libwpg::WPGraphics::generateSVG(input, output)) { + delete input; + return NULL; + } +#endif //printf("I've got a doc: \n%s", painter.document.c_str()); diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp index 27e40f552..a9cf8d068 100644 --- a/src/ui/dialog/symbols.cpp +++ b/src/ui/dialog/symbols.cpp @@ -62,8 +62,20 @@ #include "widgets/icon.h" #ifdef WITH_LIBVISIO -#include -#include + #include + + // TODO: Drop this check when librevenge is widespread. + #if WITH_LIBVISIO01 + #include + + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; + #else + #include + + typedef WPXFileStream RVNGFileStream; + typedef libvisio::VSDStringVector RVNGStringVector; + #endif #endif #include "verbs.h" @@ -495,16 +507,20 @@ void SymbolsDialog::iconChanged() { // Read Visio stencil files SPDocument* read_vss( gchar* fullname, gchar* filename ) { - librevenge::RVNGFileStream input(fullname); + RVNGFileStream input(fullname); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } - librevenge::RVNGStringVector output; + RVNGStringVector output; +#if WITH_LIBVISIO01 librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); if (!libvisio::VisioDocument::parseStencils(&input, &generator)) { +#else + if (!libvisio::VisioDocument::generateSVGStencils(&input, output)) { +#endif return NULL; } -- cgit v1.2.3 From 2d1ff6f2eb9798e2f1f24caa5af6b4e2cdde1df7 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Wed, 8 Oct 2014 15:50:02 +0200 Subject: Allow attributes to be set by scripts. This is a bit of a hack... Why does need special treatment? (bzr r13341.1.258) --- src/extension/implementation/script.cpp | 98 ++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index cac11031f..377b872bc 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -726,12 +726,12 @@ void Script::effect(Inkscape::Extension::Effect *module, vd->emitReconstructionStart(); copy_doc(vd->rroot, mydoc->rroot); vd->emitReconstructionFinish(); - SPObject *layer = NULL; - + // Getting the named view from the document generated by the extension SPNamedView *nv = sp_document_namedview(mydoc, NULL); //Check if it has a default layer set up + SPObject *layer = NULL; if ( nv != NULL) { if( nv->default_layer_id != 0 ) { @@ -760,19 +760,21 @@ void Script::effect(Inkscape::Extension::Effect *module, /** - \brief A function to take all the svg elements from one document - and put them in another. - \param oldroot The root node of the document to be replaced - \param newroot The root node of the document to replace it with - - This function first deletes all of the data in the old document. It - does this by creating a list of what needs to be deleted, and then - goes through the list. This two pass approach removes issues with - the list being change while parsing through it. Lots of nasty bugs. - - Then, it goes through the new document, duplicating all of the - elements and putting them into the old document. The copy - is then complete. + \brief A function to replace all the elements in an old document + by those from a new document. + document and repinserts them into an emptied old document. + \param oldroot The root node of the old (destination) document. + \param newroot The root node of the new (source) document. + + This function first deletes all the elements in the old document by + making two pass, the first to create a list of the old elements and + the second to actually delete them. This two pass approach removes issues + with the list being change while parsing through it... lots of nasty bugs. + + Then, it copies all the element in the new document into the old document. + + Finally, it replaces the attributes in the root element of the old document + by the attributes in root of the new document. */ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) { @@ -781,9 +783,19 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr g_warning("Error on copy_doc: NULL pointer input."); return; } + + // For copying attributes in root and in namedview + using Inkscape::Util::List; + using Inkscape::XML::AttributeRecord; + + // Question: Why is the "sodipodi:namedview" special? Treating it as a normal + // elmement results in crashes. + std::vector delete_list; Inkscape::XML::Node * oldroot_namedview = NULL; + Inkscape::XML::Node * newroot_namedview = NULL; + // Make list for (Inkscape::XML::Node * child = oldroot->firstChild(); child != NULL; child = child->next()) { @@ -798,14 +810,18 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr delete_list.push_back(child); } } + + // Unparent (delete) for (unsigned int i = 0; i < delete_list.size(); i++) { sp_repr_unparent(delete_list[i]); } + // Copy for (Inkscape::XML::Node * child = newroot->firstChild(); child != NULL; child = child->next()) { if (!strcmp("sodipodi:namedview", child->name())) { + newroot_namedview = child; if (oldroot_namedview != NULL) { for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild(); newroot_namedview_child != NULL; @@ -818,26 +834,44 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr } } - { - using Inkscape::Util::List; - using Inkscape::XML::AttributeRecord; - std::vector attribs; + std::vector attribs; - // Make a list of all attributes of the old root node. - for (List iter = oldroot->attributeList(); iter; ++iter) { - attribs.push_back(g_quark_to_string(iter->key)); - } + // Must explicitly copy root attributes. - // Delete the attributes of the old root nodes. - for (std::vector::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { - oldroot->setAttribute(*it, NULL); - } + // Make a list of all attributes of the old root node. + for (List iter = oldroot->attributeList(); iter; ++iter) { + attribs.push_back(g_quark_to_string(iter->key)); + } - // Set the new attributes. - for (List iter = newroot->attributeList(); iter; ++iter) { - gchar const *name = g_quark_to_string(iter->key); - oldroot->setAttribute(name, newroot->attribute(name)); - } + // Delete the attributes of the old root node. + for (std::vector::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { + oldroot->setAttribute(*it, NULL); + } + + // Set the new attributes. + for (List iter = newroot->attributeList(); iter; ++iter) { + gchar const *name = g_quark_to_string(iter->key); + oldroot->setAttribute(name, newroot->attribute(name)); + } + + attribs.clear(); + + // Must explicitly copy namedview attributes. + + // Make a list of all attributes of the old namedview node. + for (List iter = oldroot_namedview->attributeList(); iter; ++iter) { + attribs.push_back(g_quark_to_string(iter->key)); + } + + // Delete the attributes of the old namedview node. + for (std::vector::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { + oldroot_namedview->setAttribute(*it, NULL); + } + + // Set the new attributes. + for (List iter = newroot_namedview->attributeList(); iter; ++iter) { + gchar const *name = g_quark_to_string(iter->key); + oldroot_namedview->setAttribute(name, newroot_namedview->attribute(name)); } /** \todo Restore correct layer */ -- cgit v1.2.3 From c62bc930f1e85d3ab19fb474653ad2db9a41eca5 Mon Sep 17 00:00:00 2001 From: Alvin Penner Date: Wed, 8 Oct 2014 13:05:26 -0400 Subject: avoid round-off error when calculating viewbox transform c2p (Bug 1374614 and Bug 1235279) Fixed bugs: - https://launchpad.net/bugs/1374614 (bzr r13581) --- src/viewbox.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/viewbox.cpp b/src/viewbox.cpp index f59909abc..6d677d57d 100644 --- a/src/viewbox.cpp +++ b/src/viewbox.cpp @@ -164,16 +164,16 @@ void SPViewBox::set_preserveAspectRatio(const gchar* value) { void SPViewBox::apply_viewbox(const Geom::Rect& in) { /* Determine actual viewbox in viewport coordinates */ - double x = 0.0; - double y = 0.0; - double width = in.width(); - double height = in.height(); + float x = 0.0; + float y = 0.0; + float width = in.width(); + float height = in.height(); // std::cout << " width: " << width << " height: " << height << std::endl; if (this->aspect_align != SP_ASPECT_NONE) { /* Things are getting interesting */ - double scalex = in.width() / this->viewBox.width(); - double scaley = in.height() / this->viewBox.height(); + double scalex = in.width() / ((float) this->viewBox.width()); + double scaley = in.height() / ((float) this->viewBox.height()); double scale = (this->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley); width = this->viewBox.width() * scale; height = this->viewBox.height() * scale; @@ -217,12 +217,12 @@ void SPViewBox::apply_viewbox(const Geom::Rect& in) { /* Viewbox transform from scale and position */ Geom::Affine q; - q[0] = width / this->viewBox.width(); + q[0] = width / ((float) this->viewBox.width()); q[1] = 0.0; q[2] = 0.0; - q[3] = height / this->viewBox.height(); - q[4] = x - q[0] * this->viewBox.left(); - q[5] = y - q[3] * this->viewBox.top(); + q[3] = height / ((float) this->viewBox.height()); + q[4] = x - q[0] * ((float) this->viewBox.left()); + q[5] = y - q[3] * ((float) this->viewBox.top()); // std::cout << " q\n" << q << std::endl; -- cgit v1.2.3 From 8e6879acee859f216f5e63d97a4ba80c7cb0b94b Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Wed, 8 Oct 2014 18:56:12 -0400 Subject: Fix gtk3 build at risk of breaking windows build (bzr r13341.1.262) --- src/ui/widget/highlight-picker.cpp | 10 +--------- src/ui/widget/insertordericon.cpp | 9 --------- src/ui/widget/insertordericon.h | 4 ++-- 3 files changed, 3 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/ui/widget/highlight-picker.cpp b/src/ui/widget/highlight-picker.cpp index c03f526dc..8593f0bdf 100644 --- a/src/ui/widget/highlight-picker.cpp +++ b/src/ui/widget/highlight-picker.cpp @@ -7,19 +7,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - +#include #include - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include -#endif #include "display/cairo-utils.h" - #include "highlight-picker.h" #include "widgets/icon.h" #include "widgets/toolbox.h" diff --git a/src/ui/widget/insertordericon.cpp b/src/ui/widget/insertordericon.cpp index 17930daa2..a28b0f834 100644 --- a/src/ui/widget/insertordericon.cpp +++ b/src/ui/widget/insertordericon.cpp @@ -9,15 +9,6 @@ #include "ui/widget/insertordericon.h" -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include -#endif - - #include #include "widgets/icon.h" diff --git a/src/ui/widget/insertordericon.h b/src/ui/widget/insertordericon.h index e6c2e1c5b..fb3412d3f 100644 --- a/src/ui/widget/insertordericon.h +++ b/src/ui/widget/insertordericon.h @@ -10,12 +10,12 @@ */ #if HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif +#include #include #include -#include namespace Inkscape { namespace UI { -- cgit v1.2.3 From f11f0a40d6a7f087512a0d1c52ebde5dbba2a76f Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Wed, 8 Oct 2014 20:24:25 -0400 Subject: Un-deprecate NodeEventVector - The resulting refactoring from such a deprecation will not be worth the effort applied to it, and the current system is not wrong or broken. (bzr r13341.1.263) --- src/xml/node-event-vector.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src') diff --git a/src/xml/node-event-vector.h b/src/xml/node-event-vector.h index 416640b86..16add2960 100644 --- a/src/xml/node-event-vector.h +++ b/src/xml/node-event-vector.h @@ -58,11 +58,7 @@ struct NodeEventVector { void (* attr_changed) (Node *repr, char const *key, char const *oldval, char const *newval, bool is_interactive, void* data); void (* content_changed) (Node *repr, char const *oldcontent, char const *newcontent, void * data); void (* order_changed) (Node *repr, Node *child, Node *oldref, Node *newref, void* data); -} -#ifdef __GNUC__ -__attribute__((deprecated)) -#endif -; +}; } } -- cgit v1.2.3 From 0dc05429417f53ea871f40505f65de97480d71a3 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Thu, 9 Oct 2014 11:06:21 +0200 Subject: Add comment. (bzr r13582) --- src/viewbox.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/viewbox.cpp b/src/viewbox.cpp index 6d677d57d..662b05686 100644 --- a/src/viewbox.cpp +++ b/src/viewbox.cpp @@ -164,6 +164,7 @@ void SPViewBox::set_preserveAspectRatio(const gchar* value) { void SPViewBox::apply_viewbox(const Geom::Rect& in) { /* Determine actual viewbox in viewport coordinates */ + /* These are floats since SVGLength is a float: See bug 1374614 */ float x = 0.0; float y = 0.0; float width = in.width(); -- cgit v1.2.3 From 7245058959fe4437b76b481e8ee32ceeaee78a38 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Thu, 9 Oct 2014 11:10:01 +0200 Subject: Allow attributes to be set by scripts. (bzr r13583) --- src/extension/implementation/script.cpp | 144 ++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index f0fd3711b..f9241b1b7 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -1,28 +1,18 @@ -/** \file - * Code for handling extensions (i.e.\ scripts). - */ -/* +/** + * Code for handling extensions (i.e. scripts). + * * Authors: * Bryce Harrington * Ted Gould * Jon A. Cruz * Abhishek Sharma * - * Copyright (C) 2002-2005,2007 Authors + * Copyright (C) 2002-2007 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ -#define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include -#endif - +#include #include #include #include @@ -32,44 +22,38 @@ #include #include -#include #include -#include "ui/view/view.h" #include "desktop-handles.h" #include "desktop.h" -#include "selection.h" -#include "sp-namedview.h" -#include "io/sys.h" -#include "preferences.h" -#include "../system.h" +#include "dialogs/dialog-events.h" #include "extension/effect.h" #include "extension/output.h" #include "extension/input.h" #include "extension/db.h" -#include "script.h" -#include "dialogs/dialog-events.h" #include "inkscape.h" +#include "io/sys.h" +#include "preferences.h" +#include "script.h" +#include "selection.h" +#include "sp-namedview.h" +#include "extension/system.h" +#include "ui/view/view.h" #include "xml/node.h" #include "xml/attribute-record.h" #include "util/glib-list-iterators.h" #include "path-prefix.h" - #ifdef WIN32 #include #include #include "registrytool.h" #endif - - /** This is the command buffer that gets allocated from the stack */ #define BUFSIZE (255) - - /* Namespaces */ namespace Inkscape { namespace Extension { @@ -742,12 +726,12 @@ void Script::effect(Inkscape::Extension::Effect *module, vd->emitReconstructionStart(); copy_doc(vd->rroot, mydoc->rroot); vd->emitReconstructionFinish(); - SPObject *layer = NULL; - + // Getting the named view from the document generated by the extension SPNamedView *nv = sp_document_namedview(mydoc, NULL); //Check if it has a default layer set up + SPObject *layer = NULL; if ( nv != NULL) { if( nv->default_layer_id != 0 ) { @@ -776,19 +760,21 @@ void Script::effect(Inkscape::Extension::Effect *module, /** - \brief A function to take all the svg elements from one document - and put them in another. - \param oldroot The root node of the document to be replaced - \param newroot The root node of the document to replace it with - - This function first deletes all of the data in the old document. It - does this by creating a list of what needs to be deleted, and then - goes through the list. This two pass approach removes issues with - the list being change while parsing through it. Lots of nasty bugs. - - Then, it goes through the new document, duplicating all of the - elements and putting them into the old document. The copy - is then complete. + \brief A function to replace all the elements in an old document + by those from a new document. + document and repinserts them into an emptied old document. + \param oldroot The root node of the old (destination) document. + \param newroot The root node of the new (source) document. + + This function first deletes all the elements in the old document by + making two pass, the first to create a list of the old elements and + the second to actually delete them. This two pass approach removes issues + with the list being change while parsing through it... lots of nasty bugs. + + Then, it copies all the element in the new document into the old document. + + Finally, it replaces the attributes in the root element of the old document + by the attributes in root of the new document. */ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) { @@ -797,9 +783,21 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr g_warning("Error on copy_doc: NULL pointer input."); return; } + + // For copying attributes in root and in namedview + using Inkscape::Util::List; + using Inkscape::XML::AttributeRecord; + + // Question: Why is the "sodipodi:namedview" special? Treating it as a normal + // elmement results in crashes. + // Seems to be a bug: + // http://inkscape.13.x6.nabble.com/Effect-that-modifies-the-document-properties-tt2822126.html + std::vector delete_list; Inkscape::XML::Node * oldroot_namedview = NULL; + Inkscape::XML::Node * newroot_namedview = NULL; + // Make list for (Inkscape::XML::Node * child = oldroot->firstChild(); child != NULL; child = child->next()) { @@ -814,14 +812,18 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr delete_list.push_back(child); } } + + // Unparent (delete) for (unsigned int i = 0; i < delete_list.size(); i++) { sp_repr_unparent(delete_list[i]); } + // Copy for (Inkscape::XML::Node * child = newroot->firstChild(); child != NULL; child = child->next()) { if (!strcmp("sodipodi:namedview", child->name())) { + newroot_namedview = child; if (oldroot_namedview != NULL) { for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild(); newroot_namedview_child != NULL; @@ -834,26 +836,44 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr } } - { - using Inkscape::Util::List; - using Inkscape::XML::AttributeRecord; - std::vector attribs; + std::vector attribs; - // Make a list of all attributes of the old root node. - for (List iter = oldroot->attributeList(); iter; ++iter) { - attribs.push_back(g_quark_to_string(iter->key)); - } + // Must explicitly copy root attributes. - // Delete the attributes of the old root nodes. - for (std::vector::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { - oldroot->setAttribute(*it, NULL); - } + // Make a list of all attributes of the old root node. + for (List iter = oldroot->attributeList(); iter; ++iter) { + attribs.push_back(g_quark_to_string(iter->key)); + } - // Set the new attributes. - for (List iter = newroot->attributeList(); iter; ++iter) { - gchar const *name = g_quark_to_string(iter->key); - oldroot->setAttribute(name, newroot->attribute(name)); - } + // Delete the attributes of the old root node. + for (std::vector::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { + oldroot->setAttribute(*it, NULL); + } + + // Set the new attributes. + for (List iter = newroot->attributeList(); iter; ++iter) { + gchar const *name = g_quark_to_string(iter->key); + oldroot->setAttribute(name, newroot->attribute(name)); + } + + attribs.clear(); + + // Must explicitly copy namedview attributes. + + // Make a list of all attributes of the old namedview node. + for (List iter = oldroot_namedview->attributeList(); iter; ++iter) { + attribs.push_back(g_quark_to_string(iter->key)); + } + + // Delete the attributes of the old namedview node. + for (std::vector::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { + oldroot_namedview->setAttribute(*it, NULL); + } + + // Set the new attributes. + for (List iter = newroot_namedview->attributeList(); iter; ++iter) { + gchar const *name = g_quark_to_string(iter->key); + oldroot_namedview->setAttribute(name, newroot_namedview->attribute(name)); } /** \todo Restore correct layer */ @@ -1063,4 +1083,4 @@ int Script::execute (const std::list &in_command, fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : -- cgit v1.2.3 From 259976c863ccdd6c28698125179b20b91da32ec0 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Thu, 9 Oct 2014 14:27:49 +0200 Subject: Apply cx, cy, etc. from template to newly created document window. (bzr r13341.1.264) --- src/ui/dialog/template-widget.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/dialog/template-widget.cpp b/src/ui/dialog/template-widget.cpp index 9758b35ac..f79d166f2 100644 --- a/src/ui/dialog/template-widget.cpp +++ b/src/ui/dialog/template-widget.cpp @@ -24,6 +24,7 @@ #include "document.h" #include "document-undo.h" #include "file.h" +#include "sp-namedview.h" #include "extension/implementation/implementation.h" #include "inkscape.h" @@ -69,7 +70,10 @@ void TemplateWidget::create() _current_template.tpl_effect->effect(desc); DocumentUndo::clearUndo(sp_desktop_document(desc)); sp_desktop_document(desc)->setModifiedSinceSave(false); - + + // Apply cx,cy etc. from document + sp_namedview_window_from_document( desc ); + if (desktop) desktop->clearWaitingCursor(); } -- cgit v1.2.3 From 01a8acb49126aa37fee65b3e350ce69e87abecc8 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Thu, 9 Oct 2014 14:32:16 +0200 Subject: Apply cx, cy, etc. from template to newly created document window. (bzr r13586) --- src/ui/dialog/template-widget.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/dialog/template-widget.cpp b/src/ui/dialog/template-widget.cpp index 9758b35ac..f79d166f2 100644 --- a/src/ui/dialog/template-widget.cpp +++ b/src/ui/dialog/template-widget.cpp @@ -24,6 +24,7 @@ #include "document.h" #include "document-undo.h" #include "file.h" +#include "sp-namedview.h" #include "extension/implementation/implementation.h" #include "inkscape.h" @@ -69,7 +70,10 @@ void TemplateWidget::create() _current_template.tpl_effect->effect(desc); DocumentUndo::clearUndo(sp_desktop_document(desc)); sp_desktop_document(desc)->setModifiedSinceSave(false); - + + // Apply cx,cy etc. from document + sp_namedview_window_from_document( desc ); + if (desktop) desktop->clearWaitingCursor(); } -- cgit v1.2.3 From daa321bfda680f3a65ed9f5f09dcca42e8c65dc7 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Thu, 9 Oct 2014 14:34:04 +0200 Subject: Added comment. (bzr r13341.1.265) --- src/extension/implementation/script.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index 377b872bc..99c882a01 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -790,6 +790,8 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr // Question: Why is the "sodipodi:namedview" special? Treating it as a normal // elmement results in crashes. + // Seems to be a bug: + // http://inkscape.13.x6.nabble.com/Effect-that-modifies-the-document-properties-tt2822126.html std::vector delete_list; Inkscape::XML::Node * oldroot_namedview = NULL; -- cgit v1.2.3 From 60aeb869a2fe7980724b933484cb871c72078ca6 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sat, 11 Oct 2014 19:05:39 +0200 Subject: Fix "Argument with 'nonnull' attribute passed null" API bug in extension color parameter construction. Clang static analyzer has found an "Argument with 'nonnull' attribute passed null". The applied fix here checks if the defaulthex argument is nullptr before making the call. A default initialization value is added to the constructor in case defaulthex == nullptr. (with additional whitespace improvements) (bzr r13587) --- src/extension/param/color.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/extension/param/color.cpp b/src/extension/param/color.cpp index 0a2598c56..5bd70359f 100644 --- a/src/extension/param/color.cpp +++ b/src/extension/param/color.cpp @@ -59,9 +59,10 @@ guint32 ParamColor::set( guint32 in, SPDocument * /*doc*/, Inkscape::XML::Node * return _value; } -ParamColor::ParamColor (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _changeSignal(0) +ParamColor::ParamColor(const gchar *name, const gchar *guitext, const gchar *desc, const Parameter::_scope_t scope, + bool gui_hidden, const gchar *gui_tip, Inkscape::Extension::Extension *ext, + Inkscape::XML::Node *xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), _value(0), _changeSignal(0) { const char * defaulthex = NULL; if (xml->firstChild() != NULL) @@ -75,7 +76,8 @@ ParamColor::ParamColor (const gchar * name, const gchar * guitext, const gchar * if (!paramval.empty()) defaulthex = paramval.data(); - _value = atoi(defaulthex); + if (defaulthex) + _value = atoi(defaulthex); } void ParamColor::string(std::string &string) const @@ -87,7 +89,7 @@ void ParamColor::string(std::string &string) const Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/, sigc::signal * changeSignal ) { - if (_gui_hidden) return NULL; + if (_gui_hidden) return NULL; _changeSignal = new sigc::signal(*changeSignal); Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); -- cgit v1.2.3 From a38add027810b9725f81cebb099282a8656e3aa3 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sat, 11 Oct 2014 19:15:03 +0200 Subject: Fix uninitialized variable use in svg-color.cpp. (additionally reduce scope of variable) Bug found using clang static analyzer. The bug happens with color_out not being initialized upon definition; then 1. if(icc) == true 2. if(prof) == true 3. if(trans) == false 4. *r = color_out[0]; <-- un-init use Fixed by initializing color_out (with black). (bzr r13588) --- src/svg/svg-color.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 5108b7702..c9f22f8a4 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -511,18 +511,18 @@ sp_svg_create_color_hash() void icc_color_to_sRGB(SVGICCColor* icc, guchar* r, guchar* g, guchar* b) { - guchar color_out[4]; - guchar color_in[4]; if (icc) { g_message("profile name: %s", icc->colorProfile.c_str()); Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(icc->colorProfile.c_str()); if ( prof ) { + guchar color_out[4] = {0,0,0,0}; cmsHTRANSFORM trans = prof->getTransfToSRGB8(); if ( trans ) { std::vector comps = colorspace::getColorSpaceInfo( prof ); size_t count = CMSSystem::getChannelCount( prof ); size_t cap = std::min(count, comps.size()); + guchar color_in[4]; for (size_t i = 0; i < cap; i++) { color_in[i] = static_cast((((gdouble)icc->colors[i]) * 256.0) * (gdouble)comps[i].scale); g_message("input[%d]: %d", (int)i, (int)color_in[i]); -- cgit v1.2.3 From ccb75de1cadbe4c2e8ad833cbbcd1bc8f79cd548 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sat, 11 Oct 2014 19:21:54 +0200 Subject: LPEVonKoch : remove redundant assignment (is taken care of by the following if-statement) Found by clang static analyzer (bzr r13589) --- src/live_effects/lpe-vonkoch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index 8b0b716fe..7e396e84a 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -133,7 +133,7 @@ LPEVonKoch::doEffect_path (std::vector const & path_in) for (unsigned k = 0; k < path_in.size(); k++){ path_in_complexity+=path_in[k].size(); } - double complexity = std::pow(transforms.size(),nbgenerations)*path_in_complexity; + double complexity; if (drawall.get_value()){ int k = transforms.size(); if(k>1){ -- cgit v1.2.3 From 82565dc1a86e232f1a809624b14daa2c831b5282 Mon Sep 17 00:00:00 2001 From: Alvin Penner Date: Sat, 11 Oct 2014 17:55:07 -0400 Subject: maintain page size units when resizing page to drawing (Bug 1310787) Fixed bugs: - https://launchpad.net/bugs/1310787 (bzr r13590) --- src/document.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/document.cpp b/src/document.cpp index f94a9f04f..f7edf5ed3 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -706,7 +706,12 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) double const h = rect.height(); double const old_height = getHeight().value("px"); - Inkscape::Util::Unit const *px = unit_table.getUnit("px"); + Inkscape::Util::Unit const *nv_units = unit_table.getUnit("px"); + SPNamedView *nv = sp_document_namedview(this, NULL); + if (nv != NULL) { + if (nv->getAttribute("units")) + nv_units = unit_table.getUnit(nv->getAttribute("units")); + } /* in px */ double margin_top = 0.0; @@ -714,22 +719,16 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) double margin_right = 0.0; double margin_bottom = 0.0; - SPNamedView *nv = sp_document_namedview(this, NULL); - if (with_margins && nv) { if (nv != NULL) { - gchar const * const units_abbr = nv->getAttribute("units"); - Inkscape::Util::Unit const *margin_units = NULL; - if (units_abbr) { - margin_units = unit_table.getUnit(units_abbr); - } - if (!margin_units) { - margin_units = px; - } - margin_top = nv->getMarginLength("fit-margin-top",margin_units, px, w, h, false); - margin_left = nv->getMarginLength("fit-margin-left",margin_units, px, w, h, true); - margin_right = nv->getMarginLength("fit-margin-right",margin_units, px, w, h, true); - margin_bottom = nv->getMarginLength("fit-margin-bottom",margin_units, px, w, h, false); + margin_top = nv->getMarginLength("fit-margin-top", nv_units, unit_table.getUnit("px"), w, h, false); + margin_left = nv->getMarginLength("fit-margin-left", nv_units, unit_table.getUnit("px"), w, h, true); + margin_right = nv->getMarginLength("fit-margin-right", nv_units, unit_table.getUnit("px"), w, h, true); + margin_bottom = nv->getMarginLength("fit-margin-bottom", nv_units, unit_table.getUnit("px"), w, h, false); + margin_top = Inkscape::Util::Quantity::convert(margin_top, nv_units, "px"); + margin_left = Inkscape::Util::Quantity::convert(margin_left, nv_units, "px"); + margin_right = Inkscape::Util::Quantity::convert(margin_right, nv_units, "px"); + margin_bottom = Inkscape::Util::Quantity::convert(margin_bottom, nv_units, "px"); } } @@ -738,8 +737,8 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) rect.max() + Geom::Point(margin_right, margin_top)); - setWidth(Inkscape::Util::Quantity(rect_with_margins.width(), px)); - setHeight(Inkscape::Util::Quantity(rect_with_margins.height(), px)); + setWidth(Inkscape::Util::Quantity(Inkscape::Util::Quantity::convert(rect_with_margins.width(), "px", nv_units), nv_units)); + setHeight(Inkscape::Util::Quantity(Inkscape::Util::Quantity::convert(rect_with_margins.height(), "px", nv_units), nv_units)); Geom::Translate const tr( Geom::Point(0, old_height - rect_with_margins.height()) -- cgit v1.2.3 From f369e0ed4157c33e09c426b40ec1e39c7591d2c6 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 12:04:17 +0200 Subject: LPEVonKoch: improve rev. 13589 by removing the else branch for complexity calculation for better human readability (bzr r13591) --- src/live_effects/lpe-vonkoch.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index 7e396e84a..709c9e56e 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -132,8 +132,8 @@ LPEVonKoch::doEffect_path (std::vector const & path_in) int path_in_complexity = 0; for (unsigned k = 0; k < path_in.size(); k++){ path_in_complexity+=path_in[k].size(); - } - double complexity; + } + double complexity = std::pow(transforms.size(), nbgenerations) * path_in_complexity; if (drawall.get_value()){ int k = transforms.size(); if(k>1){ @@ -141,8 +141,6 @@ LPEVonKoch::doEffect_path (std::vector const & path_in) }else{ complexity = nbgenerations*k*path_in_complexity; } - }else{ - complexity = std::pow(transforms.size(),nbgenerations)*path_in_complexity; } if (complexity > double(maxComplexity)){ g_warning("VonKoch lpe's output too complex. Effect bypassed."); -- cgit v1.2.3 From 6fda35fe16784f75a6a98b4d44141177616a06ef Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 12:36:25 +0200 Subject: Mass fix whitespace (clang-format) before working on bug (bzr r13593) --- src/trace/potrace/trace.cpp | 2059 ++++++++++++++++++++++--------------------- 1 file changed, 1040 insertions(+), 1019 deletions(-) (limited to 'src') diff --git a/src/trace/potrace/trace.cpp b/src/trace/potrace/trace.cpp index f1e88a908..2bdefb90a 100644 --- a/src/trace/potrace/trace.cpp +++ b/src/trace/potrace/trace.cpp @@ -16,124 +16,129 @@ #include "trace.h" #include "progress.h" -#define INFTY 10000000 /* it suffices that this is longer than any - path; it need not be really infinite */ -#define COS179 -0.999847695156 /* the cosine of 179 degrees */ +#define INFTY 10000000 // it suffices that this is longer than any + // path; it need not be really infinite +#define COS179 -0.999847695156 // the cosine of 179 degrees /* ---------------------------------------------------------------------- */ #define SAFE_MALLOC(var, n, typ) \ - if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error + if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error /* ---------------------------------------------------------------------- */ /* auxiliary functions */ /* return a direction that is 90 degrees counterclockwise from p2-p0, but then restricted to one of the major wind directions (n, nw, w, etc) */ -static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) { - point_t r; - - r.y = sign(p2.x-p0.x); - r.x = -sign(p2.y-p0.y); +static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) +{ + point_t r; + + r.y = sign(p2.x - p0.x); + r.x = -sign(p2.y - p0.y); - return r; + return r; } /* return (p1-p0)x(p2-p0), the area of the parallelogram */ -static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) { - double x1, y1, x2, y2; +static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) +{ + double x1, y1, x2, y2; - x1 = p1.x-p0.x; - y1 = p1.y-p0.y; - x2 = p2.x-p0.x; - y2 = p2.y-p0.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; - return x1*y2 - x2*y1; + return x1 * y2 - x2 * y1; } /* ddenom/dpara have the property that the square of radius 1 centered at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */ -static inline double ddenom(dpoint_t p0, dpoint_t p2) { - point_t r = dorth_infty(p0, p2); +static inline double ddenom(dpoint_t p0, dpoint_t p2) +{ + point_t r = dorth_infty(p0, p2); - return r.y*(p2.x-p0.x) - r.x*(p2.y-p0.y); + return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y); } /* return 1 if a <= b < c < a, in a cyclic sense (mod n) */ -static inline int cyclic(int a, int b, int c) { - if (a<=c) { - return (a<=b && blen; - sums_t *sums = pp->sums; - - double x, y, x2, xy, y2; - double k; - double a, b, c, lambda2, l; - int r=0; /* rotations from i to j */ - - while (j>=n) { - j-=n; - r+=1; - } - while (i>=n) { - i-=n; - r-=1; - } - while (j<0) { - j+=n; - r-=1; - } - while (i<0) { - i+=n; - r+=1; - } - - x = sums[j+1].x-sums[i].x+r*sums[n].x; - y = sums[j+1].y-sums[i].y+r*sums[n].y; - x2 = sums[j+1].x2-sums[i].x2+r*sums[n].x2; - xy = sums[j+1].xy-sums[i].xy+r*sums[n].xy; - y2 = sums[j+1].y2-sums[i].y2+r*sums[n].y2; - k = j+1-i+r*n; - - ctr->x = x/k; - ctr->y = y/k; - - a = (x2-(double)x*x/k)/k; - b = (xy-(double)x*y/k)/k; - c = (y2-(double)y*y/k)/k; - - lambda2 = (a+c+sqrt((a-c)*(a-c)+4*b*b))/2; /* larger e.value */ - - /* now find e.vector for lambda2 */ - a -= lambda2; - c -= lambda2; - - if (fabs(a) >= fabs(c)) { - l = sqrt(a*a+b*b); - if (l!=0) { - dir->x = -b/l; - dir->y = a/l; +static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *dir) +{ + /* assume ilen; + sums_t *sums = pp->sums; + + double x, y, x2, xy, y2; + double k; + double a, b, c, lambda2, l; + int r = 0; /* rotations from i to j */ + + while (j >= n) { + j -= n; + r += 1; + } + while (i >= n) { + i -= n; + r -= 1; + } + while (j < 0) { + j += n; + r -= 1; + } + while (i < 0) { + i += n; + r += 1; + } + + x = sums[j + 1].x - sums[i].x + r * sums[n].x; + y = sums[j + 1].y - sums[i].y + r * sums[n].y; + x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; + xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; + y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; + k = j + 1 - i + r * n; + + ctr->x = x / k; + ctr->y = y / k; + + a = (x2 - (double)x * x / k) / k; + b = (xy - (double)x * y / k) / k; + c = (y2 - (double)y * y / k) / k; + + lambda2 = (a + c + sqrt((a - c) * (a - c) + 4 * b * b)) / 2; /* larger e.value */ + + /* now find e.vector for lambda2 */ + a -= lambda2; + c -= lambda2; + + if (fabs(a) >= fabs(c)) { + l = sqrt(a * a + b * b); + if (l != 0) { + dir->x = -b / l; + dir->y = a / l; + } + } else { + l = sqrt(c * c + b * b); + if (l != 0) { + dir->x = -c / l; + dir->y = b / l; + } } - } else { - l = sqrt(c*c+b*b); - if (l!=0) { - dir->x = -c/l; - dir->y = b/l; + if (l == 0) { + dir->x = dir->y = 0; /* sometimes this can happen when k=4: + the two eigenvalues coincide */ } - } - if (l==0) { - dir->x = dir->y = 0; /* sometimes this can happen when k=4: - the two eigenvalues coincide */ - } } /* the type of (affine) quadratic forms, represented as symmetric 3x3 @@ -142,155 +147,158 @@ static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *di typedef double quadform_t[3][3]; /* Apply quadratic form Q to vector w = (w.x,w.y) */ -static inline double quadform(quadform_t Q, dpoint_t w) { - double v[3]; - int i, j; - double sum; - - v[0] = w.x; - v[1] = w.y; - v[2] = 1; - sum = 0.0; - - for (i=0; i<3; i++) { - for (j=0; j<3; j++) { - sum += v[i] * Q[i][j] * v[j]; +static inline double quadform(quadform_t Q, dpoint_t w) +{ + double v[3]; + int i, j; + double sum; + + v[0] = w.x; + v[1] = w.y; + v[2] = 1; + sum = 0.0; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + sum += v[i] * Q[i][j] * v[j]; + } } - } - return sum; + return sum; } /* calculate p1 x p2 */ -static inline int xprod(point_t p1, point_t p2) { - return p1.x*p2.y - p1.y*p2.x; -} +static inline int xprod(point_t p1, point_t p2) { return p1.x * p2.y - p1.y * p2.x; } /* calculate (p1-p0)x(p3-p2) */ -static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double x1, y1, x2, y2; +static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; - return x1*y2 - x2*y1; + return x1 * y2 - x2 * y1; } /* calculate (p1-p0)*(p2-p0) */ -static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) { - double x1, y1, x2, y2; +static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) +{ + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; - return x1*x2 + y1*y2; + return x1 * x2 + y1 * y2; } /* calculate (p1-p0)*(p3-p2) */ -static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double x1, y1, x2, y2; +static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; - return x1*x2 + y1*y2; + return x1 * x2 + y1 * y2; } /* calculate distance between two points */ -static inline double ddist(dpoint_t p, dpoint_t q) { - return sqrt(sq(p.x-q.x)+sq(p.y-q.y)); -} +static inline double ddist(dpoint_t p, dpoint_t q) { return sqrt(sq(p.x - q.x) + sq(p.y - q.y)); } /* calculate point of a bezier curve */ -static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double s = 1-t; - dpoint_t res; +static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double s = 1 - t; + dpoint_t res; - /* Note: a good optimizing compiler (such as gcc-3) reduces the - following to 16 multiplications, using common subexpression - elimination. */ + /* Note: a good optimizing compiler (such as gcc-3) reduces the + following to 16 multiplications, using common subexpression + elimination. */ - res.x = s*s*s*p0.x + 3*(s*s*t)*p1.x + 3*(t*t*s)*p2.x + t*t*t*p3.x; - res.y = s*s*s*p0.y + 3*(s*s*t)*p1.y + 3*(t*t*s)*p2.y + t*t*t*p3.y; + res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x; + res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y; - return res; + return res; } /* calculate the point t in [0..1] on the (convex) bezier curve (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no solution in [0..1]. */ -static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) { - double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ - double a, b, c; /* a t^2 + b t + c = 0 */ - double d, s, r1, r2; - - A = cprod(p0, p1, q0, q1); - B = cprod(p1, p2, q0, q1); - C = cprod(p2, p3, q0, q1); - - a = A - 2*B + C; - b = -2*A + 2*B; - c = A; - - d = b*b - 4*a*c; - - if (a==0 || d<0) { - return -1.0; - } - - s = sqrt(d); - - r1 = (-b + s) / (2 * a); - r2 = (-b - s) / (2 * a); - - if (r1 >= 0 && r1 <= 1) { - return r1; - } else if (r2 >= 0 && r2 <= 1) { - return r2; - } else { - return -1.0; - } +static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) +{ + double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ + double a, b, c; /* a t^2 + b t + c = 0 */ + double d, s, r1, r2; + + A = cprod(p0, p1, q0, q1); + B = cprod(p1, p2, q0, q1); + C = cprod(p2, p3, q0, q1); + + a = A - 2 * B + C; + b = -2 * A + 2 * B; + c = A; + + d = b * b - 4 * a * c; + + if (a == 0 || d < 0) { + return -1.0; + } + + s = sqrt(d); + + r1 = (-b + s) / (2 * a); + r2 = (-b - s) / (2 * a); + + if (r1 >= 0 && r1 <= 1) { + return r1; + } else if (r2 >= 0 && r2 <= 1) { + return r2; + } else { + return -1.0; + } } /* ---------------------------------------------------------------------- */ /* Preparation: fill in the sum* fields of a path (used for later rapid summing). Return 0 on success, 1 with errno set on failure. */ -static int calc_sums(privpath_t *pp) { - int i, x, y; - int n = pp->len; - - SAFE_MALLOC(pp->sums, pp->len+1, sums_t); - - /* origin */ - pp->x0 = pp->pt[0].x; - pp->y0 = pp->pt[0].y; - - /* preparatory computation for later fast summing */ - pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; - for (i=0; ipt[i].x - pp->x0; - y = pp->pt[i].y - pp->y0; - pp->sums[i+1].x = pp->sums[i].x + x; - pp->sums[i+1].y = pp->sums[i].y + y; - pp->sums[i+1].x2 = pp->sums[i].x2 + x*x; - pp->sums[i+1].xy = pp->sums[i].xy + x*y; - pp->sums[i+1].y2 = pp->sums[i].y2 + y*y; - } - return 0; - - malloc_error: - return 1; +static int calc_sums(privpath_t *pp) +{ + int i, x, y; + int n = pp->len; + + SAFE_MALLOC(pp->sums, pp->len + 1, sums_t); + + /* origin */ + pp->x0 = pp->pt[0].x; + pp->y0 = pp->pt[0].y; + + /* preparatory computation for later fast summing */ + pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; + for (i = 0; i < n; i++) { + x = pp->pt[i].x - pp->x0; + y = pp->pt[i].y - pp->y0; + pp->sums[i + 1].x = pp->sums[i].x + x; + pp->sums[i + 1].y = pp->sums[i].y + y; + pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x; + pp->sums[i + 1].xy = pp->sums[i].xy + x * y; + pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y; + } + return 0; + +malloc_error: + return 1; } /* ---------------------------------------------------------------------- */ /* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the - "lon" component of a path object (based on pt/len). For each i, + "lon" component of a path object (based on pt/len). For each i, lon[i] is the furthest index such that a straight line can be drawn from i to lon[i]. Return 1 on error with errno set, else 0. */ @@ -318,206 +326,208 @@ static int calc_sums(privpath_t *pp) { substantial. */ /* returns 0 on success, 1 on error with errno set */ -static int calc_lon(privpath_t *pp) { - point_t *pt = pp->pt; - int n = pp->len; - int i, j, k, k1; - int ct[4], dir; - point_t constraint[2]; - point_t cur; - point_t off; - int *pivk = NULL; /* pivk[n] */ - int *nc = NULL; /* nc[n]: next corner */ - point_t dk; /* direction of k-k1 */ - int a, b, c, d; - - SAFE_MALLOC(pivk, n, int); - SAFE_MALLOC(nc, n, int); - - /* initialize the nc data structure. Point from each point to the - furthest future point to which it is connected by a vertical or - horizontal segment. We take advantage of the fact that there is - always a direction change at 0 (due to the path decomposition - algorithm). But even if this were not so, there is no harm, as - in practice, correctness does not depend on the word "furthest" - above. */ - k = 0; - for (i=n-1; i>=0; i--) { - if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { - k = i+1; /* necessarily ilon, n, int); - - /* determine pivot points: for each i, let pivk[i] be the furthest k - such that all j with i=0; i--) { - ct[0] = ct[1] = ct[2] = ct[3] = 0; - - /* keep track of "directions" that have occurred */ - dir = (3+3*(pt[mod(i+1,n)].x-pt[i].x)+(pt[mod(i+1,n)].y-pt[i].y))/2; - ct[dir]++; - - constraint[0].x = 0; - constraint[0].y = 0; - constraint[1].x = 0; - constraint[1].y = 0; - - /* find the next k such that no straight line from i to k */ - k = nc[i]; - k1 = i; - while (1) { - - dir = (3+3*sign(pt[k].x-pt[k1].x)+sign(pt[k].y-pt[k1].y))/2; - ct[dir]++; - - /* if all four "directions" have occurred, cut this path */ - if (ct[0] && ct[1] && ct[2] && ct[3]) { - pivk[i] = k1; - goto foundk; - } - - cur.x = pt[k].x - pt[i].x; - cur.y = pt[k].y - pt[i].y; - - /* see if current constraint is violated */ - if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { - goto constraint_viol; - } - - /* else, update constraint */ - if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { - /* no constraint */ - } else { - off.x = cur.x + ((cur.y>=0 && (cur.y>0 || cur.x<0)) ? 1 : -1); - off.y = cur.y + ((cur.x<=0 && (cur.x<0 || cur.y<0)) ? 1 : -1); - if (xprod(constraint[0], off) >= 0) { - constraint[0] = off; - } - off.x = cur.x + ((cur.y<=0 && (cur.y<0 || cur.x<0)) ? 1 : -1); - off.y = cur.y + ((cur.x>=0 && (cur.x>0 || cur.y<0)) ? 1 : -1); - if (xprod(constraint[1], off) <= 0) { - constraint[1] = off; - } - } - k1 = k; - k = nc[k1]; - if (!cyclic(k,i,k1)) { - break; - } - } - constraint_viol: - /* k1 was the last "corner" satisfying the current constraint, and - k is the first one violating it. We now need to find the last - point along k1..k which satisfied the constraint. */ - dk.x = sign(pt[k].x-pt[k1].x); - dk.y = sign(pt[k].y-pt[k1].y); - cur.x = pt[k1].x - pt[i].x; - cur.y = pt[k1].y - pt[i].y; - /* find largest integer j such that xprod(constraint[0], cur+j*dk) - >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity - of xprod. */ - a = xprod(constraint[0], cur); - b = xprod(constraint[0], dk); - c = xprod(constraint[1], cur); - d = xprod(constraint[1], dk); - /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This - can be solved with integer arithmetic. */ - j = INFTY; - if (b<0) { - j = floordiv(a,-b); - } - if (d>0) { - j = min(j, floordiv(-c,d)); +static int calc_lon(privpath_t *pp) +{ + point_t *pt = pp->pt; + int n = pp->len; + int i, j, k, k1; + int ct[4], dir; + point_t constraint[2]; + point_t cur; + point_t off; + int *pivk = NULL; /* pivk[n] */ + int *nc = NULL; /* nc[n]: next corner */ + point_t dk; /* direction of k-k1 */ + int a, b, c, d; + + SAFE_MALLOC(pivk, n, int); + SAFE_MALLOC(nc, n, int); + + /* initialize the nc data structure. Point from each point to the + furthest future point to which it is connected by a vertical or + horizontal segment. We take advantage of the fact that there is + always a direction change at 0 (due to the path decomposition + algorithm). But even if this were not so, there is no harm, as + in practice, correctness does not depend on the word "furthest" + above. */ + k = 0; + for (i = n - 1; i >= 0; i--) { + if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { + k = i + 1; /* necessarily ilon[n-1]=j; - for (i=n-2; i>=0; i--) { - if (cyclic(i+1,pivk[i],j)) { - j=pivk[i]; + + SAFE_MALLOC(pp->lon, n, int); + + /* determine pivot points: for each i, let pivk[i] be the furthest k + such that all j with i= 0; i--) { + ct[0] = ct[1] = ct[2] = ct[3] = 0; + + /* keep track of "directions" that have occurred */ + dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2; + ct[dir]++; + + constraint[0].x = 0; + constraint[0].y = 0; + constraint[1].x = 0; + constraint[1].y = 0; + + /* find the next k such that no straight line from i to k */ + k = nc[i]; + k1 = i; + while (1) { + + dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2; + ct[dir]++; + + /* if all four "directions" have occurred, cut this path */ + if (ct[0] && ct[1] && ct[2] && ct[3]) { + pivk[i] = k1; + goto foundk; + } + + cur.x = pt[k].x - pt[i].x; + cur.y = pt[k].y - pt[i].y; + + /* see if current constraint is violated */ + if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { + goto constraint_viol; + } + + /* else, update constraint */ + if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { + /* no constraint */ + } else { + off.x = cur.x + ((cur.y >= 0 && (cur.y > 0 || cur.x < 0)) ? 1 : -1); + off.y = cur.y + ((cur.x <= 0 && (cur.x < 0 || cur.y < 0)) ? 1 : -1); + if (xprod(constraint[0], off) >= 0) { + constraint[0] = off; + } + off.x = cur.x + ((cur.y <= 0 && (cur.y < 0 || cur.x < 0)) ? 1 : -1); + off.y = cur.y + ((cur.x >= 0 && (cur.x > 0 || cur.y < 0)) ? 1 : -1); + if (xprod(constraint[1], off) <= 0) { + constraint[1] = off; + } + } + k1 = k; + k = nc[k1]; + if (!cyclic(k, i, k1)) { + break; + } + } + constraint_viol: + /* k1 was the last "corner" satisfying the current constraint, and + k is the first one violating it. We now need to find the last + point along k1..k which satisfied the constraint. */ + dk.x = sign(pt[k].x - pt[k1].x); + dk.y = sign(pt[k].y - pt[k1].y); + cur.x = pt[k1].x - pt[i].x; + cur.y = pt[k1].y - pt[i].y; + /* find largest integer j such that xprod(constraint[0], cur+j*dk) + >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity + of xprod. */ + a = xprod(constraint[0], cur); + b = xprod(constraint[0], dk); + c = xprod(constraint[1], cur); + d = xprod(constraint[1], dk); + /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This + can be solved with integer arithmetic. */ + j = INFTY; + if (b < 0) { + j = floordiv(a, -b); + } + if (d > 0) { + j = min(j, floordiv(-c, d)); + } + pivk[i] = mod(k1 + j, n); + foundk: + ; + } /* for i */ + + /* clean up: for each i, let lon[i] be the largest k such that for + all i' with i<=i'lon[n - 1] = j; + for (i = n - 2; i >= 0; i--) { + if (cyclic(i + 1, pivk[i], j)) { + j = pivk[i]; + } + pp->lon[i] = j; } - pp->lon[i]=j; - } - for (i=n-1; cyclic(mod(i+1,n),j,pp->lon[i]); i--) { - pp->lon[i] = j; - } + for (i = n - 1; cyclic(mod(i + 1, n), j, pp->lon[i]); i--) { + pp->lon[i] = j; + } - free(pivk); - free(nc); - return 0; + free(pivk); + free(nc); + return 0; - malloc_error: - free(pivk); - free(nc); - return 1; +malloc_error: + free(pivk); + free(nc); + return 1; } /* ---------------------------------------------------------------------- */ -/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ +/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ /* Auxiliary function: calculate the penalty of an edge from i to j in the given path. This needs the "lon" and "sum*" data. */ -static double penalty3(privpath_t *pp, int i, int j) { - int n = pp->len; - point_t *pt = pp->pt; - sums_t *sums = pp->sums; - - /* assume 0<=i=n) { - j -= n; - r = 1; - } - - /* critical inner loop: the "if" gives a 4.6 percent speedup */ - if (r == 0) { - x = sums[j+1].x - sums[i].x; - y = sums[j+1].y - sums[i].y; - x2 = sums[j+1].x2 - sums[i].x2; - xy = sums[j+1].xy - sums[i].xy; - y2 = sums[j+1].y2 - sums[i].y2; - k = j+1 - i; - } else { - x = sums[j+1].x - sums[i].x + sums[n].x; - y = sums[j+1].y - sums[i].y + sums[n].y; - x2 = sums[j+1].x2 - sums[i].x2 + sums[n].x2; - xy = sums[j+1].xy - sums[i].xy + sums[n].xy; - y2 = sums[j+1].y2 - sums[i].y2 + sums[n].y2; - k = j+1 - i + n; - } - - px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; - py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; - ey = (pt[j].x - pt[i].x); - ex = -(pt[j].y - pt[i].y); - - a = ((x2 - 2*x*px) / k + px*px); - b = ((xy - x*py - y*px) / k + px*py); - c = ((y2 - 2*y*py) / k + py*py); - - s = ex*ex*a + 2*ex*ey*b + ey*ey*c; - - return sqrt(s); +static double penalty3(privpath_t *pp, int i, int j) +{ + int n = pp->len; + point_t *pt = pp->pt; + sums_t *sums = pp->sums; + + /* assume 0<=i= n) { + j -= n; + r = 1; + } + + /* critical inner loop: the "if" gives a 4.6 percent speedup */ + if (r == 0) { + x = sums[j + 1].x - sums[i].x; + y = sums[j + 1].y - sums[i].y; + x2 = sums[j + 1].x2 - sums[i].x2; + xy = sums[j + 1].xy - sums[i].xy; + y2 = sums[j + 1].y2 - sums[i].y2; + k = j + 1 - i; + } else { + x = sums[j + 1].x - sums[i].x + sums[n].x; + y = sums[j + 1].y - sums[i].y + sums[n].y; + x2 = sums[j + 1].x2 - sums[i].x2 + sums[n].x2; + xy = sums[j + 1].xy - sums[i].xy + sums[n].xy; + y2 = sums[j + 1].y2 - sums[i].y2 + sums[n].y2; + k = j + 1 - i + n; + } + + px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; + py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; + ey = (pt[j].x - pt[i].x); + ex = -(pt[j].y - pt[i].y); + + a = ((x2 - 2 * x * px) / k + px * px); + b = ((xy - x * py - y * px) / k + px * py); + c = ((y2 - 2 * y * py) / k + py * py); + + s = ex * ex * a + 2 * ex * ey * b + ey * ey * c; + + return sqrt(s); } /* find the optimal polygon. Fill in the m and po components. Return 1 @@ -525,109 +535,109 @@ static double penalty3(privpath_t *pp, int i, int j) { is in the polygon. Fixme: implement cyclic version. */ static int bestpolygon(privpath_t *pp) { - int i, j, m, k; - int n = pp->len; - double *pen = NULL; /* pen[n+1]: penalty vector */ - int *prev = NULL; /* prev[n+1]: best path pointer vector */ - int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ - int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ - int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ - int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ - double thispen; - double best; - int c; - - SAFE_MALLOC(pen, n+1, double); - SAFE_MALLOC(prev, n+1, int); - SAFE_MALLOC(clip0, n, int); - SAFE_MALLOC(clip1, n+1, int); - SAFE_MALLOC(seg0, n+1, int); - SAFE_MALLOC(seg1, n+1, int); - - /* calculate clipped paths */ - for (i=0; ilon[mod(i-1,n)]-1,n); - if (c == i) { - c = mod(i+1,n); + int i, j, m, k; + int n = pp->len; + double *pen = NULL; /* pen[n+1]: penalty vector */ + int *prev = NULL; /* prev[n+1]: best path pointer vector */ + int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ + int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ + int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ + int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ + double thispen; + double best; + int c; + + SAFE_MALLOC(pen, n + 1, double); + SAFE_MALLOC(prev, n + 1, int); + SAFE_MALLOC(clip0, n, int); + SAFE_MALLOC(clip1, n + 1, int); + SAFE_MALLOC(seg0, n + 1, int); + SAFE_MALLOC(seg1, n + 1, int); + + /* calculate clipped paths */ + for (i = 0; i < n; i++) { + c = mod(pp->lon[mod(i - 1, n)] - 1, n); + if (c == i) { + c = mod(i + 1, n); + } + if (c < i) { + clip0[i] = n; + } else { + clip0[i] = c; + } } - if (c < i) { - clip0[i] = n; - } else { - clip0[i] = c; + + /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff + clip1[j] <= i, for i,j=0..n. */ + j = 1; + for (i = 0; i < n; i++) { + while (j <= clip0[i]) { + clip1[j] = i; + j++; + } + } + + /* calculate seg0[j] = longest path from 0 with j segments */ + i = 0; + for (j = 0; i < n; j++) { + seg0[j] = i; + i = clip0[i]; + } + seg0[j] = n; + m = j; + + /* calculate seg1[j] = longest path to n with m-j segments */ + i = n; + for (j = m; j > 0; j--) { + seg1[j] = i; + i = clip1[i]; } - } - - /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff - clip1[j] <= i, for i,j=0..n. */ - j = 1; - for (i=0; i= clip1[i]; k--) { + thispen = penalty3(pp, k, i) + pen[k]; + if (best < 0 || thispen < best) { + prev[i] = k; + best = thispen; + } + } + pen[i] = best; + } } - } - - /* calculate seg0[j] = longest path from 0 with j segments */ - i = 0; - for (j=0; i0; j--) { - seg1[j] = i; - i = clip1[i]; - } - seg1[0] = 0; - - /* now find the shortest path with m segments, based on penalty3 */ - /* note: the outer 2 loops jointly have at most n iterations, thus - the worst-case behavior here is quadratic. In practice, it is - close to linear since the inner loop tends to be short. */ - pen[0]=0; - for (j=1; j<=m; j++) { - for (i=seg1[j]; i<=seg0[j]; i++) { - best = -1; - for (k=seg0[j-1]; k>=clip1[i]; k--) { - thispen = penalty3(pp, k, i) + pen[k]; - if (best < 0 || thispen < best) { - prev[i] = k; - best = thispen; - } - } - pen[i] = best; + + pp->m = m; + SAFE_MALLOC(pp->po, m, int); + + /* read off shortest path */ + for (i = n, j = m - 1; i > 0; j--) { + i = prev[i]; + pp->po[j] = i; } - } - - pp->m = m; - SAFE_MALLOC(pp->po, m, int); - - /* read off shortest path */ - for (i=n, j=m-1; i>0; j--) { - i = prev[i]; - pp->po[j] = i; - } - - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 0; - - malloc_error: - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 1; + + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 0; + +malloc_error: + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 1; } /* ---------------------------------------------------------------------- */ @@ -638,266 +648,269 @@ static int bestpolygon(privpath_t *pp) if it lies outside. Return 1 with errno set on error; 0 on success. */ -static int adjust_vertices(privpath_t *pp) { - int m = pp->m; - int *po = pp->po; - int n = pp->len; - point_t *pt = pp->pt; - int x0 = pp->x0; - int y0 = pp->y0; - - dpoint_t *ctr = NULL; /* ctr[m] */ - dpoint_t *dir = NULL; /* dir[m] */ - quadform_t *q = NULL; /* q[m] */ - double v[3]; - double d; - int i, j, k, l; - dpoint_t s; - int r; - - SAFE_MALLOC(ctr, m, dpoint_t); - SAFE_MALLOC(dir, m, dpoint_t); - SAFE_MALLOC(q, m, quadform_t); - - r = privcurve_init(&pp->curve, m); - if (r) { - goto malloc_error; - } - - /* calculate "optimal" point-slope representation for each line - segment */ - for (i=0; im; + int *po = pp->po; + int n = pp->len; + point_t *pt = pp->pt; + int x0 = pp->x0; + int y0 = pp->y0; + + dpoint_t *ctr = NULL; /* ctr[m] */ + dpoint_t *dir = NULL; /* dir[m] */ + quadform_t *q = NULL; /* q[m] */ + double v[3]; + double d; + int i, j, k, l; + dpoint_t s; + int r; + + SAFE_MALLOC(ctr, m, dpoint_t); + SAFE_MALLOC(dir, m, dpoint_t); + SAFE_MALLOC(q, m, quadform_t); + + r = privcurve_init(&pp->curve, m); + if (r) { + goto malloc_error; } - - while(1) { - /* minimize the quadratic form Q on the unit square */ - /* find intersection */ -#ifdef HAVE_GCC_LOOP_BUG - /* work around gcc bug #12243 */ - free(NULL); -#endif - - det = Q[0][0]*Q[1][1] - Q[0][1]*Q[1][0]; - if (det != 0.0) { - w.x = (-Q[0][2]*Q[1][1] + Q[1][2]*Q[0][1]) / det; - w.y = ( Q[0][2]*Q[1][0] - Q[1][2]*Q[0][0]) / det; - break; - } - - /* matrix is singular - lines are parallel. Add another, - orthogonal axis, through the center of the unit square */ - if (Q[0][0]>Q[1][1]) { - v[0] = -Q[0][1]; - v[1] = Q[0][0]; - } else if (Q[1][1]) { - v[0] = -Q[1][1]; - v[1] = Q[1][0]; - } else { - v[0] = 1; - v[1] = 0; - } - d = sq(v[0]) + sq(v[1]); - v[2] = - v[1] * s.y - v[0] * s.x; - for (l=0; l<3; l++) { - for (k=0; k<3; k++) { - Q[l][k] += v[l] * v[k] / d; - } - } + /* calculate "optimal" point-slope representation for each line + segment */ + for (i = 0; i < m; i++) { + j = po[mod(i + 1, m)]; + j = mod(j - po[i], n) + po[i]; + pointslope(pp, po[i], j, &ctr[i], &dir[i]); } - dx = fabs(w.x-s.x); - dy = fabs(w.y-s.y); - if (dx <= .5 && dy <= .5) { - pp->curve.vertex[i].x = w.x+x0; - pp->curve.vertex[i].y = w.y+y0; - continue; + + /* represent each line segment as a singular quadratic form; the + distance of a point (x,y) from the line segment will be + (x,y,1)Q(x,y,1)^t, where Q=q[i]. */ + for (i = 0; i < m; i++) { + d = sq(dir[i].x) + sq(dir[i].y); + if (d == 0.0) { + for (j = 0; j < 3; j++) { + for (k = 0; k < 3; k++) { + q[i][j][k] = 0; + } + } + } else { + v[0] = dir[i].y; + v[1] = -dir[i].x; + v[2] = -v[1] * ctr[i].y - v[0] * ctr[i].x; + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + q[i][l][k] = v[l] * v[k] / d; + } + } + } } - /* the minimum was not in the unit square; now minimize quadratic - on boundary of square */ - min = quadform(Q, s); - xmin = s.x; - ymin = s.y; + /* now calculate the "intersections" of consecutive segments. + Instead of using the actual intersection, we find the point + within a given unit square which minimizes the square distance to + the two lines. */ + for (i = 0; i < m; i++) { + quadform_t Q; + dpoint_t w; + double dx, dy; + double det; + double min, cand; /* minimum and candidate for minimum of quad. form */ + double xmin, ymin; /* coordinates of minimum */ + int z; + + /* let s be the vertex, in coordinates relative to x0/y0 */ + s.x = pt[po[i]].x - x0; + s.y = pt[po[i]].y - y0; + + /* intersect segments i-1 and i */ + + j = mod(i - 1, m); + + /* add quadratic forms */ + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + Q[l][k] = q[j][l][k] + q[i][l][k]; + } + } + + while (1) { +/* minimize the quadratic form Q on the unit square */ +/* find intersection */ - if (Q[0][0] == 0.0) { - goto fixx; - } - for (z=0; z<2; z++) { /* value of the y-coordinate */ - w.y = s.y-0.5+z; - w.x = - (Q[0][1] * w.y + Q[0][2]) / Q[0][0]; - dx = fabs(w.x-s.x); - cand = quadform(Q, w); - if (dx <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - fixx: - if (Q[1][1] == 0.0) { - goto corners; - } - for (z=0; z<2; z++) { /* value of the x-coordinate */ - w.x = s.x-0.5+z; - w.y = - (Q[1][0] * w.x + Q[1][2]) / Q[1][1]; - dy = fabs(w.y-s.y); - cand = quadform(Q, w); - if (dy <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - corners: - /* check four corners */ - for (l=0; l<2; l++) { - for (k=0; k<2; k++) { - w.x = s.x-0.5+l; - w.y = s.y-0.5+k; - cand = quadform(Q, w); - if (cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } +#ifdef HAVE_GCC_LOOP_BUG + /* work around gcc bug #12243 */ + free(NULL); +#endif + + det = Q[0][0] * Q[1][1] - Q[0][1] * Q[1][0]; + if (det != 0.0) { + w.x = (-Q[0][2] * Q[1][1] + Q[1][2] * Q[0][1]) / det; + w.y = (Q[0][2] * Q[1][0] - Q[1][2] * Q[0][0]) / det; + break; + } + + /* matrix is singular - lines are parallel. Add another, + orthogonal axis, through the center of the unit square */ + if (Q[0][0] > Q[1][1]) { + v[0] = -Q[0][1]; + v[1] = Q[0][0]; + } else if (Q[1][1]) { + v[0] = -Q[1][1]; + v[1] = Q[1][0]; + } else { + v[0] = 1; + v[1] = 0; + } + d = sq(v[0]) + sq(v[1]); + v[2] = -v[1] * s.y - v[0] * s.x; + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + Q[l][k] += v[l] * v[k] / d; + } + } + } + dx = fabs(w.x - s.x); + dy = fabs(w.y - s.y); + if (dx <= .5 && dy <= .5) { + pp->curve.vertex[i].x = w.x + x0; + pp->curve.vertex[i].y = w.y + y0; + continue; + } + + /* the minimum was not in the unit square; now minimize quadratic + on boundary of square */ + min = quadform(Q, s); + xmin = s.x; + ymin = s.y; + + if (Q[0][0] == 0.0) { + goto fixx; + } + for (z = 0; z < 2; z++) { /* value of the y-coordinate */ + w.y = s.y - 0.5 + z; + w.x = -(Q[0][1] * w.y + Q[0][2]) / Q[0][0]; + dx = fabs(w.x - s.x); + cand = quadform(Q, w); + if (dx <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + fixx: + if (Q[1][1] == 0.0) { + goto corners; + } + for (z = 0; z < 2; z++) { /* value of the x-coordinate */ + w.x = s.x - 0.5 + z; + w.y = -(Q[1][0] * w.x + Q[1][2]) / Q[1][1]; + dy = fabs(w.y - s.y); + cand = quadform(Q, w); + if (dy <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + corners: + /* check four corners */ + for (l = 0; l < 2; l++) { + for (k = 0; k < 2; k++) { + w.x = s.x - 0.5 + l; + w.y = s.y - 0.5 + k; + cand = quadform(Q, w); + if (cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + } + + pp->curve.vertex[i].x = xmin + x0; + pp->curve.vertex[i].y = ymin + y0; + continue; } - pp->curve.vertex[i].x = xmin + x0; - pp->curve.vertex[i].y = ymin + y0; - continue; - } - - free(ctr); - free(dir); - free(q); - return 0; - - malloc_error: - free(ctr); - free(dir); - free(q); - return 1; + free(ctr); + free(dir); + free(q); + return 0; + +malloc_error: + free(ctr); + free(dir); + free(q); + return 1; } /* ---------------------------------------------------------------------- */ /* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ /* reverse orientation of a path */ -static void reverse(privcurve_t *curve) { - int m = curve->n; - int i, j; - dpoint_t tmp; - - for (i=0, j=m-1; ivertex[i]; - curve->vertex[i] = curve->vertex[j]; - curve->vertex[j] = tmp; - } +static void reverse(privcurve_t *curve) +{ + int m = curve->n; + int i, j; + dpoint_t tmp; + + for (i = 0, j = m - 1; i < j; i++, j--) { + tmp = curve->vertex[i]; + curve->vertex[i] = curve->vertex[j]; + curve->vertex[j] = tmp; + } } /* Always succeeds */ -static void smooth(privcurve_t *curve, double alphamax) { - int m = curve->n; - - int i, j, k; - double dd, denom, alpha; - dpoint_t p2, p3, p4; - - /* examine each vertex and find its best fit */ - for (i=0; ivertex[k], curve->vertex[j]); - - denom = ddenom(curve->vertex[i], curve->vertex[k]); - if (denom != 0.0) { - dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; - dd = fabs(dd); - alpha = dd>1 ? (1 - 1.0/dd) : 0; - alpha = alpha / 0.75; - } else { - alpha = 4/3.0; - } - curve->alpha0[j] = alpha; /* remember "original" value of alpha */ - - if (alpha > alphamax) { /* pointed corner */ - curve->tag[j] = POTRACE_CORNER; - curve->c[j][1] = curve->vertex[j]; - curve->c[j][2] = p4; - } else { - if (alpha < 0.55) { - alpha = 0.55; - } else if (alpha > 1) { - alpha = 1; - } - p2 = interval(.5+.5*alpha, curve->vertex[i], curve->vertex[j]); - p3 = interval(.5+.5*alpha, curve->vertex[k], curve->vertex[j]); - curve->tag[j] = POTRACE_CURVETO; - curve->c[j][0] = p2; - curve->c[j][1] = p3; - curve->c[j][2] = p4; +static void smooth(privcurve_t *curve, double alphamax) +{ + int m = curve->n; + + int i, j, k; + double dd, denom, alpha; + dpoint_t p2, p3, p4; + + /* examine each vertex and find its best fit */ + for (i = 0; i < m; i++) { + j = mod(i + 1, m); + k = mod(i + 2, m); + p4 = interval(1 / 2.0, curve->vertex[k], curve->vertex[j]); + + denom = ddenom(curve->vertex[i], curve->vertex[k]); + if (denom != 0.0) { + dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; + dd = fabs(dd); + alpha = dd > 1 ? (1 - 1.0 / dd) : 0; + alpha = alpha / 0.75; + } else { + alpha = 4 / 3.0; + } + curve->alpha0[j] = alpha; /* remember "original" value of alpha */ + + if (alpha > alphamax) { /* pointed corner */ + curve->tag[j] = POTRACE_CORNER; + curve->c[j][1] = curve->vertex[j]; + curve->c[j][2] = p4; + } else { + if (alpha < 0.55) { + alpha = 0.55; + } else if (alpha > 1) { + alpha = 1; + } + p2 = interval(.5 + .5 * alpha, curve->vertex[i], curve->vertex[j]); + p3 = interval(.5 + .5 * alpha, curve->vertex[k], curve->vertex[j]); + curve->tag[j] = POTRACE_CURVETO; + curve->c[j][0] = p2; + curve->c[j][1] = p3; + curve->c[j][2] = p4; + } + curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ + curve->beta[j] = 0.5; } - curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ - curve->beta[j] = 0.5; - } - curve->alphacurve = 1; + curve->alphacurve = 1; - return; + return; } /* ---------------------------------------------------------------------- */ @@ -905,341 +918,349 @@ static void smooth(privcurve_t *curve, double alphamax) { /* a private type for the result of opti_penalty */ struct opti_s { - double pen; /* penalty */ - dpoint_t c[2]; /* curve parameters */ - double t, s; /* curve parameters */ - double alpha; /* curve parameter */ + double pen; /* penalty */ + dpoint_t c[2]; /* curve parameters */ + double t, s; /* curve parameters */ + double alpha; /* curve parameter */ }; typedef struct opti_s opti_t; /* calculate best fit from i+.5 to j+.5. Assume icurve.n; - int k, k1, k2, conv, i1; - double area, alpha, d, d1, d2; - dpoint_t p0, p1, p2, p3, pt; - double A, R, A1, A2, A3, A4; - double s, t; - - /* check convexity, corner-freeness, and maximum bend < 179 degrees */ +static int opti_penalty(privpath_t *pp, int i, int j, opti_t *res, double opttolerance, int *convc, double *areac) +{ + int m = pp->curve.n; + int k, k1, k2, conv, i1; + double area, alpha, d, d1, d2; + dpoint_t p0, p1, p2, p3, pt; + double A, R, A1, A2, A3, A4; + double s, t; - if (i==j) { /* sanity - a full loop can never be an opticurve */ - return 1; - } + /* check convexity, corner-freeness, and maximum bend < 179 degrees */ - k = i; - i1 = mod(i+1, m); - k1 = mod(k+1, m); - conv = convc[k1]; - if (conv == 0) { - return 1; - } - d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); - for (k=k1; k!=j; k=k1) { - k1 = mod(k+1, m); - k2 = mod(k+2, m); - if (convc[k1] != conv) { - return 1; + if (i == j) { /* sanity - a full loop can never be an opticurve */ + return 1; } - if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) { - return 1; + + k = i; + i1 = mod(i + 1, m); + k1 = mod(k + 1, m); + conv = convc[k1]; + if (conv == 0) { + return 1; } - if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { - return 1; + d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); + for (k = k1; k != j; k = k1) { + k1 = mod(k + 1, m); + k2 = mod(k + 2, m); + if (convc[k1] != conv) { + return 1; + } + if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != + conv) { + return 1; + } + if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < + d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { + return 1; + } } - } - - /* the curve we're working in: */ - p0 = pp->curve.c[mod(i,m)][2]; - p1 = pp->curve.vertex[mod(i+1,m)]; - p2 = pp->curve.vertex[mod(j,m)]; - p3 = pp->curve.c[mod(j,m)][2]; - - /* determine its area */ - area = areac[j] - areac[i]; - area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2])/2; - if (i>=j) { - area += areac[m]; - } - - /* find intersection o of p0p1 and p2p3. Let t,s such that o = - interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the - triangle (p0,o,p3). */ - - A1 = dpara(p0, p1, p2); - A2 = dpara(p0, p1, p3); - A3 = dpara(p0, p2, p3); - /* A4 = dpara(p1, p2, p3); */ - A4 = A1+A3-A2; - - if (A2 == A1) { /* this should never happen */ - return 1; - } - t = A3/(A3-A4); - s = A2/(A2-A1); - A = A2 * t / 2.0; - - if (A == 0.0) { /* this should never happen */ - return 1; - } + /* the curve we're working in: */ + p0 = pp->curve.c[mod(i, m)][2]; + p1 = pp->curve.vertex[mod(i + 1, m)]; + p2 = pp->curve.vertex[mod(j, m)]; + p3 = pp->curve.c[mod(j, m)][2]; + + /* determine its area */ + area = areac[j] - areac[i]; + area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2]) / 2; + if (i >= j) { + area += areac[m]; + } - R = area / A; /* relative area */ - alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ + /* find intersection o of p0p1 and p2p3. Let t,s such that o = + interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the + triangle (p0,o,p3). */ - res->c[0] = interval(t * alpha, p0, p1); - res->c[1] = interval(s * alpha, p3, p2); - res->alpha = alpha; - res->t = t; - res->s = s; + A1 = dpara(p0, p1, p2); + A2 = dpara(p0, p1, p3); + A3 = dpara(p0, p2, p3); + /* A4 = dpara(p1, p2, p3); */ + A4 = A1 + A3 - A2; - p1 = res->c[0]; - p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ + if (A2 == A1) { /* this should never happen */ + return 1; + } - res->pen = 0; + t = A3 / (A3 - A4); + s = A2 / (A2 - A1); + A = A2 * t / 2.0; - /* calculate penalty */ - /* check tangency with edges */ - for (k=mod(i+1,m); k!=j; k=k1) { - k1 = mod(k+1,m); - t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); - if (t<-.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); - if (d == 0.0) { /* this should never happen */ - return 1; + if (A == 0.0) { /* this should never happen */ + return 1; } - d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; - if (fabs(d1) > opttolerance) { - return 1; - } - if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { - return 1; - } - res->pen += sq(d1); - } - - /* check corners */ - for (k=i; k!=j; k=k1) { - k1 = mod(k+1,m); - t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); - if (t<-.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; - d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; - d2 *= 0.75 * pp->curve.alpha[k1]; - if (d2 < 0) { - d1 = -d1; - d2 = -d2; - } - if (d1 < d2 - opttolerance) { - return 1; + + R = area / A; /* relative area */ + alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ + + res->c[0] = interval(t * alpha, p0, p1); + res->c[1] = interval(s * alpha, p3, p2); + res->alpha = alpha; + res->t = t; + res->s = s; + + p1 = res->c[0]; + p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ + + res->pen = 0; + + /* calculate penalty */ + /* check tangency with edges */ + for (k = mod(i + 1, m); k != j; k = k1) { + k1 = mod(k + 1, m); + t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); + if (t < -.5) { + return 1; + } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); + if (d == 0.0) { /* this should never happen */ + return 1; + } + d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; + if (fabs(d1) > opttolerance) { + return 1; + } + if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || + iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { + return 1; + } + res->pen += sq(d1); } - if (d1 < d2) { - res->pen += sq(d1 - d2); + + /* check corners */ + for (k = i; k != j; k = k1) { + k1 = mod(k + 1, m); + t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); + if (t < -.5) { + return 1; + } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); + if (d == 0.0) { /* this should never happen */ + return 1; + } + d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; + d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; + d2 *= 0.75 * pp->curve.alpha[k1]; + if (d2 < 0) { + d1 = -d1; + d2 = -d2; + } + if (d1 < d2 - opttolerance) { + return 1; + } + if (d1 < d2) { + res->pen += sq(d1 - d2); + } } - } - return 0; + return 0; } /* optimize the path p, replacing sequences of Bezier segments by a single segment when possible. Return 0 on success, 1 with errno set on failure. */ -static int opticurve(privpath_t *pp, double opttolerance) { - int m = pp->curve.n; - int *pt = NULL; /* pt[m+1] */ - double *pen = NULL; /* pen[m+1] */ - int *len = NULL; /* len[m+1] */ - opti_t *opt = NULL; /* opt[m+1] */ - int om; - int i,j,r; - opti_t o; - dpoint_t p0; - int i1; - double area; - double alpha; - double *s = NULL; - double *t = NULL; - - int *convc = NULL; /* conv[m]: pre-computed convexities */ - double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ - - SAFE_MALLOC(pt, m+1, int); - SAFE_MALLOC(pen, m+1, double); - SAFE_MALLOC(len, m+1, int); - SAFE_MALLOC(opt, m+1, opti_t); - SAFE_MALLOC(convc, m, int); - SAFE_MALLOC(areac, m+1, double); - - /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ - for (i=0; icurve.tag[i] == POTRACE_CURVETO) { - convc[i] = sign(dpara(pp->curve.vertex[mod(i-1,m)], pp->curve.vertex[i], pp->curve.vertex[mod(i+1,m)])); - } else { - convc[i] = 0; +static int opticurve(privpath_t *pp, double opttolerance) +{ + int m = pp->curve.n; + int *pt = NULL; /* pt[m+1] */ + double *pen = NULL; /* pen[m+1] */ + int *len = NULL; /* len[m+1] */ + opti_t *opt = NULL; /* opt[m+1] */ + int om; + int i, j, r; + opti_t o; + dpoint_t p0; + int i1; + double area; + double alpha; + double *s = NULL; + double *t = NULL; + + int *convc = NULL; /* conv[m]: pre-computed convexities */ + double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ + + SAFE_MALLOC(pt, m + 1, int); + SAFE_MALLOC(pen, m + 1, double); + SAFE_MALLOC(len, m + 1, int); + SAFE_MALLOC(opt, m + 1, opti_t); + SAFE_MALLOC(convc, m, int); + SAFE_MALLOC(areac, m + 1, double); + + /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ + for (i = 0; i < m; i++) { + if (pp->curve.tag[i] == POTRACE_CURVETO) { + convc[i] = + sign(dpara(pp->curve.vertex[mod(i - 1, m)], pp->curve.vertex[i], pp->curve.vertex[mod(i + 1, m)])); + } else { + convc[i] = 0; + } } - } - - /* pre-calculate areas */ - area = 0.0; - areac[0] = 0.0; - p0 = pp->curve.vertex[0]; - for (i=0; icurve.tag[i1] == POTRACE_CURVETO) { - alpha = pp->curve.alpha[i1]; - area += 0.3*alpha*(4-alpha)*dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2])/2; - area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2])/2; + + /* pre-calculate areas */ + area = 0.0; + areac[0] = 0.0; + p0 = pp->curve.vertex[0]; + for (i = 0; i < m; i++) { + i1 = mod(i + 1, m); + if (pp->curve.tag[i1] == POTRACE_CURVETO) { + alpha = pp->curve.alpha[i1]; + area += 0.3 * alpha * (4 - alpha) * dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2]) / 2; + area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2]) / 2; + } + areac[i + 1] = area; } - areac[i+1] = area; - } - - pt[0] = -1; - pen[0] = 0; - len[0] = 0; - - /* Fixme: we always start from a fixed point -- should find the best - curve cyclically */ - - for (j=1; j<=m; j++) { - /* calculate best path from 0 to j */ - pt[j] = j-1; - pen[j] = pen[j-1]; - len[j] = len[j-1]+1; - - for (i=j-2; i>=0; i--) { - r = opti_penalty(pp, i, mod(j,m), &o, opttolerance, convc, areac); - if (r) { - break; - } - if (len[j] > len[i]+1 || (len[j] == len[i]+1 && pen[j] > pen[i] + o.pen)) { - pt[j] = i; - pen[j] = pen[i] + o.pen; - len[j] = len[i] + 1; - opt[j] = o; - } + + pt[0] = -1; + pen[0] = 0; + len[0] = 0; + + /* Fixme: we always start from a fixed point -- should find the best + curve cyclically */ + + for (j = 1; j <= m; j++) { + /* calculate best path from 0 to j */ + pt[j] = j - 1; + pen[j] = pen[j - 1]; + len[j] = len[j - 1] + 1; + + for (i = j - 2; i >= 0; i--) { + r = opti_penalty(pp, i, mod(j, m), &o, opttolerance, convc, areac); + if (r) { + break; + } + if (len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + o.pen)) { + pt[j] = i; + pen[j] = pen[i] + o.pen; + len[j] = len[i] + 1; + opt[j] = o; + } + } } - } - om = len[m]; - r = privcurve_init(&pp->ocurve, om); - if (r) { - goto malloc_error; - } - SAFE_MALLOC(s, om, double); - SAFE_MALLOC(t, om, double); - - j = m; - for (i=om-1; i>=0; i--) { - if (pt[j]==j-1) { - pp->ocurve.tag[i] = pp->curve.tag[mod(j,m)]; - pp->ocurve.c[i][0] = pp->curve.c[mod(j,m)][0]; - pp->ocurve.c[i][1] = pp->curve.c[mod(j,m)][1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; - pp->ocurve.vertex[i] = pp->curve.vertex[mod(j,m)]; - pp->ocurve.alpha[i] = pp->curve.alpha[mod(j,m)]; - pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j,m)]; - pp->ocurve.beta[i] = pp->curve.beta[mod(j,m)]; - s[i] = t[i] = 1.0; - } else { - pp->ocurve.tag[i] = POTRACE_CURVETO; - pp->ocurve.c[i][0] = opt[j].c[0]; - pp->ocurve.c[i][1] = opt[j].c[1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; - pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j,m)][2], pp->curve.vertex[mod(j,m)]); - pp->ocurve.alpha[i] = opt[j].alpha; - pp->ocurve.alpha0[i] = opt[j].alpha; - s[i] = opt[j].s; - t[i] = opt[j].t; + om = len[m]; + r = privcurve_init(&pp->ocurve, om); + if (r) { + goto malloc_error; } - j = pt[j]; - } - - /* calculate beta parameters */ - for (i=0; iocurve.beta[i] = s[i] / (s[i] + t[i1]); - } - pp->ocurve.alphacurve = 1; - - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 0; - - malloc_error: - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 1; + SAFE_MALLOC(s, om, double); + SAFE_MALLOC(t, om, double); + + j = m; + for (i = om - 1; i >= 0; i--) { + if (pt[j] == j - 1) { + pp->ocurve.tag[i] = pp->curve.tag[mod(j, m)]; + pp->ocurve.c[i][0] = pp->curve.c[mod(j, m)][0]; + pp->ocurve.c[i][1] = pp->curve.c[mod(j, m)][1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; + pp->ocurve.vertex[i] = pp->curve.vertex[mod(j, m)]; + pp->ocurve.alpha[i] = pp->curve.alpha[mod(j, m)]; + pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j, m)]; + pp->ocurve.beta[i] = pp->curve.beta[mod(j, m)]; + s[i] = t[i] = 1.0; + } else { + pp->ocurve.tag[i] = POTRACE_CURVETO; + pp->ocurve.c[i][0] = opt[j].c[0]; + pp->ocurve.c[i][1] = opt[j].c[1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; + pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j, m)][2], pp->curve.vertex[mod(j, m)]); + pp->ocurve.alpha[i] = opt[j].alpha; + pp->ocurve.alpha0[i] = opt[j].alpha; + s[i] = opt[j].s; + t[i] = opt[j].t; + } + j = pt[j]; + } + + /* calculate beta parameters */ + for (i = 0; i < om; i++) { + i1 = mod(i + 1, om); + pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]); + } + pp->ocurve.alphacurve = 1; + + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 0; + +malloc_error: + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 1; } /* ---------------------------------------------------------------------- */ -#define TRY(x) if (x) goto try_error +#define TRY(x) \ + if (x) \ + goto try_error /* return 0 on success, 1 on error with errno set. */ -int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) { - path_t *p; - double nn = 0, cn = 0; - - if (progress->callback) { - /* precompute task size for progress estimates */ - nn = 0; - list_forall (p, plist) { - nn += p->priv->len; - } - cn = 0; - } - - /* call downstream function with each path */ - list_forall (p, plist) { - TRY(calc_sums(p->priv)); - TRY(calc_lon(p->priv)); - TRY(bestpolygon(p->priv)); - TRY(adjust_vertices(p->priv)); - if (p->sign == '-') { /* reverse orientation of negative paths */ - reverse(&p->priv->curve); - } - smooth(&p->priv->curve, param->alphamax); - if (param->opticurve) { - TRY(opticurve(p->priv, param->opttolerance)); - p->priv->fcurve = &p->priv->ocurve; - } else { - p->priv->fcurve = &p->priv->curve; - } - privcurve_to_curve(p->priv->fcurve, &p->curve); +int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) +{ + path_t *p; + double nn = 0, cn = 0; if (progress->callback) { - cn += p->priv->len; - progress_update(cn/nn, progress); + /* precompute task size for progress estimates */ + nn = 0; + list_forall(p, plist) { nn += p->priv->len; } + cn = 0; + } + + /* call downstream function with each path */ + list_forall(p, plist) + { + TRY(calc_sums(p->priv)); + TRY(calc_lon(p->priv)); + TRY(bestpolygon(p->priv)); + TRY(adjust_vertices(p->priv)); + if (p->sign == '-') { /* reverse orientation of negative paths */ + reverse(&p->priv->curve); + } + smooth(&p->priv->curve, param->alphamax); + if (param->opticurve) { + TRY(opticurve(p->priv, param->opttolerance)); + p->priv->fcurve = &p->priv->ocurve; + } else { + p->priv->fcurve = &p->priv->curve; + } + privcurve_to_curve(p->priv->fcurve, &p->curve); + + if (progress->callback) { + cn += p->priv->len; + progress_update(cn / nn, progress); + } } - } - progress_update(1.0, progress); + progress_update(1.0, progress); - return 0; + return 0; - try_error: - return 1; +try_error: + return 1; } -- cgit v1.2.3 From 4534b82b4db9b1593bbe09d686b13655466e4a81 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 13:01:25 +0200 Subject: Mass fix whitespace (clang-format) before working on bug (bzr r13341.1.271) --- src/trace/potrace/trace.cpp | 2059 ++++++++++++++++++++++--------------------- 1 file changed, 1040 insertions(+), 1019 deletions(-) (limited to 'src') diff --git a/src/trace/potrace/trace.cpp b/src/trace/potrace/trace.cpp index f1e88a908..2bdefb90a 100644 --- a/src/trace/potrace/trace.cpp +++ b/src/trace/potrace/trace.cpp @@ -16,124 +16,129 @@ #include "trace.h" #include "progress.h" -#define INFTY 10000000 /* it suffices that this is longer than any - path; it need not be really infinite */ -#define COS179 -0.999847695156 /* the cosine of 179 degrees */ +#define INFTY 10000000 // it suffices that this is longer than any + // path; it need not be really infinite +#define COS179 -0.999847695156 // the cosine of 179 degrees /* ---------------------------------------------------------------------- */ #define SAFE_MALLOC(var, n, typ) \ - if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error + if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error /* ---------------------------------------------------------------------- */ /* auxiliary functions */ /* return a direction that is 90 degrees counterclockwise from p2-p0, but then restricted to one of the major wind directions (n, nw, w, etc) */ -static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) { - point_t r; - - r.y = sign(p2.x-p0.x); - r.x = -sign(p2.y-p0.y); +static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) +{ + point_t r; + + r.y = sign(p2.x - p0.x); + r.x = -sign(p2.y - p0.y); - return r; + return r; } /* return (p1-p0)x(p2-p0), the area of the parallelogram */ -static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) { - double x1, y1, x2, y2; +static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) +{ + double x1, y1, x2, y2; - x1 = p1.x-p0.x; - y1 = p1.y-p0.y; - x2 = p2.x-p0.x; - y2 = p2.y-p0.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; - return x1*y2 - x2*y1; + return x1 * y2 - x2 * y1; } /* ddenom/dpara have the property that the square of radius 1 centered at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */ -static inline double ddenom(dpoint_t p0, dpoint_t p2) { - point_t r = dorth_infty(p0, p2); +static inline double ddenom(dpoint_t p0, dpoint_t p2) +{ + point_t r = dorth_infty(p0, p2); - return r.y*(p2.x-p0.x) - r.x*(p2.y-p0.y); + return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y); } /* return 1 if a <= b < c < a, in a cyclic sense (mod n) */ -static inline int cyclic(int a, int b, int c) { - if (a<=c) { - return (a<=b && blen; - sums_t *sums = pp->sums; - - double x, y, x2, xy, y2; - double k; - double a, b, c, lambda2, l; - int r=0; /* rotations from i to j */ - - while (j>=n) { - j-=n; - r+=1; - } - while (i>=n) { - i-=n; - r-=1; - } - while (j<0) { - j+=n; - r-=1; - } - while (i<0) { - i+=n; - r+=1; - } - - x = sums[j+1].x-sums[i].x+r*sums[n].x; - y = sums[j+1].y-sums[i].y+r*sums[n].y; - x2 = sums[j+1].x2-sums[i].x2+r*sums[n].x2; - xy = sums[j+1].xy-sums[i].xy+r*sums[n].xy; - y2 = sums[j+1].y2-sums[i].y2+r*sums[n].y2; - k = j+1-i+r*n; - - ctr->x = x/k; - ctr->y = y/k; - - a = (x2-(double)x*x/k)/k; - b = (xy-(double)x*y/k)/k; - c = (y2-(double)y*y/k)/k; - - lambda2 = (a+c+sqrt((a-c)*(a-c)+4*b*b))/2; /* larger e.value */ - - /* now find e.vector for lambda2 */ - a -= lambda2; - c -= lambda2; - - if (fabs(a) >= fabs(c)) { - l = sqrt(a*a+b*b); - if (l!=0) { - dir->x = -b/l; - dir->y = a/l; +static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *dir) +{ + /* assume ilen; + sums_t *sums = pp->sums; + + double x, y, x2, xy, y2; + double k; + double a, b, c, lambda2, l; + int r = 0; /* rotations from i to j */ + + while (j >= n) { + j -= n; + r += 1; + } + while (i >= n) { + i -= n; + r -= 1; + } + while (j < 0) { + j += n; + r -= 1; + } + while (i < 0) { + i += n; + r += 1; + } + + x = sums[j + 1].x - sums[i].x + r * sums[n].x; + y = sums[j + 1].y - sums[i].y + r * sums[n].y; + x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; + xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; + y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; + k = j + 1 - i + r * n; + + ctr->x = x / k; + ctr->y = y / k; + + a = (x2 - (double)x * x / k) / k; + b = (xy - (double)x * y / k) / k; + c = (y2 - (double)y * y / k) / k; + + lambda2 = (a + c + sqrt((a - c) * (a - c) + 4 * b * b)) / 2; /* larger e.value */ + + /* now find e.vector for lambda2 */ + a -= lambda2; + c -= lambda2; + + if (fabs(a) >= fabs(c)) { + l = sqrt(a * a + b * b); + if (l != 0) { + dir->x = -b / l; + dir->y = a / l; + } + } else { + l = sqrt(c * c + b * b); + if (l != 0) { + dir->x = -c / l; + dir->y = b / l; + } } - } else { - l = sqrt(c*c+b*b); - if (l!=0) { - dir->x = -c/l; - dir->y = b/l; + if (l == 0) { + dir->x = dir->y = 0; /* sometimes this can happen when k=4: + the two eigenvalues coincide */ } - } - if (l==0) { - dir->x = dir->y = 0; /* sometimes this can happen when k=4: - the two eigenvalues coincide */ - } } /* the type of (affine) quadratic forms, represented as symmetric 3x3 @@ -142,155 +147,158 @@ static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *di typedef double quadform_t[3][3]; /* Apply quadratic form Q to vector w = (w.x,w.y) */ -static inline double quadform(quadform_t Q, dpoint_t w) { - double v[3]; - int i, j; - double sum; - - v[0] = w.x; - v[1] = w.y; - v[2] = 1; - sum = 0.0; - - for (i=0; i<3; i++) { - for (j=0; j<3; j++) { - sum += v[i] * Q[i][j] * v[j]; +static inline double quadform(quadform_t Q, dpoint_t w) +{ + double v[3]; + int i, j; + double sum; + + v[0] = w.x; + v[1] = w.y; + v[2] = 1; + sum = 0.0; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + sum += v[i] * Q[i][j] * v[j]; + } } - } - return sum; + return sum; } /* calculate p1 x p2 */ -static inline int xprod(point_t p1, point_t p2) { - return p1.x*p2.y - p1.y*p2.x; -} +static inline int xprod(point_t p1, point_t p2) { return p1.x * p2.y - p1.y * p2.x; } /* calculate (p1-p0)x(p3-p2) */ -static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double x1, y1, x2, y2; +static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; - return x1*y2 - x2*y1; + return x1 * y2 - x2 * y1; } /* calculate (p1-p0)*(p2-p0) */ -static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) { - double x1, y1, x2, y2; +static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) +{ + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; - return x1*x2 + y1*y2; + return x1 * x2 + y1 * y2; } /* calculate (p1-p0)*(p3-p2) */ -static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double x1, y1, x2, y2; +static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; - return x1*x2 + y1*y2; + return x1 * x2 + y1 * y2; } /* calculate distance between two points */ -static inline double ddist(dpoint_t p, dpoint_t q) { - return sqrt(sq(p.x-q.x)+sq(p.y-q.y)); -} +static inline double ddist(dpoint_t p, dpoint_t q) { return sqrt(sq(p.x - q.x) + sq(p.y - q.y)); } /* calculate point of a bezier curve */ -static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { - double s = 1-t; - dpoint_t res; +static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double s = 1 - t; + dpoint_t res; - /* Note: a good optimizing compiler (such as gcc-3) reduces the - following to 16 multiplications, using common subexpression - elimination. */ + /* Note: a good optimizing compiler (such as gcc-3) reduces the + following to 16 multiplications, using common subexpression + elimination. */ - res.x = s*s*s*p0.x + 3*(s*s*t)*p1.x + 3*(t*t*s)*p2.x + t*t*t*p3.x; - res.y = s*s*s*p0.y + 3*(s*s*t)*p1.y + 3*(t*t*s)*p2.y + t*t*t*p3.y; + res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x; + res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y; - return res; + return res; } /* calculate the point t in [0..1] on the (convex) bezier curve (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no solution in [0..1]. */ -static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) { - double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ - double a, b, c; /* a t^2 + b t + c = 0 */ - double d, s, r1, r2; - - A = cprod(p0, p1, q0, q1); - B = cprod(p1, p2, q0, q1); - C = cprod(p2, p3, q0, q1); - - a = A - 2*B + C; - b = -2*A + 2*B; - c = A; - - d = b*b - 4*a*c; - - if (a==0 || d<0) { - return -1.0; - } - - s = sqrt(d); - - r1 = (-b + s) / (2 * a); - r2 = (-b - s) / (2 * a); - - if (r1 >= 0 && r1 <= 1) { - return r1; - } else if (r2 >= 0 && r2 <= 1) { - return r2; - } else { - return -1.0; - } +static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) +{ + double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ + double a, b, c; /* a t^2 + b t + c = 0 */ + double d, s, r1, r2; + + A = cprod(p0, p1, q0, q1); + B = cprod(p1, p2, q0, q1); + C = cprod(p2, p3, q0, q1); + + a = A - 2 * B + C; + b = -2 * A + 2 * B; + c = A; + + d = b * b - 4 * a * c; + + if (a == 0 || d < 0) { + return -1.0; + } + + s = sqrt(d); + + r1 = (-b + s) / (2 * a); + r2 = (-b - s) / (2 * a); + + if (r1 >= 0 && r1 <= 1) { + return r1; + } else if (r2 >= 0 && r2 <= 1) { + return r2; + } else { + return -1.0; + } } /* ---------------------------------------------------------------------- */ /* Preparation: fill in the sum* fields of a path (used for later rapid summing). Return 0 on success, 1 with errno set on failure. */ -static int calc_sums(privpath_t *pp) { - int i, x, y; - int n = pp->len; - - SAFE_MALLOC(pp->sums, pp->len+1, sums_t); - - /* origin */ - pp->x0 = pp->pt[0].x; - pp->y0 = pp->pt[0].y; - - /* preparatory computation for later fast summing */ - pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; - for (i=0; ipt[i].x - pp->x0; - y = pp->pt[i].y - pp->y0; - pp->sums[i+1].x = pp->sums[i].x + x; - pp->sums[i+1].y = pp->sums[i].y + y; - pp->sums[i+1].x2 = pp->sums[i].x2 + x*x; - pp->sums[i+1].xy = pp->sums[i].xy + x*y; - pp->sums[i+1].y2 = pp->sums[i].y2 + y*y; - } - return 0; - - malloc_error: - return 1; +static int calc_sums(privpath_t *pp) +{ + int i, x, y; + int n = pp->len; + + SAFE_MALLOC(pp->sums, pp->len + 1, sums_t); + + /* origin */ + pp->x0 = pp->pt[0].x; + pp->y0 = pp->pt[0].y; + + /* preparatory computation for later fast summing */ + pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; + for (i = 0; i < n; i++) { + x = pp->pt[i].x - pp->x0; + y = pp->pt[i].y - pp->y0; + pp->sums[i + 1].x = pp->sums[i].x + x; + pp->sums[i + 1].y = pp->sums[i].y + y; + pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x; + pp->sums[i + 1].xy = pp->sums[i].xy + x * y; + pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y; + } + return 0; + +malloc_error: + return 1; } /* ---------------------------------------------------------------------- */ /* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the - "lon" component of a path object (based on pt/len). For each i, + "lon" component of a path object (based on pt/len). For each i, lon[i] is the furthest index such that a straight line can be drawn from i to lon[i]. Return 1 on error with errno set, else 0. */ @@ -318,206 +326,208 @@ static int calc_sums(privpath_t *pp) { substantial. */ /* returns 0 on success, 1 on error with errno set */ -static int calc_lon(privpath_t *pp) { - point_t *pt = pp->pt; - int n = pp->len; - int i, j, k, k1; - int ct[4], dir; - point_t constraint[2]; - point_t cur; - point_t off; - int *pivk = NULL; /* pivk[n] */ - int *nc = NULL; /* nc[n]: next corner */ - point_t dk; /* direction of k-k1 */ - int a, b, c, d; - - SAFE_MALLOC(pivk, n, int); - SAFE_MALLOC(nc, n, int); - - /* initialize the nc data structure. Point from each point to the - furthest future point to which it is connected by a vertical or - horizontal segment. We take advantage of the fact that there is - always a direction change at 0 (due to the path decomposition - algorithm). But even if this were not so, there is no harm, as - in practice, correctness does not depend on the word "furthest" - above. */ - k = 0; - for (i=n-1; i>=0; i--) { - if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { - k = i+1; /* necessarily ilon, n, int); - - /* determine pivot points: for each i, let pivk[i] be the furthest k - such that all j with i=0; i--) { - ct[0] = ct[1] = ct[2] = ct[3] = 0; - - /* keep track of "directions" that have occurred */ - dir = (3+3*(pt[mod(i+1,n)].x-pt[i].x)+(pt[mod(i+1,n)].y-pt[i].y))/2; - ct[dir]++; - - constraint[0].x = 0; - constraint[0].y = 0; - constraint[1].x = 0; - constraint[1].y = 0; - - /* find the next k such that no straight line from i to k */ - k = nc[i]; - k1 = i; - while (1) { - - dir = (3+3*sign(pt[k].x-pt[k1].x)+sign(pt[k].y-pt[k1].y))/2; - ct[dir]++; - - /* if all four "directions" have occurred, cut this path */ - if (ct[0] && ct[1] && ct[2] && ct[3]) { - pivk[i] = k1; - goto foundk; - } - - cur.x = pt[k].x - pt[i].x; - cur.y = pt[k].y - pt[i].y; - - /* see if current constraint is violated */ - if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { - goto constraint_viol; - } - - /* else, update constraint */ - if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { - /* no constraint */ - } else { - off.x = cur.x + ((cur.y>=0 && (cur.y>0 || cur.x<0)) ? 1 : -1); - off.y = cur.y + ((cur.x<=0 && (cur.x<0 || cur.y<0)) ? 1 : -1); - if (xprod(constraint[0], off) >= 0) { - constraint[0] = off; - } - off.x = cur.x + ((cur.y<=0 && (cur.y<0 || cur.x<0)) ? 1 : -1); - off.y = cur.y + ((cur.x>=0 && (cur.x>0 || cur.y<0)) ? 1 : -1); - if (xprod(constraint[1], off) <= 0) { - constraint[1] = off; - } - } - k1 = k; - k = nc[k1]; - if (!cyclic(k,i,k1)) { - break; - } - } - constraint_viol: - /* k1 was the last "corner" satisfying the current constraint, and - k is the first one violating it. We now need to find the last - point along k1..k which satisfied the constraint. */ - dk.x = sign(pt[k].x-pt[k1].x); - dk.y = sign(pt[k].y-pt[k1].y); - cur.x = pt[k1].x - pt[i].x; - cur.y = pt[k1].y - pt[i].y; - /* find largest integer j such that xprod(constraint[0], cur+j*dk) - >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity - of xprod. */ - a = xprod(constraint[0], cur); - b = xprod(constraint[0], dk); - c = xprod(constraint[1], cur); - d = xprod(constraint[1], dk); - /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This - can be solved with integer arithmetic. */ - j = INFTY; - if (b<0) { - j = floordiv(a,-b); - } - if (d>0) { - j = min(j, floordiv(-c,d)); +static int calc_lon(privpath_t *pp) +{ + point_t *pt = pp->pt; + int n = pp->len; + int i, j, k, k1; + int ct[4], dir; + point_t constraint[2]; + point_t cur; + point_t off; + int *pivk = NULL; /* pivk[n] */ + int *nc = NULL; /* nc[n]: next corner */ + point_t dk; /* direction of k-k1 */ + int a, b, c, d; + + SAFE_MALLOC(pivk, n, int); + SAFE_MALLOC(nc, n, int); + + /* initialize the nc data structure. Point from each point to the + furthest future point to which it is connected by a vertical or + horizontal segment. We take advantage of the fact that there is + always a direction change at 0 (due to the path decomposition + algorithm). But even if this were not so, there is no harm, as + in practice, correctness does not depend on the word "furthest" + above. */ + k = 0; + for (i = n - 1; i >= 0; i--) { + if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { + k = i + 1; /* necessarily ilon[n-1]=j; - for (i=n-2; i>=0; i--) { - if (cyclic(i+1,pivk[i],j)) { - j=pivk[i]; + + SAFE_MALLOC(pp->lon, n, int); + + /* determine pivot points: for each i, let pivk[i] be the furthest k + such that all j with i= 0; i--) { + ct[0] = ct[1] = ct[2] = ct[3] = 0; + + /* keep track of "directions" that have occurred */ + dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2; + ct[dir]++; + + constraint[0].x = 0; + constraint[0].y = 0; + constraint[1].x = 0; + constraint[1].y = 0; + + /* find the next k such that no straight line from i to k */ + k = nc[i]; + k1 = i; + while (1) { + + dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2; + ct[dir]++; + + /* if all four "directions" have occurred, cut this path */ + if (ct[0] && ct[1] && ct[2] && ct[3]) { + pivk[i] = k1; + goto foundk; + } + + cur.x = pt[k].x - pt[i].x; + cur.y = pt[k].y - pt[i].y; + + /* see if current constraint is violated */ + if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { + goto constraint_viol; + } + + /* else, update constraint */ + if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { + /* no constraint */ + } else { + off.x = cur.x + ((cur.y >= 0 && (cur.y > 0 || cur.x < 0)) ? 1 : -1); + off.y = cur.y + ((cur.x <= 0 && (cur.x < 0 || cur.y < 0)) ? 1 : -1); + if (xprod(constraint[0], off) >= 0) { + constraint[0] = off; + } + off.x = cur.x + ((cur.y <= 0 && (cur.y < 0 || cur.x < 0)) ? 1 : -1); + off.y = cur.y + ((cur.x >= 0 && (cur.x > 0 || cur.y < 0)) ? 1 : -1); + if (xprod(constraint[1], off) <= 0) { + constraint[1] = off; + } + } + k1 = k; + k = nc[k1]; + if (!cyclic(k, i, k1)) { + break; + } + } + constraint_viol: + /* k1 was the last "corner" satisfying the current constraint, and + k is the first one violating it. We now need to find the last + point along k1..k which satisfied the constraint. */ + dk.x = sign(pt[k].x - pt[k1].x); + dk.y = sign(pt[k].y - pt[k1].y); + cur.x = pt[k1].x - pt[i].x; + cur.y = pt[k1].y - pt[i].y; + /* find largest integer j such that xprod(constraint[0], cur+j*dk) + >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity + of xprod. */ + a = xprod(constraint[0], cur); + b = xprod(constraint[0], dk); + c = xprod(constraint[1], cur); + d = xprod(constraint[1], dk); + /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This + can be solved with integer arithmetic. */ + j = INFTY; + if (b < 0) { + j = floordiv(a, -b); + } + if (d > 0) { + j = min(j, floordiv(-c, d)); + } + pivk[i] = mod(k1 + j, n); + foundk: + ; + } /* for i */ + + /* clean up: for each i, let lon[i] be the largest k such that for + all i' with i<=i'lon[n - 1] = j; + for (i = n - 2; i >= 0; i--) { + if (cyclic(i + 1, pivk[i], j)) { + j = pivk[i]; + } + pp->lon[i] = j; } - pp->lon[i]=j; - } - for (i=n-1; cyclic(mod(i+1,n),j,pp->lon[i]); i--) { - pp->lon[i] = j; - } + for (i = n - 1; cyclic(mod(i + 1, n), j, pp->lon[i]); i--) { + pp->lon[i] = j; + } - free(pivk); - free(nc); - return 0; + free(pivk); + free(nc); + return 0; - malloc_error: - free(pivk); - free(nc); - return 1; +malloc_error: + free(pivk); + free(nc); + return 1; } /* ---------------------------------------------------------------------- */ -/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ +/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ /* Auxiliary function: calculate the penalty of an edge from i to j in the given path. This needs the "lon" and "sum*" data. */ -static double penalty3(privpath_t *pp, int i, int j) { - int n = pp->len; - point_t *pt = pp->pt; - sums_t *sums = pp->sums; - - /* assume 0<=i=n) { - j -= n; - r = 1; - } - - /* critical inner loop: the "if" gives a 4.6 percent speedup */ - if (r == 0) { - x = sums[j+1].x - sums[i].x; - y = sums[j+1].y - sums[i].y; - x2 = sums[j+1].x2 - sums[i].x2; - xy = sums[j+1].xy - sums[i].xy; - y2 = sums[j+1].y2 - sums[i].y2; - k = j+1 - i; - } else { - x = sums[j+1].x - sums[i].x + sums[n].x; - y = sums[j+1].y - sums[i].y + sums[n].y; - x2 = sums[j+1].x2 - sums[i].x2 + sums[n].x2; - xy = sums[j+1].xy - sums[i].xy + sums[n].xy; - y2 = sums[j+1].y2 - sums[i].y2 + sums[n].y2; - k = j+1 - i + n; - } - - px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; - py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; - ey = (pt[j].x - pt[i].x); - ex = -(pt[j].y - pt[i].y); - - a = ((x2 - 2*x*px) / k + px*px); - b = ((xy - x*py - y*px) / k + px*py); - c = ((y2 - 2*y*py) / k + py*py); - - s = ex*ex*a + 2*ex*ey*b + ey*ey*c; - - return sqrt(s); +static double penalty3(privpath_t *pp, int i, int j) +{ + int n = pp->len; + point_t *pt = pp->pt; + sums_t *sums = pp->sums; + + /* assume 0<=i= n) { + j -= n; + r = 1; + } + + /* critical inner loop: the "if" gives a 4.6 percent speedup */ + if (r == 0) { + x = sums[j + 1].x - sums[i].x; + y = sums[j + 1].y - sums[i].y; + x2 = sums[j + 1].x2 - sums[i].x2; + xy = sums[j + 1].xy - sums[i].xy; + y2 = sums[j + 1].y2 - sums[i].y2; + k = j + 1 - i; + } else { + x = sums[j + 1].x - sums[i].x + sums[n].x; + y = sums[j + 1].y - sums[i].y + sums[n].y; + x2 = sums[j + 1].x2 - sums[i].x2 + sums[n].x2; + xy = sums[j + 1].xy - sums[i].xy + sums[n].xy; + y2 = sums[j + 1].y2 - sums[i].y2 + sums[n].y2; + k = j + 1 - i + n; + } + + px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; + py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; + ey = (pt[j].x - pt[i].x); + ex = -(pt[j].y - pt[i].y); + + a = ((x2 - 2 * x * px) / k + px * px); + b = ((xy - x * py - y * px) / k + px * py); + c = ((y2 - 2 * y * py) / k + py * py); + + s = ex * ex * a + 2 * ex * ey * b + ey * ey * c; + + return sqrt(s); } /* find the optimal polygon. Fill in the m and po components. Return 1 @@ -525,109 +535,109 @@ static double penalty3(privpath_t *pp, int i, int j) { is in the polygon. Fixme: implement cyclic version. */ static int bestpolygon(privpath_t *pp) { - int i, j, m, k; - int n = pp->len; - double *pen = NULL; /* pen[n+1]: penalty vector */ - int *prev = NULL; /* prev[n+1]: best path pointer vector */ - int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ - int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ - int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ - int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ - double thispen; - double best; - int c; - - SAFE_MALLOC(pen, n+1, double); - SAFE_MALLOC(prev, n+1, int); - SAFE_MALLOC(clip0, n, int); - SAFE_MALLOC(clip1, n+1, int); - SAFE_MALLOC(seg0, n+1, int); - SAFE_MALLOC(seg1, n+1, int); - - /* calculate clipped paths */ - for (i=0; ilon[mod(i-1,n)]-1,n); - if (c == i) { - c = mod(i+1,n); + int i, j, m, k; + int n = pp->len; + double *pen = NULL; /* pen[n+1]: penalty vector */ + int *prev = NULL; /* prev[n+1]: best path pointer vector */ + int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ + int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ + int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ + int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ + double thispen; + double best; + int c; + + SAFE_MALLOC(pen, n + 1, double); + SAFE_MALLOC(prev, n + 1, int); + SAFE_MALLOC(clip0, n, int); + SAFE_MALLOC(clip1, n + 1, int); + SAFE_MALLOC(seg0, n + 1, int); + SAFE_MALLOC(seg1, n + 1, int); + + /* calculate clipped paths */ + for (i = 0; i < n; i++) { + c = mod(pp->lon[mod(i - 1, n)] - 1, n); + if (c == i) { + c = mod(i + 1, n); + } + if (c < i) { + clip0[i] = n; + } else { + clip0[i] = c; + } } - if (c < i) { - clip0[i] = n; - } else { - clip0[i] = c; + + /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff + clip1[j] <= i, for i,j=0..n. */ + j = 1; + for (i = 0; i < n; i++) { + while (j <= clip0[i]) { + clip1[j] = i; + j++; + } + } + + /* calculate seg0[j] = longest path from 0 with j segments */ + i = 0; + for (j = 0; i < n; j++) { + seg0[j] = i; + i = clip0[i]; + } + seg0[j] = n; + m = j; + + /* calculate seg1[j] = longest path to n with m-j segments */ + i = n; + for (j = m; j > 0; j--) { + seg1[j] = i; + i = clip1[i]; } - } - - /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff - clip1[j] <= i, for i,j=0..n. */ - j = 1; - for (i=0; i= clip1[i]; k--) { + thispen = penalty3(pp, k, i) + pen[k]; + if (best < 0 || thispen < best) { + prev[i] = k; + best = thispen; + } + } + pen[i] = best; + } } - } - - /* calculate seg0[j] = longest path from 0 with j segments */ - i = 0; - for (j=0; i0; j--) { - seg1[j] = i; - i = clip1[i]; - } - seg1[0] = 0; - - /* now find the shortest path with m segments, based on penalty3 */ - /* note: the outer 2 loops jointly have at most n iterations, thus - the worst-case behavior here is quadratic. In practice, it is - close to linear since the inner loop tends to be short. */ - pen[0]=0; - for (j=1; j<=m; j++) { - for (i=seg1[j]; i<=seg0[j]; i++) { - best = -1; - for (k=seg0[j-1]; k>=clip1[i]; k--) { - thispen = penalty3(pp, k, i) + pen[k]; - if (best < 0 || thispen < best) { - prev[i] = k; - best = thispen; - } - } - pen[i] = best; + + pp->m = m; + SAFE_MALLOC(pp->po, m, int); + + /* read off shortest path */ + for (i = n, j = m - 1; i > 0; j--) { + i = prev[i]; + pp->po[j] = i; } - } - - pp->m = m; - SAFE_MALLOC(pp->po, m, int); - - /* read off shortest path */ - for (i=n, j=m-1; i>0; j--) { - i = prev[i]; - pp->po[j] = i; - } - - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 0; - - malloc_error: - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 1; + + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 0; + +malloc_error: + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 1; } /* ---------------------------------------------------------------------- */ @@ -638,266 +648,269 @@ static int bestpolygon(privpath_t *pp) if it lies outside. Return 1 with errno set on error; 0 on success. */ -static int adjust_vertices(privpath_t *pp) { - int m = pp->m; - int *po = pp->po; - int n = pp->len; - point_t *pt = pp->pt; - int x0 = pp->x0; - int y0 = pp->y0; - - dpoint_t *ctr = NULL; /* ctr[m] */ - dpoint_t *dir = NULL; /* dir[m] */ - quadform_t *q = NULL; /* q[m] */ - double v[3]; - double d; - int i, j, k, l; - dpoint_t s; - int r; - - SAFE_MALLOC(ctr, m, dpoint_t); - SAFE_MALLOC(dir, m, dpoint_t); - SAFE_MALLOC(q, m, quadform_t); - - r = privcurve_init(&pp->curve, m); - if (r) { - goto malloc_error; - } - - /* calculate "optimal" point-slope representation for each line - segment */ - for (i=0; im; + int *po = pp->po; + int n = pp->len; + point_t *pt = pp->pt; + int x0 = pp->x0; + int y0 = pp->y0; + + dpoint_t *ctr = NULL; /* ctr[m] */ + dpoint_t *dir = NULL; /* dir[m] */ + quadform_t *q = NULL; /* q[m] */ + double v[3]; + double d; + int i, j, k, l; + dpoint_t s; + int r; + + SAFE_MALLOC(ctr, m, dpoint_t); + SAFE_MALLOC(dir, m, dpoint_t); + SAFE_MALLOC(q, m, quadform_t); + + r = privcurve_init(&pp->curve, m); + if (r) { + goto malloc_error; } - - while(1) { - /* minimize the quadratic form Q on the unit square */ - /* find intersection */ -#ifdef HAVE_GCC_LOOP_BUG - /* work around gcc bug #12243 */ - free(NULL); -#endif - - det = Q[0][0]*Q[1][1] - Q[0][1]*Q[1][0]; - if (det != 0.0) { - w.x = (-Q[0][2]*Q[1][1] + Q[1][2]*Q[0][1]) / det; - w.y = ( Q[0][2]*Q[1][0] - Q[1][2]*Q[0][0]) / det; - break; - } - - /* matrix is singular - lines are parallel. Add another, - orthogonal axis, through the center of the unit square */ - if (Q[0][0]>Q[1][1]) { - v[0] = -Q[0][1]; - v[1] = Q[0][0]; - } else if (Q[1][1]) { - v[0] = -Q[1][1]; - v[1] = Q[1][0]; - } else { - v[0] = 1; - v[1] = 0; - } - d = sq(v[0]) + sq(v[1]); - v[2] = - v[1] * s.y - v[0] * s.x; - for (l=0; l<3; l++) { - for (k=0; k<3; k++) { - Q[l][k] += v[l] * v[k] / d; - } - } + /* calculate "optimal" point-slope representation for each line + segment */ + for (i = 0; i < m; i++) { + j = po[mod(i + 1, m)]; + j = mod(j - po[i], n) + po[i]; + pointslope(pp, po[i], j, &ctr[i], &dir[i]); } - dx = fabs(w.x-s.x); - dy = fabs(w.y-s.y); - if (dx <= .5 && dy <= .5) { - pp->curve.vertex[i].x = w.x+x0; - pp->curve.vertex[i].y = w.y+y0; - continue; + + /* represent each line segment as a singular quadratic form; the + distance of a point (x,y) from the line segment will be + (x,y,1)Q(x,y,1)^t, where Q=q[i]. */ + for (i = 0; i < m; i++) { + d = sq(dir[i].x) + sq(dir[i].y); + if (d == 0.0) { + for (j = 0; j < 3; j++) { + for (k = 0; k < 3; k++) { + q[i][j][k] = 0; + } + } + } else { + v[0] = dir[i].y; + v[1] = -dir[i].x; + v[2] = -v[1] * ctr[i].y - v[0] * ctr[i].x; + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + q[i][l][k] = v[l] * v[k] / d; + } + } + } } - /* the minimum was not in the unit square; now minimize quadratic - on boundary of square */ - min = quadform(Q, s); - xmin = s.x; - ymin = s.y; + /* now calculate the "intersections" of consecutive segments. + Instead of using the actual intersection, we find the point + within a given unit square which minimizes the square distance to + the two lines. */ + for (i = 0; i < m; i++) { + quadform_t Q; + dpoint_t w; + double dx, dy; + double det; + double min, cand; /* minimum and candidate for minimum of quad. form */ + double xmin, ymin; /* coordinates of minimum */ + int z; + + /* let s be the vertex, in coordinates relative to x0/y0 */ + s.x = pt[po[i]].x - x0; + s.y = pt[po[i]].y - y0; + + /* intersect segments i-1 and i */ + + j = mod(i - 1, m); + + /* add quadratic forms */ + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + Q[l][k] = q[j][l][k] + q[i][l][k]; + } + } + + while (1) { +/* minimize the quadratic form Q on the unit square */ +/* find intersection */ - if (Q[0][0] == 0.0) { - goto fixx; - } - for (z=0; z<2; z++) { /* value of the y-coordinate */ - w.y = s.y-0.5+z; - w.x = - (Q[0][1] * w.y + Q[0][2]) / Q[0][0]; - dx = fabs(w.x-s.x); - cand = quadform(Q, w); - if (dx <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - fixx: - if (Q[1][1] == 0.0) { - goto corners; - } - for (z=0; z<2; z++) { /* value of the x-coordinate */ - w.x = s.x-0.5+z; - w.y = - (Q[1][0] * w.x + Q[1][2]) / Q[1][1]; - dy = fabs(w.y-s.y); - cand = quadform(Q, w); - if (dy <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - corners: - /* check four corners */ - for (l=0; l<2; l++) { - for (k=0; k<2; k++) { - w.x = s.x-0.5+l; - w.y = s.y-0.5+k; - cand = quadform(Q, w); - if (cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } +#ifdef HAVE_GCC_LOOP_BUG + /* work around gcc bug #12243 */ + free(NULL); +#endif + + det = Q[0][0] * Q[1][1] - Q[0][1] * Q[1][0]; + if (det != 0.0) { + w.x = (-Q[0][2] * Q[1][1] + Q[1][2] * Q[0][1]) / det; + w.y = (Q[0][2] * Q[1][0] - Q[1][2] * Q[0][0]) / det; + break; + } + + /* matrix is singular - lines are parallel. Add another, + orthogonal axis, through the center of the unit square */ + if (Q[0][0] > Q[1][1]) { + v[0] = -Q[0][1]; + v[1] = Q[0][0]; + } else if (Q[1][1]) { + v[0] = -Q[1][1]; + v[1] = Q[1][0]; + } else { + v[0] = 1; + v[1] = 0; + } + d = sq(v[0]) + sq(v[1]); + v[2] = -v[1] * s.y - v[0] * s.x; + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + Q[l][k] += v[l] * v[k] / d; + } + } + } + dx = fabs(w.x - s.x); + dy = fabs(w.y - s.y); + if (dx <= .5 && dy <= .5) { + pp->curve.vertex[i].x = w.x + x0; + pp->curve.vertex[i].y = w.y + y0; + continue; + } + + /* the minimum was not in the unit square; now minimize quadratic + on boundary of square */ + min = quadform(Q, s); + xmin = s.x; + ymin = s.y; + + if (Q[0][0] == 0.0) { + goto fixx; + } + for (z = 0; z < 2; z++) { /* value of the y-coordinate */ + w.y = s.y - 0.5 + z; + w.x = -(Q[0][1] * w.y + Q[0][2]) / Q[0][0]; + dx = fabs(w.x - s.x); + cand = quadform(Q, w); + if (dx <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + fixx: + if (Q[1][1] == 0.0) { + goto corners; + } + for (z = 0; z < 2; z++) { /* value of the x-coordinate */ + w.x = s.x - 0.5 + z; + w.y = -(Q[1][0] * w.x + Q[1][2]) / Q[1][1]; + dy = fabs(w.y - s.y); + cand = quadform(Q, w); + if (dy <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + corners: + /* check four corners */ + for (l = 0; l < 2; l++) { + for (k = 0; k < 2; k++) { + w.x = s.x - 0.5 + l; + w.y = s.y - 0.5 + k; + cand = quadform(Q, w); + if (cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + } + + pp->curve.vertex[i].x = xmin + x0; + pp->curve.vertex[i].y = ymin + y0; + continue; } - pp->curve.vertex[i].x = xmin + x0; - pp->curve.vertex[i].y = ymin + y0; - continue; - } - - free(ctr); - free(dir); - free(q); - return 0; - - malloc_error: - free(ctr); - free(dir); - free(q); - return 1; + free(ctr); + free(dir); + free(q); + return 0; + +malloc_error: + free(ctr); + free(dir); + free(q); + return 1; } /* ---------------------------------------------------------------------- */ /* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ /* reverse orientation of a path */ -static void reverse(privcurve_t *curve) { - int m = curve->n; - int i, j; - dpoint_t tmp; - - for (i=0, j=m-1; ivertex[i]; - curve->vertex[i] = curve->vertex[j]; - curve->vertex[j] = tmp; - } +static void reverse(privcurve_t *curve) +{ + int m = curve->n; + int i, j; + dpoint_t tmp; + + for (i = 0, j = m - 1; i < j; i++, j--) { + tmp = curve->vertex[i]; + curve->vertex[i] = curve->vertex[j]; + curve->vertex[j] = tmp; + } } /* Always succeeds */ -static void smooth(privcurve_t *curve, double alphamax) { - int m = curve->n; - - int i, j, k; - double dd, denom, alpha; - dpoint_t p2, p3, p4; - - /* examine each vertex and find its best fit */ - for (i=0; ivertex[k], curve->vertex[j]); - - denom = ddenom(curve->vertex[i], curve->vertex[k]); - if (denom != 0.0) { - dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; - dd = fabs(dd); - alpha = dd>1 ? (1 - 1.0/dd) : 0; - alpha = alpha / 0.75; - } else { - alpha = 4/3.0; - } - curve->alpha0[j] = alpha; /* remember "original" value of alpha */ - - if (alpha > alphamax) { /* pointed corner */ - curve->tag[j] = POTRACE_CORNER; - curve->c[j][1] = curve->vertex[j]; - curve->c[j][2] = p4; - } else { - if (alpha < 0.55) { - alpha = 0.55; - } else if (alpha > 1) { - alpha = 1; - } - p2 = interval(.5+.5*alpha, curve->vertex[i], curve->vertex[j]); - p3 = interval(.5+.5*alpha, curve->vertex[k], curve->vertex[j]); - curve->tag[j] = POTRACE_CURVETO; - curve->c[j][0] = p2; - curve->c[j][1] = p3; - curve->c[j][2] = p4; +static void smooth(privcurve_t *curve, double alphamax) +{ + int m = curve->n; + + int i, j, k; + double dd, denom, alpha; + dpoint_t p2, p3, p4; + + /* examine each vertex and find its best fit */ + for (i = 0; i < m; i++) { + j = mod(i + 1, m); + k = mod(i + 2, m); + p4 = interval(1 / 2.0, curve->vertex[k], curve->vertex[j]); + + denom = ddenom(curve->vertex[i], curve->vertex[k]); + if (denom != 0.0) { + dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; + dd = fabs(dd); + alpha = dd > 1 ? (1 - 1.0 / dd) : 0; + alpha = alpha / 0.75; + } else { + alpha = 4 / 3.0; + } + curve->alpha0[j] = alpha; /* remember "original" value of alpha */ + + if (alpha > alphamax) { /* pointed corner */ + curve->tag[j] = POTRACE_CORNER; + curve->c[j][1] = curve->vertex[j]; + curve->c[j][2] = p4; + } else { + if (alpha < 0.55) { + alpha = 0.55; + } else if (alpha > 1) { + alpha = 1; + } + p2 = interval(.5 + .5 * alpha, curve->vertex[i], curve->vertex[j]); + p3 = interval(.5 + .5 * alpha, curve->vertex[k], curve->vertex[j]); + curve->tag[j] = POTRACE_CURVETO; + curve->c[j][0] = p2; + curve->c[j][1] = p3; + curve->c[j][2] = p4; + } + curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ + curve->beta[j] = 0.5; } - curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ - curve->beta[j] = 0.5; - } - curve->alphacurve = 1; + curve->alphacurve = 1; - return; + return; } /* ---------------------------------------------------------------------- */ @@ -905,341 +918,349 @@ static void smooth(privcurve_t *curve, double alphamax) { /* a private type for the result of opti_penalty */ struct opti_s { - double pen; /* penalty */ - dpoint_t c[2]; /* curve parameters */ - double t, s; /* curve parameters */ - double alpha; /* curve parameter */ + double pen; /* penalty */ + dpoint_t c[2]; /* curve parameters */ + double t, s; /* curve parameters */ + double alpha; /* curve parameter */ }; typedef struct opti_s opti_t; /* calculate best fit from i+.5 to j+.5. Assume icurve.n; - int k, k1, k2, conv, i1; - double area, alpha, d, d1, d2; - dpoint_t p0, p1, p2, p3, pt; - double A, R, A1, A2, A3, A4; - double s, t; - - /* check convexity, corner-freeness, and maximum bend < 179 degrees */ +static int opti_penalty(privpath_t *pp, int i, int j, opti_t *res, double opttolerance, int *convc, double *areac) +{ + int m = pp->curve.n; + int k, k1, k2, conv, i1; + double area, alpha, d, d1, d2; + dpoint_t p0, p1, p2, p3, pt; + double A, R, A1, A2, A3, A4; + double s, t; - if (i==j) { /* sanity - a full loop can never be an opticurve */ - return 1; - } + /* check convexity, corner-freeness, and maximum bend < 179 degrees */ - k = i; - i1 = mod(i+1, m); - k1 = mod(k+1, m); - conv = convc[k1]; - if (conv == 0) { - return 1; - } - d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); - for (k=k1; k!=j; k=k1) { - k1 = mod(k+1, m); - k2 = mod(k+2, m); - if (convc[k1] != conv) { - return 1; + if (i == j) { /* sanity - a full loop can never be an opticurve */ + return 1; } - if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) { - return 1; + + k = i; + i1 = mod(i + 1, m); + k1 = mod(k + 1, m); + conv = convc[k1]; + if (conv == 0) { + return 1; } - if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { - return 1; + d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); + for (k = k1; k != j; k = k1) { + k1 = mod(k + 1, m); + k2 = mod(k + 2, m); + if (convc[k1] != conv) { + return 1; + } + if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != + conv) { + return 1; + } + if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < + d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { + return 1; + } } - } - - /* the curve we're working in: */ - p0 = pp->curve.c[mod(i,m)][2]; - p1 = pp->curve.vertex[mod(i+1,m)]; - p2 = pp->curve.vertex[mod(j,m)]; - p3 = pp->curve.c[mod(j,m)][2]; - - /* determine its area */ - area = areac[j] - areac[i]; - area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2])/2; - if (i>=j) { - area += areac[m]; - } - - /* find intersection o of p0p1 and p2p3. Let t,s such that o = - interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the - triangle (p0,o,p3). */ - - A1 = dpara(p0, p1, p2); - A2 = dpara(p0, p1, p3); - A3 = dpara(p0, p2, p3); - /* A4 = dpara(p1, p2, p3); */ - A4 = A1+A3-A2; - - if (A2 == A1) { /* this should never happen */ - return 1; - } - t = A3/(A3-A4); - s = A2/(A2-A1); - A = A2 * t / 2.0; - - if (A == 0.0) { /* this should never happen */ - return 1; - } + /* the curve we're working in: */ + p0 = pp->curve.c[mod(i, m)][2]; + p1 = pp->curve.vertex[mod(i + 1, m)]; + p2 = pp->curve.vertex[mod(j, m)]; + p3 = pp->curve.c[mod(j, m)][2]; + + /* determine its area */ + area = areac[j] - areac[i]; + area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2]) / 2; + if (i >= j) { + area += areac[m]; + } - R = area / A; /* relative area */ - alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ + /* find intersection o of p0p1 and p2p3. Let t,s such that o = + interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the + triangle (p0,o,p3). */ - res->c[0] = interval(t * alpha, p0, p1); - res->c[1] = interval(s * alpha, p3, p2); - res->alpha = alpha; - res->t = t; - res->s = s; + A1 = dpara(p0, p1, p2); + A2 = dpara(p0, p1, p3); + A3 = dpara(p0, p2, p3); + /* A4 = dpara(p1, p2, p3); */ + A4 = A1 + A3 - A2; - p1 = res->c[0]; - p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ + if (A2 == A1) { /* this should never happen */ + return 1; + } - res->pen = 0; + t = A3 / (A3 - A4); + s = A2 / (A2 - A1); + A = A2 * t / 2.0; - /* calculate penalty */ - /* check tangency with edges */ - for (k=mod(i+1,m); k!=j; k=k1) { - k1 = mod(k+1,m); - t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); - if (t<-.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); - if (d == 0.0) { /* this should never happen */ - return 1; + if (A == 0.0) { /* this should never happen */ + return 1; } - d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; - if (fabs(d1) > opttolerance) { - return 1; - } - if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { - return 1; - } - res->pen += sq(d1); - } - - /* check corners */ - for (k=i; k!=j; k=k1) { - k1 = mod(k+1,m); - t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); - if (t<-.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; - d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; - d2 *= 0.75 * pp->curve.alpha[k1]; - if (d2 < 0) { - d1 = -d1; - d2 = -d2; - } - if (d1 < d2 - opttolerance) { - return 1; + + R = area / A; /* relative area */ + alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ + + res->c[0] = interval(t * alpha, p0, p1); + res->c[1] = interval(s * alpha, p3, p2); + res->alpha = alpha; + res->t = t; + res->s = s; + + p1 = res->c[0]; + p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ + + res->pen = 0; + + /* calculate penalty */ + /* check tangency with edges */ + for (k = mod(i + 1, m); k != j; k = k1) { + k1 = mod(k + 1, m); + t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); + if (t < -.5) { + return 1; + } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); + if (d == 0.0) { /* this should never happen */ + return 1; + } + d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; + if (fabs(d1) > opttolerance) { + return 1; + } + if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || + iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { + return 1; + } + res->pen += sq(d1); } - if (d1 < d2) { - res->pen += sq(d1 - d2); + + /* check corners */ + for (k = i; k != j; k = k1) { + k1 = mod(k + 1, m); + t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); + if (t < -.5) { + return 1; + } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); + if (d == 0.0) { /* this should never happen */ + return 1; + } + d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; + d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; + d2 *= 0.75 * pp->curve.alpha[k1]; + if (d2 < 0) { + d1 = -d1; + d2 = -d2; + } + if (d1 < d2 - opttolerance) { + return 1; + } + if (d1 < d2) { + res->pen += sq(d1 - d2); + } } - } - return 0; + return 0; } /* optimize the path p, replacing sequences of Bezier segments by a single segment when possible. Return 0 on success, 1 with errno set on failure. */ -static int opticurve(privpath_t *pp, double opttolerance) { - int m = pp->curve.n; - int *pt = NULL; /* pt[m+1] */ - double *pen = NULL; /* pen[m+1] */ - int *len = NULL; /* len[m+1] */ - opti_t *opt = NULL; /* opt[m+1] */ - int om; - int i,j,r; - opti_t o; - dpoint_t p0; - int i1; - double area; - double alpha; - double *s = NULL; - double *t = NULL; - - int *convc = NULL; /* conv[m]: pre-computed convexities */ - double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ - - SAFE_MALLOC(pt, m+1, int); - SAFE_MALLOC(pen, m+1, double); - SAFE_MALLOC(len, m+1, int); - SAFE_MALLOC(opt, m+1, opti_t); - SAFE_MALLOC(convc, m, int); - SAFE_MALLOC(areac, m+1, double); - - /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ - for (i=0; icurve.tag[i] == POTRACE_CURVETO) { - convc[i] = sign(dpara(pp->curve.vertex[mod(i-1,m)], pp->curve.vertex[i], pp->curve.vertex[mod(i+1,m)])); - } else { - convc[i] = 0; +static int opticurve(privpath_t *pp, double opttolerance) +{ + int m = pp->curve.n; + int *pt = NULL; /* pt[m+1] */ + double *pen = NULL; /* pen[m+1] */ + int *len = NULL; /* len[m+1] */ + opti_t *opt = NULL; /* opt[m+1] */ + int om; + int i, j, r; + opti_t o; + dpoint_t p0; + int i1; + double area; + double alpha; + double *s = NULL; + double *t = NULL; + + int *convc = NULL; /* conv[m]: pre-computed convexities */ + double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ + + SAFE_MALLOC(pt, m + 1, int); + SAFE_MALLOC(pen, m + 1, double); + SAFE_MALLOC(len, m + 1, int); + SAFE_MALLOC(opt, m + 1, opti_t); + SAFE_MALLOC(convc, m, int); + SAFE_MALLOC(areac, m + 1, double); + + /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ + for (i = 0; i < m; i++) { + if (pp->curve.tag[i] == POTRACE_CURVETO) { + convc[i] = + sign(dpara(pp->curve.vertex[mod(i - 1, m)], pp->curve.vertex[i], pp->curve.vertex[mod(i + 1, m)])); + } else { + convc[i] = 0; + } } - } - - /* pre-calculate areas */ - area = 0.0; - areac[0] = 0.0; - p0 = pp->curve.vertex[0]; - for (i=0; icurve.tag[i1] == POTRACE_CURVETO) { - alpha = pp->curve.alpha[i1]; - area += 0.3*alpha*(4-alpha)*dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2])/2; - area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2])/2; + + /* pre-calculate areas */ + area = 0.0; + areac[0] = 0.0; + p0 = pp->curve.vertex[0]; + for (i = 0; i < m; i++) { + i1 = mod(i + 1, m); + if (pp->curve.tag[i1] == POTRACE_CURVETO) { + alpha = pp->curve.alpha[i1]; + area += 0.3 * alpha * (4 - alpha) * dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2]) / 2; + area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2]) / 2; + } + areac[i + 1] = area; } - areac[i+1] = area; - } - - pt[0] = -1; - pen[0] = 0; - len[0] = 0; - - /* Fixme: we always start from a fixed point -- should find the best - curve cyclically */ - - for (j=1; j<=m; j++) { - /* calculate best path from 0 to j */ - pt[j] = j-1; - pen[j] = pen[j-1]; - len[j] = len[j-1]+1; - - for (i=j-2; i>=0; i--) { - r = opti_penalty(pp, i, mod(j,m), &o, opttolerance, convc, areac); - if (r) { - break; - } - if (len[j] > len[i]+1 || (len[j] == len[i]+1 && pen[j] > pen[i] + o.pen)) { - pt[j] = i; - pen[j] = pen[i] + o.pen; - len[j] = len[i] + 1; - opt[j] = o; - } + + pt[0] = -1; + pen[0] = 0; + len[0] = 0; + + /* Fixme: we always start from a fixed point -- should find the best + curve cyclically */ + + for (j = 1; j <= m; j++) { + /* calculate best path from 0 to j */ + pt[j] = j - 1; + pen[j] = pen[j - 1]; + len[j] = len[j - 1] + 1; + + for (i = j - 2; i >= 0; i--) { + r = opti_penalty(pp, i, mod(j, m), &o, opttolerance, convc, areac); + if (r) { + break; + } + if (len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + o.pen)) { + pt[j] = i; + pen[j] = pen[i] + o.pen; + len[j] = len[i] + 1; + opt[j] = o; + } + } } - } - om = len[m]; - r = privcurve_init(&pp->ocurve, om); - if (r) { - goto malloc_error; - } - SAFE_MALLOC(s, om, double); - SAFE_MALLOC(t, om, double); - - j = m; - for (i=om-1; i>=0; i--) { - if (pt[j]==j-1) { - pp->ocurve.tag[i] = pp->curve.tag[mod(j,m)]; - pp->ocurve.c[i][0] = pp->curve.c[mod(j,m)][0]; - pp->ocurve.c[i][1] = pp->curve.c[mod(j,m)][1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; - pp->ocurve.vertex[i] = pp->curve.vertex[mod(j,m)]; - pp->ocurve.alpha[i] = pp->curve.alpha[mod(j,m)]; - pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j,m)]; - pp->ocurve.beta[i] = pp->curve.beta[mod(j,m)]; - s[i] = t[i] = 1.0; - } else { - pp->ocurve.tag[i] = POTRACE_CURVETO; - pp->ocurve.c[i][0] = opt[j].c[0]; - pp->ocurve.c[i][1] = opt[j].c[1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; - pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j,m)][2], pp->curve.vertex[mod(j,m)]); - pp->ocurve.alpha[i] = opt[j].alpha; - pp->ocurve.alpha0[i] = opt[j].alpha; - s[i] = opt[j].s; - t[i] = opt[j].t; + om = len[m]; + r = privcurve_init(&pp->ocurve, om); + if (r) { + goto malloc_error; } - j = pt[j]; - } - - /* calculate beta parameters */ - for (i=0; iocurve.beta[i] = s[i] / (s[i] + t[i1]); - } - pp->ocurve.alphacurve = 1; - - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 0; - - malloc_error: - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 1; + SAFE_MALLOC(s, om, double); + SAFE_MALLOC(t, om, double); + + j = m; + for (i = om - 1; i >= 0; i--) { + if (pt[j] == j - 1) { + pp->ocurve.tag[i] = pp->curve.tag[mod(j, m)]; + pp->ocurve.c[i][0] = pp->curve.c[mod(j, m)][0]; + pp->ocurve.c[i][1] = pp->curve.c[mod(j, m)][1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; + pp->ocurve.vertex[i] = pp->curve.vertex[mod(j, m)]; + pp->ocurve.alpha[i] = pp->curve.alpha[mod(j, m)]; + pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j, m)]; + pp->ocurve.beta[i] = pp->curve.beta[mod(j, m)]; + s[i] = t[i] = 1.0; + } else { + pp->ocurve.tag[i] = POTRACE_CURVETO; + pp->ocurve.c[i][0] = opt[j].c[0]; + pp->ocurve.c[i][1] = opt[j].c[1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; + pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j, m)][2], pp->curve.vertex[mod(j, m)]); + pp->ocurve.alpha[i] = opt[j].alpha; + pp->ocurve.alpha0[i] = opt[j].alpha; + s[i] = opt[j].s; + t[i] = opt[j].t; + } + j = pt[j]; + } + + /* calculate beta parameters */ + for (i = 0; i < om; i++) { + i1 = mod(i + 1, om); + pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]); + } + pp->ocurve.alphacurve = 1; + + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 0; + +malloc_error: + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 1; } /* ---------------------------------------------------------------------- */ -#define TRY(x) if (x) goto try_error +#define TRY(x) \ + if (x) \ + goto try_error /* return 0 on success, 1 on error with errno set. */ -int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) { - path_t *p; - double nn = 0, cn = 0; - - if (progress->callback) { - /* precompute task size for progress estimates */ - nn = 0; - list_forall (p, plist) { - nn += p->priv->len; - } - cn = 0; - } - - /* call downstream function with each path */ - list_forall (p, plist) { - TRY(calc_sums(p->priv)); - TRY(calc_lon(p->priv)); - TRY(bestpolygon(p->priv)); - TRY(adjust_vertices(p->priv)); - if (p->sign == '-') { /* reverse orientation of negative paths */ - reverse(&p->priv->curve); - } - smooth(&p->priv->curve, param->alphamax); - if (param->opticurve) { - TRY(opticurve(p->priv, param->opttolerance)); - p->priv->fcurve = &p->priv->ocurve; - } else { - p->priv->fcurve = &p->priv->curve; - } - privcurve_to_curve(p->priv->fcurve, &p->curve); +int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) +{ + path_t *p; + double nn = 0, cn = 0; if (progress->callback) { - cn += p->priv->len; - progress_update(cn/nn, progress); + /* precompute task size for progress estimates */ + nn = 0; + list_forall(p, plist) { nn += p->priv->len; } + cn = 0; + } + + /* call downstream function with each path */ + list_forall(p, plist) + { + TRY(calc_sums(p->priv)); + TRY(calc_lon(p->priv)); + TRY(bestpolygon(p->priv)); + TRY(adjust_vertices(p->priv)); + if (p->sign == '-') { /* reverse orientation of negative paths */ + reverse(&p->priv->curve); + } + smooth(&p->priv->curve, param->alphamax); + if (param->opticurve) { + TRY(opticurve(p->priv, param->opttolerance)); + p->priv->fcurve = &p->priv->ocurve; + } else { + p->priv->fcurve = &p->priv->curve; + } + privcurve_to_curve(p->priv->fcurve, &p->curve); + + if (progress->callback) { + cn += p->priv->len; + progress_update(cn / nn, progress); + } } - } - progress_update(1.0, progress); + progress_update(1.0, progress); - return 0; + return 0; - try_error: - return 1; +try_error: + return 1; } -- cgit v1.2.3 From a2dca92e95dabcdaa66310f489dba5178e3e1914 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 13:16:16 +0200 Subject: undo whitespace edit. it's an external dependency, and let's assume that the potential bug is not a bug (line 611, possibly uninitialized used of prev[i]) (bzr r13594) --- src/trace/potrace/trace.cpp | 2059 +++++++++++++++++++++---------------------- 1 file changed, 1019 insertions(+), 1040 deletions(-) (limited to 'src') diff --git a/src/trace/potrace/trace.cpp b/src/trace/potrace/trace.cpp index 2bdefb90a..f1e88a908 100644 --- a/src/trace/potrace/trace.cpp +++ b/src/trace/potrace/trace.cpp @@ -16,129 +16,124 @@ #include "trace.h" #include "progress.h" -#define INFTY 10000000 // it suffices that this is longer than any - // path; it need not be really infinite -#define COS179 -0.999847695156 // the cosine of 179 degrees +#define INFTY 10000000 /* it suffices that this is longer than any + path; it need not be really infinite */ +#define COS179 -0.999847695156 /* the cosine of 179 degrees */ /* ---------------------------------------------------------------------- */ #define SAFE_MALLOC(var, n, typ) \ - if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error + if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error /* ---------------------------------------------------------------------- */ /* auxiliary functions */ /* return a direction that is 90 degrees counterclockwise from p2-p0, but then restricted to one of the major wind directions (n, nw, w, etc) */ -static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) -{ - point_t r; - - r.y = sign(p2.x - p0.x); - r.x = -sign(p2.y - p0.y); +static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) { + point_t r; + + r.y = sign(p2.x-p0.x); + r.x = -sign(p2.y-p0.y); - return r; + return r; } /* return (p1-p0)x(p2-p0), the area of the parallelogram */ -static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) -{ - double x1, y1, x2, y2; +static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) { + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; + x1 = p1.x-p0.x; + y1 = p1.y-p0.y; + x2 = p2.x-p0.x; + y2 = p2.y-p0.y; - return x1 * y2 - x2 * y1; + return x1*y2 - x2*y1; } /* ddenom/dpara have the property that the square of radius 1 centered at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */ -static inline double ddenom(dpoint_t p0, dpoint_t p2) -{ - point_t r = dorth_infty(p0, p2); +static inline double ddenom(dpoint_t p0, dpoint_t p2) { + point_t r = dorth_infty(p0, p2); - return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y); + return r.y*(p2.x-p0.x) - r.x*(p2.y-p0.y); } /* return 1 if a <= b < c < a, in a cyclic sense (mod n) */ -static inline int cyclic(int a, int b, int c) -{ - if (a <= c) { - return (a <= b && b < c); - } else { - return (a <= b || b < c); - } +static inline int cyclic(int a, int b, int c) { + if (a<=c) { + return (a<=b && blen; - sums_t *sums = pp->sums; - - double x, y, x2, xy, y2; - double k; - double a, b, c, lambda2, l; - int r = 0; /* rotations from i to j */ - - while (j >= n) { - j -= n; - r += 1; - } - while (i >= n) { - i -= n; - r -= 1; - } - while (j < 0) { - j += n; - r -= 1; - } - while (i < 0) { - i += n; - r += 1; - } - - x = sums[j + 1].x - sums[i].x + r * sums[n].x; - y = sums[j + 1].y - sums[i].y + r * sums[n].y; - x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; - xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; - y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; - k = j + 1 - i + r * n; - - ctr->x = x / k; - ctr->y = y / k; - - a = (x2 - (double)x * x / k) / k; - b = (xy - (double)x * y / k) / k; - c = (y2 - (double)y * y / k) / k; - - lambda2 = (a + c + sqrt((a - c) * (a - c) + 4 * b * b)) / 2; /* larger e.value */ - - /* now find e.vector for lambda2 */ - a -= lambda2; - c -= lambda2; - - if (fabs(a) >= fabs(c)) { - l = sqrt(a * a + b * b); - if (l != 0) { - dir->x = -b / l; - dir->y = a / l; - } - } else { - l = sqrt(c * c + b * b); - if (l != 0) { - dir->x = -c / l; - dir->y = b / l; - } +static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *dir) { + /* assume ilen; + sums_t *sums = pp->sums; + + double x, y, x2, xy, y2; + double k; + double a, b, c, lambda2, l; + int r=0; /* rotations from i to j */ + + while (j>=n) { + j-=n; + r+=1; + } + while (i>=n) { + i-=n; + r-=1; + } + while (j<0) { + j+=n; + r-=1; + } + while (i<0) { + i+=n; + r+=1; + } + + x = sums[j+1].x-sums[i].x+r*sums[n].x; + y = sums[j+1].y-sums[i].y+r*sums[n].y; + x2 = sums[j+1].x2-sums[i].x2+r*sums[n].x2; + xy = sums[j+1].xy-sums[i].xy+r*sums[n].xy; + y2 = sums[j+1].y2-sums[i].y2+r*sums[n].y2; + k = j+1-i+r*n; + + ctr->x = x/k; + ctr->y = y/k; + + a = (x2-(double)x*x/k)/k; + b = (xy-(double)x*y/k)/k; + c = (y2-(double)y*y/k)/k; + + lambda2 = (a+c+sqrt((a-c)*(a-c)+4*b*b))/2; /* larger e.value */ + + /* now find e.vector for lambda2 */ + a -= lambda2; + c -= lambda2; + + if (fabs(a) >= fabs(c)) { + l = sqrt(a*a+b*b); + if (l!=0) { + dir->x = -b/l; + dir->y = a/l; } - if (l == 0) { - dir->x = dir->y = 0; /* sometimes this can happen when k=4: - the two eigenvalues coincide */ + } else { + l = sqrt(c*c+b*b); + if (l!=0) { + dir->x = -c/l; + dir->y = b/l; } + } + if (l==0) { + dir->x = dir->y = 0; /* sometimes this can happen when k=4: + the two eigenvalues coincide */ + } } /* the type of (affine) quadratic forms, represented as symmetric 3x3 @@ -147,158 +142,155 @@ static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *di typedef double quadform_t[3][3]; /* Apply quadratic form Q to vector w = (w.x,w.y) */ -static inline double quadform(quadform_t Q, dpoint_t w) -{ - double v[3]; - int i, j; - double sum; - - v[0] = w.x; - v[1] = w.y; - v[2] = 1; - sum = 0.0; - - for (i = 0; i < 3; i++) { - for (j = 0; j < 3; j++) { - sum += v[i] * Q[i][j] * v[j]; - } +static inline double quadform(quadform_t Q, dpoint_t w) { + double v[3]; + int i, j; + double sum; + + v[0] = w.x; + v[1] = w.y; + v[2] = 1; + sum = 0.0; + + for (i=0; i<3; i++) { + for (j=0; j<3; j++) { + sum += v[i] * Q[i][j] * v[j]; } - return sum; + } + return sum; } /* calculate p1 x p2 */ -static inline int xprod(point_t p1, point_t p2) { return p1.x * p2.y - p1.y * p2.x; } +static inline int xprod(point_t p1, point_t p2) { + return p1.x*p2.y - p1.y*p2.x; +} /* calculate (p1-p0)x(p3-p2) */ -static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double x1, y1, x2, y2; +static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; - return x1 * y2 - x2 * y1; + return x1*y2 - x2*y1; } /* calculate (p1-p0)*(p2-p0) */ -static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) -{ - double x1, y1, x2, y2; +static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) { + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; - return x1 * x2 + y1 * y2; + return x1*x2 + y1*y2; } /* calculate (p1-p0)*(p3-p2) */ -static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double x1, y1, x2, y2; +static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; - return x1 * x2 + y1 * y2; + return x1*x2 + y1*y2; } /* calculate distance between two points */ -static inline double ddist(dpoint_t p, dpoint_t q) { return sqrt(sq(p.x - q.x) + sq(p.y - q.y)); } +static inline double ddist(dpoint_t p, dpoint_t q) { + return sqrt(sq(p.x-q.x)+sq(p.y-q.y)); +} /* calculate point of a bezier curve */ -static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double s = 1 - t; - dpoint_t res; +static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { + double s = 1-t; + dpoint_t res; - /* Note: a good optimizing compiler (such as gcc-3) reduces the - following to 16 multiplications, using common subexpression - elimination. */ + /* Note: a good optimizing compiler (such as gcc-3) reduces the + following to 16 multiplications, using common subexpression + elimination. */ - res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x; - res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y; + res.x = s*s*s*p0.x + 3*(s*s*t)*p1.x + 3*(t*t*s)*p2.x + t*t*t*p3.x; + res.y = s*s*s*p0.y + 3*(s*s*t)*p1.y + 3*(t*t*s)*p2.y + t*t*t*p3.y; - return res; + return res; } /* calculate the point t in [0..1] on the (convex) bezier curve (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no solution in [0..1]. */ -static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) -{ - double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ - double a, b, c; /* a t^2 + b t + c = 0 */ - double d, s, r1, r2; - - A = cprod(p0, p1, q0, q1); - B = cprod(p1, p2, q0, q1); - C = cprod(p2, p3, q0, q1); - - a = A - 2 * B + C; - b = -2 * A + 2 * B; - c = A; - - d = b * b - 4 * a * c; - - if (a == 0 || d < 0) { - return -1.0; - } - - s = sqrt(d); - - r1 = (-b + s) / (2 * a); - r2 = (-b - s) / (2 * a); - - if (r1 >= 0 && r1 <= 1) { - return r1; - } else if (r2 >= 0 && r2 <= 1) { - return r2; - } else { - return -1.0; - } +static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) { + double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ + double a, b, c; /* a t^2 + b t + c = 0 */ + double d, s, r1, r2; + + A = cprod(p0, p1, q0, q1); + B = cprod(p1, p2, q0, q1); + C = cprod(p2, p3, q0, q1); + + a = A - 2*B + C; + b = -2*A + 2*B; + c = A; + + d = b*b - 4*a*c; + + if (a==0 || d<0) { + return -1.0; + } + + s = sqrt(d); + + r1 = (-b + s) / (2 * a); + r2 = (-b - s) / (2 * a); + + if (r1 >= 0 && r1 <= 1) { + return r1; + } else if (r2 >= 0 && r2 <= 1) { + return r2; + } else { + return -1.0; + } } /* ---------------------------------------------------------------------- */ /* Preparation: fill in the sum* fields of a path (used for later rapid summing). Return 0 on success, 1 with errno set on failure. */ -static int calc_sums(privpath_t *pp) -{ - int i, x, y; - int n = pp->len; - - SAFE_MALLOC(pp->sums, pp->len + 1, sums_t); - - /* origin */ - pp->x0 = pp->pt[0].x; - pp->y0 = pp->pt[0].y; - - /* preparatory computation for later fast summing */ - pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; - for (i = 0; i < n; i++) { - x = pp->pt[i].x - pp->x0; - y = pp->pt[i].y - pp->y0; - pp->sums[i + 1].x = pp->sums[i].x + x; - pp->sums[i + 1].y = pp->sums[i].y + y; - pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x; - pp->sums[i + 1].xy = pp->sums[i].xy + x * y; - pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y; - } - return 0; - -malloc_error: - return 1; +static int calc_sums(privpath_t *pp) { + int i, x, y; + int n = pp->len; + + SAFE_MALLOC(pp->sums, pp->len+1, sums_t); + + /* origin */ + pp->x0 = pp->pt[0].x; + pp->y0 = pp->pt[0].y; + + /* preparatory computation for later fast summing */ + pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; + for (i=0; ipt[i].x - pp->x0; + y = pp->pt[i].y - pp->y0; + pp->sums[i+1].x = pp->sums[i].x + x; + pp->sums[i+1].y = pp->sums[i].y + y; + pp->sums[i+1].x2 = pp->sums[i].x2 + x*x; + pp->sums[i+1].xy = pp->sums[i].xy + x*y; + pp->sums[i+1].y2 = pp->sums[i].y2 + y*y; + } + return 0; + + malloc_error: + return 1; } /* ---------------------------------------------------------------------- */ /* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the - "lon" component of a path object (based on pt/len). For each i, + "lon" component of a path object (based on pt/len). For each i, lon[i] is the furthest index such that a straight line can be drawn from i to lon[i]. Return 1 on error with errno set, else 0. */ @@ -326,208 +318,206 @@ malloc_error: substantial. */ /* returns 0 on success, 1 on error with errno set */ -static int calc_lon(privpath_t *pp) -{ - point_t *pt = pp->pt; - int n = pp->len; - int i, j, k, k1; - int ct[4], dir; - point_t constraint[2]; - point_t cur; - point_t off; - int *pivk = NULL; /* pivk[n] */ - int *nc = NULL; /* nc[n]: next corner */ - point_t dk; /* direction of k-k1 */ - int a, b, c, d; - - SAFE_MALLOC(pivk, n, int); - SAFE_MALLOC(nc, n, int); - - /* initialize the nc data structure. Point from each point to the - furthest future point to which it is connected by a vertical or - horizontal segment. We take advantage of the fact that there is - always a direction change at 0 (due to the path decomposition - algorithm). But even if this were not so, there is no harm, as - in practice, correctness does not depend on the word "furthest" - above. */ - k = 0; - for (i = n - 1; i >= 0; i--) { - if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { - k = i + 1; /* necessarily ipt; + int n = pp->len; + int i, j, k, k1; + int ct[4], dir; + point_t constraint[2]; + point_t cur; + point_t off; + int *pivk = NULL; /* pivk[n] */ + int *nc = NULL; /* nc[n]: next corner */ + point_t dk; /* direction of k-k1 */ + int a, b, c, d; + + SAFE_MALLOC(pivk, n, int); + SAFE_MALLOC(nc, n, int); + + /* initialize the nc data structure. Point from each point to the + furthest future point to which it is connected by a vertical or + horizontal segment. We take advantage of the fact that there is + always a direction change at 0 (due to the path decomposition + algorithm). But even if this were not so, there is no harm, as + in practice, correctness does not depend on the word "furthest" + above. */ + k = 0; + for (i=n-1; i>=0; i--) { + if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { + k = i+1; /* necessarily ilon, n, int); - - /* determine pivot points: for each i, let pivk[i] be the furthest k - such that all j with i= 0; i--) { - ct[0] = ct[1] = ct[2] = ct[3] = 0; - - /* keep track of "directions" that have occurred */ - dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2; - ct[dir]++; - - constraint[0].x = 0; - constraint[0].y = 0; - constraint[1].x = 0; - constraint[1].y = 0; - - /* find the next k such that no straight line from i to k */ - k = nc[i]; - k1 = i; - while (1) { - - dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2; - ct[dir]++; - - /* if all four "directions" have occurred, cut this path */ - if (ct[0] && ct[1] && ct[2] && ct[3]) { - pivk[i] = k1; - goto foundk; - } - - cur.x = pt[k].x - pt[i].x; - cur.y = pt[k].y - pt[i].y; - - /* see if current constraint is violated */ - if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { - goto constraint_viol; - } - - /* else, update constraint */ - if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { - /* no constraint */ - } else { - off.x = cur.x + ((cur.y >= 0 && (cur.y > 0 || cur.x < 0)) ? 1 : -1); - off.y = cur.y + ((cur.x <= 0 && (cur.x < 0 || cur.y < 0)) ? 1 : -1); - if (xprod(constraint[0], off) >= 0) { - constraint[0] = off; - } - off.x = cur.x + ((cur.y <= 0 && (cur.y < 0 || cur.x < 0)) ? 1 : -1); - off.y = cur.y + ((cur.x >= 0 && (cur.x > 0 || cur.y < 0)) ? 1 : -1); - if (xprod(constraint[1], off) <= 0) { - constraint[1] = off; - } - } - k1 = k; - k = nc[k1]; - if (!cyclic(k, i, k1)) { - break; - } - } - constraint_viol: - /* k1 was the last "corner" satisfying the current constraint, and - k is the first one violating it. We now need to find the last - point along k1..k which satisfied the constraint. */ - dk.x = sign(pt[k].x - pt[k1].x); - dk.y = sign(pt[k].y - pt[k1].y); - cur.x = pt[k1].x - pt[i].x; - cur.y = pt[k1].y - pt[i].y; - /* find largest integer j such that xprod(constraint[0], cur+j*dk) - >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity - of xprod. */ - a = xprod(constraint[0], cur); - b = xprod(constraint[0], dk); - c = xprod(constraint[1], cur); - d = xprod(constraint[1], dk); - /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This - can be solved with integer arithmetic. */ - j = INFTY; - if (b < 0) { - j = floordiv(a, -b); - } - if (d > 0) { - j = min(j, floordiv(-c, d)); - } - pivk[i] = mod(k1 + j, n); - foundk: - ; - } /* for i */ - - /* clean up: for each i, let lon[i] be the largest k such that for - all i' with i<=i'lon[n - 1] = j; - for (i = n - 2; i >= 0; i--) { - if (cyclic(i + 1, pivk[i], j)) { - j = pivk[i]; - } - pp->lon[i] = j; + nc[i] = k; + } + + SAFE_MALLOC(pp->lon, n, int); + + /* determine pivot points: for each i, let pivk[i] be the furthest k + such that all j with i=0; i--) { + ct[0] = ct[1] = ct[2] = ct[3] = 0; + + /* keep track of "directions" that have occurred */ + dir = (3+3*(pt[mod(i+1,n)].x-pt[i].x)+(pt[mod(i+1,n)].y-pt[i].y))/2; + ct[dir]++; + + constraint[0].x = 0; + constraint[0].y = 0; + constraint[1].x = 0; + constraint[1].y = 0; + + /* find the next k such that no straight line from i to k */ + k = nc[i]; + k1 = i; + while (1) { + + dir = (3+3*sign(pt[k].x-pt[k1].x)+sign(pt[k].y-pt[k1].y))/2; + ct[dir]++; + + /* if all four "directions" have occurred, cut this path */ + if (ct[0] && ct[1] && ct[2] && ct[3]) { + pivk[i] = k1; + goto foundk; + } + + cur.x = pt[k].x - pt[i].x; + cur.y = pt[k].y - pt[i].y; + + /* see if current constraint is violated */ + if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { + goto constraint_viol; + } + + /* else, update constraint */ + if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { + /* no constraint */ + } else { + off.x = cur.x + ((cur.y>=0 && (cur.y>0 || cur.x<0)) ? 1 : -1); + off.y = cur.y + ((cur.x<=0 && (cur.x<0 || cur.y<0)) ? 1 : -1); + if (xprod(constraint[0], off) >= 0) { + constraint[0] = off; + } + off.x = cur.x + ((cur.y<=0 && (cur.y<0 || cur.x<0)) ? 1 : -1); + off.y = cur.y + ((cur.x>=0 && (cur.x>0 || cur.y<0)) ? 1 : -1); + if (xprod(constraint[1], off) <= 0) { + constraint[1] = off; + } + } + k1 = k; + k = nc[k1]; + if (!cyclic(k,i,k1)) { + break; + } } - - for (i = n - 1; cyclic(mod(i + 1, n), j, pp->lon[i]); i--) { - pp->lon[i] = j; + constraint_viol: + /* k1 was the last "corner" satisfying the current constraint, and + k is the first one violating it. We now need to find the last + point along k1..k which satisfied the constraint. */ + dk.x = sign(pt[k].x-pt[k1].x); + dk.y = sign(pt[k].y-pt[k1].y); + cur.x = pt[k1].x - pt[i].x; + cur.y = pt[k1].y - pt[i].y; + /* find largest integer j such that xprod(constraint[0], cur+j*dk) + >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity + of xprod. */ + a = xprod(constraint[0], cur); + b = xprod(constraint[0], dk); + c = xprod(constraint[1], cur); + d = xprod(constraint[1], dk); + /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This + can be solved with integer arithmetic. */ + j = INFTY; + if (b<0) { + j = floordiv(a,-b); + } + if (d>0) { + j = min(j, floordiv(-c,d)); } + pivk[i] = mod(k1+j,n); + foundk: + ; + } /* for i */ + + /* clean up: for each i, let lon[i] be the largest k such that for + all i' with i<=i'lon[n-1]=j; + for (i=n-2; i>=0; i--) { + if (cyclic(i+1,pivk[i],j)) { + j=pivk[i]; + } + pp->lon[i]=j; + } - free(pivk); - free(nc); - return 0; + for (i=n-1; cyclic(mod(i+1,n),j,pp->lon[i]); i--) { + pp->lon[i] = j; + } -malloc_error: - free(pivk); - free(nc); - return 1; + free(pivk); + free(nc); + return 0; + + malloc_error: + free(pivk); + free(nc); + return 1; } /* ---------------------------------------------------------------------- */ -/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ +/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ /* Auxiliary function: calculate the penalty of an edge from i to j in the given path. This needs the "lon" and "sum*" data. */ -static double penalty3(privpath_t *pp, int i, int j) -{ - int n = pp->len; - point_t *pt = pp->pt; - sums_t *sums = pp->sums; - - /* assume 0<=i= n) { - j -= n; - r = 1; - } - - /* critical inner loop: the "if" gives a 4.6 percent speedup */ - if (r == 0) { - x = sums[j + 1].x - sums[i].x; - y = sums[j + 1].y - sums[i].y; - x2 = sums[j + 1].x2 - sums[i].x2; - xy = sums[j + 1].xy - sums[i].xy; - y2 = sums[j + 1].y2 - sums[i].y2; - k = j + 1 - i; - } else { - x = sums[j + 1].x - sums[i].x + sums[n].x; - y = sums[j + 1].y - sums[i].y + sums[n].y; - x2 = sums[j + 1].x2 - sums[i].x2 + sums[n].x2; - xy = sums[j + 1].xy - sums[i].xy + sums[n].xy; - y2 = sums[j + 1].y2 - sums[i].y2 + sums[n].y2; - k = j + 1 - i + n; - } - - px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; - py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; - ey = (pt[j].x - pt[i].x); - ex = -(pt[j].y - pt[i].y); - - a = ((x2 - 2 * x * px) / k + px * px); - b = ((xy - x * py - y * px) / k + px * py); - c = ((y2 - 2 * y * py) / k + py * py); - - s = ex * ex * a + 2 * ex * ey * b + ey * ey * c; - - return sqrt(s); +static double penalty3(privpath_t *pp, int i, int j) { + int n = pp->len; + point_t *pt = pp->pt; + sums_t *sums = pp->sums; + + /* assume 0<=i=n) { + j -= n; + r = 1; + } + + /* critical inner loop: the "if" gives a 4.6 percent speedup */ + if (r == 0) { + x = sums[j+1].x - sums[i].x; + y = sums[j+1].y - sums[i].y; + x2 = sums[j+1].x2 - sums[i].x2; + xy = sums[j+1].xy - sums[i].xy; + y2 = sums[j+1].y2 - sums[i].y2; + k = j+1 - i; + } else { + x = sums[j+1].x - sums[i].x + sums[n].x; + y = sums[j+1].y - sums[i].y + sums[n].y; + x2 = sums[j+1].x2 - sums[i].x2 + sums[n].x2; + xy = sums[j+1].xy - sums[i].xy + sums[n].xy; + y2 = sums[j+1].y2 - sums[i].y2 + sums[n].y2; + k = j+1 - i + n; + } + + px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; + py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; + ey = (pt[j].x - pt[i].x); + ex = -(pt[j].y - pt[i].y); + + a = ((x2 - 2*x*px) / k + px*px); + b = ((xy - x*py - y*px) / k + px*py); + c = ((y2 - 2*y*py) / k + py*py); + + s = ex*ex*a + 2*ex*ey*b + ey*ey*c; + + return sqrt(s); } /* find the optimal polygon. Fill in the m and po components. Return 1 @@ -535,109 +525,109 @@ static double penalty3(privpath_t *pp, int i, int j) is in the polygon. Fixme: implement cyclic version. */ static int bestpolygon(privpath_t *pp) { - int i, j, m, k; - int n = pp->len; - double *pen = NULL; /* pen[n+1]: penalty vector */ - int *prev = NULL; /* prev[n+1]: best path pointer vector */ - int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ - int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ - int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ - int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ - double thispen; - double best; - int c; - - SAFE_MALLOC(pen, n + 1, double); - SAFE_MALLOC(prev, n + 1, int); - SAFE_MALLOC(clip0, n, int); - SAFE_MALLOC(clip1, n + 1, int); - SAFE_MALLOC(seg0, n + 1, int); - SAFE_MALLOC(seg1, n + 1, int); - - /* calculate clipped paths */ - for (i = 0; i < n; i++) { - c = mod(pp->lon[mod(i - 1, n)] - 1, n); - if (c == i) { - c = mod(i + 1, n); - } - if (c < i) { - clip0[i] = n; - } else { - clip0[i] = c; - } - } - - /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff - clip1[j] <= i, for i,j=0..n. */ - j = 1; - for (i = 0; i < n; i++) { - while (j <= clip0[i]) { - clip1[j] = i; - j++; - } - } - - /* calculate seg0[j] = longest path from 0 with j segments */ - i = 0; - for (j = 0; i < n; j++) { - seg0[j] = i; - i = clip0[i]; + int i, j, m, k; + int n = pp->len; + double *pen = NULL; /* pen[n+1]: penalty vector */ + int *prev = NULL; /* prev[n+1]: best path pointer vector */ + int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ + int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ + int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ + int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ + double thispen; + double best; + int c; + + SAFE_MALLOC(pen, n+1, double); + SAFE_MALLOC(prev, n+1, int); + SAFE_MALLOC(clip0, n, int); + SAFE_MALLOC(clip1, n+1, int); + SAFE_MALLOC(seg0, n+1, int); + SAFE_MALLOC(seg1, n+1, int); + + /* calculate clipped paths */ + for (i=0; ilon[mod(i-1,n)]-1,n); + if (c == i) { + c = mod(i+1,n); } - seg0[j] = n; - m = j; - - /* calculate seg1[j] = longest path to n with m-j segments */ - i = n; - for (j = m; j > 0; j--) { - seg1[j] = i; - i = clip1[i]; + if (c < i) { + clip0[i] = n; + } else { + clip0[i] = c; } - seg1[0] = 0; - - /* now find the shortest path with m segments, based on penalty3 */ - /* note: the outer 2 loops jointly have at most n iterations, thus - the worst-case behavior here is quadratic. In practice, it is - close to linear since the inner loop tends to be short. */ - pen[0] = 0; - for (j = 1; j <= m; j++) { - for (i = seg1[j]; i <= seg0[j]; i++) { - best = -1; - for (k = seg0[j - 1]; k >= clip1[i]; k--) { - thispen = penalty3(pp, k, i) + pen[k]; - if (best < 0 || thispen < best) { - prev[i] = k; - best = thispen; - } - } - pen[i] = best; - } + } + + /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff + clip1[j] <= i, for i,j=0..n. */ + j = 1; + for (i=0; im = m; - SAFE_MALLOC(pp->po, m, int); - - /* read off shortest path */ - for (i = n, j = m - 1; i > 0; j--) { - i = prev[i]; - pp->po[j] = i; + } + + /* calculate seg0[j] = longest path from 0 with j segments */ + i = 0; + for (j=0; i0; j--) { + seg1[j] = i; + i = clip1[i]; + } + seg1[0] = 0; + + /* now find the shortest path with m segments, based on penalty3 */ + /* note: the outer 2 loops jointly have at most n iterations, thus + the worst-case behavior here is quadratic. In practice, it is + close to linear since the inner loop tends to be short. */ + pen[0]=0; + for (j=1; j<=m; j++) { + for (i=seg1[j]; i<=seg0[j]; i++) { + best = -1; + for (k=seg0[j-1]; k>=clip1[i]; k--) { + thispen = penalty3(pp, k, i) + pen[k]; + if (best < 0 || thispen < best) { + prev[i] = k; + best = thispen; + } + } + pen[i] = best; } - - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 0; - -malloc_error: - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 1; + } + + pp->m = m; + SAFE_MALLOC(pp->po, m, int); + + /* read off shortest path */ + for (i=n, j=m-1; i>0; j--) { + i = prev[i]; + pp->po[j] = i; + } + + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 0; + + malloc_error: + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 1; } /* ---------------------------------------------------------------------- */ @@ -648,269 +638,266 @@ malloc_error: if it lies outside. Return 1 with errno set on error; 0 on success. */ -static int adjust_vertices(privpath_t *pp) -{ - int m = pp->m; - int *po = pp->po; - int n = pp->len; - point_t *pt = pp->pt; - int x0 = pp->x0; - int y0 = pp->y0; - - dpoint_t *ctr = NULL; /* ctr[m] */ - dpoint_t *dir = NULL; /* dir[m] */ - quadform_t *q = NULL; /* q[m] */ - double v[3]; - double d; - int i, j, k, l; - dpoint_t s; - int r; - - SAFE_MALLOC(ctr, m, dpoint_t); - SAFE_MALLOC(dir, m, dpoint_t); - SAFE_MALLOC(q, m, quadform_t); - - r = privcurve_init(&pp->curve, m); - if (r) { - goto malloc_error; - } - - /* calculate "optimal" point-slope representation for each line - segment */ - for (i = 0; i < m; i++) { - j = po[mod(i + 1, m)]; - j = mod(j - po[i], n) + po[i]; - pointslope(pp, po[i], j, &ctr[i], &dir[i]); +static int adjust_vertices(privpath_t *pp) { + int m = pp->m; + int *po = pp->po; + int n = pp->len; + point_t *pt = pp->pt; + int x0 = pp->x0; + int y0 = pp->y0; + + dpoint_t *ctr = NULL; /* ctr[m] */ + dpoint_t *dir = NULL; /* dir[m] */ + quadform_t *q = NULL; /* q[m] */ + double v[3]; + double d; + int i, j, k, l; + dpoint_t s; + int r; + + SAFE_MALLOC(ctr, m, dpoint_t); + SAFE_MALLOC(dir, m, dpoint_t); + SAFE_MALLOC(q, m, quadform_t); + + r = privcurve_init(&pp->curve, m); + if (r) { + goto malloc_error; + } + + /* calculate "optimal" point-slope representation for each line + segment */ + for (i=0; i Q[1][1]) { - v[0] = -Q[0][1]; - v[1] = Q[0][0]; - } else if (Q[1][1]) { - v[0] = -Q[1][1]; - v[1] = Q[1][0]; - } else { - v[0] = 1; - v[1] = 0; - } - d = sq(v[0]) + sq(v[1]); - v[2] = -v[1] * s.y - v[0] * s.x; - for (l = 0; l < 3; l++) { - for (k = 0; k < 3; k++) { - Q[l][k] += v[l] * v[k] / d; - } - } - } - dx = fabs(w.x - s.x); - dy = fabs(w.y - s.y); - if (dx <= .5 && dy <= .5) { - pp->curve.vertex[i].x = w.x + x0; - pp->curve.vertex[i].y = w.y + y0; - continue; - } - - /* the minimum was not in the unit square; now minimize quadratic - on boundary of square */ - min = quadform(Q, s); - xmin = s.x; - ymin = s.y; - - if (Q[0][0] == 0.0) { - goto fixx; - } - for (z = 0; z < 2; z++) { /* value of the y-coordinate */ - w.y = s.y - 0.5 + z; - w.x = -(Q[0][1] * w.y + Q[0][2]) / Q[0][0]; - dx = fabs(w.x - s.x); - cand = quadform(Q, w); - if (dx <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - fixx: - if (Q[1][1] == 0.0) { - goto corners; - } - for (z = 0; z < 2; z++) { /* value of the x-coordinate */ - w.x = s.x - 0.5 + z; - w.y = -(Q[1][0] * w.x + Q[1][2]) / Q[1][1]; - dy = fabs(w.y - s.y); - cand = quadform(Q, w); - if (dy <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - corners: - /* check four corners */ - for (l = 0; l < 2; l++) { - for (k = 0; k < 2; k++) { - w.x = s.x - 0.5 + l; - w.y = s.y - 0.5 + k; - cand = quadform(Q, w); - if (cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - } - - pp->curve.vertex[i].x = xmin + x0; - pp->curve.vertex[i].y = ymin + y0; - continue; + + det = Q[0][0]*Q[1][1] - Q[0][1]*Q[1][0]; + if (det != 0.0) { + w.x = (-Q[0][2]*Q[1][1] + Q[1][2]*Q[0][1]) / det; + w.y = ( Q[0][2]*Q[1][0] - Q[1][2]*Q[0][0]) / det; + break; + } + + /* matrix is singular - lines are parallel. Add another, + orthogonal axis, through the center of the unit square */ + if (Q[0][0]>Q[1][1]) { + v[0] = -Q[0][1]; + v[1] = Q[0][0]; + } else if (Q[1][1]) { + v[0] = -Q[1][1]; + v[1] = Q[1][0]; + } else { + v[0] = 1; + v[1] = 0; + } + d = sq(v[0]) + sq(v[1]); + v[2] = - v[1] * s.y - v[0] * s.x; + for (l=0; l<3; l++) { + for (k=0; k<3; k++) { + Q[l][k] += v[l] * v[k] / d; + } + } + } + dx = fabs(w.x-s.x); + dy = fabs(w.y-s.y); + if (dx <= .5 && dy <= .5) { + pp->curve.vertex[i].x = w.x+x0; + pp->curve.vertex[i].y = w.y+y0; + continue; } - free(ctr); - free(dir); - free(q); - return 0; + /* the minimum was not in the unit square; now minimize quadratic + on boundary of square */ + min = quadform(Q, s); + xmin = s.x; + ymin = s.y; -malloc_error: - free(ctr); - free(dir); - free(q); - return 1; + if (Q[0][0] == 0.0) { + goto fixx; + } + for (z=0; z<2; z++) { /* value of the y-coordinate */ + w.y = s.y-0.5+z; + w.x = - (Q[0][1] * w.y + Q[0][2]) / Q[0][0]; + dx = fabs(w.x-s.x); + cand = quadform(Q, w); + if (dx <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + fixx: + if (Q[1][1] == 0.0) { + goto corners; + } + for (z=0; z<2; z++) { /* value of the x-coordinate */ + w.x = s.x-0.5+z; + w.y = - (Q[1][0] * w.x + Q[1][2]) / Q[1][1]; + dy = fabs(w.y-s.y); + cand = quadform(Q, w); + if (dy <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + corners: + /* check four corners */ + for (l=0; l<2; l++) { + for (k=0; k<2; k++) { + w.x = s.x-0.5+l; + w.y = s.y-0.5+k; + cand = quadform(Q, w); + if (cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + } + + pp->curve.vertex[i].x = xmin + x0; + pp->curve.vertex[i].y = ymin + y0; + continue; + } + + free(ctr); + free(dir); + free(q); + return 0; + + malloc_error: + free(ctr); + free(dir); + free(q); + return 1; } /* ---------------------------------------------------------------------- */ /* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ /* reverse orientation of a path */ -static void reverse(privcurve_t *curve) -{ - int m = curve->n; - int i, j; - dpoint_t tmp; - - for (i = 0, j = m - 1; i < j; i++, j--) { - tmp = curve->vertex[i]; - curve->vertex[i] = curve->vertex[j]; - curve->vertex[j] = tmp; - } +static void reverse(privcurve_t *curve) { + int m = curve->n; + int i, j; + dpoint_t tmp; + + for (i=0, j=m-1; ivertex[i]; + curve->vertex[i] = curve->vertex[j]; + curve->vertex[j] = tmp; + } } /* Always succeeds */ -static void smooth(privcurve_t *curve, double alphamax) -{ - int m = curve->n; - - int i, j, k; - double dd, denom, alpha; - dpoint_t p2, p3, p4; - - /* examine each vertex and find its best fit */ - for (i = 0; i < m; i++) { - j = mod(i + 1, m); - k = mod(i + 2, m); - p4 = interval(1 / 2.0, curve->vertex[k], curve->vertex[j]); - - denom = ddenom(curve->vertex[i], curve->vertex[k]); - if (denom != 0.0) { - dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; - dd = fabs(dd); - alpha = dd > 1 ? (1 - 1.0 / dd) : 0; - alpha = alpha / 0.75; - } else { - alpha = 4 / 3.0; - } - curve->alpha0[j] = alpha; /* remember "original" value of alpha */ - - if (alpha > alphamax) { /* pointed corner */ - curve->tag[j] = POTRACE_CORNER; - curve->c[j][1] = curve->vertex[j]; - curve->c[j][2] = p4; - } else { - if (alpha < 0.55) { - alpha = 0.55; - } else if (alpha > 1) { - alpha = 1; - } - p2 = interval(.5 + .5 * alpha, curve->vertex[i], curve->vertex[j]); - p3 = interval(.5 + .5 * alpha, curve->vertex[k], curve->vertex[j]); - curve->tag[j] = POTRACE_CURVETO; - curve->c[j][0] = p2; - curve->c[j][1] = p3; - curve->c[j][2] = p4; - } - curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ - curve->beta[j] = 0.5; +static void smooth(privcurve_t *curve, double alphamax) { + int m = curve->n; + + int i, j, k; + double dd, denom, alpha; + dpoint_t p2, p3, p4; + + /* examine each vertex and find its best fit */ + for (i=0; ivertex[k], curve->vertex[j]); + + denom = ddenom(curve->vertex[i], curve->vertex[k]); + if (denom != 0.0) { + dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; + dd = fabs(dd); + alpha = dd>1 ? (1 - 1.0/dd) : 0; + alpha = alpha / 0.75; + } else { + alpha = 4/3.0; + } + curve->alpha0[j] = alpha; /* remember "original" value of alpha */ + + if (alpha > alphamax) { /* pointed corner */ + curve->tag[j] = POTRACE_CORNER; + curve->c[j][1] = curve->vertex[j]; + curve->c[j][2] = p4; + } else { + if (alpha < 0.55) { + alpha = 0.55; + } else if (alpha > 1) { + alpha = 1; + } + p2 = interval(.5+.5*alpha, curve->vertex[i], curve->vertex[j]); + p3 = interval(.5+.5*alpha, curve->vertex[k], curve->vertex[j]); + curve->tag[j] = POTRACE_CURVETO; + curve->c[j][0] = p2; + curve->c[j][1] = p3; + curve->c[j][2] = p4; } - curve->alphacurve = 1; + curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ + curve->beta[j] = 0.5; + } + curve->alphacurve = 1; - return; + return; } /* ---------------------------------------------------------------------- */ @@ -918,349 +905,341 @@ static void smooth(privcurve_t *curve, double alphamax) /* a private type for the result of opti_penalty */ struct opti_s { - double pen; /* penalty */ - dpoint_t c[2]; /* curve parameters */ - double t, s; /* curve parameters */ - double alpha; /* curve parameter */ + double pen; /* penalty */ + dpoint_t c[2]; /* curve parameters */ + double t, s; /* curve parameters */ + double alpha; /* curve parameter */ }; typedef struct opti_s opti_t; /* calculate best fit from i+.5 to j+.5. Assume icurve.n; - int k, k1, k2, conv, i1; - double area, alpha, d, d1, d2; - dpoint_t p0, p1, p2, p3, pt; - double A, R, A1, A2, A3, A4; - double s, t; +static int opti_penalty(privpath_t *pp, int i, int j, opti_t *res, double opttolerance, int *convc, double *areac) { + int m = pp->curve.n; + int k, k1, k2, conv, i1; + double area, alpha, d, d1, d2; + dpoint_t p0, p1, p2, p3, pt; + double A, R, A1, A2, A3, A4; + double s, t; - /* check convexity, corner-freeness, and maximum bend < 179 degrees */ + /* check convexity, corner-freeness, and maximum bend < 179 degrees */ - if (i == j) { /* sanity - a full loop can never be an opticurve */ - return 1; - } + if (i==j) { /* sanity - a full loop can never be an opticurve */ + return 1; + } - k = i; - i1 = mod(i + 1, m); - k1 = mod(k + 1, m); - conv = convc[k1]; - if (conv == 0) { - return 1; + k = i; + i1 = mod(i+1, m); + k1 = mod(k+1, m); + conv = convc[k1]; + if (conv == 0) { + return 1; + } + d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); + for (k=k1; k!=j; k=k1) { + k1 = mod(k+1, m); + k2 = mod(k+2, m); + if (convc[k1] != conv) { + return 1; } - d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); - for (k = k1; k != j; k = k1) { - k1 = mod(k + 1, m); - k2 = mod(k + 2, m); - if (convc[k1] != conv) { - return 1; - } - if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != - conv) { - return 1; - } - if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < - d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { - return 1; - } + if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) { + return 1; } - - /* the curve we're working in: */ - p0 = pp->curve.c[mod(i, m)][2]; - p1 = pp->curve.vertex[mod(i + 1, m)]; - p2 = pp->curve.vertex[mod(j, m)]; - p3 = pp->curve.c[mod(j, m)][2]; - - /* determine its area */ - area = areac[j] - areac[i]; - area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2]) / 2; - if (i >= j) { - area += areac[m]; + if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { + return 1; } + } + + /* the curve we're working in: */ + p0 = pp->curve.c[mod(i,m)][2]; + p1 = pp->curve.vertex[mod(i+1,m)]; + p2 = pp->curve.vertex[mod(j,m)]; + p3 = pp->curve.c[mod(j,m)][2]; + + /* determine its area */ + area = areac[j] - areac[i]; + area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2])/2; + if (i>=j) { + area += areac[m]; + } + + /* find intersection o of p0p1 and p2p3. Let t,s such that o = + interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the + triangle (p0,o,p3). */ + + A1 = dpara(p0, p1, p2); + A2 = dpara(p0, p1, p3); + A3 = dpara(p0, p2, p3); + /* A4 = dpara(p1, p2, p3); */ + A4 = A1+A3-A2; + + if (A2 == A1) { /* this should never happen */ + return 1; + } - /* find intersection o of p0p1 and p2p3. Let t,s such that o = - interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the - triangle (p0,o,p3). */ + t = A3/(A3-A4); + s = A2/(A2-A1); + A = A2 * t / 2.0; + + if (A == 0.0) { /* this should never happen */ + return 1; + } - A1 = dpara(p0, p1, p2); - A2 = dpara(p0, p1, p3); - A3 = dpara(p0, p2, p3); - /* A4 = dpara(p1, p2, p3); */ - A4 = A1 + A3 - A2; + R = area / A; /* relative area */ + alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ - if (A2 == A1) { /* this should never happen */ - return 1; - } + res->c[0] = interval(t * alpha, p0, p1); + res->c[1] = interval(s * alpha, p3, p2); + res->alpha = alpha; + res->t = t; + res->s = s; - t = A3 / (A3 - A4); - s = A2 / (A2 - A1); - A = A2 * t / 2.0; + p1 = res->c[0]; + p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ - if (A == 0.0) { /* this should never happen */ - return 1; - } + res->pen = 0; - R = area / A; /* relative area */ - alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ - - res->c[0] = interval(t * alpha, p0, p1); - res->c[1] = interval(s * alpha, p3, p2); - res->alpha = alpha; - res->t = t; - res->s = s; - - p1 = res->c[0]; - p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ - - res->pen = 0; - - /* calculate penalty */ - /* check tangency with edges */ - for (k = mod(i + 1, m); k != j; k = k1) { - k1 = mod(k + 1, m); - t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); - if (t < -.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; - if (fabs(d1) > opttolerance) { - return 1; - } - if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || - iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { - return 1; - } - res->pen += sq(d1); + /* calculate penalty */ + /* check tangency with edges */ + for (k=mod(i+1,m); k!=j; k=k1) { + k1 = mod(k+1,m); + t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); + if (t<-.5) { + return 1; } - - /* check corners */ - for (k = i; k != j; k = k1) { - k1 = mod(k + 1, m); - t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); - if (t < -.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; - d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; - d2 *= 0.75 * pp->curve.alpha[k1]; - if (d2 < 0) { - d1 = -d1; - d2 = -d2; - } - if (d1 < d2 - opttolerance) { - return 1; - } - if (d1 < d2) { - res->pen += sq(d1 - d2); - } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); + if (d == 0.0) { /* this should never happen */ + return 1; } + d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; + if (fabs(d1) > opttolerance) { + return 1; + } + if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { + return 1; + } + res->pen += sq(d1); + } + + /* check corners */ + for (k=i; k!=j; k=k1) { + k1 = mod(k+1,m); + t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); + if (t<-.5) { + return 1; + } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); + if (d == 0.0) { /* this should never happen */ + return 1; + } + d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; + d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; + d2 *= 0.75 * pp->curve.alpha[k1]; + if (d2 < 0) { + d1 = -d1; + d2 = -d2; + } + if (d1 < d2 - opttolerance) { + return 1; + } + if (d1 < d2) { + res->pen += sq(d1 - d2); + } + } - return 0; + return 0; } /* optimize the path p, replacing sequences of Bezier segments by a single segment when possible. Return 0 on success, 1 with errno set on failure. */ -static int opticurve(privpath_t *pp, double opttolerance) -{ - int m = pp->curve.n; - int *pt = NULL; /* pt[m+1] */ - double *pen = NULL; /* pen[m+1] */ - int *len = NULL; /* len[m+1] */ - opti_t *opt = NULL; /* opt[m+1] */ - int om; - int i, j, r; - opti_t o; - dpoint_t p0; - int i1; - double area; - double alpha; - double *s = NULL; - double *t = NULL; - - int *convc = NULL; /* conv[m]: pre-computed convexities */ - double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ - - SAFE_MALLOC(pt, m + 1, int); - SAFE_MALLOC(pen, m + 1, double); - SAFE_MALLOC(len, m + 1, int); - SAFE_MALLOC(opt, m + 1, opti_t); - SAFE_MALLOC(convc, m, int); - SAFE_MALLOC(areac, m + 1, double); - - /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ - for (i = 0; i < m; i++) { - if (pp->curve.tag[i] == POTRACE_CURVETO) { - convc[i] = - sign(dpara(pp->curve.vertex[mod(i - 1, m)], pp->curve.vertex[i], pp->curve.vertex[mod(i + 1, m)])); - } else { - convc[i] = 0; - } - } - - /* pre-calculate areas */ - area = 0.0; - areac[0] = 0.0; - p0 = pp->curve.vertex[0]; - for (i = 0; i < m; i++) { - i1 = mod(i + 1, m); - if (pp->curve.tag[i1] == POTRACE_CURVETO) { - alpha = pp->curve.alpha[i1]; - area += 0.3 * alpha * (4 - alpha) * dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2]) / 2; - area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2]) / 2; - } - areac[i + 1] = area; - } - - pt[0] = -1; - pen[0] = 0; - len[0] = 0; - - /* Fixme: we always start from a fixed point -- should find the best - curve cyclically */ - - for (j = 1; j <= m; j++) { - /* calculate best path from 0 to j */ - pt[j] = j - 1; - pen[j] = pen[j - 1]; - len[j] = len[j - 1] + 1; - - for (i = j - 2; i >= 0; i--) { - r = opti_penalty(pp, i, mod(j, m), &o, opttolerance, convc, areac); - if (r) { - break; - } - if (len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + o.pen)) { - pt[j] = i; - pen[j] = pen[i] + o.pen; - len[j] = len[i] + 1; - opt[j] = o; - } - } +static int opticurve(privpath_t *pp, double opttolerance) { + int m = pp->curve.n; + int *pt = NULL; /* pt[m+1] */ + double *pen = NULL; /* pen[m+1] */ + int *len = NULL; /* len[m+1] */ + opti_t *opt = NULL; /* opt[m+1] */ + int om; + int i,j,r; + opti_t o; + dpoint_t p0; + int i1; + double area; + double alpha; + double *s = NULL; + double *t = NULL; + + int *convc = NULL; /* conv[m]: pre-computed convexities */ + double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ + + SAFE_MALLOC(pt, m+1, int); + SAFE_MALLOC(pen, m+1, double); + SAFE_MALLOC(len, m+1, int); + SAFE_MALLOC(opt, m+1, opti_t); + SAFE_MALLOC(convc, m, int); + SAFE_MALLOC(areac, m+1, double); + + /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ + for (i=0; icurve.tag[i] == POTRACE_CURVETO) { + convc[i] = sign(dpara(pp->curve.vertex[mod(i-1,m)], pp->curve.vertex[i], pp->curve.vertex[mod(i+1,m)])); + } else { + convc[i] = 0; } - om = len[m]; - r = privcurve_init(&pp->ocurve, om); - if (r) { - goto malloc_error; + } + + /* pre-calculate areas */ + area = 0.0; + areac[0] = 0.0; + p0 = pp->curve.vertex[0]; + for (i=0; icurve.tag[i1] == POTRACE_CURVETO) { + alpha = pp->curve.alpha[i1]; + area += 0.3*alpha*(4-alpha)*dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2])/2; + area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2])/2; } - SAFE_MALLOC(s, om, double); - SAFE_MALLOC(t, om, double); - - j = m; - for (i = om - 1; i >= 0; i--) { - if (pt[j] == j - 1) { - pp->ocurve.tag[i] = pp->curve.tag[mod(j, m)]; - pp->ocurve.c[i][0] = pp->curve.c[mod(j, m)][0]; - pp->ocurve.c[i][1] = pp->curve.c[mod(j, m)][1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; - pp->ocurve.vertex[i] = pp->curve.vertex[mod(j, m)]; - pp->ocurve.alpha[i] = pp->curve.alpha[mod(j, m)]; - pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j, m)]; - pp->ocurve.beta[i] = pp->curve.beta[mod(j, m)]; - s[i] = t[i] = 1.0; - } else { - pp->ocurve.tag[i] = POTRACE_CURVETO; - pp->ocurve.c[i][0] = opt[j].c[0]; - pp->ocurve.c[i][1] = opt[j].c[1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; - pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j, m)][2], pp->curve.vertex[mod(j, m)]); - pp->ocurve.alpha[i] = opt[j].alpha; - pp->ocurve.alpha0[i] = opt[j].alpha; - s[i] = opt[j].s; - t[i] = opt[j].t; - } - j = pt[j]; + areac[i+1] = area; + } + + pt[0] = -1; + pen[0] = 0; + len[0] = 0; + + /* Fixme: we always start from a fixed point -- should find the best + curve cyclically */ + + for (j=1; j<=m; j++) { + /* calculate best path from 0 to j */ + pt[j] = j-1; + pen[j] = pen[j-1]; + len[j] = len[j-1]+1; + + for (i=j-2; i>=0; i--) { + r = opti_penalty(pp, i, mod(j,m), &o, opttolerance, convc, areac); + if (r) { + break; + } + if (len[j] > len[i]+1 || (len[j] == len[i]+1 && pen[j] > pen[i] + o.pen)) { + pt[j] = i; + pen[j] = pen[i] + o.pen; + len[j] = len[i] + 1; + opt[j] = o; + } } - - /* calculate beta parameters */ - for (i = 0; i < om; i++) { - i1 = mod(i + 1, om); - pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]); + } + om = len[m]; + r = privcurve_init(&pp->ocurve, om); + if (r) { + goto malloc_error; + } + SAFE_MALLOC(s, om, double); + SAFE_MALLOC(t, om, double); + + j = m; + for (i=om-1; i>=0; i--) { + if (pt[j]==j-1) { + pp->ocurve.tag[i] = pp->curve.tag[mod(j,m)]; + pp->ocurve.c[i][0] = pp->curve.c[mod(j,m)][0]; + pp->ocurve.c[i][1] = pp->curve.c[mod(j,m)][1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; + pp->ocurve.vertex[i] = pp->curve.vertex[mod(j,m)]; + pp->ocurve.alpha[i] = pp->curve.alpha[mod(j,m)]; + pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j,m)]; + pp->ocurve.beta[i] = pp->curve.beta[mod(j,m)]; + s[i] = t[i] = 1.0; + } else { + pp->ocurve.tag[i] = POTRACE_CURVETO; + pp->ocurve.c[i][0] = opt[j].c[0]; + pp->ocurve.c[i][1] = opt[j].c[1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; + pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j,m)][2], pp->curve.vertex[mod(j,m)]); + pp->ocurve.alpha[i] = opt[j].alpha; + pp->ocurve.alpha0[i] = opt[j].alpha; + s[i] = opt[j].s; + t[i] = opt[j].t; } - pp->ocurve.alphacurve = 1; - - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 0; - -malloc_error: - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 1; + j = pt[j]; + } + + /* calculate beta parameters */ + for (i=0; iocurve.beta[i] = s[i] / (s[i] + t[i1]); + } + pp->ocurve.alphacurve = 1; + + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 0; + + malloc_error: + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 1; } /* ---------------------------------------------------------------------- */ -#define TRY(x) \ - if (x) \ - goto try_error +#define TRY(x) if (x) goto try_error /* return 0 on success, 1 on error with errno set. */ -int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) -{ - path_t *p; - double nn = 0, cn = 0; - - if (progress->callback) { - /* precompute task size for progress estimates */ - nn = 0; - list_forall(p, plist) { nn += p->priv->len; } - cn = 0; +int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) { + path_t *p; + double nn = 0, cn = 0; + + if (progress->callback) { + /* precompute task size for progress estimates */ + nn = 0; + list_forall (p, plist) { + nn += p->priv->len; + } + cn = 0; + } + + /* call downstream function with each path */ + list_forall (p, plist) { + TRY(calc_sums(p->priv)); + TRY(calc_lon(p->priv)); + TRY(bestpolygon(p->priv)); + TRY(adjust_vertices(p->priv)); + if (p->sign == '-') { /* reverse orientation of negative paths */ + reverse(&p->priv->curve); } + smooth(&p->priv->curve, param->alphamax); + if (param->opticurve) { + TRY(opticurve(p->priv, param->opttolerance)); + p->priv->fcurve = &p->priv->ocurve; + } else { + p->priv->fcurve = &p->priv->curve; + } + privcurve_to_curve(p->priv->fcurve, &p->curve); - /* call downstream function with each path */ - list_forall(p, plist) - { - TRY(calc_sums(p->priv)); - TRY(calc_lon(p->priv)); - TRY(bestpolygon(p->priv)); - TRY(adjust_vertices(p->priv)); - if (p->sign == '-') { /* reverse orientation of negative paths */ - reverse(&p->priv->curve); - } - smooth(&p->priv->curve, param->alphamax); - if (param->opticurve) { - TRY(opticurve(p->priv, param->opttolerance)); - p->priv->fcurve = &p->priv->ocurve; - } else { - p->priv->fcurve = &p->priv->curve; - } - privcurve_to_curve(p->priv->fcurve, &p->curve); - - if (progress->callback) { - cn += p->priv->len; - progress_update(cn / nn, progress); - } + if (progress->callback) { + cn += p->priv->len; + progress_update(cn/nn, progress); } + } - progress_update(1.0, progress); + progress_update(1.0, progress); - return 0; + return 0; -try_error: - return 1; + try_error: + return 1; } -- cgit v1.2.3 From a7f2b5c335699603e03dc38d3bb8d1d020f2e672 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 13:18:33 +0200 Subject: revert previous commit. (bzr r13341.1.272) --- src/trace/potrace/trace.cpp | 2059 +++++++++++++++++++++---------------------- 1 file changed, 1019 insertions(+), 1040 deletions(-) (limited to 'src') diff --git a/src/trace/potrace/trace.cpp b/src/trace/potrace/trace.cpp index 2bdefb90a..f1e88a908 100644 --- a/src/trace/potrace/trace.cpp +++ b/src/trace/potrace/trace.cpp @@ -16,129 +16,124 @@ #include "trace.h" #include "progress.h" -#define INFTY 10000000 // it suffices that this is longer than any - // path; it need not be really infinite -#define COS179 -0.999847695156 // the cosine of 179 degrees +#define INFTY 10000000 /* it suffices that this is longer than any + path; it need not be really infinite */ +#define COS179 -0.999847695156 /* the cosine of 179 degrees */ /* ---------------------------------------------------------------------- */ #define SAFE_MALLOC(var, n, typ) \ - if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error + if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error /* ---------------------------------------------------------------------- */ /* auxiliary functions */ /* return a direction that is 90 degrees counterclockwise from p2-p0, but then restricted to one of the major wind directions (n, nw, w, etc) */ -static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) -{ - point_t r; - - r.y = sign(p2.x - p0.x); - r.x = -sign(p2.y - p0.y); +static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) { + point_t r; + + r.y = sign(p2.x-p0.x); + r.x = -sign(p2.y-p0.y); - return r; + return r; } /* return (p1-p0)x(p2-p0), the area of the parallelogram */ -static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) -{ - double x1, y1, x2, y2; +static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) { + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; + x1 = p1.x-p0.x; + y1 = p1.y-p0.y; + x2 = p2.x-p0.x; + y2 = p2.y-p0.y; - return x1 * y2 - x2 * y1; + return x1*y2 - x2*y1; } /* ddenom/dpara have the property that the square of radius 1 centered at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */ -static inline double ddenom(dpoint_t p0, dpoint_t p2) -{ - point_t r = dorth_infty(p0, p2); +static inline double ddenom(dpoint_t p0, dpoint_t p2) { + point_t r = dorth_infty(p0, p2); - return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y); + return r.y*(p2.x-p0.x) - r.x*(p2.y-p0.y); } /* return 1 if a <= b < c < a, in a cyclic sense (mod n) */ -static inline int cyclic(int a, int b, int c) -{ - if (a <= c) { - return (a <= b && b < c); - } else { - return (a <= b || b < c); - } +static inline int cyclic(int a, int b, int c) { + if (a<=c) { + return (a<=b && blen; - sums_t *sums = pp->sums; - - double x, y, x2, xy, y2; - double k; - double a, b, c, lambda2, l; - int r = 0; /* rotations from i to j */ - - while (j >= n) { - j -= n; - r += 1; - } - while (i >= n) { - i -= n; - r -= 1; - } - while (j < 0) { - j += n; - r -= 1; - } - while (i < 0) { - i += n; - r += 1; - } - - x = sums[j + 1].x - sums[i].x + r * sums[n].x; - y = sums[j + 1].y - sums[i].y + r * sums[n].y; - x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; - xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; - y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; - k = j + 1 - i + r * n; - - ctr->x = x / k; - ctr->y = y / k; - - a = (x2 - (double)x * x / k) / k; - b = (xy - (double)x * y / k) / k; - c = (y2 - (double)y * y / k) / k; - - lambda2 = (a + c + sqrt((a - c) * (a - c) + 4 * b * b)) / 2; /* larger e.value */ - - /* now find e.vector for lambda2 */ - a -= lambda2; - c -= lambda2; - - if (fabs(a) >= fabs(c)) { - l = sqrt(a * a + b * b); - if (l != 0) { - dir->x = -b / l; - dir->y = a / l; - } - } else { - l = sqrt(c * c + b * b); - if (l != 0) { - dir->x = -c / l; - dir->y = b / l; - } +static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *dir) { + /* assume ilen; + sums_t *sums = pp->sums; + + double x, y, x2, xy, y2; + double k; + double a, b, c, lambda2, l; + int r=0; /* rotations from i to j */ + + while (j>=n) { + j-=n; + r+=1; + } + while (i>=n) { + i-=n; + r-=1; + } + while (j<0) { + j+=n; + r-=1; + } + while (i<0) { + i+=n; + r+=1; + } + + x = sums[j+1].x-sums[i].x+r*sums[n].x; + y = sums[j+1].y-sums[i].y+r*sums[n].y; + x2 = sums[j+1].x2-sums[i].x2+r*sums[n].x2; + xy = sums[j+1].xy-sums[i].xy+r*sums[n].xy; + y2 = sums[j+1].y2-sums[i].y2+r*sums[n].y2; + k = j+1-i+r*n; + + ctr->x = x/k; + ctr->y = y/k; + + a = (x2-(double)x*x/k)/k; + b = (xy-(double)x*y/k)/k; + c = (y2-(double)y*y/k)/k; + + lambda2 = (a+c+sqrt((a-c)*(a-c)+4*b*b))/2; /* larger e.value */ + + /* now find e.vector for lambda2 */ + a -= lambda2; + c -= lambda2; + + if (fabs(a) >= fabs(c)) { + l = sqrt(a*a+b*b); + if (l!=0) { + dir->x = -b/l; + dir->y = a/l; } - if (l == 0) { - dir->x = dir->y = 0; /* sometimes this can happen when k=4: - the two eigenvalues coincide */ + } else { + l = sqrt(c*c+b*b); + if (l!=0) { + dir->x = -c/l; + dir->y = b/l; } + } + if (l==0) { + dir->x = dir->y = 0; /* sometimes this can happen when k=4: + the two eigenvalues coincide */ + } } /* the type of (affine) quadratic forms, represented as symmetric 3x3 @@ -147,158 +142,155 @@ static void pointslope(privpath_t *pp, int i, int j, dpoint_t *ctr, dpoint_t *di typedef double quadform_t[3][3]; /* Apply quadratic form Q to vector w = (w.x,w.y) */ -static inline double quadform(quadform_t Q, dpoint_t w) -{ - double v[3]; - int i, j; - double sum; - - v[0] = w.x; - v[1] = w.y; - v[2] = 1; - sum = 0.0; - - for (i = 0; i < 3; i++) { - for (j = 0; j < 3; j++) { - sum += v[i] * Q[i][j] * v[j]; - } +static inline double quadform(quadform_t Q, dpoint_t w) { + double v[3]; + int i, j; + double sum; + + v[0] = w.x; + v[1] = w.y; + v[2] = 1; + sum = 0.0; + + for (i=0; i<3; i++) { + for (j=0; j<3; j++) { + sum += v[i] * Q[i][j] * v[j]; } - return sum; + } + return sum; } /* calculate p1 x p2 */ -static inline int xprod(point_t p1, point_t p2) { return p1.x * p2.y - p1.y * p2.x; } +static inline int xprod(point_t p1, point_t p2) { + return p1.x*p2.y - p1.y*p2.x; +} /* calculate (p1-p0)x(p3-p2) */ -static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double x1, y1, x2, y2; +static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; - return x1 * y2 - x2 * y1; + return x1*y2 - x2*y1; } /* calculate (p1-p0)*(p2-p0) */ -static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) -{ - double x1, y1, x2, y2; +static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) { + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; - return x1 * x2 + y1 * y2; + return x1*x2 + y1*y2; } /* calculate (p1-p0)*(p3-p2) */ -static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double x1, y1, x2, y2; +static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { + double x1, y1, x2, y2; - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; - return x1 * x2 + y1 * y2; + return x1*x2 + y1*y2; } /* calculate distance between two points */ -static inline double ddist(dpoint_t p, dpoint_t q) { return sqrt(sq(p.x - q.x) + sq(p.y - q.y)); } +static inline double ddist(dpoint_t p, dpoint_t q) { + return sqrt(sq(p.x-q.x)+sq(p.y-q.y)); +} /* calculate point of a bezier curve */ -static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double s = 1 - t; - dpoint_t res; +static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) { + double s = 1-t; + dpoint_t res; - /* Note: a good optimizing compiler (such as gcc-3) reduces the - following to 16 multiplications, using common subexpression - elimination. */ + /* Note: a good optimizing compiler (such as gcc-3) reduces the + following to 16 multiplications, using common subexpression + elimination. */ - res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x; - res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y; + res.x = s*s*s*p0.x + 3*(s*s*t)*p1.x + 3*(t*t*s)*p2.x + t*t*t*p3.x; + res.y = s*s*s*p0.y + 3*(s*s*t)*p1.y + 3*(t*t*s)*p2.y + t*t*t*p3.y; - return res; + return res; } /* calculate the point t in [0..1] on the (convex) bezier curve (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no solution in [0..1]. */ -static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) -{ - double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ - double a, b, c; /* a t^2 + b t + c = 0 */ - double d, s, r1, r2; - - A = cprod(p0, p1, q0, q1); - B = cprod(p1, p2, q0, q1); - C = cprod(p2, p3, q0, q1); - - a = A - 2 * B + C; - b = -2 * A + 2 * B; - c = A; - - d = b * b - 4 * a * c; - - if (a == 0 || d < 0) { - return -1.0; - } - - s = sqrt(d); - - r1 = (-b + s) / (2 * a); - r2 = (-b - s) / (2 * a); - - if (r1 >= 0 && r1 <= 1) { - return r1; - } else if (r2 >= 0 && r2 <= 1) { - return r2; - } else { - return -1.0; - } +static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) { + double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ + double a, b, c; /* a t^2 + b t + c = 0 */ + double d, s, r1, r2; + + A = cprod(p0, p1, q0, q1); + B = cprod(p1, p2, q0, q1); + C = cprod(p2, p3, q0, q1); + + a = A - 2*B + C; + b = -2*A + 2*B; + c = A; + + d = b*b - 4*a*c; + + if (a==0 || d<0) { + return -1.0; + } + + s = sqrt(d); + + r1 = (-b + s) / (2 * a); + r2 = (-b - s) / (2 * a); + + if (r1 >= 0 && r1 <= 1) { + return r1; + } else if (r2 >= 0 && r2 <= 1) { + return r2; + } else { + return -1.0; + } } /* ---------------------------------------------------------------------- */ /* Preparation: fill in the sum* fields of a path (used for later rapid summing). Return 0 on success, 1 with errno set on failure. */ -static int calc_sums(privpath_t *pp) -{ - int i, x, y; - int n = pp->len; - - SAFE_MALLOC(pp->sums, pp->len + 1, sums_t); - - /* origin */ - pp->x0 = pp->pt[0].x; - pp->y0 = pp->pt[0].y; - - /* preparatory computation for later fast summing */ - pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; - for (i = 0; i < n; i++) { - x = pp->pt[i].x - pp->x0; - y = pp->pt[i].y - pp->y0; - pp->sums[i + 1].x = pp->sums[i].x + x; - pp->sums[i + 1].y = pp->sums[i].y + y; - pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x; - pp->sums[i + 1].xy = pp->sums[i].xy + x * y; - pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y; - } - return 0; - -malloc_error: - return 1; +static int calc_sums(privpath_t *pp) { + int i, x, y; + int n = pp->len; + + SAFE_MALLOC(pp->sums, pp->len+1, sums_t); + + /* origin */ + pp->x0 = pp->pt[0].x; + pp->y0 = pp->pt[0].y; + + /* preparatory computation for later fast summing */ + pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; + for (i=0; ipt[i].x - pp->x0; + y = pp->pt[i].y - pp->y0; + pp->sums[i+1].x = pp->sums[i].x + x; + pp->sums[i+1].y = pp->sums[i].y + y; + pp->sums[i+1].x2 = pp->sums[i].x2 + x*x; + pp->sums[i+1].xy = pp->sums[i].xy + x*y; + pp->sums[i+1].y2 = pp->sums[i].y2 + y*y; + } + return 0; + + malloc_error: + return 1; } /* ---------------------------------------------------------------------- */ /* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the - "lon" component of a path object (based on pt/len). For each i, + "lon" component of a path object (based on pt/len). For each i, lon[i] is the furthest index such that a straight line can be drawn from i to lon[i]. Return 1 on error with errno set, else 0. */ @@ -326,208 +318,206 @@ malloc_error: substantial. */ /* returns 0 on success, 1 on error with errno set */ -static int calc_lon(privpath_t *pp) -{ - point_t *pt = pp->pt; - int n = pp->len; - int i, j, k, k1; - int ct[4], dir; - point_t constraint[2]; - point_t cur; - point_t off; - int *pivk = NULL; /* pivk[n] */ - int *nc = NULL; /* nc[n]: next corner */ - point_t dk; /* direction of k-k1 */ - int a, b, c, d; - - SAFE_MALLOC(pivk, n, int); - SAFE_MALLOC(nc, n, int); - - /* initialize the nc data structure. Point from each point to the - furthest future point to which it is connected by a vertical or - horizontal segment. We take advantage of the fact that there is - always a direction change at 0 (due to the path decomposition - algorithm). But even if this were not so, there is no harm, as - in practice, correctness does not depend on the word "furthest" - above. */ - k = 0; - for (i = n - 1; i >= 0; i--) { - if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { - k = i + 1; /* necessarily ipt; + int n = pp->len; + int i, j, k, k1; + int ct[4], dir; + point_t constraint[2]; + point_t cur; + point_t off; + int *pivk = NULL; /* pivk[n] */ + int *nc = NULL; /* nc[n]: next corner */ + point_t dk; /* direction of k-k1 */ + int a, b, c, d; + + SAFE_MALLOC(pivk, n, int); + SAFE_MALLOC(nc, n, int); + + /* initialize the nc data structure. Point from each point to the + furthest future point to which it is connected by a vertical or + horizontal segment. We take advantage of the fact that there is + always a direction change at 0 (due to the path decomposition + algorithm). But even if this were not so, there is no harm, as + in practice, correctness does not depend on the word "furthest" + above. */ + k = 0; + for (i=n-1; i>=0; i--) { + if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { + k = i+1; /* necessarily ilon, n, int); - - /* determine pivot points: for each i, let pivk[i] be the furthest k - such that all j with i= 0; i--) { - ct[0] = ct[1] = ct[2] = ct[3] = 0; - - /* keep track of "directions" that have occurred */ - dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2; - ct[dir]++; - - constraint[0].x = 0; - constraint[0].y = 0; - constraint[1].x = 0; - constraint[1].y = 0; - - /* find the next k such that no straight line from i to k */ - k = nc[i]; - k1 = i; - while (1) { - - dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2; - ct[dir]++; - - /* if all four "directions" have occurred, cut this path */ - if (ct[0] && ct[1] && ct[2] && ct[3]) { - pivk[i] = k1; - goto foundk; - } - - cur.x = pt[k].x - pt[i].x; - cur.y = pt[k].y - pt[i].y; - - /* see if current constraint is violated */ - if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { - goto constraint_viol; - } - - /* else, update constraint */ - if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { - /* no constraint */ - } else { - off.x = cur.x + ((cur.y >= 0 && (cur.y > 0 || cur.x < 0)) ? 1 : -1); - off.y = cur.y + ((cur.x <= 0 && (cur.x < 0 || cur.y < 0)) ? 1 : -1); - if (xprod(constraint[0], off) >= 0) { - constraint[0] = off; - } - off.x = cur.x + ((cur.y <= 0 && (cur.y < 0 || cur.x < 0)) ? 1 : -1); - off.y = cur.y + ((cur.x >= 0 && (cur.x > 0 || cur.y < 0)) ? 1 : -1); - if (xprod(constraint[1], off) <= 0) { - constraint[1] = off; - } - } - k1 = k; - k = nc[k1]; - if (!cyclic(k, i, k1)) { - break; - } - } - constraint_viol: - /* k1 was the last "corner" satisfying the current constraint, and - k is the first one violating it. We now need to find the last - point along k1..k which satisfied the constraint. */ - dk.x = sign(pt[k].x - pt[k1].x); - dk.y = sign(pt[k].y - pt[k1].y); - cur.x = pt[k1].x - pt[i].x; - cur.y = pt[k1].y - pt[i].y; - /* find largest integer j such that xprod(constraint[0], cur+j*dk) - >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity - of xprod. */ - a = xprod(constraint[0], cur); - b = xprod(constraint[0], dk); - c = xprod(constraint[1], cur); - d = xprod(constraint[1], dk); - /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This - can be solved with integer arithmetic. */ - j = INFTY; - if (b < 0) { - j = floordiv(a, -b); - } - if (d > 0) { - j = min(j, floordiv(-c, d)); - } - pivk[i] = mod(k1 + j, n); - foundk: - ; - } /* for i */ - - /* clean up: for each i, let lon[i] be the largest k such that for - all i' with i<=i'lon[n - 1] = j; - for (i = n - 2; i >= 0; i--) { - if (cyclic(i + 1, pivk[i], j)) { - j = pivk[i]; - } - pp->lon[i] = j; + nc[i] = k; + } + + SAFE_MALLOC(pp->lon, n, int); + + /* determine pivot points: for each i, let pivk[i] be the furthest k + such that all j with i=0; i--) { + ct[0] = ct[1] = ct[2] = ct[3] = 0; + + /* keep track of "directions" that have occurred */ + dir = (3+3*(pt[mod(i+1,n)].x-pt[i].x)+(pt[mod(i+1,n)].y-pt[i].y))/2; + ct[dir]++; + + constraint[0].x = 0; + constraint[0].y = 0; + constraint[1].x = 0; + constraint[1].y = 0; + + /* find the next k such that no straight line from i to k */ + k = nc[i]; + k1 = i; + while (1) { + + dir = (3+3*sign(pt[k].x-pt[k1].x)+sign(pt[k].y-pt[k1].y))/2; + ct[dir]++; + + /* if all four "directions" have occurred, cut this path */ + if (ct[0] && ct[1] && ct[2] && ct[3]) { + pivk[i] = k1; + goto foundk; + } + + cur.x = pt[k].x - pt[i].x; + cur.y = pt[k].y - pt[i].y; + + /* see if current constraint is violated */ + if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { + goto constraint_viol; + } + + /* else, update constraint */ + if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { + /* no constraint */ + } else { + off.x = cur.x + ((cur.y>=0 && (cur.y>0 || cur.x<0)) ? 1 : -1); + off.y = cur.y + ((cur.x<=0 && (cur.x<0 || cur.y<0)) ? 1 : -1); + if (xprod(constraint[0], off) >= 0) { + constraint[0] = off; + } + off.x = cur.x + ((cur.y<=0 && (cur.y<0 || cur.x<0)) ? 1 : -1); + off.y = cur.y + ((cur.x>=0 && (cur.x>0 || cur.y<0)) ? 1 : -1); + if (xprod(constraint[1], off) <= 0) { + constraint[1] = off; + } + } + k1 = k; + k = nc[k1]; + if (!cyclic(k,i,k1)) { + break; + } } - - for (i = n - 1; cyclic(mod(i + 1, n), j, pp->lon[i]); i--) { - pp->lon[i] = j; + constraint_viol: + /* k1 was the last "corner" satisfying the current constraint, and + k is the first one violating it. We now need to find the last + point along k1..k which satisfied the constraint. */ + dk.x = sign(pt[k].x-pt[k1].x); + dk.y = sign(pt[k].y-pt[k1].y); + cur.x = pt[k1].x - pt[i].x; + cur.y = pt[k1].y - pt[i].y; + /* find largest integer j such that xprod(constraint[0], cur+j*dk) + >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity + of xprod. */ + a = xprod(constraint[0], cur); + b = xprod(constraint[0], dk); + c = xprod(constraint[1], cur); + d = xprod(constraint[1], dk); + /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This + can be solved with integer arithmetic. */ + j = INFTY; + if (b<0) { + j = floordiv(a,-b); + } + if (d>0) { + j = min(j, floordiv(-c,d)); } + pivk[i] = mod(k1+j,n); + foundk: + ; + } /* for i */ + + /* clean up: for each i, let lon[i] be the largest k such that for + all i' with i<=i'lon[n-1]=j; + for (i=n-2; i>=0; i--) { + if (cyclic(i+1,pivk[i],j)) { + j=pivk[i]; + } + pp->lon[i]=j; + } - free(pivk); - free(nc); - return 0; + for (i=n-1; cyclic(mod(i+1,n),j,pp->lon[i]); i--) { + pp->lon[i] = j; + } -malloc_error: - free(pivk); - free(nc); - return 1; + free(pivk); + free(nc); + return 0; + + malloc_error: + free(pivk); + free(nc); + return 1; } /* ---------------------------------------------------------------------- */ -/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ +/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ /* Auxiliary function: calculate the penalty of an edge from i to j in the given path. This needs the "lon" and "sum*" data. */ -static double penalty3(privpath_t *pp, int i, int j) -{ - int n = pp->len; - point_t *pt = pp->pt; - sums_t *sums = pp->sums; - - /* assume 0<=i= n) { - j -= n; - r = 1; - } - - /* critical inner loop: the "if" gives a 4.6 percent speedup */ - if (r == 0) { - x = sums[j + 1].x - sums[i].x; - y = sums[j + 1].y - sums[i].y; - x2 = sums[j + 1].x2 - sums[i].x2; - xy = sums[j + 1].xy - sums[i].xy; - y2 = sums[j + 1].y2 - sums[i].y2; - k = j + 1 - i; - } else { - x = sums[j + 1].x - sums[i].x + sums[n].x; - y = sums[j + 1].y - sums[i].y + sums[n].y; - x2 = sums[j + 1].x2 - sums[i].x2 + sums[n].x2; - xy = sums[j + 1].xy - sums[i].xy + sums[n].xy; - y2 = sums[j + 1].y2 - sums[i].y2 + sums[n].y2; - k = j + 1 - i + n; - } - - px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; - py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; - ey = (pt[j].x - pt[i].x); - ex = -(pt[j].y - pt[i].y); - - a = ((x2 - 2 * x * px) / k + px * px); - b = ((xy - x * py - y * px) / k + px * py); - c = ((y2 - 2 * y * py) / k + py * py); - - s = ex * ex * a + 2 * ex * ey * b + ey * ey * c; - - return sqrt(s); +static double penalty3(privpath_t *pp, int i, int j) { + int n = pp->len; + point_t *pt = pp->pt; + sums_t *sums = pp->sums; + + /* assume 0<=i=n) { + j -= n; + r = 1; + } + + /* critical inner loop: the "if" gives a 4.6 percent speedup */ + if (r == 0) { + x = sums[j+1].x - sums[i].x; + y = sums[j+1].y - sums[i].y; + x2 = sums[j+1].x2 - sums[i].x2; + xy = sums[j+1].xy - sums[i].xy; + y2 = sums[j+1].y2 - sums[i].y2; + k = j+1 - i; + } else { + x = sums[j+1].x - sums[i].x + sums[n].x; + y = sums[j+1].y - sums[i].y + sums[n].y; + x2 = sums[j+1].x2 - sums[i].x2 + sums[n].x2; + xy = sums[j+1].xy - sums[i].xy + sums[n].xy; + y2 = sums[j+1].y2 - sums[i].y2 + sums[n].y2; + k = j+1 - i + n; + } + + px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; + py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; + ey = (pt[j].x - pt[i].x); + ex = -(pt[j].y - pt[i].y); + + a = ((x2 - 2*x*px) / k + px*px); + b = ((xy - x*py - y*px) / k + px*py); + c = ((y2 - 2*y*py) / k + py*py); + + s = ex*ex*a + 2*ex*ey*b + ey*ey*c; + + return sqrt(s); } /* find the optimal polygon. Fill in the m and po components. Return 1 @@ -535,109 +525,109 @@ static double penalty3(privpath_t *pp, int i, int j) is in the polygon. Fixme: implement cyclic version. */ static int bestpolygon(privpath_t *pp) { - int i, j, m, k; - int n = pp->len; - double *pen = NULL; /* pen[n+1]: penalty vector */ - int *prev = NULL; /* prev[n+1]: best path pointer vector */ - int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ - int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ - int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ - int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ - double thispen; - double best; - int c; - - SAFE_MALLOC(pen, n + 1, double); - SAFE_MALLOC(prev, n + 1, int); - SAFE_MALLOC(clip0, n, int); - SAFE_MALLOC(clip1, n + 1, int); - SAFE_MALLOC(seg0, n + 1, int); - SAFE_MALLOC(seg1, n + 1, int); - - /* calculate clipped paths */ - for (i = 0; i < n; i++) { - c = mod(pp->lon[mod(i - 1, n)] - 1, n); - if (c == i) { - c = mod(i + 1, n); - } - if (c < i) { - clip0[i] = n; - } else { - clip0[i] = c; - } - } - - /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff - clip1[j] <= i, for i,j=0..n. */ - j = 1; - for (i = 0; i < n; i++) { - while (j <= clip0[i]) { - clip1[j] = i; - j++; - } - } - - /* calculate seg0[j] = longest path from 0 with j segments */ - i = 0; - for (j = 0; i < n; j++) { - seg0[j] = i; - i = clip0[i]; + int i, j, m, k; + int n = pp->len; + double *pen = NULL; /* pen[n+1]: penalty vector */ + int *prev = NULL; /* prev[n+1]: best path pointer vector */ + int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ + int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ + int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ + int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ + double thispen; + double best; + int c; + + SAFE_MALLOC(pen, n+1, double); + SAFE_MALLOC(prev, n+1, int); + SAFE_MALLOC(clip0, n, int); + SAFE_MALLOC(clip1, n+1, int); + SAFE_MALLOC(seg0, n+1, int); + SAFE_MALLOC(seg1, n+1, int); + + /* calculate clipped paths */ + for (i=0; ilon[mod(i-1,n)]-1,n); + if (c == i) { + c = mod(i+1,n); } - seg0[j] = n; - m = j; - - /* calculate seg1[j] = longest path to n with m-j segments */ - i = n; - for (j = m; j > 0; j--) { - seg1[j] = i; - i = clip1[i]; + if (c < i) { + clip0[i] = n; + } else { + clip0[i] = c; } - seg1[0] = 0; - - /* now find the shortest path with m segments, based on penalty3 */ - /* note: the outer 2 loops jointly have at most n iterations, thus - the worst-case behavior here is quadratic. In practice, it is - close to linear since the inner loop tends to be short. */ - pen[0] = 0; - for (j = 1; j <= m; j++) { - for (i = seg1[j]; i <= seg0[j]; i++) { - best = -1; - for (k = seg0[j - 1]; k >= clip1[i]; k--) { - thispen = penalty3(pp, k, i) + pen[k]; - if (best < 0 || thispen < best) { - prev[i] = k; - best = thispen; - } - } - pen[i] = best; - } + } + + /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff + clip1[j] <= i, for i,j=0..n. */ + j = 1; + for (i=0; im = m; - SAFE_MALLOC(pp->po, m, int); - - /* read off shortest path */ - for (i = n, j = m - 1; i > 0; j--) { - i = prev[i]; - pp->po[j] = i; + } + + /* calculate seg0[j] = longest path from 0 with j segments */ + i = 0; + for (j=0; i0; j--) { + seg1[j] = i; + i = clip1[i]; + } + seg1[0] = 0; + + /* now find the shortest path with m segments, based on penalty3 */ + /* note: the outer 2 loops jointly have at most n iterations, thus + the worst-case behavior here is quadratic. In practice, it is + close to linear since the inner loop tends to be short. */ + pen[0]=0; + for (j=1; j<=m; j++) { + for (i=seg1[j]; i<=seg0[j]; i++) { + best = -1; + for (k=seg0[j-1]; k>=clip1[i]; k--) { + thispen = penalty3(pp, k, i) + pen[k]; + if (best < 0 || thispen < best) { + prev[i] = k; + best = thispen; + } + } + pen[i] = best; } - - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 0; - -malloc_error: - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 1; + } + + pp->m = m; + SAFE_MALLOC(pp->po, m, int); + + /* read off shortest path */ + for (i=n, j=m-1; i>0; j--) { + i = prev[i]; + pp->po[j] = i; + } + + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 0; + + malloc_error: + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 1; } /* ---------------------------------------------------------------------- */ @@ -648,269 +638,266 @@ malloc_error: if it lies outside. Return 1 with errno set on error; 0 on success. */ -static int adjust_vertices(privpath_t *pp) -{ - int m = pp->m; - int *po = pp->po; - int n = pp->len; - point_t *pt = pp->pt; - int x0 = pp->x0; - int y0 = pp->y0; - - dpoint_t *ctr = NULL; /* ctr[m] */ - dpoint_t *dir = NULL; /* dir[m] */ - quadform_t *q = NULL; /* q[m] */ - double v[3]; - double d; - int i, j, k, l; - dpoint_t s; - int r; - - SAFE_MALLOC(ctr, m, dpoint_t); - SAFE_MALLOC(dir, m, dpoint_t); - SAFE_MALLOC(q, m, quadform_t); - - r = privcurve_init(&pp->curve, m); - if (r) { - goto malloc_error; - } - - /* calculate "optimal" point-slope representation for each line - segment */ - for (i = 0; i < m; i++) { - j = po[mod(i + 1, m)]; - j = mod(j - po[i], n) + po[i]; - pointslope(pp, po[i], j, &ctr[i], &dir[i]); +static int adjust_vertices(privpath_t *pp) { + int m = pp->m; + int *po = pp->po; + int n = pp->len; + point_t *pt = pp->pt; + int x0 = pp->x0; + int y0 = pp->y0; + + dpoint_t *ctr = NULL; /* ctr[m] */ + dpoint_t *dir = NULL; /* dir[m] */ + quadform_t *q = NULL; /* q[m] */ + double v[3]; + double d; + int i, j, k, l; + dpoint_t s; + int r; + + SAFE_MALLOC(ctr, m, dpoint_t); + SAFE_MALLOC(dir, m, dpoint_t); + SAFE_MALLOC(q, m, quadform_t); + + r = privcurve_init(&pp->curve, m); + if (r) { + goto malloc_error; + } + + /* calculate "optimal" point-slope representation for each line + segment */ + for (i=0; i Q[1][1]) { - v[0] = -Q[0][1]; - v[1] = Q[0][0]; - } else if (Q[1][1]) { - v[0] = -Q[1][1]; - v[1] = Q[1][0]; - } else { - v[0] = 1; - v[1] = 0; - } - d = sq(v[0]) + sq(v[1]); - v[2] = -v[1] * s.y - v[0] * s.x; - for (l = 0; l < 3; l++) { - for (k = 0; k < 3; k++) { - Q[l][k] += v[l] * v[k] / d; - } - } - } - dx = fabs(w.x - s.x); - dy = fabs(w.y - s.y); - if (dx <= .5 && dy <= .5) { - pp->curve.vertex[i].x = w.x + x0; - pp->curve.vertex[i].y = w.y + y0; - continue; - } - - /* the minimum was not in the unit square; now minimize quadratic - on boundary of square */ - min = quadform(Q, s); - xmin = s.x; - ymin = s.y; - - if (Q[0][0] == 0.0) { - goto fixx; - } - for (z = 0; z < 2; z++) { /* value of the y-coordinate */ - w.y = s.y - 0.5 + z; - w.x = -(Q[0][1] * w.y + Q[0][2]) / Q[0][0]; - dx = fabs(w.x - s.x); - cand = quadform(Q, w); - if (dx <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - fixx: - if (Q[1][1] == 0.0) { - goto corners; - } - for (z = 0; z < 2; z++) { /* value of the x-coordinate */ - w.x = s.x - 0.5 + z; - w.y = -(Q[1][0] * w.x + Q[1][2]) / Q[1][1]; - dy = fabs(w.y - s.y); - cand = quadform(Q, w); - if (dy <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - corners: - /* check four corners */ - for (l = 0; l < 2; l++) { - for (k = 0; k < 2; k++) { - w.x = s.x - 0.5 + l; - w.y = s.y - 0.5 + k; - cand = quadform(Q, w); - if (cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - } - - pp->curve.vertex[i].x = xmin + x0; - pp->curve.vertex[i].y = ymin + y0; - continue; + + det = Q[0][0]*Q[1][1] - Q[0][1]*Q[1][0]; + if (det != 0.0) { + w.x = (-Q[0][2]*Q[1][1] + Q[1][2]*Q[0][1]) / det; + w.y = ( Q[0][2]*Q[1][0] - Q[1][2]*Q[0][0]) / det; + break; + } + + /* matrix is singular - lines are parallel. Add another, + orthogonal axis, through the center of the unit square */ + if (Q[0][0]>Q[1][1]) { + v[0] = -Q[0][1]; + v[1] = Q[0][0]; + } else if (Q[1][1]) { + v[0] = -Q[1][1]; + v[1] = Q[1][0]; + } else { + v[0] = 1; + v[1] = 0; + } + d = sq(v[0]) + sq(v[1]); + v[2] = - v[1] * s.y - v[0] * s.x; + for (l=0; l<3; l++) { + for (k=0; k<3; k++) { + Q[l][k] += v[l] * v[k] / d; + } + } + } + dx = fabs(w.x-s.x); + dy = fabs(w.y-s.y); + if (dx <= .5 && dy <= .5) { + pp->curve.vertex[i].x = w.x+x0; + pp->curve.vertex[i].y = w.y+y0; + continue; } - free(ctr); - free(dir); - free(q); - return 0; + /* the minimum was not in the unit square; now minimize quadratic + on boundary of square */ + min = quadform(Q, s); + xmin = s.x; + ymin = s.y; -malloc_error: - free(ctr); - free(dir); - free(q); - return 1; + if (Q[0][0] == 0.0) { + goto fixx; + } + for (z=0; z<2; z++) { /* value of the y-coordinate */ + w.y = s.y-0.5+z; + w.x = - (Q[0][1] * w.y + Q[0][2]) / Q[0][0]; + dx = fabs(w.x-s.x); + cand = quadform(Q, w); + if (dx <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + fixx: + if (Q[1][1] == 0.0) { + goto corners; + } + for (z=0; z<2; z++) { /* value of the x-coordinate */ + w.x = s.x-0.5+z; + w.y = - (Q[1][0] * w.x + Q[1][2]) / Q[1][1]; + dy = fabs(w.y-s.y); + cand = quadform(Q, w); + if (dy <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + corners: + /* check four corners */ + for (l=0; l<2; l++) { + for (k=0; k<2; k++) { + w.x = s.x-0.5+l; + w.y = s.y-0.5+k; + cand = quadform(Q, w); + if (cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + } + + pp->curve.vertex[i].x = xmin + x0; + pp->curve.vertex[i].y = ymin + y0; + continue; + } + + free(ctr); + free(dir); + free(q); + return 0; + + malloc_error: + free(ctr); + free(dir); + free(q); + return 1; } /* ---------------------------------------------------------------------- */ /* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ /* reverse orientation of a path */ -static void reverse(privcurve_t *curve) -{ - int m = curve->n; - int i, j; - dpoint_t tmp; - - for (i = 0, j = m - 1; i < j; i++, j--) { - tmp = curve->vertex[i]; - curve->vertex[i] = curve->vertex[j]; - curve->vertex[j] = tmp; - } +static void reverse(privcurve_t *curve) { + int m = curve->n; + int i, j; + dpoint_t tmp; + + for (i=0, j=m-1; ivertex[i]; + curve->vertex[i] = curve->vertex[j]; + curve->vertex[j] = tmp; + } } /* Always succeeds */ -static void smooth(privcurve_t *curve, double alphamax) -{ - int m = curve->n; - - int i, j, k; - double dd, denom, alpha; - dpoint_t p2, p3, p4; - - /* examine each vertex and find its best fit */ - for (i = 0; i < m; i++) { - j = mod(i + 1, m); - k = mod(i + 2, m); - p4 = interval(1 / 2.0, curve->vertex[k], curve->vertex[j]); - - denom = ddenom(curve->vertex[i], curve->vertex[k]); - if (denom != 0.0) { - dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; - dd = fabs(dd); - alpha = dd > 1 ? (1 - 1.0 / dd) : 0; - alpha = alpha / 0.75; - } else { - alpha = 4 / 3.0; - } - curve->alpha0[j] = alpha; /* remember "original" value of alpha */ - - if (alpha > alphamax) { /* pointed corner */ - curve->tag[j] = POTRACE_CORNER; - curve->c[j][1] = curve->vertex[j]; - curve->c[j][2] = p4; - } else { - if (alpha < 0.55) { - alpha = 0.55; - } else if (alpha > 1) { - alpha = 1; - } - p2 = interval(.5 + .5 * alpha, curve->vertex[i], curve->vertex[j]); - p3 = interval(.5 + .5 * alpha, curve->vertex[k], curve->vertex[j]); - curve->tag[j] = POTRACE_CURVETO; - curve->c[j][0] = p2; - curve->c[j][1] = p3; - curve->c[j][2] = p4; - } - curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ - curve->beta[j] = 0.5; +static void smooth(privcurve_t *curve, double alphamax) { + int m = curve->n; + + int i, j, k; + double dd, denom, alpha; + dpoint_t p2, p3, p4; + + /* examine each vertex and find its best fit */ + for (i=0; ivertex[k], curve->vertex[j]); + + denom = ddenom(curve->vertex[i], curve->vertex[k]); + if (denom != 0.0) { + dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; + dd = fabs(dd); + alpha = dd>1 ? (1 - 1.0/dd) : 0; + alpha = alpha / 0.75; + } else { + alpha = 4/3.0; + } + curve->alpha0[j] = alpha; /* remember "original" value of alpha */ + + if (alpha > alphamax) { /* pointed corner */ + curve->tag[j] = POTRACE_CORNER; + curve->c[j][1] = curve->vertex[j]; + curve->c[j][2] = p4; + } else { + if (alpha < 0.55) { + alpha = 0.55; + } else if (alpha > 1) { + alpha = 1; + } + p2 = interval(.5+.5*alpha, curve->vertex[i], curve->vertex[j]); + p3 = interval(.5+.5*alpha, curve->vertex[k], curve->vertex[j]); + curve->tag[j] = POTRACE_CURVETO; + curve->c[j][0] = p2; + curve->c[j][1] = p3; + curve->c[j][2] = p4; } - curve->alphacurve = 1; + curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ + curve->beta[j] = 0.5; + } + curve->alphacurve = 1; - return; + return; } /* ---------------------------------------------------------------------- */ @@ -918,349 +905,341 @@ static void smooth(privcurve_t *curve, double alphamax) /* a private type for the result of opti_penalty */ struct opti_s { - double pen; /* penalty */ - dpoint_t c[2]; /* curve parameters */ - double t, s; /* curve parameters */ - double alpha; /* curve parameter */ + double pen; /* penalty */ + dpoint_t c[2]; /* curve parameters */ + double t, s; /* curve parameters */ + double alpha; /* curve parameter */ }; typedef struct opti_s opti_t; /* calculate best fit from i+.5 to j+.5. Assume icurve.n; - int k, k1, k2, conv, i1; - double area, alpha, d, d1, d2; - dpoint_t p0, p1, p2, p3, pt; - double A, R, A1, A2, A3, A4; - double s, t; +static int opti_penalty(privpath_t *pp, int i, int j, opti_t *res, double opttolerance, int *convc, double *areac) { + int m = pp->curve.n; + int k, k1, k2, conv, i1; + double area, alpha, d, d1, d2; + dpoint_t p0, p1, p2, p3, pt; + double A, R, A1, A2, A3, A4; + double s, t; - /* check convexity, corner-freeness, and maximum bend < 179 degrees */ + /* check convexity, corner-freeness, and maximum bend < 179 degrees */ - if (i == j) { /* sanity - a full loop can never be an opticurve */ - return 1; - } + if (i==j) { /* sanity - a full loop can never be an opticurve */ + return 1; + } - k = i; - i1 = mod(i + 1, m); - k1 = mod(k + 1, m); - conv = convc[k1]; - if (conv == 0) { - return 1; + k = i; + i1 = mod(i+1, m); + k1 = mod(k+1, m); + conv = convc[k1]; + if (conv == 0) { + return 1; + } + d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); + for (k=k1; k!=j; k=k1) { + k1 = mod(k+1, m); + k2 = mod(k+2, m); + if (convc[k1] != conv) { + return 1; } - d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); - for (k = k1; k != j; k = k1) { - k1 = mod(k + 1, m); - k2 = mod(k + 2, m); - if (convc[k1] != conv) { - return 1; - } - if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != - conv) { - return 1; - } - if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < - d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { - return 1; - } + if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) { + return 1; } - - /* the curve we're working in: */ - p0 = pp->curve.c[mod(i, m)][2]; - p1 = pp->curve.vertex[mod(i + 1, m)]; - p2 = pp->curve.vertex[mod(j, m)]; - p3 = pp->curve.c[mod(j, m)][2]; - - /* determine its area */ - area = areac[j] - areac[i]; - area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2]) / 2; - if (i >= j) { - area += areac[m]; + if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { + return 1; } + } + + /* the curve we're working in: */ + p0 = pp->curve.c[mod(i,m)][2]; + p1 = pp->curve.vertex[mod(i+1,m)]; + p2 = pp->curve.vertex[mod(j,m)]; + p3 = pp->curve.c[mod(j,m)][2]; + + /* determine its area */ + area = areac[j] - areac[i]; + area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2])/2; + if (i>=j) { + area += areac[m]; + } + + /* find intersection o of p0p1 and p2p3. Let t,s such that o = + interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the + triangle (p0,o,p3). */ + + A1 = dpara(p0, p1, p2); + A2 = dpara(p0, p1, p3); + A3 = dpara(p0, p2, p3); + /* A4 = dpara(p1, p2, p3); */ + A4 = A1+A3-A2; + + if (A2 == A1) { /* this should never happen */ + return 1; + } - /* find intersection o of p0p1 and p2p3. Let t,s such that o = - interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the - triangle (p0,o,p3). */ + t = A3/(A3-A4); + s = A2/(A2-A1); + A = A2 * t / 2.0; + + if (A == 0.0) { /* this should never happen */ + return 1; + } - A1 = dpara(p0, p1, p2); - A2 = dpara(p0, p1, p3); - A3 = dpara(p0, p2, p3); - /* A4 = dpara(p1, p2, p3); */ - A4 = A1 + A3 - A2; + R = area / A; /* relative area */ + alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ - if (A2 == A1) { /* this should never happen */ - return 1; - } + res->c[0] = interval(t * alpha, p0, p1); + res->c[1] = interval(s * alpha, p3, p2); + res->alpha = alpha; + res->t = t; + res->s = s; - t = A3 / (A3 - A4); - s = A2 / (A2 - A1); - A = A2 * t / 2.0; + p1 = res->c[0]; + p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ - if (A == 0.0) { /* this should never happen */ - return 1; - } + res->pen = 0; - R = area / A; /* relative area */ - alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ - - res->c[0] = interval(t * alpha, p0, p1); - res->c[1] = interval(s * alpha, p3, p2); - res->alpha = alpha; - res->t = t; - res->s = s; - - p1 = res->c[0]; - p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ - - res->pen = 0; - - /* calculate penalty */ - /* check tangency with edges */ - for (k = mod(i + 1, m); k != j; k = k1) { - k1 = mod(k + 1, m); - t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); - if (t < -.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; - if (fabs(d1) > opttolerance) { - return 1; - } - if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || - iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { - return 1; - } - res->pen += sq(d1); + /* calculate penalty */ + /* check tangency with edges */ + for (k=mod(i+1,m); k!=j; k=k1) { + k1 = mod(k+1,m); + t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); + if (t<-.5) { + return 1; } - - /* check corners */ - for (k = i; k != j; k = k1) { - k1 = mod(k + 1, m); - t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); - if (t < -.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; - d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; - d2 *= 0.75 * pp->curve.alpha[k1]; - if (d2 < 0) { - d1 = -d1; - d2 = -d2; - } - if (d1 < d2 - opttolerance) { - return 1; - } - if (d1 < d2) { - res->pen += sq(d1 - d2); - } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); + if (d == 0.0) { /* this should never happen */ + return 1; } + d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; + if (fabs(d1) > opttolerance) { + return 1; + } + if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { + return 1; + } + res->pen += sq(d1); + } + + /* check corners */ + for (k=i; k!=j; k=k1) { + k1 = mod(k+1,m); + t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); + if (t<-.5) { + return 1; + } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); + if (d == 0.0) { /* this should never happen */ + return 1; + } + d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; + d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; + d2 *= 0.75 * pp->curve.alpha[k1]; + if (d2 < 0) { + d1 = -d1; + d2 = -d2; + } + if (d1 < d2 - opttolerance) { + return 1; + } + if (d1 < d2) { + res->pen += sq(d1 - d2); + } + } - return 0; + return 0; } /* optimize the path p, replacing sequences of Bezier segments by a single segment when possible. Return 0 on success, 1 with errno set on failure. */ -static int opticurve(privpath_t *pp, double opttolerance) -{ - int m = pp->curve.n; - int *pt = NULL; /* pt[m+1] */ - double *pen = NULL; /* pen[m+1] */ - int *len = NULL; /* len[m+1] */ - opti_t *opt = NULL; /* opt[m+1] */ - int om; - int i, j, r; - opti_t o; - dpoint_t p0; - int i1; - double area; - double alpha; - double *s = NULL; - double *t = NULL; - - int *convc = NULL; /* conv[m]: pre-computed convexities */ - double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ - - SAFE_MALLOC(pt, m + 1, int); - SAFE_MALLOC(pen, m + 1, double); - SAFE_MALLOC(len, m + 1, int); - SAFE_MALLOC(opt, m + 1, opti_t); - SAFE_MALLOC(convc, m, int); - SAFE_MALLOC(areac, m + 1, double); - - /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ - for (i = 0; i < m; i++) { - if (pp->curve.tag[i] == POTRACE_CURVETO) { - convc[i] = - sign(dpara(pp->curve.vertex[mod(i - 1, m)], pp->curve.vertex[i], pp->curve.vertex[mod(i + 1, m)])); - } else { - convc[i] = 0; - } - } - - /* pre-calculate areas */ - area = 0.0; - areac[0] = 0.0; - p0 = pp->curve.vertex[0]; - for (i = 0; i < m; i++) { - i1 = mod(i + 1, m); - if (pp->curve.tag[i1] == POTRACE_CURVETO) { - alpha = pp->curve.alpha[i1]; - area += 0.3 * alpha * (4 - alpha) * dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2]) / 2; - area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2]) / 2; - } - areac[i + 1] = area; - } - - pt[0] = -1; - pen[0] = 0; - len[0] = 0; - - /* Fixme: we always start from a fixed point -- should find the best - curve cyclically */ - - for (j = 1; j <= m; j++) { - /* calculate best path from 0 to j */ - pt[j] = j - 1; - pen[j] = pen[j - 1]; - len[j] = len[j - 1] + 1; - - for (i = j - 2; i >= 0; i--) { - r = opti_penalty(pp, i, mod(j, m), &o, opttolerance, convc, areac); - if (r) { - break; - } - if (len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + o.pen)) { - pt[j] = i; - pen[j] = pen[i] + o.pen; - len[j] = len[i] + 1; - opt[j] = o; - } - } +static int opticurve(privpath_t *pp, double opttolerance) { + int m = pp->curve.n; + int *pt = NULL; /* pt[m+1] */ + double *pen = NULL; /* pen[m+1] */ + int *len = NULL; /* len[m+1] */ + opti_t *opt = NULL; /* opt[m+1] */ + int om; + int i,j,r; + opti_t o; + dpoint_t p0; + int i1; + double area; + double alpha; + double *s = NULL; + double *t = NULL; + + int *convc = NULL; /* conv[m]: pre-computed convexities */ + double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ + + SAFE_MALLOC(pt, m+1, int); + SAFE_MALLOC(pen, m+1, double); + SAFE_MALLOC(len, m+1, int); + SAFE_MALLOC(opt, m+1, opti_t); + SAFE_MALLOC(convc, m, int); + SAFE_MALLOC(areac, m+1, double); + + /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ + for (i=0; icurve.tag[i] == POTRACE_CURVETO) { + convc[i] = sign(dpara(pp->curve.vertex[mod(i-1,m)], pp->curve.vertex[i], pp->curve.vertex[mod(i+1,m)])); + } else { + convc[i] = 0; } - om = len[m]; - r = privcurve_init(&pp->ocurve, om); - if (r) { - goto malloc_error; + } + + /* pre-calculate areas */ + area = 0.0; + areac[0] = 0.0; + p0 = pp->curve.vertex[0]; + for (i=0; icurve.tag[i1] == POTRACE_CURVETO) { + alpha = pp->curve.alpha[i1]; + area += 0.3*alpha*(4-alpha)*dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2])/2; + area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2])/2; } - SAFE_MALLOC(s, om, double); - SAFE_MALLOC(t, om, double); - - j = m; - for (i = om - 1; i >= 0; i--) { - if (pt[j] == j - 1) { - pp->ocurve.tag[i] = pp->curve.tag[mod(j, m)]; - pp->ocurve.c[i][0] = pp->curve.c[mod(j, m)][0]; - pp->ocurve.c[i][1] = pp->curve.c[mod(j, m)][1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; - pp->ocurve.vertex[i] = pp->curve.vertex[mod(j, m)]; - pp->ocurve.alpha[i] = pp->curve.alpha[mod(j, m)]; - pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j, m)]; - pp->ocurve.beta[i] = pp->curve.beta[mod(j, m)]; - s[i] = t[i] = 1.0; - } else { - pp->ocurve.tag[i] = POTRACE_CURVETO; - pp->ocurve.c[i][0] = opt[j].c[0]; - pp->ocurve.c[i][1] = opt[j].c[1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; - pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j, m)][2], pp->curve.vertex[mod(j, m)]); - pp->ocurve.alpha[i] = opt[j].alpha; - pp->ocurve.alpha0[i] = opt[j].alpha; - s[i] = opt[j].s; - t[i] = opt[j].t; - } - j = pt[j]; + areac[i+1] = area; + } + + pt[0] = -1; + pen[0] = 0; + len[0] = 0; + + /* Fixme: we always start from a fixed point -- should find the best + curve cyclically */ + + for (j=1; j<=m; j++) { + /* calculate best path from 0 to j */ + pt[j] = j-1; + pen[j] = pen[j-1]; + len[j] = len[j-1]+1; + + for (i=j-2; i>=0; i--) { + r = opti_penalty(pp, i, mod(j,m), &o, opttolerance, convc, areac); + if (r) { + break; + } + if (len[j] > len[i]+1 || (len[j] == len[i]+1 && pen[j] > pen[i] + o.pen)) { + pt[j] = i; + pen[j] = pen[i] + o.pen; + len[j] = len[i] + 1; + opt[j] = o; + } } - - /* calculate beta parameters */ - for (i = 0; i < om; i++) { - i1 = mod(i + 1, om); - pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]); + } + om = len[m]; + r = privcurve_init(&pp->ocurve, om); + if (r) { + goto malloc_error; + } + SAFE_MALLOC(s, om, double); + SAFE_MALLOC(t, om, double); + + j = m; + for (i=om-1; i>=0; i--) { + if (pt[j]==j-1) { + pp->ocurve.tag[i] = pp->curve.tag[mod(j,m)]; + pp->ocurve.c[i][0] = pp->curve.c[mod(j,m)][0]; + pp->ocurve.c[i][1] = pp->curve.c[mod(j,m)][1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; + pp->ocurve.vertex[i] = pp->curve.vertex[mod(j,m)]; + pp->ocurve.alpha[i] = pp->curve.alpha[mod(j,m)]; + pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j,m)]; + pp->ocurve.beta[i] = pp->curve.beta[mod(j,m)]; + s[i] = t[i] = 1.0; + } else { + pp->ocurve.tag[i] = POTRACE_CURVETO; + pp->ocurve.c[i][0] = opt[j].c[0]; + pp->ocurve.c[i][1] = opt[j].c[1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; + pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j,m)][2], pp->curve.vertex[mod(j,m)]); + pp->ocurve.alpha[i] = opt[j].alpha; + pp->ocurve.alpha0[i] = opt[j].alpha; + s[i] = opt[j].s; + t[i] = opt[j].t; } - pp->ocurve.alphacurve = 1; - - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 0; - -malloc_error: - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 1; + j = pt[j]; + } + + /* calculate beta parameters */ + for (i=0; iocurve.beta[i] = s[i] / (s[i] + t[i1]); + } + pp->ocurve.alphacurve = 1; + + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 0; + + malloc_error: + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 1; } /* ---------------------------------------------------------------------- */ -#define TRY(x) \ - if (x) \ - goto try_error +#define TRY(x) if (x) goto try_error /* return 0 on success, 1 on error with errno set. */ -int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) -{ - path_t *p; - double nn = 0, cn = 0; - - if (progress->callback) { - /* precompute task size for progress estimates */ - nn = 0; - list_forall(p, plist) { nn += p->priv->len; } - cn = 0; +int process_path(path_t *plist, const potrace_param_t *param, progress_t *progress) { + path_t *p; + double nn = 0, cn = 0; + + if (progress->callback) { + /* precompute task size for progress estimates */ + nn = 0; + list_forall (p, plist) { + nn += p->priv->len; + } + cn = 0; + } + + /* call downstream function with each path */ + list_forall (p, plist) { + TRY(calc_sums(p->priv)); + TRY(calc_lon(p->priv)); + TRY(bestpolygon(p->priv)); + TRY(adjust_vertices(p->priv)); + if (p->sign == '-') { /* reverse orientation of negative paths */ + reverse(&p->priv->curve); } + smooth(&p->priv->curve, param->alphamax); + if (param->opticurve) { + TRY(opticurve(p->priv, param->opttolerance)); + p->priv->fcurve = &p->priv->ocurve; + } else { + p->priv->fcurve = &p->priv->curve; + } + privcurve_to_curve(p->priv->fcurve, &p->curve); - /* call downstream function with each path */ - list_forall(p, plist) - { - TRY(calc_sums(p->priv)); - TRY(calc_lon(p->priv)); - TRY(bestpolygon(p->priv)); - TRY(adjust_vertices(p->priv)); - if (p->sign == '-') { /* reverse orientation of negative paths */ - reverse(&p->priv->curve); - } - smooth(&p->priv->curve, param->alphamax); - if (param->opticurve) { - TRY(opticurve(p->priv, param->opttolerance)); - p->priv->fcurve = &p->priv->ocurve; - } else { - p->priv->fcurve = &p->priv->curve; - } - privcurve_to_curve(p->priv->fcurve, &p->curve); - - if (progress->callback) { - cn += p->priv->len; - progress_update(cn / nn, progress); - } + if (progress->callback) { + cn += p->priv->len; + progress_update(cn/nn, progress); } + } - progress_update(1.0, progress); + progress_update(1.0, progress); - return 0; + return 0; -try_error: - return 1; + try_error: + return 1; } -- cgit v1.2.3 From 61da6fdd1620ae1d4d47e7bcc8c0a5671fcfe122 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Sun, 12 Oct 2014 14:11:21 +0200 Subject: Fix "Access to field 'image_out' results in a dereference of a null pointer" (bzr r13595) --- src/filters/blend.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/filters/blend.cpp b/src/filters/blend.cpp index fd5a9871e..ca1d5bf96 100644 --- a/src/filters/blend.cpp +++ b/src/filters/blend.cpp @@ -183,6 +183,8 @@ void SPFeBlend::update(SPCtx *ctx, guint flags) { /* Unlike normal in, in2 is required attribute. Make sure, we can call * it by some name. */ + /* This may not be true.... see issue at + * http://www.w3.org/TR/filter-effects/#feBlendElement (but it doesn't hurt). */ if (this->in2 == Inkscape::Filters::NR_FILTER_SLOT_NOT_SET || this->in2 == Inkscape::Filters::NR_FILTER_UNNAMED_SLOT) { @@ -206,26 +208,30 @@ Inkscape::XML::Node* SPFeBlend::write(Inkscape::XML::Document *doc, Inkscape::XM repr = doc->createElement("svg:feBlend"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + if( !in2_name ) { + + // This code is very similar to sp_filter_primtive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feBlend"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feBlend"); + } + char const *mode; switch(this->blend_mode) { case Inkscape::Filters::BLEND_NORMAL: -- cgit v1.2.3 From 17cb730370ab87bdb323c1e6972d09fe75b59f5a Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Sun, 12 Oct 2014 14:16:05 +0200 Subject: Fix "Access to field 'image_out' results in a dereference of a null pointer" (bzr r13596) --- src/filters/composite.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/filters/composite.cpp b/src/filters/composite.cpp index 257292f12..e600b6d20 100644 --- a/src/filters/composite.cpp +++ b/src/filters/composite.cpp @@ -205,6 +205,8 @@ void SPFeComposite::update(SPCtx *ctx, guint flags) { /* Unlike normal in, in2 is required attribute. Make sure, we can call * it by some name. */ + /* This may not be true.... see issue at + * http://www.w3.org/TR/filter-effects/#feBlendElement (but it doesn't hurt). */ if (this->in2 == Inkscape::Filters::NR_FILTER_SLOT_NOT_SET || this->in2 == Inkscape::Filters::NR_FILTER_UNNAMED_SLOT) { @@ -228,26 +230,30 @@ Inkscape::XML::Node* SPFeComposite::write(Inkscape::XML::Document *doc, Inkscape repr = doc->createElement("svg:feComposite"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + if( !in2_name ) { + + // This code is very similar to sp_filter_primitive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feComposite"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feComposite"); + } + char const *comp_op; switch (this->composite_operator) { -- cgit v1.2.3 From be65f03b5b02746d3d21b5e905663d3264226321 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Sun, 12 Oct 2014 14:19:49 +0200 Subject: Fix "Access to field 'image_out' results in a dereference of a null pointer" (bzr r13597) --- src/filters/displacementmap.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/filters/displacementmap.cpp b/src/filters/displacementmap.cpp index 473e4913f..7dedfc031 100644 --- a/src/filters/displacementmap.cpp +++ b/src/filters/displacementmap.cpp @@ -202,25 +202,30 @@ Inkscape::XML::Node* SPFeDisplacementMap::write(Inkscape::XML::Document *doc, In repr = doc->createElement("svg:feDisplacementMap"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); + + if( !in2_name ) { + + // This code is very similar to sp_filter_primtive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feDisplacementMap"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feDisplacementMap"); + } + sp_repr_set_svg_double(repr, "scale", this->scale); repr->setAttribute("xChannelSelector", get_channelselector_name(this->xChannelSelector)); -- cgit v1.2.3 From 7fdadb51f279180e7613a7ef703cda1818fe007d Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Sun, 12 Oct 2014 14:27:21 +0200 Subject: Fix "Value stored to 'child' is never read" (bzr r13598) --- src/xml/repr-io.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp index 0319bb5e3..a4146f215 100644 --- a/src/xml/repr-io.cpp +++ b/src/xml/repr-io.cpp @@ -623,7 +623,6 @@ static Node *sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gc repr->setContent(reinterpret_cast(node->content)); } - child = node->xmlChildrenNode; for (child = node->xmlChildrenNode; child != NULL; child = child->next) { Node *crepr = sp_repr_svg_read_node (xml_doc, child, default_ns, prefix_map); if (crepr) { -- cgit v1.2.3 From e85f0be00539e62796c7160d2a18f20f5d264b7a Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 15:50:17 +0200 Subject: Extension enumeration/dropdownbox parameter: fix potential NULL-deref crash on xml == nullptr. (bzr r13599) --- src/extension/param/enum.cpp | 55 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/extension/param/enum.cpp b/src/extension/param/enum.cpp index bb50c06e1..74b2a75ad 100644 --- a/src/extension/param/enum.cpp +++ b/src/extension/param/enum.cpp @@ -41,8 +41,8 @@ namespace Extension { class enumentry { public: enumentry (Glib::ustring &val, Glib::ustring &text) : - value(val), - guitext(text) + value(val), + guitext(text) {} Glib::ustring value; @@ -50,16 +50,19 @@ public: }; -ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), _indent(0) +ParamComboBox::ParamComboBox(const gchar *name, const gchar *guitext, const gchar *desc, + const Parameter::_scope_t scope, bool gui_hidden, const gchar *gui_tip, + Inkscape::Extension::Extension *ext, Inkscape::XML::Node *xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext) + , _value(NULL) + , _indent(0) + , choices(NULL) { - choices = NULL; - _value = NULL; + const char *xmlval = NULL; // the value stored in XML - // Read XML tree to add enumeration items: - // printf("Extension Constructor: "); if (xml != NULL) { - for (Inkscape::XML::Node *node = xml->firstChild(); node; node = node->next()) { + // Read XML tree to add enumeration items: + for (Inkscape::XML::Node *node = xml->firstChild(); node; node = node->next()) { char const * chname = node->name(); if (!strcmp(chname, INKSCAPE_EXTENSION_NS "item") || !strcmp(chname, INKSCAPE_EXTENSION_NS "_item")) { Glib::ustring newguitext, newvalue; @@ -69,8 +72,8 @@ ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const g } if (contents != NULL) { // don't translate when 'item' but do translate when '_item' - // NOTE: internal extensions use build_from_mem and don't need _item but - // still need to include if are to be localized + // NOTE: internal extensions use build_from_mem and don't need _item but + // still need to include if are to be localized if (!strcmp(chname, INKSCAPE_EXTENSION_NS "_item")) { if (node->attribute("msgctxt") != NULL) { newguitext = g_dpgettext2(NULL, node->attribute("msgctxt"), contents); @@ -95,30 +98,28 @@ ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const g } } } - } - - // Initialize _value with the default value from xml - // for simplicity : default to the contents of the first xml-child - const char * defaultval = NULL; - if (xml->firstChild() && xml->firstChild()->firstChild()) { - defaultval = xml->firstChild()->attribute("value"); - } + + // Initialize _value with the default value from xml + // for simplicity : default to the contents of the first xml-child + if (xml->firstChild() && xml->firstChild()->firstChild()) { + xmlval = xml->firstChild()->attribute("value"); + } - const char * indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; + const char *indent = xml->attribute("indent"); + if (indent != NULL) { + _indent = atoi(indent) * 12; + } } gchar * pref_name = this->pref_name(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name); + Glib::ustring paramval = prefs ? prefs->getString(extension_pref_root + pref_name) : ""; g_free(pref_name); if (!paramval.empty()) { - defaultval = paramval.data(); - } - if (defaultval != NULL) { - _value = g_strdup(defaultval); + _value = g_strdup(paramval.data()); + } else if (xmlval) { + _value = g_strdup(xmlval); } } -- cgit v1.2.3 From b021cb56b6da1e6e08da95209e75bb3d102d5267 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 17:55:41 +0200 Subject: noop: remove duplicate assignment in sp-lpe-item.cpp (bzr r13600) --- src/sp-lpe-item.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 321d2fc42..17cacbae2 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -655,9 +655,8 @@ sp_lpe_item_apply_to_mask(SPItem * item) { SPMask *mask = item->mask_ref->getObject(); if(mask) { - SPObject * mask_data = mask->firstChild(); + SPObject *mask_data = mask->firstChild(); SPCurve * mask_curve = NULL; - mask_data = mask->firstChild(); if (SP_IS_PATH(mask_data)) { mask_curve = SP_PATH(mask_data)->get_original_curve(); } else if(SP_IS_SHAPE(mask_data)) { -- cgit v1.2.3 From acb22a496132ae58d7a500b4f92e16786d8a27c9 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 17:59:21 +0200 Subject: object-snapper.cpp : make the logic easier to read (use else-clause as default initialization value) (bzr r13601) --- src/object-snapper.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src') diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 9ff6df3bf..2b29814b0 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -289,7 +289,7 @@ void Inkscape::ObjectSnapper::_snapNodes(IntermSnapResults &isr, for (std::vector::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); ++k) { if (_allowSourceToSnapToTarget(p.getSourceType(), (*k).getTargetType(), strict_snapping)) { Geom::Point target_pt = (*k).getPoint(); - Geom::Coord dist = Geom::infinity(); + Geom::Coord dist = Geom::L2(target_pt - p.getPoint()); // Default: free (unconstrained) snapping if (!c.isUndefined()) { // We're snapping to nodes along a constraint only, so find out if this node // is at the constraint, while allowing for a small margin @@ -299,9 +299,6 @@ void Inkscape::ObjectSnapper::_snapNodes(IntermSnapResults &isr, continue; } dist = Geom::L2(target_pt - p_proj_on_constraint); - } else { - // Free (unconstrained) snapping - dist = Geom::L2(target_pt - p.getPoint()); } if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { -- cgit v1.2.3 From 1d31429ac10d5fa47a611cfc2e89f4c313c1ad16 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 18:19:14 +0200 Subject: path combine: add an assert to indicate that item list cannot be empty (bzr r13602) --- src/path-chemistry.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index d355b49fe..787193060 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -78,6 +78,7 @@ sp_selected_path_combine(SPDesktop *desktop) items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); items = g_slist_reverse(items); + assert(items); // cannot be NULL because of list length check at top of function // remember the position, id, transform and style of the topmost path, they will be assigned to the combined one gint position = 0; -- cgit v1.2.3 From 2f2f164bbe75b4a4bb6d553242cecd587ed03456 Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Sun, 12 Oct 2014 19:25:47 +0200 Subject: DrawingItem: add NULL check (bug found by clang static analyzer) (bzr r13603) --- src/display/drawing-item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 80eb69546..2ea3641dc 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -967,7 +967,7 @@ DrawingItem::_setStyleCommon(SPStyle *&_style, SPStyle *style) if (_style) sp_style_unref(_style); _style = style; - if (style->filter.set && style->getFilter()) { + if (style && style->filter.set && style->getFilter()) { if (!_filter) { int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); _filter = new Inkscape::Filters::Filter(primitives); -- cgit v1.2.3 From 932ab442403e4007b7dfb8e4c587e5d979379df1 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Mon, 13 Oct 2014 10:05:35 +0200 Subject: Fix: "Forming reference to null pointer" (bzr r13606) --- src/style-internal.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/style-internal.cpp b/src/style-internal.cpp index a55a11403..f8a4a43f7 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -981,7 +981,16 @@ SPIPaint::read( gchar const *str ) { if (streq(str, "currentColor")) { set = true; currentcolor = true; - setColor( style->color.value.color ); + if (style) { + setColor( style->color.value.color ); + } else { + // Normally an SPIPaint is part of an SPStyle and the value of 'color' is + // available. SPIPaint can be used 'stand-alone' (e.g. to parse color values) in + // which case a value of 'currentColor' is meaningless, thus we shouldn't reach + // here. + std::cerr << "SPIPaint::read(): value is 'currentColor' but 'color' not available." << std::endl; + setColor( 0 ); + } } else if (streq(str, "none")) { set = true; noneSet = true; -- cgit v1.2.3 From fd587105dcf2e883a6712013b7ea7ce6e3b3b11d Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Mon, 13 Oct 2014 10:21:07 +0200 Subject: Fix "Called C++ object pointer is null" (bzr r13607) --- src/style-internal.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/style-internal.cpp b/src/style-internal.cpp index f8a4a43f7..a81a2799d 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -1405,11 +1405,20 @@ SPIFilter::read( gchar const *str ) { set = true; // Create href if not already done. - if (!href && style->object) { - href = new SPFilterReference(style->object); - href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); + if (!href) { + if (style->object) { + href = new SPFilterReference(style->object); + } + // Do we have href now? + if ( href ) { + href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); + } else { + std::cerr << "SPIFilter::read(): Could not allocate 'href'" << std::endl; + return; + } } + // We have href try { href->attach(Inkscape::URI(uri)); } catch (Inkscape::BadURIException &e) { -- cgit v1.2.3 From 25f2a039c44b34fce5f0f4eb84259c4c1755aa62 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Mon, 13 Oct 2014 10:50:39 +0200 Subject: Fix multiple: "Called C++ object pointer is null" (bzr r13608) --- src/widgets/stroke-style.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/widgets/stroke-style.cpp b/src/widgets/stroke-style.cpp index 0e0a4fd72..9c8d45908 100644 --- a/src/widgets/stroke-style.cpp +++ b/src/widgets/stroke-style.cpp @@ -763,6 +763,9 @@ StrokeStyle::setJoinType (unsigned const jointype) tb = joinBevel; break; default: + // Should not happen + std::cerr << "StrokeStyle::setJoinType(): Invalid value" << std::endl; + tb = joinMiter; break; } setJoinButtons(tb); @@ -786,6 +789,9 @@ StrokeStyle::setCapType (unsigned const captype) tb = capSquare; break; default: + // Should not happen + std::cerr << "StrokeStyle::setCapType(): Invalid value" << std::endl; + tb = capButt; break; } setCapButtons(tb); -- cgit v1.2.3 From a2c47e60a02938013ab913bfee2ef9c9fa5ea870 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Mon, 13 Oct 2014 11:11:01 +0200 Subject: Fixed: "Called C++ object pointer is null" (bzr r13609) --- src/ui/dialog/clonetiler.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index e5f18216c..9b67132c0 100644 --- a/src/ui/dialog/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -2235,7 +2235,11 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) gdk_window_process_all_updates(); SPObject *obj = selection->singleItem(); - SPItem *item = SP_IS_ITEM(obj) ? SP_ITEM(obj) : 0; + if (!obj) { + // Should never happen (empty selection checked above). + std::cerr << "CloneTiler::clonetile_apply(): No object in single item selection!!!" << std::endl; + return; + } Inkscape::XML::Node *obj_repr = obj->getRepr(); const char *id_href = g_strdup_printf("#%s", obj_repr->attribute("id")); SPObject *parent = obj->parent; @@ -2326,6 +2330,7 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) bool invert_picked = prefs->getBool(prefs_path + "invert_picked"); double gamma_picked = prefs->getDoubleLimited(prefs_path + "gamma_picked", 0, -10, 10); + SPItem *item = SP_IS_ITEM(obj) ? SP_ITEM(obj) : 0; if (dotrace) { clonetiler_trace_setup (sp_desktop_document(desktop), 1.0, item); } -- cgit v1.2.3 From 9c35de31702bb501f596555d347e9db76b248f14 Mon Sep 17 00:00:00 2001 From: Bryce Harrington Date: Mon, 13 Oct 2014 19:33:59 -0700 Subject: 2geom: Re-fix IS_NAN and IS_FINITE for solaris This patch should go upstream to 2geom... (bzr r13610) --- src/2geom/math-utils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/2geom/math-utils.h b/src/2geom/math-utils.h index 83c7b4f5e..0d5d3667b 100644 --- a/src/2geom/math-utils.h +++ b/src/2geom/math-utils.h @@ -111,7 +111,7 @@ inline void sincos(double angle, double &sin_, double &cos_) { # define IS_NAN(_a) (_isnan(_a)) /* Win32 definition */ #elif defined(isnan) || defined(__FreeBSD__) || defined(__osf__) # define IS_NAN(_a) (isnan(_a)) /* GNU definition */ -#elif defined (SOLARIS_2_8) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 +#elif defined (SOLARIS) # define IS_NAN(_a) (isnan(_a)) /* GNU definition */ #else # define IS_NAN(_a) (boost::math::isnan(_a)) @@ -129,7 +129,7 @@ inline void sincos(double angle, double &sin_, double &cos_) { # define IS_FINITE(_a) (isfinite(_a)) #elif defined(__osf__) # define IS_FINITE(_a) (finite(_a) && !IS_NAN(_a)) -#elif defined (SOLARIS_2_8) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 +#elif defined (SOLARIS) #include #define IS_FINITE(_a) (finite(_a) && !IS_NAN(_a)) #else -- cgit v1.2.3 From 35153b45814dcc844213951352f410b2fc13e2ad Mon Sep 17 00:00:00 2001 From: Bryce Harrington Date: Mon, 13 Oct 2014 20:49:44 -0700 Subject: Fix crash with GTK+ 3.14 on launch Patch from Liam to check for undefined desktop during startup. ** (inkscape:57708): CRITICAL **: SPCanvas* sp_desktop_canvas(const SPDesktop*): assertion 'desktop != NULL' failed Fixes: https://bugs.launchpad.net/inkscape/+bug/1375085 Fixed bugs: - https://launchpad.net/bugs/1375085 (bzr r13611) --- src/ui/widget/selected-style.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp index f01366e19..a575dd592 100644 --- a/src/ui/widget/selected-style.cpp +++ b/src/ui/widget/selected-style.cpp @@ -1199,7 +1199,9 @@ void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) { menu->show_all(); } -void SelectedStyle::on_opacity_changed () { +void SelectedStyle::on_opacity_changed () +{ + g_return_if_fail(_desktop); // TODO this shouldn't happen! if (_opacity_blocked) return; _opacity_blocked = true; -- cgit v1.2.3 From 703ddb77480e2e7ddeda21aa3b7d9a91ff8f23d2 Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 11:26:38 +0200 Subject: Merged src/display folder from svg-paints-support branch (bzr r13611.1.1) --- src/display/CMakeLists.txt | 2 + src/display/Makefile_insert | 2 + src/display/drawing-item.cpp | 45 ++++++++++ src/display/drawing-item.h | 9 +- src/display/drawing-pattern.cpp | 195 ++++++++++++++++++++++++++++++++++++++++ src/display/drawing-pattern.h | 88 ++++++++++++++++++ src/display/drawing-shape.cpp | 8 +- src/display/drawing-text.cpp | 8 +- src/display/nr-style.cpp | 78 +++++++++++----- src/display/nr-style.h | 9 +- src/sp-paint-server.cpp | 5 ++ src/sp-paint-server.h | 1 + 12 files changed, 415 insertions(+), 35 deletions(-) create mode 100644 src/display/drawing-pattern.cpp create mode 100644 src/display/drawing-pattern.h (limited to 'src') diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index 20424c845..800c4d0d4 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -13,6 +13,7 @@ set(display_SRC drawing-group.cpp drawing-image.cpp drawing-item.cpp + drawing-pattern.cpp drawing-shape.cpp drawing-surface.cpp drawing-text.cpp @@ -75,6 +76,7 @@ set(display_SRC drawing-group.h drawing-image.h drawing-item.h + drawing-pattern.h drawing-shape.h drawing-surface.h drawing-text.h diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index abbd89a68..2355c3653 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -33,6 +33,8 @@ ink_common_sources += \ display/drawing-image.h \ display/drawing-item.cpp \ display/drawing-item.h \ + display/drawing-pattern.cpp \ + display/drawing-pattern.h \ display/drawing-shape.cpp \ display/drawing-shape.h \ display/drawing-surface.cpp \ diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 2ea3641dc..5226edda3 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -16,6 +16,7 @@ #include "display/drawing-context.h" #include "display/drawing-item.h" #include "display/drawing-group.h" +#include "display/drawing-pattern.h" #include "display/drawing-surface.h" #include "nr-filter.h" #include "preferences.h" @@ -110,6 +111,8 @@ DrawingItem::DrawingItem(Drawing &drawing) , _transform(NULL) , _clip(NULL) , _mask(NULL) + , _fill_pattern(NULL) + , _stroke_pattern(NULL) , _filter(NULL) , _user_data(NULL) , _cache(NULL) @@ -164,6 +167,12 @@ DrawingItem::~DrawingItem() case CHILD_ROOT: _drawing._root = NULL; break; + case CHILD_FILL_PATTERN: + _parent->_fill_pattern = NULL; + break; + case CHILD_STROKE_PATTERN: + _parent->_stroke_pattern = NULL; + break; default: ; } @@ -172,6 +181,8 @@ DrawingItem::~DrawingItem() } clearChildren(); delete _transform; + delete _stroke_pattern; + delete _fill_pattern; delete _clip; delete _mask; delete _filter; @@ -366,6 +377,34 @@ DrawingItem::setMask(DrawingItem *item) _markForUpdate(STATE_ALL, true); } +void +DrawingItem::setFillPattern(DrawingPattern *pattern) +{ + _markForRendering(); + delete _fill_pattern; + _fill_pattern = pattern; + if (pattern) { + pattern->_parent = this; + assert(pattern->_child_type == CHILD_ORPHAN); + pattern->_child_type = CHILD_FILL_PATTERN; + } + _markForUpdate(STATE_ALL, true); +} + +void +DrawingItem::setStrokePattern(DrawingPattern *pattern) +{ + _markForRendering(); + delete _stroke_pattern; + _stroke_pattern = pattern; + if (pattern) { + pattern->_parent = this; + assert(pattern->_child_type == CHILD_ORPHAN); + pattern->_child_type = CHILD_STROKE_PATTERN; + } + _markForUpdate(STATE_ALL, true); +} + /// Move this item to the given place in the Z order of siblings. /// Does nothing if the item has no parent. void @@ -530,6 +569,12 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne if (to_update & STATE_RENDER) { // now that we know drawbox, dirty the corresponding rect on canvas // unless filtered, groups do not need to render by themselves, only their members + if (_fill_pattern) { + _fill_pattern->update(area, child_ctx, flags, reset); + } + if (_stroke_pattern) { + _stroke_pattern->update(area, child_ctx, flags, reset); + } if (!is_drawing_group(this) || (_filter && render_filters)) { _markForRendering(); } diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index 925bcbddb..c0c6e321e 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -28,6 +28,7 @@ class Drawing; class DrawingCache; class DrawingContext; class DrawingItem; +class DrawingPattern; namespace Filters { @@ -114,6 +115,8 @@ public: void setTransform(Geom::Affine const &trans); void setClip(DrawingItem *item); void setMask(DrawingItem *item); + void setFillPattern(DrawingPattern *pattern); + void setStrokePattern(DrawingPattern *pattern); void setZOrder(unsigned z); void setItemBounds(Geom::OptRect const &bounds); void setFilterBounds(Geom::OptRect const &bounds); @@ -135,8 +138,8 @@ protected: CHILD_CLIP = 2, // referenced by _clip member of parent CHILD_MASK = 3, // referenced by _mask member of parent CHILD_ROOT = 4, // root item of _drawing - CHILD_FILL_PATTERN = 5, // not yet implemented: referenced by fill pattern of parent - CHILD_STROKE_PATTERN = 6 // not yet implemented: referenced by stroke pattern of parent + CHILD_FILL_PATTERN = 5, // referenced by fill pattern of parent + CHILD_STROKE_PATTERN = 6 // referenced by stroke pattern of parent }; enum RenderResult { RENDER_OK = 0, @@ -185,6 +188,8 @@ protected: DrawingItem *_clip; DrawingItem *_mask; + DrawingPattern *_fill_pattern; + DrawingPattern *_stroke_pattern; Inkscape::Filters::Filter *_filter; void *_user_data; ///< Used to associate DrawingItems with SPItems that created them DrawingCache *_cache; diff --git a/src/display/drawing-pattern.cpp b/src/display/drawing-pattern.cpp new file mode 100644 index 000000000..77752bce3 --- /dev/null +++ b/src/display/drawing-pattern.cpp @@ -0,0 +1,195 @@ +/** + * @file + * Canvas belonging to SVG pattern. + *//* + * Authors: + * Tomasz Boczkowski + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing-pattern.h" +#include "display/drawing-surface.h" + +namespace Inkscape { + +DrawingPattern::DrawingPattern(Drawing &drawing, bool debug) + : DrawingGroup(drawing) + , _pattern_to_user(NULL) + , _overflow_steps(1) + , _debug(debug) +{ +} + +DrawingPattern::~DrawingPattern() +{ + delete _pattern_to_user; // delete NULL; is safe +} + +void +DrawingPattern::setPatternToUserTransform(Geom::Affine const &new_trans) { + Geom::Affine current; + if (_pattern_to_user) { + current = *_pattern_to_user; + } + + if (!Geom::are_near(current, new_trans, 1e-18)) { + // mark the area where the object was for redraw. + _markForRendering(); + if (new_trans.isIdentity()) { + delete _pattern_to_user; // delete NULL; is safe + _pattern_to_user = NULL; + } else { + _pattern_to_user = new Geom::Affine(new_trans); + } + _markForUpdate(STATE_ALL, true); + } +} + +void +DrawingPattern::setTileRect(Geom::Rect const &tile_rect) { + _tile_rect = tile_rect; +} + +void +DrawingPattern::setOverflow(Geom::Affine initial_transform, int steps, Geom::Affine step_transform) { + _overflow_initial_transform = initial_transform; + _overflow_steps = steps; + _overflow_step_transform = step_transform; +} + +cairo_pattern_t * +DrawingPattern::renderPattern(float opacity) { + bool needs_opacity = (1.0 - opacity) >= 1e-3; + bool visible = opacity >= 1e-3; + + if (!visible) { + return NULL; + } + + if (!_tile_rect || (_tile_rect->area() == 0)) { + return NULL; + } + Geom::Rect pattern_tile = *_tile_rect; + + //TODO: If pattern_to_user set it to identity transform + + // The DrawingSurface class handles the mapping from "logical space" + // (coordinates in the rendering) to "physical space" (surface pixels). + // An oversampling is done as the pattern may not pixel align with the final surface. + // The cairo surface is created when the DrawingContext is declared. + // Create drawing surface with size of pattern tile (in pattern space) but with number of pixels + // based on required resolution (c). + Inkscape::DrawingSurface pattern_surface(pattern_tile, _pattern_resolution); + Inkscape::DrawingContext dc(pattern_surface); + dc.transform( pattern_surface.drawingTransform().inverse() ); + + pattern_tile *= pattern_surface.drawingTransform(); + Geom::IntRect one_tile = pattern_tile.roundOutwards(); + + // Render pattern. + if (needs_opacity) { + dc.pushGroup(); // this group is for pattern + opacity + } + + if (_debug) { + dc.setSource(0.8, 0.0, 0.8); + dc.paint(); + } + + //FIXME: What flags to choose? + if (_overflow_steps == 1) { + render(dc, one_tile, RENDER_DEFAULT); + } else { + //Overflow transforms need to be transformed to the new coordinate system + //introduced by dc.transform( pattern_surface.drawingTransform().inverse() ); + Geom::Affine dt = pattern_surface.drawingTransform(); + Geom::Affine idt = pattern_surface.drawingTransform().inverse(); + Geom::Affine initial_transform = idt * _overflow_initial_transform * dt; + Geom::Affine step_transform = idt * _overflow_step_transform * dt; + + dc.transform(initial_transform); + for (int i = 0; i < _overflow_steps; i++) { + render(dc, one_tile, RENDER_DEFAULT); + dc.transform(step_transform); + } + } + + //Uncomment to debug + // cairo_surface_t* raw = pattern_surface.raw(); + // std::cout << " cairo_surface (sp-pattern): " + // << " width: " << cairo_image_surface_get_width( raw ) + // << " height: " << cairo_image_surface_get_height( raw ) + // << std::endl; + // std::string filename = "drawing-pattern.png"; + // cairo_surface_write_to_png( pattern_surface.raw(), filename.c_str() ); + + if (needs_opacity) { + dc.popGroupToSource(); // pop raw pattern + dc.paint(opacity); // apply opacity + } + + cairo_pattern_t *cp = cairo_pattern_create_for_surface(pattern_surface.raw()); + // Apply transformation to user space. Also compensate for oversampling. + if (_pattern_to_user) { + ink_cairo_pattern_set_matrix(cp, _pattern_to_user->inverse() * pattern_surface.drawingTransform()); + } else { + ink_cairo_pattern_set_matrix(cp, pattern_surface.drawingTransform()); + } + + if (_debug) { + cairo_pattern_set_extend(cp, CAIRO_EXTEND_NONE); + } else { + cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT); + } + + return cp; +} + +unsigned +DrawingPattern::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) +{ + UpdateContext pattern_ctx; + + if (!_tile_rect || (_tile_rect->area() == 0)) { + return STATE_NONE; + } + + Geom::Rect pattern_tile = *_tile_rect; + + Geom::Coord det_ctm = ctx.ctm.descrim(); + Geom::Coord det_ps2user = _pattern_to_user ? _pattern_to_user->descrim() : 1.0; + Geom::Coord det_child_transform = _child_transform ? _child_transform->descrim() : 1.0; + const double oversampling = 2.0; + double scale = det_ctm*det_ps2user*det_child_transform * oversampling; + //FIXME: When scale is too big (zooming in a hatch), cairo doesn't render the pattern + //More precisely it fails when seting pattern matrix in DrawingPattern::renderPattern + //Fully correct solution should make use of visible area bbox and change hach tile rect + //accordingly + if (scale > 25) { + scale = 25; + } + Geom::Point c(pattern_tile.dimensions()*scale*oversampling); + _pattern_resolution = c.ceil(); + + Inkscape::DrawingSurface pattern_surface(pattern_tile, _pattern_resolution); + + pattern_ctx.ctm = pattern_surface.drawingTransform(); + return DrawingGroup::_updateItem(Geom::IntRect::infinite(), pattern_ctx, flags, reset); +} + +} // end 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/display/drawing-pattern.h b/src/display/drawing-pattern.h new file mode 100644 index 000000000..487da51b7 --- /dev/null +++ b/src/display/drawing-pattern.h @@ -0,0 +1,88 @@ +/** + * @file + * Canvas belonging to SVG pattern. + *//* + * Authors: + * Tomasz Boczkowski + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_H + +#include "display/drawing-group.h" + +typedef struct _cairo_pattern cairo_pattern_t; +class SPStyle; + +namespace Inkscape { + +/** + * @brief Drawing tree node used for rendering paints. + * + * DrawingPattern is used for rendering patterns and hatches. + * + * It renders it's children to a cairo_pattern_t structure that can be + * applied as source for fill or stroke operations. + */ +class DrawingPattern + : public DrawingGroup +{ +public: + DrawingPattern(Drawing &drawing, bool debug = false); + ~DrawingPattern(); + + /** + * Set the transformation from pattern to user coordinate systems. + * @see SPPattern description for explanation of coordinate systems. + */ + void setPatternToUserTransform(Geom::Affine const &new_trans); + /** + * Set the tile rect position and dimentions in content coordinate system + */ + void setTileRect(Geom::Rect const &tile_rect); + /** + * Turn on overflow rendering. + * + * Overflow is implemented as repeated rendering of pattern contents. In every step + * a translation transform is applied. + */ + void setOverflow(Geom::Affine initial_transform, int steps, Geom::Affine step_transform); + /** + * Render the pattern. + * + * Returns caito_pattern_t structure that can be set as source surface. + */ + cairo_pattern_t *renderPattern(float opacity); +protected: + virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, + unsigned flags, unsigned reset); + + Geom::Affine *_pattern_to_user; + Geom::Affine _overflow_initial_transform; + Geom::Affine _overflow_step_transform; + int _overflow_steps; + Geom::OptRect _tile_rect; + bool _debug; + + Geom::IntPoint _pattern_resolution; +}; + +bool is_drawing_group(DrawingItem *item); + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_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/display/drawing-shape.cpp b/src/display/drawing-shape.cpp index 1a41bdb3a..fe525b1a5 100644 --- a/src/display/drawing-shape.cpp +++ b/src/display/drawing-shape.cpp @@ -155,7 +155,7 @@ DrawingShape::_renderFill(DrawingContext &dc) Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - bool has_fill = _nrstyle.prepareFill(dc, _item_bbox); + bool has_fill = _nrstyle.prepareFill(dc, _item_bbox, _fill_pattern); if( has_fill ) { dc.path(_curve->get_pathvector()); @@ -171,7 +171,7 @@ DrawingShape::_renderStroke(DrawingContext &dc) Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox); + bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox, _stroke_pattern); has_stroke &= (_nrstyle.stroke_width != 0); if( has_stroke ) { @@ -231,8 +231,8 @@ DrawingShape::_renderItem(DrawingContext &dc, 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 - bool has_fill = _nrstyle.prepareFill(dc, _item_bbox); - bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox); + bool has_fill = _nrstyle.prepareFill(dc, _item_bbox, _fill_pattern); + bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox, _stroke_pattern); 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 9f3b447df..cb6520bcd 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -445,13 +445,13 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*are Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - has_fill = _nrstyle.prepareFill( dc, _item_bbox); - has_stroke = _nrstyle.prepareStroke( dc, _item_bbox); + has_fill = _nrstyle.prepareFill( dc, _item_bbox, _fill_pattern); + has_stroke = _nrstyle.prepareStroke( dc, _item_bbox, _stroke_pattern); // Avoid creating patterns if not needed if( decorate ) { - has_td_fill = _nrstyle.prepareTextDecorationFill( dc, _item_bbox); - has_td_stroke = _nrstyle.prepareTextDecorationStroke(dc, _item_bbox); + has_td_fill = _nrstyle.prepareTextDecorationFill( dc, _item_bbox, _fill_pattern); + has_td_stroke = _nrstyle.prepareTextDecorationStroke(dc, _item_bbox, _stroke_pattern); } } diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index ec3117079..96d16bf06 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -14,6 +14,7 @@ #include "sp-paint-server.h" #include "display/canvas-bpath.h" // contains SPStrokeJoinType, SPStrokeCapType etc. (WTF!) #include "display/drawing-context.h" +#include "display/drawing-pattern.h" void NRStyle::Paint::clear() { @@ -95,7 +96,14 @@ NRStyle::~NRStyle() void NRStyle::set(SPStyle *style) { if ( style->fill.isPaintserver() ) { - fill.set(style->getFillPaintServer()); + SPPaintServer* server = style->getFillPaintServer(); + if ( server && server->isValid() ) { + fill.set(server); + } else if ( style->fill.colorSet ) { + fill.set(style->fill.value.color); + } else { + fill.clear(); + } } else if ( style->fill.isColor() ) { fill.set(style->fill.value.color); } else if ( style->fill.isNone() ) { @@ -117,7 +125,14 @@ void NRStyle::set(SPStyle *style) } if ( style->stroke.isPaintserver() ) { - stroke.set(style->getStrokePaintServer()); + SPPaintServer* server = style->getStrokePaintServer(); + if ( server && server->isValid() ) { + stroke.set(server); + } else if ( style->stroke.isColor() ) { + stroke.set(style->stroke.colorSet); + } else { + stroke.clear(); + } } else if ( style->stroke.isColor() ) { stroke.set(style->stroke.value.color); } else if ( style->stroke.isNone() ) { @@ -285,13 +300,16 @@ void NRStyle::set(SPStyle *style) update(); } -bool NRStyle::prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { // update fill pattern if (!fill_pattern) { switch (fill.type) { - case PAINT_SERVER: { - fill_pattern = fill.server->pattern_new(dc.raw(), paintbox, fill.opacity); + case PAINT_SERVER: + if (pattern) { + fill_pattern = pattern->renderPattern(fill.opacity); + } else { + fill_pattern = fill.server->pattern_new(dc.raw(), paintbox, fill.opacity); } break; case PAINT_COLOR: { SPColor const &c = fill.color; @@ -311,14 +329,20 @@ void NRStyle::applyFill(Inkscape::DrawingContext &dc) dc.setFillRule(fill_rule); } -bool NRStyle::prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { // update text decoration pattern if (!text_decoration_fill_pattern) { switch (text_decoration_fill.type) { - case PAINT_SERVER: { - text_decoration_fill_pattern = text_decoration_fill.server->pattern_new(dc.raw(), paintbox, text_decoration_fill.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + text_decoration_fill_pattern = pattern->renderPattern( + text_decoration_fill.opacity); + } else { + text_decoration_fill_pattern = text_decoration_fill.server->pattern_new(dc.raw(), + paintbox, text_decoration_fill.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = text_decoration_fill.color; text_decoration_fill_pattern = cairo_pattern_create_rgba( @@ -337,19 +361,25 @@ void NRStyle::applyTextDecorationFill(Inkscape::DrawingContext &dc) // Fill rule does not matter, no intersections. } -bool NRStyle::prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { if (!stroke_pattern) { switch (stroke.type) { - case PAINT_SERVER: { - stroke_pattern = stroke.server->pattern_new(dc.raw(), paintbox, stroke.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + stroke_pattern = pattern->renderPattern(stroke.opacity); + } else { + stroke_pattern = stroke.server->pattern_new(dc.raw(), paintbox, stroke.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = stroke.color; - stroke_pattern = cairo_pattern_create_rgba( - c.v.c[0], c.v.c[1], c.v.c[2], stroke.opacity); - } break; - default: break; + stroke_pattern = cairo_pattern_create_rgba(c.v.c[0], c.v.c[1], c.v.c[2], + stroke.opacity); + } + break; + default: + break; } } if (!stroke_pattern) return false; @@ -366,13 +396,19 @@ void NRStyle::applyStroke(Inkscape::DrawingContext &dc) cairo_set_dash(dc.raw(), dash, n_dash, dash_offset); // fixme } -bool NRStyle::prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { if (!text_decoration_stroke_pattern) { switch (text_decoration_stroke.type) { - case PAINT_SERVER: { - text_decoration_stroke_pattern = text_decoration_stroke.server->pattern_new(dc.raw(), paintbox, text_decoration_stroke.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + text_decoration_stroke_pattern = pattern->renderPattern( + text_decoration_stroke.opacity); + } else { + text_decoration_stroke_pattern = text_decoration_stroke.server->pattern_new( + dc.raw(), paintbox, text_decoration_stroke.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = text_decoration_stroke.color; text_decoration_stroke_pattern = cairo_pattern_create_rgba( diff --git a/src/display/nr-style.h b/src/display/nr-style.h index 83bcb1ab7..f324fdb56 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -21,6 +21,7 @@ class SPStyle; namespace Inkscape { class DrawingContext; +class DrawingPattern; } struct NRStyle { @@ -28,10 +29,10 @@ struct NRStyle { ~NRStyle(); void set(SPStyle *); - bool prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); + bool prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); void applyFill(Inkscape::DrawingContext &dc); void applyStroke(Inkscape::DrawingContext &dc); void applyTextDecorationFill(Inkscape::DrawingContext &dc); diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index 692265bd8..d692fc611 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -58,6 +58,11 @@ bool SPPaintServer::isSolid() const return solid; } +bool SPPaintServer::isValid() const +{ + return true; +} + /* Local Variables: mode:c++ diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index 02e2f10ec..c1c8d651e 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -29,6 +29,7 @@ public: bool isSwatch() const; bool isSolid() const; + virtual bool isValid() const; virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity) = 0; -- cgit v1.2.3 From b7dc999f9754c4a2c139d4fafd49b3bed1c063a3 Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 11:44:36 +0200 Subject: Merged src/style.h from svg-paints-support branch (bzr r13611.1.2) --- src/style.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/style.h b/src/style.h index 3627b4ec2..267398693 100644 --- a/src/style.h +++ b/src/style.h @@ -243,6 +243,11 @@ public: sigc::connection filter_modified_connection; sigc::connection fill_ps_modified_connection; sigc::connection stroke_ps_modified_connection; + sigc::connection fill_ps_changed_connection; + sigc::connection stroke_ps_changed_connection; + + sigc::signal signal_fill_ps_changed; + sigc::signal signal_stroke_ps_changed; SPObject *getFilter() { return (filter.href) ? filter.href->getObject() : NULL; } SPObject const *getFilter() const { return (filter.href) ? filter.href->getObject() : NULL; } -- cgit v1.2.3 From 5b12691684dfd3cb640aa26d5ea90ac4d5173485 Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 11:45:29 +0200 Subject: Merged src/style.h from svg-paints-support branch (bzr r13611.1.3) --- src/style.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/style.h b/src/style.h index 267398693..cb7597918 100644 --- a/src/style.h +++ b/src/style.h @@ -246,7 +246,18 @@ public: sigc::connection fill_ps_changed_connection; sigc::connection stroke_ps_changed_connection; + /** + * Emitted when paint server object, fill paint refers to, is changed. That is + * when the reference starts pointing to a different address in memory. + * + * NB It is different from fill_ps_modified signal. When paint server is modified + * it means some of it's attributes or chilren change. + */ sigc::signal signal_fill_ps_changed; + /** + * Emitted when paint server object, fill paint refers to, is changed. That is + * when the reference starts pointing to a different address in memory. + */ sigc::signal signal_stroke_ps_changed; SPObject *getFilter() { return (filter.href) ? filter.href->getObject() : NULL; } -- cgit v1.2.3 From ca13fe9ec12404692ecf1a8429023b2e38356b30 Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 11:46:46 +0200 Subject: Merged src/style.cpp from svg-paints-support branch (bzr r13611.1.4) --- src/style.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/style.cpp b/src/style.cpp index abc928d76..c20d7ed70 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -437,6 +437,8 @@ SPStyle::~SPStyle() { // Remove connections release_connection.disconnect(); + fill_ps_changed_connection.disconnect(); + stroke_ps_changed_connection.disconnect(); // The following shoud be moved into SPIPaint and SPIFilter if (fill.value.href) { @@ -496,10 +498,10 @@ SPStyle::clear() { filter.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), this)); fill.value.href = new SPPaintServerReference(document); - fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), this)); + fill_ps_changed_connection = fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), this)); stroke.value.href = new SPPaintServerReference(document); - stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), this)); + stroke_ps_changed_connection = stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), this)); } cloned = false; @@ -1112,6 +1114,7 @@ sp_style_fill_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } + style->signal_fill_ps_changed.emit(old_ref, ref); sp_style_paint_server_ref_modified(ref, 0, style); } @@ -1129,6 +1132,7 @@ sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPSty ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } + style->signal_stroke_ps_changed.emit(old_ref, ref); sp_style_paint_server_ref_modified(ref, 0, style); } @@ -1357,7 +1361,11 @@ sp_style_set_ipaint_to_uri(SPStyle *style, SPIPaint *paint, const Inkscape::URI // now that we have a document, we can create it here if (!paint->value.href && document) { paint->value.href = new SPPaintServerReference(document); - paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((paint == &style->fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), style)); + if (paint == &style->fill) { + style->fill_ps_changed_connection = paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), style)); + } else { + style->stroke_ps_changed_connection = paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style)); + } } if (paint->value.href){ -- cgit v1.2.3 From dc40ef8fd79f61fb7735911744f68657aef05c70 Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 11:48:00 +0200 Subject: Merged src/style-internal.cpp from svg-paints-support branch (bzr r13611.1.5) --- src/style-internal.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/style-internal.cpp b/src/style-internal.cpp index a81a2799d..b858e5cb6 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -964,7 +964,11 @@ SPIPaint::read( gchar const *str ) { if (!value.href && document) { // std::cout << " Creating value.href" << std::endl; value.href = new SPPaintServerReference(document); - value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((this == &style->fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), style)); + if (this == &style->fill) { + style->fill_ps_changed_connection = value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), style)); + } else { + style->stroke_ps_changed_connection = value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style)); + } } // std::cout << "uri: " << (uri?uri:"null") << std::endl; -- cgit v1.2.3 From dbf713649843be309496ac4199dd36299911009d Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 12:06:13 +0200 Subject: Merged src/sp-item.h from svg-paints-support branch (bzr r13611.1.6) --- src/sp-item.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/sp-item.h b/src/sp-item.h index 15784d041..98aed0fd6 100644 --- a/src/sp-item.h +++ b/src/sp-item.h @@ -33,6 +33,7 @@ class SPClipPathReference; class SPMaskReference; class SPAvoidRef; +class SPPattern; struct SPPrintContext; namespace Inkscape { @@ -236,12 +237,15 @@ private: static SPItemView *sp_item_view_new_prepend(SPItemView *list, SPItem *item, unsigned flags, unsigned key, Inkscape::DrawingItem *arenaitem); static void clip_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); static void mask_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); + static void fill_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); + static void stroke_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); public: 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 void modified(unsigned int flags); virtual Inkscape::XML::Node* write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags); virtual Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const; -- cgit v1.2.3 From b9875e6c09fcb62fa4f9c667ee6a48c8b62f8508 Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 12:06:31 +0200 Subject: Merged src/sp-item.cpp from svg-paints-support branch (bzr r13611.1.7) --- src/sp-item.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'src') diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 71cdfa394..edf758e7d 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -118,6 +118,9 @@ SPItem::SPItem() : SPObject() { mask_ref = new SPMaskReference(this); mask_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(mask_ref_changed), this)); + style->signal_fill_ps_changed.connect(sigc::bind(sigc::ptr_fun(fill_ps_ref_changed), this)); + style->signal_stroke_ps_changed.connect(sigc::bind(sigc::ptr_fun(stroke_ps_ref_changed), this)); + avoidRef = new SPAvoidRef(this); } @@ -442,7 +445,15 @@ void SPItem::release() { SPObject::release(); + SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); + SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); while (item->display) { + if (fill_pattern) { + fill_pattern->hide(item->display->arenaitem->key()); + } + if (stroke_pattern) { + stroke_pattern->hide(item->display->arenaitem->key()); + } item->display = sp_item_view_list_remove(item->display, item->display); } @@ -588,6 +599,34 @@ void SPItem::mask_ref_changed(SPObject *old_mask, SPObject *mask, SPItem *item) } } +void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { + SPPattern *old_fill_pattern = SP_PATTERN(old_ps); + if (old_fill_pattern) { + for (SPItemView *v =item->display; v != NULL; v = v->next) { + old_fill_pattern->hide(v->arenaitem->key()); + } + } + + SPPattern *new_fill_pattern = SP_PATTERN(ps); + if (new_fill_pattern) { + Geom::OptRect bbox = item->geometricBounds(); + for (SPItemView *v = item->display; v != NULL; v = v->next) { + if (!v->arenaitem->key()) { + v->arenaitem->setKey(SPItem::display_key_new(3)); + } + Inkscape::DrawingPattern *pi = new_fill_pattern->show( + v->arenaitem->drawing(), v->arenaitem->key()); + v->arenaitem->setFillPattern(pi); + new_fill_pattern->setBBox(v->arenaitem->key(), bbox); + new_fill_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } +} + +void SPItem::stroke_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item) { + +} + void SPItem::update(SPCtx* /*ctx*/, guint flags) { SPItem *item = this; SPItem* object = item; @@ -647,6 +686,9 @@ void SPItem::update(SPCtx* /*ctx*/, guint flags) { item->avoidRef->handleSettingChange(); } +void SPItem::modified(unsigned int flags) { +} + Inkscape::XML::Node* SPItem::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { SPItem *item = this; SPItem* object = item; @@ -1090,6 +1132,31 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned SP_MASK(mask)->sp_mask_set_bbox(mask_key, item_bbox); mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } + + SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); + if (fill_pattern) { + if (!display->arenaitem->key()) { + display->arenaitem->setKey(display_key_new(3)); + } + int fill_key = display->arenaitem->key(); + + Inkscape::DrawingPattern *ac = fill_pattern->show(drawing, fill_key); + ai->setFillPattern(ac); + fill_pattern->setBBox(fill_key, item_bbox); + fill_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); + if (stroke_pattern) { + if (!display->arenaitem->key()) { + display->arenaitem->setKey(display_key_new(3)); + } + int stroke_key = display->arenaitem->key(); + + Inkscape::DrawingPattern *ac = stroke_pattern->show(drawing, stroke_key); + ai->setStrokePattern(ac); + stroke_pattern->setBBox(stroke_key, item_bbox); + stroke_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } ai->setData(this); ai->setItemBounds(geometricBounds()); } @@ -1119,6 +1186,14 @@ void SPItem::invoke_hide(unsigned key) mask_ref->getObject()->sp_mask_hide(v->arenaitem->key()); v->arenaitem->setMask(NULL); } + SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); + if (fill_pattern) { + fill_pattern->hide(v->arenaitem->key()); + } + SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); + if (stroke_pattern) { + stroke_pattern->hide(v->arenaitem->key()); + } if (!ref) { display = v->next; } else { -- cgit v1.2.3 From f3b182a2facfd52c7bbfee67f207ad765f536beb Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 13:01:49 +0200 Subject: Merged hatch rendering code (bzr r13611.1.8) --- src/CMakeLists.txt | 4 + src/Makefile_insert | 2 + src/attributes.cpp | 5 + src/attributes.h | 5 + src/display/drawing-pattern.h | 2 +- src/sp-hatch-path.cpp | 290 ++++++++++++++++++++ src/sp-hatch-path.h | 86 ++++++ src/sp-hatch.cpp | 616 ++++++++++++++++++++++++++++++++++++++++++ src/sp-hatch.h | 158 +++++++++++ src/sp-item.cpp | 70 ++--- src/sp-paint-server.h | 57 +++- src/style-internal.h | 3 +- src/svg/CMakeLists.txt | 2 + src/svg/Makefile_insert | 2 + src/svg/svg-angle.cpp | 136 ++++++++++ src/svg/svg-angle.h | 69 +++++ 16 files changed, 1471 insertions(+), 36 deletions(-) create mode 100644 src/sp-hatch-path.cpp create mode 100644 src/sp-hatch-path.h create mode 100644 src/sp-hatch.cpp create mode 100644 src/sp-hatch.h create mode 100644 src/svg/svg-angle.cpp create mode 100644 src/svg/svg-angle.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a792af6b..25556e3bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,8 @@ set(sp_SRC sp-gradient-reference.cpp sp-gradient.cpp sp-guide.cpp + sp-hatch.cpp + sp-hatch-path.cpp sp-image.cpp sp-item-group.cpp sp-item-notify-moveto.cpp @@ -115,6 +117,8 @@ set(sp_SRC sp-guide-attachment.h sp-guide-constraint.h sp-guide.h + sp-hatch.h + sp-hatch-path.h sp-image.h sp-item-group.h sp-item-notify-moveto.h diff --git a/src/Makefile_insert b/src/Makefile_insert index f191839a1..66188f502 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -158,6 +158,8 @@ ink_common_sources += \ sp-guide-attachment.h \ sp-guide-constraint.h \ sp-guide.cpp sp-guide.h \ + sp-hatch.cpp sp-hatch.h \ + sp-hatch-path.cpp sp-hatch-path.h \ sp-image.cpp sp-image.h \ sp-item.cpp sp-item.h \ sp-item-group.cpp sp-item-group.h \ diff --git a/src/attributes.cpp b/src/attributes.cpp index fec5d3af4..75e78957f 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -292,6 +292,11 @@ static SPStyleProp const props[] = { {SP_ATTR_PATTERNUNITS, "patternUnits"}, {SP_ATTR_PATTERNCONTENTUNITS, "patternContentUnits"}, {SP_ATTR_PATTERNTRANSFORM, "patternTransform"}, + /* SPHatch */ + {SP_ATTR_HATCHUNITS, "hatchUnits"}, + {SP_ATTR_HATCHCONTENTUNITS, "hatchContentUnits"}, + {SP_ATTR_HATCHTRANSFORM, "hatchTransform"}, + {SP_ATTR_PITCH, "pitch"}, /* SPClipPath */ {SP_ATTR_CLIPPATHUNITS, "clipPathUnits"}, /* SPMask */ diff --git a/src/attributes.h b/src/attributes.h index 3397e4034..8d3c6399b 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -293,6 +293,11 @@ enum SPAttributeEnum { SP_ATTR_PATTERNUNITS, SP_ATTR_PATTERNCONTENTUNITS, SP_ATTR_PATTERNTRANSFORM, + /* SPHatch */ + SP_ATTR_HATCHUNITS, + SP_ATTR_HATCHCONTENTUNITS, + SP_ATTR_HATCHTRANSFORM, + SP_ATTR_PITCH, /* SPClipPath */ SP_ATTR_CLIPPATHUNITS, /* SPMask */ diff --git a/src/display/drawing-pattern.h b/src/display/drawing-pattern.h index 487da51b7..b614785dc 100644 --- a/src/display/drawing-pattern.h +++ b/src/display/drawing-pattern.h @@ -59,6 +59,7 @@ public: protected: virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset); + Geom::IntPoint _patternDimentions(); Geom::Affine *_pattern_to_user; Geom::Affine _overflow_initial_transform; @@ -66,7 +67,6 @@ protected: int _overflow_steps; Geom::OptRect _tile_rect; bool _debug; - Geom::IntPoint _pattern_resolution; }; diff --git a/src/sp-hatch-path.cpp b/src/sp-hatch-path.cpp new file mode 100644 index 000000000..bdefd32d8 --- /dev/null +++ b/src/sp-hatch-path.cpp @@ -0,0 +1,290 @@ +/** @file + * SVG implementation + *//* + * Author: + * Tomasz Boczkowski + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include <2geom/path.h> +#include <2geom/transforms.h> + +#include "svg/svg.h" +#include "display/cairo-utils.h" +#include "display/curve.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing.h" +#include "display/drawing-shape.h" +#include "attributes.h" +#include "document-private.h" +#include "uri.h" +#include "style.h" +#include "sp-hatch-path.h" +#include "svg/css-ostringstream.h" +#include "xml/repr.h" + +#include "sp-factory.h" + +namespace { +SPObject* createHatchPath() { + return new SPHatchPath(); +} + +bool hatchRegistered = SPFactory::instance().registerObject("svg:hatchPath", createHatchPath); +} + +SPHatchPath::SPHatchPath() + : _curve(NULL) +{ + offset.unset(); +} + +SPHatchPath::~SPHatchPath() { +} + +void SPHatchPath::setCurve(SPCurve *new_curve, bool owner) { + if (_curve) { + _curve = _curve->unref(); + } + + if (new_curve) { + if (owner) { + _curve = new_curve->ref(); + } else { + _curve = new_curve->copy(); + } + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void SPHatchPath::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPObject::build(doc, repr); + + this->readAttr("d"); + this->readAttr("offset"); + this->readAttr( "style" ); + + style->fill.setNone(); +} + +void SPHatchPath::release() { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + delete iter->arenaitem; + iter->arenaitem = NULL; + } + + SPObject::release(); +} + +void SPHatchPath::set(unsigned int key, const gchar* value) { + switch (key) { + case SP_ATTR_D: + if (value) { + Geom::PathVector pv; + _readHatchPathVector(value, pv, _continuous); + SPCurve *curve = new SPCurve(pv); + + if (curve) { + this->setCurve(curve, true); + curve->unref(); + } + } else { + this->setCurve(NULL, true); + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_OFFSET: + offset.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + default: + if (SP_ATTRIBUTE_IS_CSS(key)) { + sp_style_read_from_object(this->style, this); + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } else { + SPObject::set(key, value); + } + break; + } +} + + +void SPHatchPath::update(SPCtx* ctx, unsigned int flags) { + + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; + } + + if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + if (this->style->stroke_width.unit == SP_CSS_UNIT_PERCENT) { + SPItemCtx *ictx = (SPItemCtx *) ctx; + double const aw = 1.0 / ictx->i2vp.descrim(); + this->style->stroke_width.computed = this->style->stroke_width.value * aw; + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + iter->arenaitem->setStyle(this->style); + } + } + } + + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + _updateView(*iter); + } + } +} + +bool SPHatchPath::isValid() const { + if (_curve && (_repeatLength() <= 0)) { + return false; + } + return true; +} + +Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key) { + Inkscape::DrawingShape *s = new Inkscape::DrawingShape(drawing); + _display.push_front(View(s, key)); + + _updateView(_display.front()); + + return s; +} + +void SPHatchPath::hide(unsigned int key) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + delete iter->arenaitem; + _display.erase(iter); + return; + } + } + + g_assert_not_reached(); +} + +void SPHatchPath::setStripExtents(unsigned int key, Geom::OptInterval const &extents) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + iter->extents = extents; + break; + } + } +} + +gdouble SPHatchPath::_repeatLength() const { + if (!_curve) { + return 0; + } + + if (!_curve->last_point()) { + return 0; + } + + return _curve->last_point()->y(); +} + +void SPHatchPath::_updateView(View &view) { + SPCurve *calculated_curve = new SPCurve; + + if (!view.extents) { + return; + } + + if (!_curve) { + calculated_curve->moveto(0, view.extents->min()); + calculated_curve->lineto(0, view.extents->max()); + //TODO: if hatch has a dasharray defined, adjust line ends + } else { + gdouble repeatLength = _repeatLength(); + if (repeatLength > 0) { + gdouble initial_y = floor(view.extents->min() / repeatLength) * repeatLength; + int segment_cnt = ceil((view.extents->extent()) / repeatLength) + 1; + + SPCurve *segment =_curve->copy(); + segment->transform(Geom::Translate(0, initial_y)); + + Geom::Affine step_transform = Geom::Translate(0, repeatLength); + for (int i = 0; i < segment_cnt; i++) { + if (_continuous) { + calculated_curve->append_continuous(segment, 0.0625); + } else { + calculated_curve->append(segment, false); + } + segment->transform(step_transform); + } + + segment->unref(); + } + } + + Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); + view.arenaitem->setTransform(offset_transform); + style->fill.setNone(); + view.arenaitem->setStyle(this->style); + view.arenaitem->setPath(calculated_curve); + + calculated_curve->unref(); +} + +void SPHatchPath::_readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join) { + if (!str) { + return; + } + + pathv = sp_svg_read_pathv(str); + + if (!pathv.empty()) { + continous_join = false; + } else { + Glib::ustring str2 = Glib::ustring::compose("M0,0 %1", str); + pathv = sp_svg_read_pathv(str2.c_str()); + if (pathv.empty()) { + return; + } + + gdouble last_point_x = pathv.back().finalPoint().x(); + Inkscape::CSSOStringStream stream; + stream << last_point_x; + Glib::ustring str3 = Glib::ustring::compose("M%1,0 %2", stream.str(), str); + Geom::PathVector pathv3 = sp_svg_read_pathv(str3.c_str()); + + //Path can be composed of relative commands only. In this case final point + //coordinates would depend on first point position. If this happens, fall + //back to using 0,0 as first path point + if (pathv3.back().finalPoint().y() == pathv.back().finalPoint().y()) { + pathv = pathv3; + } + continous_join = true; + } +} + +SPHatchPath::View::View(Inkscape::DrawingShape *arenaitem, int key) + : arenaitem(arenaitem), key(key) +{ +} + + +/* + 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-hatch-path.h b/src/sp-hatch-path.h new file mode 100644 index 000000000..ff091c6f9 --- /dev/null +++ b/src/sp-hatch-path.h @@ -0,0 +1,86 @@ +/** @file + * SVG implementation + *//* + * Author: + * Tomasz Boczkowski + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SP_HATCH_PATH_H +#define SEEN_SP_HATCH_PATH_H + +#include +#include +#include +#include + +#include "svg/svg-length.h" + +namespace Inkscape { + +class Drawing; +class DrawingShape; + +} + +#define SP_HATCH_PATH(obj) (dynamic_cast((SPObject*)obj)) +#define SP_IS_HATCH_PATH(obj) (dynamic_cast((SPObject*)obj) != NULL) + +class SPHatchPath : public SPObject { +public: + SPHatchPath(); + virtual ~SPHatchPath(); + + SVGLength offset; + + void setCurve(SPCurve *curve, bool owner); + + bool isValid() const; + + Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key); + void hide(unsigned int key); + + void setStripExtents(unsigned int key, Geom::OptInterval const &extents); + +protected: + virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); + virtual void release(); + virtual void set(unsigned int key, const gchar* value); + virtual void update(SPCtx* ctx, unsigned int flags); + +private: + struct View { + View(Inkscape::DrawingShape *arenaitem, int key); + //Do not delete arenaitem in destructor. + + Inkscape::DrawingShape *arenaitem; + Geom::OptInterval extents; + unsigned int key; + }; + typedef std::list::iterator ViewIterator; + std::list _display; + + gdouble _repeatLength() const; + void _updateView(View &view); + + void _readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join); + + SPCurve *_curve; + bool _continuous; +}; + +#endif // SEEN_SP_HATCH_PATH_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/sp-hatch.cpp b/src/sp-hatch.cpp new file mode 100644 index 000000000..cde6c86cb --- /dev/null +++ b/src/sp-hatch.cpp @@ -0,0 +1,616 @@ +/** @file + * SVG implementation + *//* + * Author: + * Tomasz Boczkowski + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include <2geom/transforms.h> +#include + +#include "svg/svg.h" +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing.h" +#include "display/drawing-pattern.h" +#include "attributes.h" +#include "document-private.h" +#include "uri.h" +#include "style.h" +#include "sp-hatch.h" +#include "sp-hatch-path.h" +#include "xml/repr.h" + +#include "sp-factory.h" + +namespace { +SPObject* createHatch() { + return new SPHatch(); +} + +bool hatchRegistered = SPFactory::instance().registerObject("svg:hatch", createHatch); +} + +SPHatch::SPHatch() + : SPPaintServer() +{ + this->ref = new SPHatchReference(this); + this->ref->changedSignal().connect(sigc::mem_fun(this, &SPHatch::_onRefChanged)); + + this->_hatchUnits = UNITS_OBJECTBOUNDINGBOX; + this->_hatchUnits_set = false; + + this->_hatchContentUnits = UNITS_USERSPACEONUSE; + this->_hatchContentUnits_set = false; + + this->_hatchTransform = Geom::identity(); + this->_hatchTransform_set = false; + + this->_x.unset(); + this->_y.unset(); + this->_pitch.unset(); + this->_rotate.unset(); +} + +SPHatch::~SPHatch() { +} + +void SPHatch::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPPaintServer::build(doc, repr); + + this->readAttr("hatchUnits"); + this->readAttr("hatchContentUnits"); + this->readAttr("hatchTransform"); + this->readAttr("x"); + this->readAttr("y"); + this->readAttr("pitch"); + this->readAttr("rotate"); + this->readAttr("xlink:href"); + + /* Register ourselves */ + doc->addResource("hatch", this); +} + +void SPHatch::release() { + if (this->document) { + // Unregister ourselves + this->document->removeResource("hatch", this); + } + + std::vector children; + _children(children); + for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { + for (ChildIterator child_iter = children.begin(); child_iter != children.end(); + child_iter++) { + SPHatchPath *child = *child_iter; + child->hide(view_iter->key); + } + delete view_iter->arenaitem; + view_iter->arenaitem = NULL; + } + + if (this->ref) { + this->_modified_connection.disconnect(); + this->ref->detach(); + delete this->ref; + this->ref = NULL; + } + + SPPaintServer::release(); +} + +void SPHatch::child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref) { + SPObject::child_added(child, ref); + + SPHatchPath *path_child = SP_HATCH_PATH(this->document->getObjectByRepr(child)); + + if (path_child) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + Inkscape::DrawingItem *ac = path_child->show(iter->arenaitem->drawing(), iter->key); + + Geom::OptInterval strip_extents = _calculateStripExtents(iter->bbox); + path_child->setStripExtents(iter->key, strip_extents); + path_child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (ac) { + iter->arenaitem->prependChild(ac); + } + } + } + //FIXME: notify all hatches that refer to this child set +} + +void SPHatch::set(unsigned int key, const gchar* value) { + switch (key) { + case SP_ATTR_HATCHUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_hatchUnits = UNITS_USERSPACEONUSE; + } else { + this->_hatchUnits = UNITS_OBJECTBOUNDINGBOX; + } + + this->_hatchUnits_set = true; + } else { + this->_hatchUnits_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HATCHCONTENTUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_hatchContentUnits = UNITS_USERSPACEONUSE; + } else { + this->_hatchContentUnits = UNITS_OBJECTBOUNDINGBOX; + } + + this->_hatchContentUnits_set = true; + } else { + this->_hatchContentUnits_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HATCHTRANSFORM: { + Geom::Affine t; + + if (value && sp_svg_transform_read(value, &t)) { + this->_hatchTransform = t; + this->_hatchTransform_set = true; + } else { + this->_hatchTransform = Geom::identity(); + this->_hatchTransform_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + } + case SP_ATTR_X: + this->_x.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_Y: + this->_y.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_PITCH: + this->_pitch.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_ROTATE: + this->_rotate.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_XLINK_HREF: + if (value && this->href == value) { + /* Href unchanged, do nothing. */ + } else { + this->href.clear(); + + if (value) { + // First, set the href field; it's only used in the "unchanged" check above. + this->href = value; + // Now do the attaching, which emits the changed signal. + if (value) { + try { + this->ref->attach(Inkscape::URI(value)); + } catch (Inkscape::BadURIException &e) { + g_warning("%s", e.what()); + this->ref->detach(); + } + } else { + this->ref->detach(); + } + } + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + default: + SPPaintServer::set(key, value); + break; + } +} + +bool SPHatch::_hasHatchPatchChildren(SPHatch const *hatch) { + for (SPObject const *child = hatch->firstChild(); child; child = child->getNext() ) { + if (SP_IS_HATCH_PATH(child)) { + return true; + } + } + return false; +} + +void SPHatch::_children(std::vector& l) { + SPHatch *src = chase_hrefs(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + + if (src) { + for (SPObject *child = src->firstChild(); child; child = child->getNext()) { + if (SP_IS_HATCH_PATH(child)) { + l.push_back(SP_HATCH_PATH(child)); + } + } + } +} + +void SPHatch::_children(std::vector& l) const { + SPHatch const *src = chase_hrefs(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + + if (src) { + for (SPObject const *child = src->firstChild(); child; child = child->getNext()) { + if (SP_IS_HATCH_PATH(child)) { + l.push_back(SP_HATCH_PATH(child)); + } + } + } +} + +/* TODO: ::remove_child and ::order_changed handles - see SPPattern */ + + +void SPHatch::update(SPCtx* ctx, unsigned int flags) { + typedef std::list::iterator ViewIterator; + + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + flags &= SP_OBJECT_MODIFIED_CASCADE; + + std::vector children; + _children(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath* child = *iter; + + sp_object_ref(child, NULL); + + for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { + Geom::OptInterval strip_extents = _calculateStripExtents(view_iter->bbox); + child->setStripExtents(view_iter->key, strip_extents); + } + + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + + child->updateDisplay(ctx, flags); + } + + sp_object_unref(child, NULL); + } + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + _updateView(*iter); + } +} + +void SPHatch::modified(unsigned int flags) { + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + flags &= SP_OBJECT_MODIFIED_CASCADE; + + std::vector children; + _children(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPObject *child = *iter; + + sp_object_ref(child, NULL); + + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + child->emitModified(flags); + } + + sp_object_unref(child, NULL); + } +} + +void SPHatch::_onRefChanged(SPObject *old_ref, SPObject *ref) { + typedef std::list::iterator ViewIterator; + + if (old_ref) { + _modified_connection.disconnect(); + } + + if (SP_IS_HATCH(ref)) { + _modified_connection = ref->connectModified(sigc::mem_fun(this, &SPHatch::_onRefModified)); + } + + if (!_hasHatchPatchChildren(this)) { + SPHatch *old_shown = NULL; + SPHatch *new_shown = NULL; + std::vector old_children; + std::vector new_children; + if (SP_IS_HATCH(old_ref)) { + old_shown = SP_HATCH(old_ref)->rootHatch(); + old_shown->_children(old_children); + } + if (SP_IS_HATCH(ref)) { + new_shown = SP_HATCH(ref)->rootHatch(); + new_shown->_children(new_children); + } + if (old_shown != new_shown) { + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + Geom::OptInterval extents = _calculateStripExtents(iter->bbox); + + for (ChildIterator child_iter = old_children.begin(); child_iter != old_children.end(); child_iter++) { + SPHatchPath *child = *child_iter; + child->hide(iter->key); + } + for (ChildIterator child_iter = new_children.begin(); child_iter != new_children.end(); child_iter++) { + SPHatchPath *child = *child_iter; + Inkscape::DrawingItem *cai = child->show(iter->arenaitem->drawing(), iter->key); + child->setStripExtents(iter->key, extents); + child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (cai) { + iter->arenaitem->appendChild(cai); + } + + } + } + } + } + + _onRefModified(ref, 0); +} + +void SPHatch::_onRefModified(SPObject */*ref*/, guint /*flags*/) { + requestModified(SP_OBJECT_MODIFIED_FLAG); + // Conditional to avoid causing infinite loop if there's a cycle in the href chain. +} + + +SPHatch *SPHatch::rootHatch() { + SPHatch *src = chase_hrefs(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + return src ? src : this; // document is broken, we can't get to root; but at least we can return pat which is supposedly a valid hatch +} + +// Access functions that look up fields up the chain of referenced hatchs and return the first one which is set +// FIXME: all of them must use chase_hrefs as children() and rootHatch() + +SPHatch::HatchUnits SPHatch::hatchUnits() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchUnits_set) + return pat_i->_hatchUnits; + } + return _hatchUnits; +} + +SPHatch::HatchUnits SPHatch::hatchContentUnits() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchContentUnits_set) + return pat_i->_hatchContentUnits; + } + return _hatchContentUnits; +} + +Geom::Affine const &SPHatch::hatchTransform() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchTransform_set) + return pat_i->_hatchTransform; + } + return _hatchTransform; +} + +gdouble SPHatch::x() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_x._set) + return pat_i->_x.computed; + } + return 0; +} + +gdouble SPHatch::y() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_y._set) + return pat_i->_y.computed; + } + return 0; +} + +gdouble SPHatch::pitch() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_pitch._set) + return pat_i->_pitch.computed; + } + return 0; +} + +gdouble SPHatch::rotate() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_rotate._set) + return pat_i->_rotate.computed; + } + return 0; +} + +bool SPHatch::isValid() const { + double strip_pitch = pitch(); + if (strip_pitch <= 0) { + return false; + } + + std::vector children; + _children(children); + if (children.empty()) { + return false; + } + for (ConstChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath const *child = *iter; + if (!child->isValid()) { + return false; + } + } + + return true; +} + +Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int key) { + Inkscape::DrawingPattern *ai = new Inkscape::DrawingPattern(drawing); + //TODO: set some debug flag to see DrawingPattern + _display.push_front(View(ai, key)); + + std::vector children; + _children(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *child = *iter; + Inkscape::DrawingItem *cai = child->show(drawing, key); + if (cai) { + ai->appendChild(cai); + } + } + + View& view = _display.front(); + _updateView(view); + + return ai; +} + +void SPHatch::hide(unsigned int key) { + std::vector children; + _children(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *child = *iter; + child->hide(key); + } + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + delete iter->arenaitem; + _display.erase(iter); + return; + } + } + + g_assert_not_reached(); +} + +void SPHatch::_updateView(View &view) { + Geom::OptInterval extents = _calculateStripExtents(view.bbox); + if (!extents) { + return; + } + + double tile_x = x(); + double tile_y = y(); + double tile_width = pitch(); + double tile_height = extents->max() - extents->min(); + double tile_rotate = rotate(); + double tile_render_y = extents->min(); + + if (view.bbox && (hatchUnits() == UNITS_OBJECTBOUNDINGBOX)) { + tile_x *= view.bbox->width(); + tile_y *= view.bbox->height(); + tile_width *= view.bbox->width(); + tile_height *= view.bbox->height(); + tile_render_y *= view.bbox->height(); + } + + // Pattern size in hatch space + Geom::Rect hatch_tile = Geom::Rect::from_xywh(0, tile_render_y, tile_width, tile_height); + // Content to bbox + Geom::Affine content2ps; + if (view.bbox && (hatchContentUnits() == UNITS_OBJECTBOUNDINGBOX)) { + content2ps = Geom::Affine(view.bbox->width(), 0.0, 0.0, view.bbox->height(), 0, 0); + } + + // Tile (hatch space) to user. + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); + + view.arenaitem->setChildTransform(content2ps); + view.arenaitem->setPatternToUserTransform(ps2user); + view.arenaitem->setTileRect(hatch_tile); + view.arenaitem->setStyle(this->style); +} + +//calculates strip extents in content space +Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) { + if (!bbox || (bbox->area() == 0)) { + return Geom::OptInterval(); + } + + double tile_x = x(); + double tile_y = y(); + double tile_rotate = rotate(); + + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); + Geom::Affine user2ps = ps2user.inverse(); + + Geom::Interval extents; + for (int i = 0; i < 4; i++) { + Geom::Point corner = bbox->corner(i); + Geom::Point corner_ps = corner * user2ps; + if (i == 0 || corner_ps.y() < extents.min()) { + extents.setMin(corner_ps.y()); + } + if (i == 0 || corner_ps.y() > extents.max()) { + extents.setMax(corner_ps.y()); + } + } + + if (hatchUnits() == UNITS_OBJECTBOUNDINGBOX) { + extents /= bbox->height(); + } + + return extents; +} + +cairo_pattern_t* SPHatch::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbox, double opacity) { + //this code should not be used + //it is however required by the fact that SPPaintServer::hatch_new is pure virtual + return cairo_pattern_create_rgb(0.5, 0.5, 1.0); +} + +void SPHatch::setBBox(unsigned int key, Geom::OptRect const &bbox) { + typedef std::list::iterator ViewIter; + + for (ViewIter iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + iter->bbox = bbox; + break; + } + } +} + +SPHatch::View::View(Inkscape::DrawingPattern *arenaitem, int key) + : arenaitem(arenaitem), key(key) +{ +} +/* + 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-hatch.h b/src/sp-hatch.h new file mode 100644 index 000000000..170f68a7a --- /dev/null +++ b/src/sp-hatch.h @@ -0,0 +1,158 @@ +/** @file + * SVG implementation + *//* + * Author: + * Tomasz Boczkowski + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SP_HATCH_H +#define SEEN_SP_HATCH_H + +#include +#include +#include +#include + +#include "svg/svg-length.h" +#include "svg/svg-angle.h" +#include "sp-paint-server.h" +#include "uri-references.h" + +class SPHatchReference; +class SPHatchPath; +class SPItem; + +namespace Inkscape { + +class Drawing; +class DrawingPattern; + +namespace XML { + +class Node; + +} +} + +#define SP_HATCH(obj) (dynamic_cast((SPObject*)obj)) +#define SP_IS_HATCH(obj) (dynamic_cast((SPObject*)obj) != NULL) + +class SPHatch : public SPPaintServer { +public: + enum HatchUnits { + UNITS_USERSPACEONUSE, + UNITS_OBJECTBOUNDINGBOX + }; + + SPHatch(); + virtual ~SPHatch(); + + /* Reference (href) */ + Glib::ustring href; + SPHatchReference *ref; + + gdouble x() const; + gdouble y() const; + gdouble pitch() const; + gdouble rotate() const; + HatchUnits hatchUnits() const; + HatchUnits hatchContentUnits() const; + Geom::Affine const &hatchTransform() const; + SPHatch *rootHatch(); //TODO: const + + bool isValid() const; + + Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key); + void hide(unsigned int key); + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); + + void setBBox(unsigned int key, Geom::OptRect const &bbox); + +protected: + virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); + virtual void release(); + virtual void child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref); + virtual void set(unsigned int key, const gchar* value); + virtual void update(SPCtx* ctx, unsigned int flags); + virtual void modified(unsigned int flags); + +private: + struct View { + View(Inkscape::DrawingPattern *arenaitem, int key); + //Do not delete arenaitem in destructor. + + Inkscape::DrawingPattern *arenaitem; + Geom::OptRect bbox; + unsigned int key; + }; + typedef std::vector::iterator ChildIterator; + typedef std::vector::const_iterator ConstChildIterator; + typedef std::list::iterator ViewIterator; + void _updateView(View &view); + + static bool _hasHatchPatchChildren(SPHatch const* hatch); + + void _children(std::vector& l); + void _children(std::vector& l) const; + + Geom::OptInterval _calculateStripExtents(Geom::OptRect bbox); + + /** + Gets called when the hatch is reattached to another + */ + void _onRefChanged(SPObject *old_ref, SPObject *ref); + + /** + Gets called when the referenced is changed + */ + void _onRefModified(SPObject *ref, guint flags); + + /* patternUnits and patternContentUnits attribute */ + HatchUnits _hatchUnits : 1; + bool _hatchUnits_set : 1; + HatchUnits _hatchContentUnits : 1; + bool _hatchContentUnits_set : 1; + /* hatchTransform attribute */ + Geom::Affine _hatchTransform; + bool _hatchTransform_set : 1; + /* Strip */ + SVGLength _x; + SVGLength _y; + SVGLength _pitch; + SVGAngle _rotate; + + sigc::connection _modified_connection; + + std::list _display; +}; + + +class SPHatchReference : public Inkscape::URIReference { +public: + SPHatchReference (SPObject *obj) : URIReference(obj) {} + SPHatch *getObject() const { + return reinterpret_cast(URIReference::getObject()); + } + +protected: + virtual bool _acceptObject(SPObject *obj) const { + return SP_IS_HATCH (obj); + } +}; + +#endif // SEEN_SP_HATCH_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/sp-item.cpp b/src/sp-item.cpp index edf758e7d..da6e6ea58 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -445,14 +445,14 @@ void SPItem::release() { SPObject::release(); - SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); - SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); + SPPaintServer *fill_ps = style->getFillPaintServer(); + SPPaintServer *stroke_ps = style->getStrokePaintServer(); while (item->display) { - if (fill_pattern) { - fill_pattern->hide(item->display->arenaitem->key()); + if (fill_ps) { + fill_ps->hide(item->display->arenaitem->key()); } - if (stroke_pattern) { - stroke_pattern->hide(item->display->arenaitem->key()); + if (stroke_ps) { + stroke_ps->hide(item->display->arenaitem->key()); } item->display = sp_item_view_list_remove(item->display, item->display); } @@ -600,25 +600,27 @@ void SPItem::mask_ref_changed(SPObject *old_mask, SPObject *mask, SPItem *item) } void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { - SPPattern *old_fill_pattern = SP_PATTERN(old_ps); - if (old_fill_pattern) { + SPPaintServer *old_fill_ps = SP_PAINT_SERVER(old_ps); + if (old_fill_ps) { for (SPItemView *v =item->display; v != NULL; v = v->next) { - old_fill_pattern->hide(v->arenaitem->key()); + old_fill_ps->hide(v->arenaitem->key()); } } - SPPattern *new_fill_pattern = SP_PATTERN(ps); - if (new_fill_pattern) { + SPPaintServer *new_fill_ps = SP_PAINT_SERVER(ps); + if (new_fill_ps) { Geom::OptRect bbox = item->geometricBounds(); for (SPItemView *v = item->display; v != NULL; v = v->next) { if (!v->arenaitem->key()) { v->arenaitem->setKey(SPItem::display_key_new(3)); } - Inkscape::DrawingPattern *pi = new_fill_pattern->show( + Inkscape::DrawingPattern *pi = new_fill_ps->show( v->arenaitem->drawing(), v->arenaitem->key()); v->arenaitem->setFillPattern(pi); - new_fill_pattern->setBBox(v->arenaitem->key(), bbox); - new_fill_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + new_fill_ps->setBBox(v->arenaitem->key(), bbox); + if (pi) { + new_fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } } } } @@ -1133,29 +1135,33 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } - SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); - if (fill_pattern) { + SPPaintServer *fill_ps = style->getFillPaintServer(); + if (fill_ps) { if (!display->arenaitem->key()) { display->arenaitem->setKey(display_key_new(3)); } int fill_key = display->arenaitem->key(); - Inkscape::DrawingPattern *ac = fill_pattern->show(drawing, fill_key); - ai->setFillPattern(ac); - fill_pattern->setBBox(fill_key, item_bbox); - fill_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + Inkscape::DrawingPattern *ap = fill_ps->show(drawing, fill_key); + ai->setFillPattern(ap); + fill_ps->setBBox(fill_key, item_bbox); + if (ap) { + fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } } - SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); - if (stroke_pattern) { + SPPaintServer *stroke_ps = style->getStrokePaintServer(); + if (stroke_ps) { if (!display->arenaitem->key()) { display->arenaitem->setKey(display_key_new(3)); } int stroke_key = display->arenaitem->key(); - Inkscape::DrawingPattern *ac = stroke_pattern->show(drawing, stroke_key); - ai->setStrokePattern(ac); - stroke_pattern->setBBox(stroke_key, item_bbox); - stroke_pattern->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + Inkscape::DrawingPattern *ap = stroke_ps->show(drawing, stroke_key); + ai->setStrokePattern(ap); + stroke_ps->setBBox(stroke_key, item_bbox); + if (ap) { + stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } } ai->setData(this); ai->setItemBounds(geometricBounds()); @@ -1186,13 +1192,13 @@ void SPItem::invoke_hide(unsigned key) mask_ref->getObject()->sp_mask_hide(v->arenaitem->key()); v->arenaitem->setMask(NULL); } - SPPattern *fill_pattern = SP_PATTERN(style->getFillPaintServer()); - if (fill_pattern) { - fill_pattern->hide(v->arenaitem->key()); + SPPaintServer *fill_ps = style->getFillPaintServer(); + if (fill_ps) { + fill_ps->hide(v->arenaitem->key()); } - SPPattern *stroke_pattern = SP_PATTERN(style->getStrokePaintServer()); - if (stroke_pattern) { - stroke_pattern->hide(v->arenaitem->key()); + SPPaintServer *stroke_ps = style->getStrokePaintServer(); + if (stroke_ps) { + stroke_ps->hide(v->arenaitem->key()); } if (!ref) { display = v->next; diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index c1c8d651e..91d292424 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -17,8 +17,16 @@ #include #include <2geom/rect.h> +#include #include "sp-object.h" +namespace Inkscape { + +class Drawing; +class DrawingPattern; + +} + #define SP_PAINT_SERVER(obj) (dynamic_cast((SPObject*)obj)) #define SP_IS_PAINT_SERVER(obj) (dynamic_cast((SPObject*)obj) != NULL) @@ -31,12 +39,59 @@ public: bool isSolid() const; virtual bool isValid() const; - virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity) = 0; + //There are two ways to render a paint. The simple one is to create cairo_pattern_t structure + //on demand by pattern_new method. It is used for gradients. The other one is to add elements + //representing PaintServer in NR tree. It is used by hatches and patterns. + //Either pattern new or all three methods show, hide, setBBox need to be implemented + virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key) {return NULL;} + virtual void hide(unsigned int key) {}; + virtual void setBBox(unsigned int key, Geom::OptRect const &bbox) {}; + + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity) {return NULL;} protected: bool swatch; }; +/** + * Returns the first of {src, src-\>ref-\>getObject(), + * src-\>ref-\>getObject()-\>ref-\>getObject(),...} + * for which \a match is true, or NULL if none found. + * + * The raison d'être of this routine is that it correctly handles cycles in the href chain (e.g., if + * a gradient gives itself as its href, or if each of two gradients gives the other as its href). + * + * \pre SP_IS_GRADIENT(src). + */ +template +PaintServer *chase_hrefs(PaintServer *src, sigc::slot match) { + /* Use a pair of pointers for detecting loops: p1 advances half as fast as p2. If there is a + loop, then once p1 has entered the loop, we'll detect it the next time the distance between + p1 and p2 is a multiple of the loop size. */ + PaintServer *p1 = src, *p2 = src; + bool do1 = false; + for (;;) { + if (match(p2)) { + return p2; + } + + p2 = p2->ref->getObject(); + if (!p2) { + return p2; + } + if (do1) { + p1 = p1->ref->getObject(); + } + do1 = !do1; + + if ( p2 == p1 ) { + /* We've been here before, so return NULL to indicate that no matching gradient found + * in the chain. */ + return NULL; + } + } +} + #endif // SEEN_SP_PAINT_SERVER_H /* Local Variables: diff --git a/src/style-internal.h b/src/style-internal.h index 69784199c..0b2b40001 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -505,8 +505,7 @@ class SPIPaint : public SPIBase { void setColor( float r, float g, float b ) {value.color.set( r, g, b ); colorSet = true;} void setColor( guint32 val ) {value.color.set( val ); colorSet = true;} void setColor( SPColor const& color ) {value.color = color; colorSet = true;} - - + void setNone() {noneSet = true; colorSet=false;} // To do: make private public: diff --git a/src/svg/CMakeLists.txt b/src/svg/CMakeLists.txt index 968287895..f9d0bc52d 100644 --- a/src/svg/CMakeLists.txt +++ b/src/svg/CMakeLists.txt @@ -7,6 +7,7 @@ set(svg_SRC strip-trailing-zeros.cpp svg-affine.cpp svg-color.cpp + svg-angle.cpp svg-length.cpp svg-path.cpp # test-stubs.cpp @@ -24,6 +25,7 @@ set(svg_SRC svg-color-test.h svg-color.h svg-icc-color.h + svg-angle.h svg-length-test.h svg-length.h svg-path-geom-test.h diff --git a/src/svg/Makefile_insert b/src/svg/Makefile_insert index cf9bf3fbb..4f82bdd76 100644 --- a/src/svg/Makefile_insert +++ b/src/svg/Makefile_insert @@ -13,6 +13,8 @@ ink_common_sources += \ svg/svg-color.cpp \ svg/svg-color.h \ svg/svg-icc-color.h \ + svg/svg-angle.cpp \ + svg/svg-angle.h \ svg/svg-length.cpp \ svg/svg-length.h \ svg/svg-path.cpp \ diff --git a/src/svg/svg-angle.cpp b/src/svg/svg-angle.cpp new file mode 100644 index 000000000..63152368e --- /dev/null +++ b/src/svg/svg-angle.cpp @@ -0,0 +1,136 @@ +/** + * \file src/svg/svg-angle.cpp + * \brief SVG angle type + */ +/* + * Authors: + * Tomasz Boczkowski + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "svg.h" +#include "stringstream.h" +#include "svg/svg-angle.h" +#include "util/units.h" + + +static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed); + +SVGAngle::SVGAngle() + : _set(false) + , unit(NONE) + , value(0) + , computed(0) +{ +} + +/* Angle */ + +bool SVGAngle::read(gchar const *str) +{ + if (!str) { + return false; + } + + SVGAngle::Unit u; + float v; + float c; + if (!sp_svg_angle_read_lff(str, u, v, c)) { + return false; + } + + _set = true; + unit = u; + value = v; + computed = c; + + return true; +} + +void SVGAngle::unset(SVGAngle::Unit u, float v, float c) { + _set = false; + unit = u; + value = v; + computed = c; +} + +void SVGAngle::readOrUnset(gchar const *str, Unit u, float v, float c) { + if (!read(str)) { + unset(u, v, c); + } +} + +static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed) +{ + if (!str) { + return false; + } + + gchar const *e; + float const v = g_ascii_strtod(str, (char **) &e); + if (e == str) { + return false; + } + + if (!e[0]) { + /* Unitless (defaults to degrees)*/ + unit = SVGAngle::NONE; + val = v; + computed = v; + return true; + } else if (!g_ascii_isalnum(e[0])) { + if (g_ascii_isspace(e[0]) && e[1] && g_ascii_isalpha(e[1])) { + return false; // spaces between value and unit are not allowed + } else { + /* Unitless (defaults to degrees)*/ + unit = SVGAngle::NONE; + val = v; + computed = v; + return true; + } + } else { + if (strncmp(e, "deg", 3) == 0) { + unit = SVGAngle::DEG; + computed = v; + } else if (strncmp(e, "grad", 4) == 0) { + unit = SVGAngle::GRAD; + computed = Inkscape::Util::Quantity::convert(v, "grad", "°"); + } else if (strncmp(e, "rad", 3) == 0) { + unit = SVGAngle::RAD; + computed = Inkscape::Util::Quantity::convert(v, "rad", "°"); + } else if (strncmp(e, "turn", 4) == 0) { + unit = SVGAngle::TURN; + computed = Inkscape::Util::Quantity::convert(v, "turn", "°"); + } else { + return false; + } + return true; + } + + /* Invalid */ + return false; +} + +/* + 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/svg/svg-angle.h b/src/svg/svg-angle.h new file mode 100644 index 000000000..966491174 --- /dev/null +++ b/src/svg/svg-angle.h @@ -0,0 +1,69 @@ +#ifndef SEEN_SP_SVG_ANGLE_H +#define SEEN_SP_SVG_ANGLE_H + +/** + * \file src/svg/svg-angle.h + * \brief SVG angle type + */ +/* + * Authors: + * Tomasz Boczkowski + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +class SVGAngle +{ +public: + SVGAngle(); + + enum Unit { + NONE, + DEG, + GRAD, + RAD, + TURN, + LAST_UNIT = TURN + }; + + // The object's value is valid / exists in SVG. + bool _set; + + // The unit of value. + Unit unit; + + // The value of this SVGAngle as found in the SVG. + float value; + + // The value in degrees. + float computed; + + float operator=(float v) { + _set = true; + unit = NONE; + value = computed = v; + return v; + } + + bool read(gchar const *str); + void unset(Unit u = NONE, float v = 0, float c = 0); + void readOrUnset(gchar const *str, Unit u = NONE, float v = 0, float c = 0); +}; + +#endif // SEEN_SP_SVG_ANGLE_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 : -- cgit v1.2.3 From bf11ecdfe29082801833fa565f0a1710f6fcd281 Mon Sep 17 00:00:00 2001 From: Tomasz Boczkowski Date: Tue, 14 Oct 2014 13:30:31 +0200 Subject: Merged hatch pdf and png export code (bzr r13611.1.9) --- src/display/drawing-pattern.cpp | 1 - src/display/drawing-pattern.h | 1 - src/extension/internal/cairo-render-context.cpp | 116 ++++++++++++++++++---- src/extension/internal/cairo-render-context.h | 1 + src/extension/internal/cairo-renderer.cpp | 16 +++ src/extension/internal/cairo-renderer.h | 2 + src/sp-hatch-path.cpp | 61 ++++++++++-- src/sp-hatch-path.h | 7 +- src/sp-hatch.cpp | 124 ++++++++++++++++++------ src/sp-hatch.h | 26 ++++- src/sp-item.cpp | 32 ++++-- src/sp-paint-server.h | 2 +- 12 files changed, 315 insertions(+), 74 deletions(-) (limited to 'src') diff --git a/src/display/drawing-pattern.cpp b/src/display/drawing-pattern.cpp index 77752bce3..cf6358278 100644 --- a/src/display/drawing-pattern.cpp +++ b/src/display/drawing-pattern.cpp @@ -159,7 +159,6 @@ DrawingPattern::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, } Geom::Rect pattern_tile = *_tile_rect; - Geom::Coord det_ctm = ctx.ctm.descrim(); Geom::Coord det_ps2user = _pattern_to_user ? _pattern_to_user->descrim() : 1.0; Geom::Coord det_child_transform = _child_transform ? _child_transform->descrim() : 1.0; diff --git a/src/display/drawing-pattern.h b/src/display/drawing-pattern.h index b614785dc..7483ba067 100644 --- a/src/display/drawing-pattern.h +++ b/src/display/drawing-pattern.h @@ -59,7 +59,6 @@ public: protected: virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset); - Geom::IntPoint _patternDimentions(); Geom::Affine *_pattern_to_user; Geom::Affine _overflow_initial_transform; diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index c09b8e9c8..7e61cdbbb 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -39,6 +39,7 @@ #include "sp-item.h" #include "sp-item-group.h" #include "style.h" +#include "sp-hatch.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" #include "sp-pattern.h" @@ -1129,6 +1130,82 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver return result; } +cairo_pattern_t* +CairoRenderContext::_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox) { + g_assert( SP_IS_HATCH(paintserver) ); + SPHatch *hatch = SP_HATCH(paintserver); + + g_assert(hatch->pitch() > 0); + + // create drawing and group + Inkscape::Drawing drawing; + unsigned dkey = SPItem::display_key_new(1); + + hatch->show(drawing, dkey, pbox); + + SPHatch::RenderInfo render_info = hatch->calculateRenderInfo(dkey); + Geom::Rect tile_rect = render_info.tile_rect; + + // Cairo requires an integer pattern surface width/height. + // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel. + // Multiply by SUBPIX_SCALE to allow for less than a pixel precision + const int subpix_scale = 10; + double surface_width = MAX(ceil(subpix_scale * tile_rect.width() - 0.5), 1); + double surface_height = MAX(ceil(subpix_scale * tile_rect.height() - 0.5), 1); + Geom::Affine drawing_scale = Geom::Scale(surface_width / tile_rect.width(), surface_height / tile_rect.height()); + Geom::Affine drawing_transform = Geom::Translate(-tile_rect.min()) * drawing_scale; + + Geom::Affine child_transform = render_info.child_transform; + child_transform *= drawing_transform; + + //The rendering of hatch overflow is implemented by repeated drawing + //of hatch paths over one strip. Within each iteration paths are moved by pitch value. + //The movement progresses from right to left. This gives the same result + //as drawing whole strips in left-to-right order. + gdouble overflow_right_strip = 0.0; + int overflow_steps = 1; + Geom::Affine overflow_transform; + if (hatch->style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) { + Geom::Interval bounds = hatch->bounds(); + overflow_right_strip = floor(bounds.max() / hatch->pitch()) * hatch->pitch(); + overflow_steps = ceil((overflow_right_strip - bounds.min()) / hatch->pitch()) + 1; + overflow_transform = Geom::Translate(hatch->pitch(), 0.0); + } + + CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height); + pattern_ctx->setTransform(child_transform); + pattern_ctx->transform(Geom::Translate(-overflow_right_strip, 0.0)); + pattern_ctx->pushState(); + + std::vector children; + hatch->hatchPaths(children); + + for (int i = 0; i < overflow_steps; i++) { + for (std::vector::iterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *path = *iter; + _renderer->renderHatchPath(pattern_ctx, *path, dkey); + } + pattern_ctx->transform(overflow_transform); + } + + pattern_ctx->popState(); + + // setup a cairo_pattern_t + cairo_surface_t *pattern_surface = pattern_ctx->getSurface(); + TEST(pattern_ctx->saveAsPng("hatch.png")); + cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface); + cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT); + + Geom::Affine pattern_transform; + pattern_transform = render_info.pattern_to_user_transform.inverse() * drawing_transform; + ink_cairo_pattern_set_matrix(result, pattern_transform); + + hatch->hide(dkey); + + delete pattern_ctx; + return result; +} + cairo_pattern_t* CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha) @@ -1182,8 +1259,9 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha); } } else if (SP_IS_PATTERN (paintserver)) { - pattern = _createPatternPainter(paintserver, pbox); + } else if (SP_IS_HATCH (paintserver)) { + pattern = _createHatchPainter(paintserver, pbox); } else { return NULL; } @@ -1249,26 +1327,29 @@ CairoRenderContext::_setFillStyle(SPStyle const *const style, Geom::OptRect cons TRACE(("merged op=%f\n", alpha)); } - if (style->fill.isColor()) { - float rgb[3]; - sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + SPPaintServer const *paint_server = style->getFillPaintServer(); + if (paint_server && paint_server->isValid()) { - cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha); - - } else if (!style->fill.set) { // unset fill is black - cairo_set_source_rgba(_cr, 0, 0, 0, alpha); - - } else { - g_assert( style->fill.isPaintserver() - || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) - || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) ); - - cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha); + g_assert(SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) + || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) + || SP_IS_HATCH(SP_STYLE_FILL_SERVER(style))); + cairo_pattern_t *pattern = _createPatternForPaintServer(paint_server, pbox, alpha); if (pattern) { cairo_set_source(_cr, pattern); cairo_pattern_destroy(pattern); } + } else if (style->fill.colorSet) { + float rgb[3]; + sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + + cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha); + + } else { // unset fill is black + g_assert(!style->fill.set + || (paint_server && !paint_server->isValid())); + + cairo_set_source_rgba(_cr, 0, 0, 0, alpha); } } @@ -1279,7 +1360,7 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p if (_state->merge_opacity) alpha *= _state->opacity; - if (style->stroke.isColor()) { + if (style->stroke.isColor() || (style->stroke.isPaintserver() && !style->getStrokePaintServer()->isValid())) { float rgb[3]; sp_color_get_rgb_floatv(&style->stroke.value.color, rgb); @@ -1287,7 +1368,8 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p } else { g_assert( style->stroke.isPaintserver() || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style)) - || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) ); + || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) + || SP_IS_HATCH(SP_STYLE_STROKE_SERVER(style))); cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha); diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h index 8d3e63775..59781a49c 100644 --- a/src/extension/internal/cairo-render-context.h +++ b/src/extension/internal/cairo-render-context.h @@ -201,6 +201,7 @@ protected: cairo_pattern_t *_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha); cairo_pattern_t *_createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox); + cairo_pattern_t *_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox); unsigned int _showGlyphs(cairo_t *cr, PangoFont *font, std::vector const &glyphtext, bool is_stroke); diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 6fbc85c05..ec302bbfc 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -50,6 +50,7 @@ #include "sp-use.h" #include "sp-text.h" #include "sp-flowtext.h" +#include "sp-hatch-path.h" #include "sp-image.h" #include "sp-symbol.h" #include "sp-pattern.h" @@ -596,6 +597,21 @@ void CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) ctx->popState(); } +void CairoRenderer::renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key) { + ctx->pushState(); + ctx->setStateForStyle(hatchPath.style); + ctx->transform(Geom::Translate(hatchPath.offset.computed, 0)); + + SPCurve *curve = hatchPath.calculateRenderCurve(key); + Geom::PathVector const & pathv =curve->get_pathvector(); + if (!pathv.empty()) { + ctx->renderPathVector(pathv, hatchPath.style, Geom::OptRect()); + } + + curve->unref(); + ctx->popState(); +} + bool CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, float bleedmargin_px, SPItem *base) { diff --git a/src/extension/internal/cairo-renderer.h b/src/extension/internal/cairo-renderer.h index cfef6bdea..abc0447d8 100644 --- a/src/extension/internal/cairo-renderer.h +++ b/src/extension/internal/cairo-renderer.h @@ -29,6 +29,7 @@ class SPClipPath; class SPMask; +class SPHatchPath; namespace Inkscape { namespace Extension { @@ -57,6 +58,7 @@ public: /** Traverses the object tree and invokes the render methods. */ void renderItem(CairoRenderContext *ctx, SPItem *item); + void renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key); }; // FIXME: this should be a static method of CairoRenderer diff --git a/src/sp-hatch-path.cpp b/src/sp-hatch-path.cpp index bdefd32d8..f7138fac2 100644 --- a/src/sp-hatch-path.cpp +++ b/src/sp-hatch-path.cpp @@ -25,6 +25,7 @@ #include "display/drawing-surface.h" #include "display/drawing.h" #include "display/drawing-shape.h" +#include "helper/geom.h" #include "attributes.h" #include "document-private.h" #include "uri.h" @@ -45,6 +46,7 @@ bool hatchRegistered = SPFactory::instance().registerObject("svg:hatchPath", cre SPHatchPath::SPHatchPath() : _curve(NULL) + , _continuous(false) { offset.unset(); } @@ -131,6 +133,8 @@ void SPHatchPath::update(SPCtx* ctx, unsigned int flags) { if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { if (this->style->stroke_width.unit == SP_CSS_UNIT_PERCENT) { + //TODO: Check specification + SPItemCtx *ictx = (SPItemCtx *) ctx; double const aw = 1.0 / ictx->i2vp.descrim(); this->style->stroke_width.computed = this->style->stroke_width.value * aw; @@ -155,9 +159,10 @@ bool SPHatchPath::isValid() const { return true; } -Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key) { +Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents) { Inkscape::DrawingShape *s = new Inkscape::DrawingShape(drawing); _display.push_front(View(s, key)); + _display.front().extents = extents; _updateView(_display.front()); @@ -185,6 +190,36 @@ void SPHatchPath::setStripExtents(unsigned int key, Geom::OptInterval const &ext } } +Geom::Interval SPHatchPath::bounds() const { + Geom::OptRect bbox; + Geom::Interval result; + + Geom::Affine transform = Geom::Translate(offset.computed, 0); + if (!this->_curve) { + SPCurve test_curve; + test_curve.moveto(Geom::Point(0, 0)); + test_curve.moveto(Geom::Point(0, 1)); + bbox = bounds_exact_transformed(test_curve.get_pathvector(), transform); + } else { + bbox = bounds_exact_transformed(this->_curve->get_pathvector(), transform); + } + + gdouble stroke_width = style->stroke_width.computed; + result.setMin(bbox->left() - stroke_width / 2); + result.setMax(bbox->right() + stroke_width / 2); + return result; +} + +SPCurve *SPHatchPath::calculateRenderCurve(unsigned key) const { + for (ConstViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + return _calculateRenderCurve(*iter); + } + } + g_assert_not_reached(); + return NULL; +} + gdouble SPHatchPath::_repeatLength() const { if (!_curve) { return 0; @@ -198,10 +233,22 @@ gdouble SPHatchPath::_repeatLength() const { } void SPHatchPath::_updateView(View &view) { + SPCurve *calculated_curve = _calculateRenderCurve(view); + + Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); + view.arenaitem->setTransform(offset_transform); + style->fill.setNone(); + view.arenaitem->setStyle(this->style); + view.arenaitem->setPath(calculated_curve); + + calculated_curve->unref(); +} + +SPCurve *SPHatchPath::_calculateRenderCurve(View const &view) const { SPCurve *calculated_curve = new SPCurve; if (!view.extents) { - return; + return calculated_curve; } if (!_curve) { @@ -230,16 +277,10 @@ void SPHatchPath::_updateView(View &view) { segment->unref(); } } - - Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); - view.arenaitem->setTransform(offset_transform); - style->fill.setNone(); - view.arenaitem->setStyle(this->style); - view.arenaitem->setPath(calculated_curve); - - calculated_curve->unref(); + return calculated_curve; } + void SPHatchPath::_readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join) { if (!str) { return; diff --git a/src/sp-hatch-path.h b/src/sp-hatch-path.h index ff091c6f9..57b3a8237 100644 --- a/src/sp-hatch-path.h +++ b/src/sp-hatch-path.h @@ -40,10 +40,13 @@ public: bool isValid() const; - Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key); + Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents); void hide(unsigned int key); void setStripExtents(unsigned int key, Geom::OptInterval const &extents); + Geom::Interval bounds() const; + + SPCurve *calculateRenderCurve(unsigned key) const; protected: virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); @@ -61,10 +64,12 @@ private: unsigned int key; }; typedef std::list::iterator ViewIterator; + typedef std::list::const_iterator ConstViewIterator; std::list _display; gdouble _repeatLength() const; void _updateView(View &view); + SPCurve *_calculateRenderCurve(View const &view) const; void _readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join); diff --git a/src/sp-hatch.cpp b/src/sp-hatch.cpp index cde6c86cb..98cc596d7 100644 --- a/src/sp-hatch.cpp +++ b/src/sp-hatch.cpp @@ -77,6 +77,7 @@ void SPHatch::build(SPDocument* doc, Inkscape::XML::Node* repr) { this->readAttr("pitch"); this->readAttr("rotate"); this->readAttr("xlink:href"); + this->readAttr( "style" ); /* Register ourselves */ doc->addResource("hatch", this); @@ -89,7 +90,7 @@ void SPHatch::release() { } std::vector children; - _children(children); + hatchPaths(children); for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { for (ChildIterator child_iter = children.begin(); child_iter != children.end(); child_iter++) { @@ -117,10 +118,9 @@ void SPHatch::child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref) if (path_child) { for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { - Inkscape::DrawingItem *ac = path_child->show(iter->arenaitem->drawing(), iter->key); + Geom::OptInterval extents = _calculateStripExtents(iter->bbox); + Inkscape::DrawingItem *ac = path_child->show(iter->arenaitem->drawing(), iter->key, extents); - Geom::OptInterval strip_extents = _calculateStripExtents(iter->bbox); - path_child->setStripExtents(iter->key, strip_extents); path_child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); if (ac) { iter->arenaitem->prependChild(ac); @@ -224,7 +224,12 @@ void SPHatch::set(unsigned int key, const gchar* value) { break; default: - SPPaintServer::set(key, value); + if (SP_ATTRIBUTE_IS_CSS(key)) { + sp_style_read_from_object(this->style, this); + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } else { + SPPaintServer::set(key, value); + } break; } } @@ -238,7 +243,7 @@ bool SPHatch::_hasHatchPatchChildren(SPHatch const *hatch) { return false; } -void SPHatch::_children(std::vector& l) { +void SPHatch::hatchPaths(std::vector& l) { SPHatch *src = chase_hrefs(this, sigc::ptr_fun(&_hasHatchPatchChildren)); if (src) { @@ -250,7 +255,7 @@ void SPHatch::_children(std::vector& l) { } } -void SPHatch::_children(std::vector& l) const { +void SPHatch::hatchPaths(std::vector& l) const { SPHatch const *src = chase_hrefs(this, sigc::ptr_fun(&_hasHatchPatchChildren)); if (src) { @@ -275,7 +280,7 @@ void SPHatch::update(SPCtx* ctx, unsigned int flags) { flags &= SP_OBJECT_MODIFIED_CASCADE; std::vector children; - _children(children); + hatchPaths(children); for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { SPHatchPath* child = *iter; @@ -308,7 +313,7 @@ void SPHatch::modified(unsigned int flags) { flags &= SP_OBJECT_MODIFIED_CASCADE; std::vector children; - _children(children); + hatchPaths(children); for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { SPObject *child = *iter; @@ -337,29 +342,28 @@ void SPHatch::_onRefChanged(SPObject *old_ref, SPObject *ref) { if (!_hasHatchPatchChildren(this)) { SPHatch *old_shown = NULL; SPHatch *new_shown = NULL; - std::vector old_children; - std::vector new_children; + std::vector oldhatchPaths; + std::vector newhatchPaths; if (SP_IS_HATCH(old_ref)) { old_shown = SP_HATCH(old_ref)->rootHatch(); - old_shown->_children(old_children); + old_shown->hatchPaths(oldhatchPaths); } if (SP_IS_HATCH(ref)) { new_shown = SP_HATCH(ref)->rootHatch(); - new_shown->_children(new_children); + new_shown->hatchPaths(newhatchPaths); } if (old_shown != new_shown) { for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { Geom::OptInterval extents = _calculateStripExtents(iter->bbox); - for (ChildIterator child_iter = old_children.begin(); child_iter != old_children.end(); child_iter++) { + for (ChildIterator child_iter = oldhatchPaths.begin(); child_iter != oldhatchPaths.end(); child_iter++) { SPHatchPath *child = *child_iter; child->hide(iter->key); } - for (ChildIterator child_iter = new_children.begin(); child_iter != new_children.end(); child_iter++) { + for (ChildIterator child_iter = newhatchPaths.begin(); child_iter != newhatchPaths.end(); child_iter++) { SPHatchPath *child = *child_iter; - Inkscape::DrawingItem *cai = child->show(iter->arenaitem->drawing(), iter->key); - child->setStripExtents(iter->key, extents); + Inkscape::DrawingItem *cai = child->show(iter->arenaitem->drawing(), iter->key, extents); child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); if (cai) { iter->arenaitem->appendChild(cai); @@ -457,7 +461,7 @@ bool SPHatch::isValid() const { } std::vector children; - _children(children); + hatchPaths(children); if (children.empty()) { return false; } @@ -471,17 +475,19 @@ bool SPHatch::isValid() const { return true; } -Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int key) { +Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox) { Inkscape::DrawingPattern *ai = new Inkscape::DrawingPattern(drawing); //TODO: set some debug flag to see DrawingPattern _display.push_front(View(ai, key)); + _display.front().bbox = bbox; std::vector children; - _children(children); + hatchPaths(children); + Geom::OptInterval extents = _calculateStripExtents(bbox); for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { SPHatchPath *child = *iter; - Inkscape::DrawingItem *cai = child->show(drawing, key); + Inkscape::DrawingItem *cai = child->show(drawing, key, extents); if (cai) { ai->appendChild(cai); } @@ -495,7 +501,7 @@ Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int void SPHatch::hide(unsigned int key) { std::vector children; - _children(children); + hatchPaths(children); for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { SPHatchPath *child = *iter; @@ -513,10 +519,56 @@ void SPHatch::hide(unsigned int key) { g_assert_not_reached(); } + +Geom::Interval SPHatch::bounds() const { + Geom::Interval result; + std::vector children; + hatchPaths(children); + + for (ConstChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath const *child = *iter; + if (result.extent() == 0) { + result = child->bounds(); + } else { + result |= child->bounds(); + } + } + return result; +} + +SPHatch::RenderInfo SPHatch::calculateRenderInfo(unsigned key) const { + RenderInfo info; + for (ConstViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + return _calculateRenderInfo(*iter); + } + } + g_assert_not_reached(); + return info; +} + void SPHatch::_updateView(View &view) { + RenderInfo info = _calculateRenderInfo(view); + //The rendering of hatch overflow is implemented by repeated drawing + //of hatch paths over one strip. Within each iteration paths are moved by pitch value. + //The movement progresses from right to left. This gives the same result + //as drawing whole strips in left-to-right order. + + + view.arenaitem->setChildTransform(info.child_transform); + view.arenaitem->setPatternToUserTransform(info.pattern_to_user_transform); + view.arenaitem->setTileRect(info.tile_rect); + view.arenaitem->setStyle(this->style); + view.arenaitem->setOverflow(info.overflow_initial_transform, info.overflow_steps, + info.overflow_step_transform); +} + +SPHatch::RenderInfo SPHatch::_calculateRenderInfo(View const &view) const { + RenderInfo info; + Geom::OptInterval extents = _calculateStripExtents(view.bbox); if (!extents) { - return; + return info; } double tile_x = x(); @@ -545,14 +597,26 @@ void SPHatch::_updateView(View &view) { // Tile (hatch space) to user. Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); - view.arenaitem->setChildTransform(content2ps); - view.arenaitem->setPatternToUserTransform(ps2user); - view.arenaitem->setTileRect(hatch_tile); - view.arenaitem->setStyle(this->style); + info.child_transform = content2ps; + info.pattern_to_user_transform = ps2user; + info.tile_rect = hatch_tile; + + if (style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) { + Geom::Interval bounds = this->bounds(); + gdouble pitch = this->pitch(); + gdouble overflow_right_strip = floor(bounds.max() / pitch) * pitch; + info.overflow_steps = ceil((overflow_right_strip - bounds.min()) / pitch) + 1; + info.overflow_step_transform = Geom::Translate(pitch, 0.0); + info.overflow_initial_transform = Geom::Translate(-overflow_right_strip, 0.0); + } else { + info.overflow_steps = 1; + } + + return info; } //calculates strip extents in content space -Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) { +Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) const { if (!bbox || (bbox->area() == 0)) { return Geom::OptInterval(); } @@ -590,9 +654,7 @@ cairo_pattern_t* SPHatch::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbo } void SPHatch::setBBox(unsigned int key, Geom::OptRect const &bbox) { - typedef std::list::iterator ViewIter; - - for (ViewIter iter = _display.begin(); iter != _display.end(); iter++) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { if (iter->key == key) { iter->bbox = bbox; break; diff --git a/src/sp-hatch.h b/src/sp-hatch.h index 170f68a7a..dc6ee0add 100644 --- a/src/sp-hatch.h +++ b/src/sp-hatch.h @@ -48,6 +48,16 @@ public: UNITS_OBJECTBOUNDINGBOX }; + struct RenderInfo { + Geom::Affine child_transform; + Geom::Affine pattern_to_user_transform; + Geom::Rect tile_rect; + + int overflow_steps; + Geom::Affine overflow_step_transform; + Geom::Affine overflow_initial_transform; + }; + SPHatch(); virtual ~SPHatch(); @@ -64,12 +74,17 @@ public: Geom::Affine const &hatchTransform() const; SPHatch *rootHatch(); //TODO: const + void hatchPaths(std::vector& l); + void hatchPaths(std::vector& l) const; + bool isValid() const; - Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key); + Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox); void hide(unsigned int key); virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); + RenderInfo calculateRenderInfo(unsigned key) const; + Geom::Interval bounds() const; void setBBox(unsigned int key, Geom::OptRect const &bbox); protected: @@ -89,17 +104,18 @@ private: Geom::OptRect bbox; unsigned int key; }; + typedef std::vector::iterator ChildIterator; typedef std::vector::const_iterator ConstChildIterator; typedef std::list::iterator ViewIterator; - void _updateView(View &view); + typedef std::list::const_iterator ConstViewIterator; static bool _hasHatchPatchChildren(SPHatch const* hatch); - void _children(std::vector& l); - void _children(std::vector& l) const; + void _updateView(View &view); + RenderInfo _calculateRenderInfo(View const &view) const; + Geom::OptInterval _calculateStripExtents(Geom::OptRect bbox) const; - Geom::OptInterval _calculateStripExtents(Geom::OptRect bbox); /** Gets called when the hatch is reattached to another diff --git a/src/sp-item.cpp b/src/sp-item.cpp index da6e6ea58..f6de165a6 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -615,9 +615,8 @@ void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { v->arenaitem->setKey(SPItem::display_key_new(3)); } Inkscape::DrawingPattern *pi = new_fill_ps->show( - v->arenaitem->drawing(), v->arenaitem->key()); + v->arenaitem->drawing(), v->arenaitem->key(), bbox); v->arenaitem->setFillPattern(pi); - new_fill_ps->setBBox(v->arenaitem->key(), bbox); if (pi) { new_fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } @@ -625,8 +624,29 @@ void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { } } -void SPItem::stroke_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item) { +void SPItem::stroke_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { + SPPaintServer *old_stroke_ps = SP_PAINT_SERVER(old_ps); + if (old_stroke_ps) { + for (SPItemView *v =item->display; v != NULL; v = v->next) { + old_stroke_ps->hide(v->arenaitem->key()); + } + } + SPPaintServer *new_stroke_ps = SP_PAINT_SERVER(ps); + if (new_stroke_ps) { + Geom::OptRect bbox = item->geometricBounds(); + for (SPItemView *v = item->display; v != NULL; v = v->next) { + if (!v->arenaitem->key()) { + v->arenaitem->setKey(SPItem::display_key_new(3)); + } + Inkscape::DrawingPattern *pi = new_stroke_ps->show( + v->arenaitem->drawing(), v->arenaitem->key(), bbox); + v->arenaitem->setStrokePattern(pi); + if (pi) { + new_stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + } } void SPItem::update(SPCtx* /*ctx*/, guint flags) { @@ -1142,9 +1162,8 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned } int fill_key = display->arenaitem->key(); - Inkscape::DrawingPattern *ap = fill_ps->show(drawing, fill_key); + Inkscape::DrawingPattern *ap = fill_ps->show(drawing, fill_key, item_bbox); ai->setFillPattern(ap); - fill_ps->setBBox(fill_key, item_bbox); if (ap) { fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } @@ -1156,9 +1175,8 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned } int stroke_key = display->arenaitem->key(); - Inkscape::DrawingPattern *ap = stroke_ps->show(drawing, stroke_key); + Inkscape::DrawingPattern *ap = stroke_ps->show(drawing, stroke_key, item_bbox); ai->setStrokePattern(ap); - stroke_ps->setBBox(stroke_key, item_bbox); if (ap) { stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index 91d292424..6c88199b9 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -43,7 +43,7 @@ public: //on demand by pattern_new method. It is used for gradients. The other one is to add elements //representing PaintServer in NR tree. It is used by hatches and patterns. //Either pattern new or all three methods show, hide, setBBox need to be implemented - virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key) {return NULL;} + virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox) {return NULL;} virtual void hide(unsigned int key) {}; virtual void setBBox(unsigned int key, Geom::OptRect const &bbox) {}; -- cgit v1.2.3 From f8d50684eddc79a56c79a679b6d0d608b739a0cc Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Tue, 14 Oct 2014 13:49:43 +0200 Subject: Add 'white-space' CSS property (replaces deprecated xml:space). (bzr r13612) --- src/attributes.cpp | 10 +++-- src/attributes.h | 31 ++++++++++---- src/sp-string.cpp | 119 ++++++++++++++++++++++++++++++++++++++++++----------- src/style-enums.h | 17 ++++++++ src/style.cpp | 10 +++++ src/style.h | 40 ++++++++++++------ 6 files changed, 177 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/attributes.cpp b/src/attributes.cpp index fec5d3af4..2474e4abe 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -413,10 +413,6 @@ static SPStyleProp const props[] = { /* Text */ {SP_PROP_TEXT_INDENT, "text-indent"}, {SP_PROP_TEXT_ALIGN, "text-align"}, - {SP_PROP_TEXT_DECORATION, "text-decoration"}, - {SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"}, - {SP_PROP_TEXT_DECORATION_STYLE,"text-decoration-style"}, - {SP_PROP_TEXT_DECORATION_COLOR,"text-decoration-color"}, {SP_PROP_LINE_HEIGHT, "line-height"}, {SP_PROP_LETTER_SPACING, "letter-spacing"}, {SP_PROP_WORD_SPACING, "word-spacing"}, @@ -433,6 +429,12 @@ static SPStyleProp const props[] = { {SP_PROP_GLYPH_ORIENTATION_VERTICAL, "glyph-orientation-vertical"}, {SP_PROP_KERNING, "kerning"}, {SP_PROP_TEXT_ANCHOR, "text-anchor"}, + {SP_PROP_WHITE_SPACE, "white-space"}, + /* Text Decoration */ + {SP_PROP_TEXT_DECORATION, "text-decoration"}, + {SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"}, + {SP_PROP_TEXT_DECORATION_STYLE,"text-decoration-style"}, + {SP_PROP_TEXT_DECORATION_COLOR,"text-decoration-color"}, /* Misc */ {SP_PROP_CLIP, "clip"}, {SP_PROP_COLOR, "color"}, diff --git a/src/attributes.h b/src/attributes.h index 3397e4034..7f18cb5ea 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -399,9 +399,11 @@ enum SPAttributeEnum { SP_ATTR_TEXT_EXCLUDE, SP_ATTR_LAYOUT_OPTIONS, - /* CSS2 */ - /* Custom full font name because Font stuff below is inadequate */ + /* CSS & SVG Properties */ + + /* Custom full font name because Font stuff below is inadequate REMOVE ME */ SP_PROP_INKSCAPE_FONT_SPEC, + /* Font */ SP_PROP_FONT, SP_PROP_FONT_FAMILY, @@ -411,18 +413,16 @@ enum SPAttributeEnum { SP_PROP_FONT_STYLE, SP_PROP_FONT_VARIANT, SP_PROP_FONT_WEIGHT, - /* Text */ + + /* Text Layout */ SP_PROP_TEXT_INDENT, SP_PROP_TEXT_ALIGN, - SP_PROP_TEXT_DECORATION, /* SVG 1 underline etc.( no color or style) OR SVG2 with _LINE, _STYLE, _COLOR values */ - SP_PROP_TEXT_DECORATION_LINE, /* SVG 2 underline etc. */ - SP_PROP_TEXT_DECORATION_STYLE, /* SVG 2 proposed solid [SVG 1], dotted, etc.)*/ - SP_PROP_TEXT_DECORATION_COLOR, /* SVG 2 proposed same as text [SVG 1], specified*/ + SP_PROP_LINE_HEIGHT, SP_PROP_LETTER_SPACING, SP_PROP_WORD_SPACING, SP_PROP_TEXT_TRANSFORM, - /* text (css3) */ + SP_PROP_DIRECTION, SP_PROP_BLOCK_PROGRESSION, SP_PROP_WRITING_MODE, @@ -434,6 +434,14 @@ enum SPAttributeEnum { SP_PROP_GLYPH_ORIENTATION_VERTICAL, SP_PROP_KERNING, SP_PROP_TEXT_ANCHOR, + SP_PROP_WHITE_SPACE, + + /* Text Decoration */ + SP_PROP_TEXT_DECORATION, /* SVG 1 underline etc.( no color or style) OR SVG2 with _LINE, _STYLE, _COLOR values */ + SP_PROP_TEXT_DECORATION_LINE, /* SVG 2 underline etc. */ + SP_PROP_TEXT_DECORATION_STYLE, /* SVG 2 proposed solid [SVG 1], dotted, etc.)*/ + SP_PROP_TEXT_DECORATION_COLOR, /* SVG 2 proposed same as text [SVG 1], specified*/ + /* Misc */ SP_PROP_CLIP, SP_PROP_COLOR, @@ -443,24 +451,29 @@ enum SPAttributeEnum { SP_PROP_VISIBILITY, SP_PROP_MIX_BLEND_MODE, SP_PROP_ISOLATION, + /* SVG */ /* Clip/Mask */ SP_PROP_CLIP_PATH, SP_PROP_CLIP_RULE, SP_PROP_MASK, SP_PROP_OPACITY, + /* Filter */ SP_PROP_ENABLE_BACKGROUND, SP_PROP_FILTER, SP_PROP_FLOOD_COLOR, SP_PROP_FLOOD_OPACITY, SP_PROP_LIGHTING_COLOR, + /* Gradient */ SP_PROP_STOP_COLOR, SP_PROP_STOP_OPACITY, SP_PROP_STOP_PATH, + /* Interactivity */ SP_PROP_POINTER_EVENTS, + /* Paint */ SP_PROP_COLOR_INTERPOLATION, SP_PROP_COLOR_INTERPOLATION_FILTERS, @@ -487,10 +500,12 @@ enum SPAttributeEnum { SP_PROP_STROKE_OPACITY, SP_PROP_STROKE_WIDTH, SP_PROP_TEXT_RENDERING, + /* Conditional */ SP_PROP_SYSTEM_LANGUAGE, SP_PROP_REQUIRED_FEATURES, SP_PROP_REQUIRED_EXTENSIONS, + /* LivePathEffect */ SP_PROP_PATH_EFFECT, }; diff --git a/src/sp-string.cpp b/src/sp-string.cpp index e9dfc168b..b561187d0 100644 --- a/src/sp-string.cpp +++ b/src/sp-string.cpp @@ -30,10 +30,14 @@ #include "sp-string.h" +#include "style.h" + #include "xml/repr.h" #include "sp-factory.h" +#include + namespace { SPObject* createString() { return new SPString(); @@ -65,45 +69,110 @@ void SPString::release() { void SPString::read_content() { - SPString* object = this; + SPString* object = this; SPString *string = SP_STRING(object); string->string.clear(); //XML Tree being used directly here while it shouldn't be. gchar const *xml_string = string->getRepr()->content(); - // see algorithms described in svg 1.1 section 10.15 - if (object->xml_space.value == SP_XML_SPACE_PRESERVE) { - for ( ; *xml_string ; xml_string = g_utf8_next_char(xml_string) ) { - gunichar c = g_utf8_get_char(xml_string); - if ((c == 0xa) || (c == 0xd) || (c == '\t')) { - c = ' '; - } - string->string += c; + + // std::cout << ">" << (xml_string?xml_string:"Null") << "<" << std::endl; + + // SVG2/CSS Text Level 3 'white-space' has five values. + // See: http://dev.w3.org/csswg/css-text/#white-space + // | New Lines | Spaces/Tabs | Text Wrapping + // ---------|------------|--------------|-------------- + // normal | Collapes | Collapse | Wrap + // pre | Preserve | Preserve | No Wrap + // nowrap | Collapse | Collapse | No Wrap + // pre-wrap | Preserve | Preserve | Wrap + // pre-line | Preserve | Collapse | Wrap + + // 'xml:space' has two values: + // 'default' which corresponds to 'normal' (without wrapping). + // 'preserve' which corresponds to 'pre' except new lines are converted to spaces. + // See algorithms described in svg 1.1 section 10.15 + + bool collapse_space = true; + bool collapse_line = true; + bool is_css = false; + + // Strings don't have style, check parent for style + if( object->parent && object->parent->style ) { + if( object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRE || + object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PREWRAP || + object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRELINE ) { + collapse_line = false; + } + if( object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRE || + object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PREWRAP ) { + collapse_space = false; + } + if( object->parent->style->white_space.computed != SP_CSS_WHITE_SPACE_NORMAL ) { + is_css = true; // If white-space not normal, we assume white-space is set. } } - else { - bool whitespace = false; - for ( ; *xml_string ; xml_string = g_utf8_next_char(xml_string) ) { - gunichar c = g_utf8_get_char(xml_string); - if ((c == 0xa) || (c == 0xd)) { + if( !is_css ) { + // SVG 2: Use 'xml:space' only if 'white-space' not 'normal'. + if (object->xml_space.value == SP_XML_SPACE_PRESERVE) { + collapse_space = false; + } + } + + bool white_space = false; + for ( ; *xml_string ; xml_string = g_utf8_next_char(xml_string) ) { + + gunichar c = g_utf8_get_char(xml_string); + switch (c) { + case 0xd: // Carriage return + // XML Parsers convert 0xa, 0xd, 0xD 0xA to 0xA. CSS also follows this rule so we + // should never see 0xd. + std::cerr << "SPString: Carriage Return found! Argh!" << std::endl; continue; - } - if ((c == ' ') || (c == '\t')) { - whitespace = true; - } else { - if (whitespace && (!string->string.empty() || (object->getPrev() != NULL))) { + break; + case 0xa: // Line feed + if( collapse_line ) { + if( !is_css && collapse_space ) continue; // xml:space == 'default' strips LFs. + white_space = true; // Convert to space and collapse + } else { + string->string += c; // Preserve line feed + continue; + } + break; + case '\t': // Tab + if( collapse_space ) { + white_space = true; // Convert to space and collapse + } else { + string->string += c; // Preserve tab + continue; + } + break; + case ' ': // Space + if( collapse_space ) { + white_space = true; // Collapse white space + } else { + string->string += c; // Preserve space + continue; + } + break; + default: + if( white_space && (!string->string.empty() || (object->getPrev() != NULL))) { string->string += ' '; } string->string += c; - whitespace = false; - } - } - if (whitespace && object->getRepr()->next() != NULL) { // can't use SPObject::getNext() when the SPObject tree is still being built - string->string += ' '; - } + white_space = false; + + } // End switch + } // End loop + + // Insert white space at end if more text follows + if (white_space && object->getRepr()->next() != NULL) { // can't use SPObject::getNext() when the SPObject tree is still being built + string->string += ' '; } + + // std::cout << ">" << string->string << "<" << std::endl; object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } diff --git a/src/style-enums.h b/src/style-enums.h index 356029a40..024943458 100644 --- a/src/style-enums.h +++ b/src/style-enums.h @@ -113,6 +113,14 @@ enum SPTextAnchor { SP_CSS_TEXT_ANCHOR_END }; +enum SPWhiteSpace { + SP_CSS_WHITE_SPACE_NORMAL, + SP_CSS_WHITE_SPACE_PRE, + SP_CSS_WHITE_SPACE_NOWRAP, + SP_CSS_WHITE_SPACE_PREWRAP, + SP_CSS_WHITE_SPACE_PRELINE +}; + enum SPCSSBaselineShift { SP_CSS_BASELINE_SHIFT_BASELINE, SP_CSS_BASELINE_SHIFT_SUB, @@ -326,6 +334,15 @@ static SPStyleEnum const enum_text_anchor[] = { {NULL, -1} }; +static SPStyleEnum const enum_white_space[] = { + {"normal", SP_CSS_WHITE_SPACE_NORMAL }, + {"pre", SP_CSS_WHITE_SPACE_PRE }, + {"nowrap", SP_CSS_WHITE_SPACE_NOWRAP }, + {"pre-wrap", SP_CSS_WHITE_SPACE_PREWRAP}, + {"pre-line", SP_CSS_WHITE_SPACE_PRELINE}, + {NULL, -1} +}; + static SPStyleEnum const enum_direction[] = { {"ltr", SP_CSS_DIRECTION_LTR}, {"rtl", SP_CSS_DIRECTION_RTL}, diff --git a/src/style.cpp b/src/style.cpp index abc928d76..a91611d89 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -135,6 +135,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : writing_mode( "writing-mode", enum_writing_mode, SP_CSS_WRITING_MODE_LR_TB ), baseline_shift(), text_anchor( "text-anchor", enum_text_anchor, SP_CSS_TEXT_ANCHOR_START ), + white_space( "white-space", enum_white_space, SP_CSS_WHITE_SPACE_NORMAL ), // General visual properties clip_rule( "clip-rule", enum_clip_rule, SP_WIND_RULE_NONZERO ), @@ -297,6 +298,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : _properties.push_back( &writing_mode ); _properties.push_back( &baseline_shift ); _properties.push_back( &text_anchor ); + _properties.push_back( &white_space ); _properties.push_back( &clip_rule ); _properties.push_back( &display ); @@ -379,6 +381,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : // _propmap.insert( std::make_pair( writing_mode.name, reinterpret_cast(&SPStyle::writing_mode ) ) ); // _propmap.insert( std::make_pair( baseline_shift.name, reinterpret_cast(&SPStyle::baseline_shift ) ) ); // _propmap.insert( std::make_pair( text_anchor.name, reinterpret_cast(&SPStyle::text_anchor ) ) ); + // _propmap.insert( std::make_pair( white_space.name, reinterpret_cast(&SPStyle::white_space ) ) ); // _propmap.insert( std::make_pair( clip_rule.name, reinterpret_cast(&SPStyle::clip_rule ) ) ); // _propmap.insert( std::make_pair( display.name, reinterpret_cast(&SPStyle::display ) ) ); @@ -670,6 +673,9 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { case SP_PROP_TEXT_ANCHOR: text_anchor.readIfUnset( val ); break; + case SP_PROP_WHITE_SPACE: + white_space.readIfUnset( val ); + break; case SP_PROP_BASELINE_SHIFT: baseline_shift.readIfUnset( val ); break; @@ -1623,6 +1629,9 @@ sp_style_unset_property_attrs(SPObject *o) if (style->text_anchor.set) { repr->setAttribute("text-anchor", NULL); } + if (style->white_space.set) { + repr->setAttribute("white_space", NULL); + } if (style->writing_mode.set) { repr->setAttribute("writing_mode", NULL); } @@ -1712,6 +1721,7 @@ sp_css_attr_unset_text(SPCSSAttr *css) sp_repr_css_set_property(css, "block-progression", NULL); sp_repr_css_set_property(css, "writing-mode", NULL); sp_repr_css_set_property(css, "text-anchor", NULL); + sp_repr_css_set_property(css, "white_space", NULL); sp_repr_css_set_property(css, "kerning", NULL); // not implemented yet sp_repr_css_set_property(css, "dominant-baseline", NULL); // not implemented yet sp_repr_css_set_property(css, "alignment-baseline", NULL); // not implemented yet diff --git a/src/style.h b/src/style.h index 3627b4ec2..01a9d4b84 100644 --- a/src/style.h +++ b/src/style.h @@ -92,8 +92,10 @@ private: public: /* ----------------------- THE PROPERTIES ------------------------- */ + /* Match order in style.cpp. */ + + /* Font ---------------------------- */ - /* Font */ /** Font style */ SPIEnum font_style; /** Which substyle of the font */ @@ -113,23 +115,13 @@ public: /** Full font name, as font_factory::ConstructFontSpecification would give, for internal use. */ SPIString font_specification; + /* Text ----------------------------- */ + /** First line indent of paragraphs (css2 16.1) */ SPILength text_indent; /** text alignment (css2 16.2) (not to be confused with text-anchor) */ SPIEnum text_align; - /** text decoration (css2 16.3.1) */ - SPITextDecoration text_decoration; - /** CSS 3 2.1, 2.2, 2.3 */ - /** Not done yet, test_decoration3 = css3 2.4*/ - SPITextDecorationLine text_decoration_line; - SPITextDecorationStyle text_decoration_style; // SPIEnum? Only one can be set at time. - SPIColor text_decoration_color; - // used to implement text_decoration, not saved to or read from SVG file - SPITextDecorationData text_decoration_data; - - // 16.3.2 is text-shadow. That's complicated. - /** letter spacing (css2 16.4) */ SPILengthOrNormal letter_spacing; /** word spacing (also css2 16.4) */ @@ -151,6 +143,25 @@ public: /** Anchor of the text (svg1.1 10.9.1) */ SPIEnum text_anchor; + /** white space (svg2) */ + SPIEnum white_space; + + /* Text Decoration ----------------------- */ + + /** text decoration (css2 16.3.1) */ + SPITextDecoration text_decoration; + /** CSS 3 2.1, 2.2, 2.3 */ + /** Not done yet, test_decoration3 = css3 2.4*/ + SPITextDecorationLine text_decoration_line; + SPITextDecorationStyle text_decoration_style; // SPIEnum? Only one can be set at time. + SPIColor text_decoration_color; + // used to implement text_decoration, not saved to or read from SVG file + SPITextDecorationData text_decoration_data; + + // 16.3.2 is text-shadow. That's complicated. + + /* General visual properties ------------- */ + /** clip-rule: 0 nonzero, 1 evenodd */ SPIEnum clip_rule; @@ -215,6 +226,8 @@ public: SPIString marker_end; SPIString* marker_ptrs[SP_MARKER_LOC_QTY]; + /* Filter effects ------------------------ */ + /** Filter effect */ SPIFilter filter; /** Filter blend mode */ @@ -225,6 +238,7 @@ public: /** enable-background, used for defining where filter effects get their background image */ SPIEnum enable_background; + /* Rendering hints ----------------------- */ /** hints on how to render: e.g. speed vs. accuracy. * As of April, 2013, only image_rendering used. */ -- cgit v1.2.3 From bf8e83bf9a5a48eda128d0428f72824360f196a7 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Tue, 14 Oct 2014 14:04:51 +0200 Subject: Implement marker 'orient' attribute value 'auto-start-reverse'. (bzr r13613) --- src/extension/internal/cairo-renderer.cpp | 12 +++++---- src/marker.cpp | 41 +++++++++++++++++++------------ src/marker.h | 8 +++++- src/sp-shape.cpp | 20 ++++++++++----- 4 files changed, 53 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 6fbc85c05..0fec68c06 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -202,8 +202,10 @@ static void sp_shape_render (SPItem *item, CairoRenderContext *ctx) if ( shape->_marker[i] ) { SPMarker* marker = SP_MARKER (shape->_marker[i]); Geom::Affine tr; - if (marker->orient_auto) { + if (marker->orient_mode == MARKER_ORIENT_AUTO) { tr = sp_shape_marker_get_transform_at_start(pathv.begin()->front()); + } else if (marker->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) { + tr = Geom::Rotate::from_degrees( 180.0 ) * sp_shape_marker_get_transform_at_start(pathv.begin()->front()); } else { tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(pathv.begin()->front().pointAt(0)); } @@ -220,7 +222,7 @@ static void sp_shape_render (SPItem *item, CairoRenderContext *ctx) && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there { Geom::Affine tr; - if (marker->orient_auto) { + if (marker->orient_mode != MARKER_ORIENT_ANGLE) { tr = sp_shape_marker_get_transform_at_start(path_it->front()); } else { tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(path_it->front().pointAt(0)); @@ -237,7 +239,7 @@ static void sp_shape_render (SPItem *item, CairoRenderContext *ctx) * Loop to end_default (so including closing segment), because when a path is closed, * there should be a midpoint marker between last segment and closing straight line segment */ Geom::Affine tr; - if (marker->orient_auto) { + if (marker->orient_mode != MARKER_ORIENT_ANGLE) { tr = sp_shape_marker_get_transform(*curve_it1, *curve_it2); } else { tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(curve_it1->pointAt(1)); @@ -253,7 +255,7 @@ static void sp_shape_render (SPItem *item, CairoRenderContext *ctx) if ( path_it != (pathv.end()-1) && !path_it->empty()) { Geom::Curve const &lastcurve = path_it->back_default(); Geom::Affine tr; - if (marker->orient_auto) { + if (marker->orient_mode != MARKER_ORIENT_ANGLE) { tr = sp_shape_marker_get_transform_at_end(lastcurve); } else { tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(lastcurve.pointAt(1)); @@ -277,7 +279,7 @@ static void sp_shape_render (SPItem *item, CairoRenderContext *ctx) Geom::Curve const &lastcurve = path_last[index]; Geom::Affine tr; - if (marker->orient_auto) { + if (marker->orient_mode != MARKER_ORIENT_ANGLE) { tr = sp_shape_marker_get_transform_at_end(lastcurve); } else { tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(lastcurve.pointAt(1)); diff --git a/src/marker.cpp b/src/marker.cpp index 7fee16ead..a4cbc30ca 100644 --- a/src/marker.cpp +++ b/src/marker.cpp @@ -52,7 +52,7 @@ SPMarker::SPMarker() : SPGroup(), SPViewBox() { this->markerUnits = 0; this->markerUnits_set = 0; - this->orient_auto = 0; + this->orient_mode = MARKER_ORIENT_ANGLE; this->orient_set = 0; this->orient = 0; @@ -158,16 +158,20 @@ void SPMarker::set(unsigned int key, const gchar* value) { case SP_ATTR_ORIENT: this->orient_set = FALSE; - this->orient_auto = FALSE; + this->orient_mode = MARKER_ORIENT_ANGLE; this->orient = 0.0; if (value) { - if (!strcmp (value, "auto")) { - this->orient_auto = TRUE; - this->orient_set = TRUE; - } else if (sp_svg_number_read_f (value, &this->orient)) { - this->orient_set = TRUE; - } + if (!strcmp (value, "auto")) { + this->orient_mode = MARKER_ORIENT_AUTO; + this->orient_set = TRUE; + } else if (!strcmp (value, "auto-start-reverse")) { + this->orient_mode = MARKER_ORIENT_AUTO_START_REVERSE; + this->orient_set = TRUE; + } else if (sp_svg_number_read_f (value, &this->orient)) { + this->orient_mode = MARKER_ORIENT_ANGLE; + this->orient_set = TRUE; + } } this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); @@ -264,15 +268,17 @@ Inkscape::XML::Node* SPMarker::write(Inkscape::XML::Document *xml_doc, Inkscape: } if (this->orient_set) { - if (this->orient_auto) { - repr->setAttribute("orient", "auto"); - } else { - sp_repr_set_css_double(repr, "orient", this->orient); - } + if (this->orient_mode == MARKER_ORIENT_AUTO) { + repr->setAttribute("orient", "auto"); + } else if (this->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) { + repr->setAttribute("orient", "auto-start-reverse"); + } else { + sp_repr_set_css_double(repr, "orient", this->orient); + } } else { - repr->setAttribute("orient", NULL); + repr->setAttribute("orient", NULL); } - + /* fixme: */ //XML Tree being used directly here while it shouldn't be.... repr->setAttribute("viewBox", this->getRepr()->attribute("viewBox")); @@ -381,7 +387,10 @@ sp_marker_show_instance ( SPMarker *marker, Inkscape::DrawingItem *parent, } if (v->items[pos]) { Geom::Affine m; - if (marker->orient_auto) { + if (marker->orient_mode == MARKER_ORIENT_AUTO) { + m = base; + } else if (marker->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) { + m = Geom::Rotate::from_degrees( 180.0 ) * base; m = base; } else { /* fixme: Orient units (Lauris) */ diff --git a/src/marker.h b/src/marker.h index 585615476..9eefcdf16 100644 --- a/src/marker.h +++ b/src/marker.h @@ -32,6 +32,12 @@ struct SPMarkerView; #include "uri-references.h" #include "viewbox.h" +enum markerOrient { + MARKER_ORIENT_ANGLE, + MARKER_ORIENT_AUTO, + MARKER_ORIENT_AUTO_START_REVERSE +}; + class SPMarker : public SPGroup, public SPViewBox { public: SPMarker(); @@ -51,7 +57,7 @@ public: /* orient */ unsigned int orient_set : 1; - unsigned int orient_auto : 1; + markerOrient orient_mode : 2; float orient; /* Private views */ diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index de9103dee..d76bd9780 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -288,8 +288,13 @@ sp_shape_update_marker_view(SPShape *shape, Inkscape::DrawingItem *ai) Geom::Affine const m (sp_shape_marker_get_transform_at_start(pathv.begin()->front())); for (int i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START if ( shape->_marker[i] ) { + Geom::Affine m_auto = m; + // Reverse start marker if necessary. + if (SP_MARKER(shape->_marker[i])->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) { + m_auto = Geom::Rotate::from_degrees( 180.0 ) * m; + } sp_marker_show_instance ((SPMarker* ) shape->_marker[i], ai, - ai->key() + i, counter[i], m, + ai->key() + i, counter[i], m_auto, shape->style->stroke_width.computed); counter[i]++; } @@ -425,7 +430,10 @@ Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox if (marker_item) { Geom::Affine tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front())); - if (!marker->orient_auto) { + if (marker->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) { + // Reverse start marker if necessary + tr = Geom::Rotate::from_degrees( 180.0 ) * tr; + } else if (marker->orient_mode == MARKER_ORIENT_ANGLE) { Geom::Point transl = tr.translation(); tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); } @@ -463,7 +471,7 @@ Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox { Geom::Affine tr(sp_shape_marker_get_transform_at_start(path_it->front())); - if (!marker->orient_auto) { + if (marker->orient_mode == MARKER_ORIENT_ANGLE) { Geom::Point transl = tr.translation(); tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); } @@ -493,7 +501,7 @@ Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox if (marker_item) { Geom::Affine tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2)); - if (!marker->orient_auto) { + if (marker->orient_mode == MARKER_ORIENT_ANGLE) { Geom::Point transl = tr.translation(); tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); } @@ -516,7 +524,7 @@ Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox Geom::Curve const &lastcurve = path_it->back_default(); Geom::Affine tr = sp_shape_marker_get_transform_at_end(lastcurve); - if (!marker->orient_auto) { + if (marker->orient_mode == MARKER_ORIENT_ANGLE) { Geom::Point transl = tr.translation(); tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); } @@ -551,7 +559,7 @@ Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox Geom::Affine tr = sp_shape_marker_get_transform_at_end(lastcurve); - if (!marker->orient_auto) { + if (marker->orient_mode == MARKER_ORIENT_ANGLE) { Geom::Point transl = tr.translation(); tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); } -- cgit v1.2.3 From dcbfb36e7c6279b1cc651131758700961152a8b0 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Wed, 15 Oct 2014 02:43:52 -0700 Subject: Post-merge cleanup. (bzr r13615) --- src/sp-hatch.cpp | 3 +- src/sp-item.cpp | 3 +- src/sp-paint-server.cpp | 20 +- src/sp-paint-server.h | 8 +- src/style-internal.h | 654 +++++++++++++++++++++++++++++++++++------------- src/style.cpp | 9 +- 6 files changed, 510 insertions(+), 187 deletions(-) (limited to 'src') diff --git a/src/sp-hatch.cpp b/src/sp-hatch.cpp index 98cc596d7..b007fc846 100644 --- a/src/sp-hatch.cpp +++ b/src/sp-hatch.cpp @@ -647,7 +647,8 @@ Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) const { return extents; } -cairo_pattern_t* SPHatch::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbox, double opacity) { +cairo_pattern_t* SPHatch::pattern_new(cairo_t * /*base_ct*/, Geom::OptRect const &/*bbox*/, double /*opacity*/) +{ //this code should not be used //it is however required by the fact that SPPaintServer::hatch_new is pure virtual return cairo_pattern_create_rgb(0.5, 0.5, 1.0); diff --git a/src/sp-item.cpp b/src/sp-item.cpp index f6de165a6..52ee6b7d8 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -708,7 +708,8 @@ void SPItem::update(SPCtx* /*ctx*/, guint flags) { item->avoidRef->handleSettingChange(); } -void SPItem::modified(unsigned int flags) { +void SPItem::modified(unsigned int /*flags*/) +{ } Inkscape::XML::Node* SPItem::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index d692fc611..3211b75d7 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -60,9 +60,27 @@ bool SPPaintServer::isSolid() const bool SPPaintServer::isValid() const { - return true; + return true; } +Inkscape::DrawingPattern *SPPaintServer::show(Inkscape::Drawing &/*drawing*/, unsigned int /*key*/, Geom::OptRect /*bbox*/) +{ + return NULL; +} + +void SPPaintServer::hide(unsigned int /*key*/) +{ +} + +void SPPaintServer::setBBox(unsigned int /*key*/, Geom::OptRect const &/*bbox*/) +{ +} + +cairo_pattern_t* SPPaintServer::pattern_new(cairo_t * /*ct*/, Geom::OptRect const &/*bbox*/, double /*opacity*/) +{ +} + + /* Local Variables: mode:c++ diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index 6c88199b9..7f3bfcba0 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -43,11 +43,11 @@ public: //on demand by pattern_new method. It is used for gradients. The other one is to add elements //representing PaintServer in NR tree. It is used by hatches and patterns. //Either pattern new or all three methods show, hide, setBBox need to be implemented - virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox) {return NULL;} - virtual void hide(unsigned int key) {}; - virtual void setBBox(unsigned int key, Geom::OptRect const &bbox) {}; + virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox); // TODO check passing bbox by value. Looks suspicious. + virtual void hide(unsigned int key); + virtual void setBBox(unsigned int key, Geom::OptRect const &bbox); - virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity) {return NULL;} + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); protected: bool swatch; diff --git a/src/style-internal.h b/src/style-internal.h index 0b2b40001..039b0bbe3 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -104,22 +104,45 @@ static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1); */ /// Virtual base class for all SPStyle interal classes -class SPIBase { +class SPIBase +{ - public: +public: SPIBase( Glib::ustring const &name, bool inherits = true ) - : name(name), inherits(inherits), set(false), inherit(false), style_att(false), style(NULL) {}; - virtual ~SPIBase() {}; + : name(name), + inherits(inherits), + set(false), + inherit(false), + style_att(false), + style(NULL) + {} + + virtual ~SPIBase() + {} + virtual void read( gchar const *str ) = 0; - virtual void readIfUnset( gchar const *str ) { if( !set ) read( str ); } - virtual void readAttribute( Inkscape::XML::Node *repr ) { readIfUnset( repr->attribute( name.c_str() ) ); } + virtual void readIfUnset( gchar const *str ) { + if ( !set ) { + read( str ); + } + } + + virtual void readAttribute( Inkscape::XML::Node *repr ) { + readIfUnset( repr->attribute( name.c_str() ) ); + } + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const = 0; - virtual void clear() { set = false, inherit = false; }; + virtual void clear() { + set = false, inherit = false; + } + virtual void cascade( const SPIBase* const parent ) = 0; virtual void merge( const SPIBase* const parent ) = 0; - virtual void setStylePointer( SPStyle *style_in ) { style = style_in; }; + virtual void setStylePointer( SPStyle *style_in ) { + style = style_in; + } // Explicit assignment operator required due to templates. SPIBase& operator=(const SPIBase& rhs) { @@ -133,11 +156,16 @@ class SPIBase { } // Check apples being compared to apples - virtual bool operator==(const SPIBase& rhs) { return (name == rhs.name); }; - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator==(const SPIBase& rhs) { + return (name == rhs.name); + } + + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: Glib::ustring name; // Make const unsigned inherits : 1; // Property inherits by default from parent. unsigned set : 1; // Property has been explicitly set (vs. inherited). @@ -145,22 +173,35 @@ class SPIBase { unsigned style_att : 2; // Source (attribute, style attribute, style-sheet). NOT USED YET FIX ME // To do: make private after g_asserts removed - public: +public: SPStyle* style; // Used by SPIPaint, SPIFilter... to find values of other properties }; /// Float type internal to SPStyle. (Only 'stroke-miterlimit') -class SPIFloat : public SPIBase { - - public: - SPIFloat() : SPIBase( "anonymous_float" ), value(0.0) {}; - SPIFloat( Glib::ustring name, float value_default = 0.0 ) - : SPIBase( name ), value(value_default), value_default(value_default) {}; - virtual ~SPIFloat() {}; +class SPIFloat : public SPIBase +{ + +public: + SPIFloat() + : SPIBase( "anonymous_float" ), + value(0.0) + {} + + SPIFloat( Glib::ustring const &name, float value_default = 0.0 ) + : SPIBase( name ), + value(value_default), + value_default(value_default) + {} + + virtual ~SPIFloat() {} virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -172,13 +213,15 @@ class SPIFloat : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: float value; - private: +private: float value_default; }; @@ -214,17 +257,32 @@ static const unsigned SP_SCALE24_MAX = 0xff0000; /// 24 bit data type internal to SPStyle. // Used only for opacity, fill-opacity, stroke-opacity. // Opacity does not inherit but stroke-opacity and fill-opacity do. -class SPIScale24 : public SPIBase { +class SPIScale24 : public SPIBase +{ + +public: + SPIScale24() + : SPIBase( "anonymous_scale24" ), + value(0) + {} + + SPIScale24( Glib::ustring const &name, unsigned value = 0, bool inherits = true ) + : SPIBase( name, inherits ), + value(value), + value_default(value) + {} + + virtual ~SPIScale24() + {} - public: - SPIScale24() : SPIBase( "anonymous_scale24" ), value(0) {}; - SPIScale24( Glib::ustring name, unsigned value = 0, bool inherits = true ) - : SPIBase( name, inherits ), value(value), value_default(value) {}; - virtual ~SPIScale24() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -236,14 +294,16 @@ class SPIScale24 : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: unsigned value : 24; - private: +private: unsigned value_default : 24; }; @@ -265,17 +325,37 @@ enum SPCSSUnit { /// Length type internal to SPStyle. // Needs access to 'font-size' and 'font-family' for computed values. // Used for 'stroke-width' 'stroke-dash-offset' ('none' not handled), text-indent -class SPILength : public SPIBase { +class SPILength : public SPIBase +{ + +public: + SPILength() + : SPIBase( "anonymous_length" ), + unit(SP_CSS_UNIT_NONE), + value(0), + computed(0) + {} + + SPILength( Glib::ustring const &name, unsigned value = 0 ) + : SPIBase( name ), + unit(SP_CSS_UNIT_NONE), + value(value), + computed(value), + value_default(value) + {} + + virtual ~SPILength() + {} - public: - SPILength() : SPIBase( "anonymous_length" ), unit(SP_CSS_UNIT_NONE), value(0), computed(0) {}; - SPILength( Glib::ustring name, unsigned value = 0 ) - : SPIBase( name ), unit(SP_CSS_UNIT_NONE), value(value), computed(value), value_default(value) {}; - virtual ~SPILength() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); unit = SP_CSS_UNIT_NONE, value = value_default; computed = value_default; }; + virtual void clear() { + SPIBase::clear(); + unit = SP_CSS_UNIT_NONE, value = value_default; + computed = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -286,35 +366,51 @@ class SPILength : public SPIBase { computed = rhs.computed; value_default = rhs.value_default; return *this; - }; + } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: unsigned unit : 4; float value; float computed; - private: +private: float value_default; }; /// Extended length type internal to SPStyle. // Used for: line-height, letter-spacing, word-spacing -class SPILengthOrNormal : public SPILength { +class SPILengthOrNormal : public SPILength +{ + +public: + SPILengthOrNormal() + : SPILength( "anonymous_length" ), + normal(true) + {} + + SPILengthOrNormal( Glib::ustring const &name, unsigned value = 0 ) + : SPILength( name, value ), + normal(true) + {} + + virtual ~SPILengthOrNormal() + {} - public: - SPILengthOrNormal() : SPILength( "anonymous_length" ), normal(true) {}; - SPILengthOrNormal( Glib::ustring name, unsigned value = 0 ) - : SPILength( name, value ), normal(true) {}; - virtual ~SPILengthOrNormal() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPILength::clear(); normal = true; }; + virtual void clear() { + SPILength::clear(); + normal = true; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -325,33 +421,59 @@ class SPILengthOrNormal : public SPILength { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool normal : 1; }; /// Enum type internal to SPStyle. // Used for many properties. 'font-stretch' and 'font-weight' must be special cased. -class SPIEnum : public SPIBase { +class SPIEnum : public SPIBase +{ - public: +public: SPIEnum() : - SPIBase( "anonymous_enum" ), enums( NULL ), value(0), computed(0) {}; - SPIEnum( Glib::ustring name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : - SPIBase( name, inherits ), enums( enums ), value(value), computed(value), - value_default(value), computed_default(value) {}; + SPIBase( "anonymous_enum" ), + enums( NULL ), + value(0), + computed(0) + {} + + SPIEnum( Glib::ustring const &name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : + SPIBase( name, inherits ), + enums( enums ), + value(value), + computed(value), + value_default(value), + computed_default(value) + {} + // Following is needed for font-weight - SPIEnum( Glib::ustring name, SPStyleEnum const *enums, SPCSSFontWeight value, SPCSSFontWeight computed ) : - SPIBase( name ), enums( enums ), value(value), computed(computed), - value_default(value), computed_default(computed) {}; - virtual ~SPIEnum() {}; + SPIEnum( Glib::ustring const &name, SPStyleEnum const *enums, SPCSSFontWeight value, SPCSSFontWeight computed ) : + SPIBase( name ), + enums( enums ), + value(value), + computed(computed), + value_default(value), + computed_default(computed) + {} + + virtual ~SPIEnum() + {} + virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default, computed = computed_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default, computed = computed_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -365,16 +487,18 @@ class SPIEnum : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPStyleEnum const *enums; unsigned value : 8; unsigned computed: 8; - private: +private: unsigned value_default : 8; unsigned computed_default: 8; // for font-weight }; @@ -382,20 +506,31 @@ class SPIEnum : public SPIBase { /// String type internal to SPStyle. // Used for 'marker', ..., 'font', 'font-family', 'inkscape-font-specification' -class SPIString : public SPIBase { - - public: - SPIString() : - SPIBase( "anonymous_string" ), value(NULL) {}; - SPIString( Glib::ustring name, gchar* value_default_in = NULL ) : - SPIBase( name ) , value(NULL) , value_default(NULL) { - value_default = value_default_in?g_strdup(value_default_in):NULL; - }; - virtual ~SPIString() { g_free(value); g_free(value_default); }; +class SPIString : public SPIBase +{ + +public: + SPIString() + : SPIBase( "anonymous_string" ), + value(NULL) + {} + + // TODO probably want to avoid gchar* and c-style strings. + SPIString( Glib::ustring const &name, gchar* value_default_in = NULL ) + : SPIBase( name ), + value(NULL), + value_default(value_default_in ? g_strdup(value_default_in) : NULL) + {} + + virtual ~SPIString() { + g_free(value); + g_free(value_default); + } + virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear(); + virtual void clear(); // TODO check about value and value_default virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -403,31 +538,50 @@ class SPIString : public SPIBase { SPIBase::operator=(rhs); g_free(value); g_free(value_default); - value = rhs.value?g_strdup(rhs.value):NULL; - value_default = rhs.value_default?g_strdup(rhs.value_default):NULL; + value = rhs.value ? g_strdup(rhs.value) : NULL; + value_default = rhs.value_default ? g_strdup(rhs.value_default) : NULL; return *this; } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private, convert value to Glib::ustring - public: +public: gchar *value; gchar *value_default; }; /// Color type interal to SPStyle, FIXME Add string value to store SVG named color. -class SPIColor : public SPIBase { +class SPIColor : public SPIBase +{ + +public: + SPIColor() + : SPIBase( "anonymous_color" ), + currentcolor(false) { + value.color.set(0); + } + + SPIColor( Glib::ustring const &name ) + : SPIBase( name ), + currentcolor(false) { + value.color.set(0); + } + + virtual ~SPIColor() + {} - public: - SPIColor() : SPIBase( "anonymous_color" ), currentcolor(false) { value.color.set(0); } - SPIColor( Glib::ustring name ) : SPIBase( name ), currentcolor(false) { value.color.set(0); } - virtual ~SPIColor() {} virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value.color.set(0); } + virtual void clear() { + SPIBase::clear(); + value.color.set(0); + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -439,13 +593,23 @@ class SPIColor : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); } + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - void setColor( float r, float g, float b ) { value.color.set( r, g, b ); } - void setColor( guint32 val ) { value.color.set( val ); } - void setColor( SPColor const& color ) { value.color = color; } + void setColor( float r, float g, float b ) { + value.color.set( r, g, b ); + } + + void setColor( guint32 val ) { + value.color.set( val ); + } + + void setColor( SPColor const& color ) { + value.color = color; + } - public: +public: bool currentcolor : 1; // FIXME: remove structure and derive SPIPaint from this class. struct { @@ -459,18 +623,28 @@ class SPIColor : public SPIBase { #define SP_STYLE_STROKE_SERVER(s) ((const_cast (s))->getStrokePaintServer()) /// Paint type internal to SPStyle. -class SPIPaint : public SPIBase { - - public: - SPIPaint() : SPIBase( "anonymous_paint" ), currentcolor(false), colorSet(false), noneSet(false) { +class SPIPaint : public SPIBase +{ + +public: + SPIPaint() + : SPIBase( "anonymous_paint" ), + currentcolor(false), + colorSet(false), + noneSet(false) { value.href = NULL; clear(); - }; - SPIPaint( Glib::ustring name ) - : SPIBase( name ), currentcolor(false), colorSet(false), noneSet(false) { + } + + SPIPaint( Glib::ustring const &name ) + : SPIBase( name ), + currentcolor(false), + colorSet(false), + noneSet(false) { value.href = NULL; clear(); // Sets defaults - }; + } + virtual ~SPIPaint(); // Clear and delete href. virtual void read( gchar const *str ); virtual void read( gchar const *str, SPStyle &style, SPDocument *document = 0); @@ -492,23 +666,46 @@ class SPIPaint : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } + + bool isSameType( SPIPaint const & other ) const { + return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (currentcolor == other.currentcolor); + } + + bool isNoneSet() const { + return noneSet; + } + + bool isNone() const { + return !currentcolor && !colorSet && !isPaintserver(); + } // TODO refine + + bool isColor() const { + return colorSet && !isPaintserver(); + } - bool isSameType( SPIPaint const & other ) const {return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (currentcolor == other.currentcolor);} + bool isPaintserver() const { + return (value.href) ? value.href->getObject() : 0; + } - bool isNoneSet() const {return noneSet;} + void setColor( float r, float g, float b ) { + value.color.set( r, g, b ); colorSet = true; + } - bool isNone() const {return !currentcolor && !colorSet && !isPaintserver();} // TODO refine - bool isColor() const {return colorSet && !isPaintserver();} - bool isPaintserver() const {return (value.href) ? value.href->getObject():0;} + void setColor( guint32 val ) { + value.color.set( val ); colorSet = true; + } + + void setColor( SPColor const& color ) { + value.color = color; colorSet = true; + } - void setColor( float r, float g, float b ) {value.color.set( r, g, b ); colorSet = true;} - void setColor( guint32 val ) {value.color.set( val ); colorSet = true;} - void setColor( SPColor const& color ) {value.color = color; colorSet = true;} void setNone() {noneSet = true; colorSet=false;} // To do: make private - public: +public: bool currentcolor : 1; bool colorSet : 1; bool noneSet : 1; @@ -535,11 +732,20 @@ enum SPPaintOrderLayer { const size_t PAINT_ORDER_LAYERS = 3; /// Paint order type internal to SPStyle -class SPIPaintOrder : public SPIBase { +class SPIPaintOrder : public SPIBase +{ + +public: + SPIPaintOrder() + : SPIBase( "paint-order" ), + value(NULL) { + this->clear(); + } + + virtual ~SPIPaintOrder() { + g_free( value ); + } - public: - SPIPaintOrder() : SPIBase( "paint-order" ), value(NULL) { this->clear(); }; - virtual ~SPIPaintOrder() { g_free( value ); }; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; @@ -567,11 +773,13 @@ class SPIPaintOrder : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPPaintOrderLayer layer[PAINT_ORDER_LAYERS]; bool layer_set[PAINT_ORDER_LAYERS]; gchar *value; // Raw string @@ -579,15 +787,25 @@ class SPIPaintOrder : public SPIBase { /// Filter type internal to SPStyle -class SPIDashArray : public SPIBase { +class SPIDashArray : public SPIBase +{ + +public: + SPIDashArray() + : SPIBase( "stroke-dasharray" ) + {} // Only one instance of SPIDashArray + + virtual ~SPIDashArray() + {} - public: - SPIDashArray() : SPIBase( "stroke-dasharray" ) {}; // Only one instance of SPIDashArray - virtual ~SPIDashArray() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); values.clear(); }; + virtual void clear() { + SPIBase::clear(); + values.clear(); + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -598,19 +816,26 @@ class SPIDashArray : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private, change double to SVGLength - public: +public: std::vector values; }; /// Filter type internal to SPStyle -class SPIFilter : public SPIBase { +class SPIFilter : public SPIBase +{ + +public: + SPIFilter() + : SPIBase( "filter", false ), + href(NULL) + {} - public: - SPIFilter() : SPIBase( "filter", false ), href(NULL) {}; virtual ~SPIFilter(); virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, @@ -626,10 +851,12 @@ class SPIFilter : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPFilterReference *href; }; @@ -642,16 +869,27 @@ enum { }; /// Fontsize type internal to SPStyle (also used by libnrtype/Layout-TNG-Input.cpp). -class SPIFontSize : public SPIBase { +class SPIFontSize : public SPIBase +{ + +public: + SPIFontSize() + : SPIBase( "font-size" ) { + this->clear(); + } + + virtual ~SPIFontSize() + {} - public: - SPIFontSize() : SPIBase( "font-size" ) { this->clear(); }; - virtual ~SPIFontSize() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); type = SP_FONT_SIZE_LITERAL, unit = SP_CSS_UNIT_NONE, - literal = SP_CSS_FONT_SIZE_MEDIUM, value = 12.0, computed = 12.0; } + virtual void clear() { + SPIBase::clear(); + type = SP_FONT_SIZE_LITERAL, unit = SP_CSS_UNIT_NONE, + literal = SP_CSS_FONT_SIZE_MEDIUM, value = 12.0, computed = 12.0; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -666,39 +904,51 @@ class SPIFontSize : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - public: +public: static float const font_size_default; // To do: make private - public: +public: unsigned type : 2; unsigned unit : 4; unsigned literal : 4; float value; float computed; - private: +private: double relative_fraction() const; static float const font_size_table[]; }; /// Font type internal to SPStyle ('font' shorthand) -class SPIFont : public SPIBase { +class SPIFont : public SPIBase +{ + +public: + SPIFont() + : SPIBase( "font" ) + {} + + virtual ~SPIFont() + {} - public: - SPIFont() : SPIBase( "font" ) {}; - virtual ~SPIFont() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); - }; - virtual void cascade( const SPIBase* const parent ) { (void)parent; }; // Done in dependent properties - virtual void merge( const SPIBase* const parent ) { (void)parent; }; + } + + virtual void cascade( const SPIBase* const /*parent*/ ) + {} // Done in dependent properties + + virtual void merge( const SPIBase* const /*parent*/ ) + {} SPIFont& operator=(const SPIFont& rhs) { SPIBase::operator=(rhs); @@ -706,7 +956,9 @@ class SPIFont : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } }; @@ -717,16 +969,27 @@ enum { }; /// Baseline shift type internal to SPStyle. (This is actually just like SPIFontSize) -class SPIBaselineShift : public SPIBase { +class SPIBaselineShift : public SPIBase +{ + +public: + SPIBaselineShift() + : SPIBase( "baseline-shift", false ) { + this->clear(); + } + + virtual ~SPIBaselineShift() + {} - public: - SPIBaselineShift() : SPIBase( "baseline-shift", false ) { this->clear(); }; - virtual ~SPIBaselineShift() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); type=SP_BASELINE_SHIFT_LITERAL, unit=SP_CSS_UNIT_NONE, - literal = SP_CSS_BASELINE_SHIFT_BASELINE, value = 0.0, computed = 0.0; } + virtual void clear() { + SPIBase::clear(); + type=SP_BASELINE_SHIFT_LITERAL, unit=SP_CSS_UNIT_NONE, + literal = SP_CSS_BASELINE_SHIFT_BASELINE, value = 0.0, computed = 0.0; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -742,11 +1005,14 @@ class SPIBaselineShift : public SPIBase { // This is not used but we have it for completeness, it has not been tested. virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } + bool isZero() const; // To do: make private - public: +public: unsigned type : 2; unsigned unit : 4; unsigned literal: 2; @@ -759,15 +1025,26 @@ class SPIBaselineShift : public SPIBase { // CSS3 2.2 /// Text decoration line type internal to SPStyle. THIS SHOULD BE A GENERIC CLASS -class SPITextDecorationLine : public SPIBase { +class SPITextDecorationLine : public SPIBase +{ + +public: + SPITextDecorationLine() + : SPIBase( "text-decoration-line" ) { + this->clear(); + } + + virtual ~SPITextDecorationLine() + {} - public: - SPITextDecorationLine() : SPIBase( "text-decoration-line" ) { this->clear(); }; - virtual ~SPITextDecorationLine() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); underline = false, overline = false, line_through = false, blink = false; } + virtual void clear() { + SPIBase::clear(); + underline = false, overline = false, line_through = false, blink = false; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -781,10 +1058,12 @@ class SPITextDecorationLine : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool underline : 1; bool overline : 1; bool line_through : 1; @@ -793,15 +1072,26 @@ class SPITextDecorationLine : public SPIBase { // CSS3 2.2 /// Text decoration style type internal to SPStyle. THIS SHOULD JUST BE SPIEnum! -class SPITextDecorationStyle : public SPIBase { +class SPITextDecorationStyle : public SPIBase +{ + +public: + SPITextDecorationStyle() + : SPIBase( "text-decoration-style" ) { + this->clear(); + } + + virtual ~SPITextDecorationStyle() + {} - public: - SPITextDecorationStyle() : SPIBase( "text-decoration-style" ) { this->clear(); }; - virtual ~SPITextDecorationStyle() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); solid = true, isdouble = false, dotted = false, dashed = false, wavy = false; } + virtual void clear() { + SPIBase::clear(); + solid = true, isdouble = false, dotted = false, dashed = false, wavy = false; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -816,10 +1106,12 @@ class SPITextDecorationStyle : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool solid : 1; bool isdouble : 1; // cannot use "double" as it is a reserved keyword bool dotted : 1; @@ -836,18 +1128,26 @@ class SPITextDecorationStyle : public SPIBase { // the right style. (See http://www.w3.org/TR/css-text-decor-3/#text-decoration-property ) /// Text decoration type internal to SPStyle. -class SPITextDecoration : public SPIBase { +class SPITextDecoration : public SPIBase +{ + +public: + SPITextDecoration() + : SPIBase( "text-decoration" ), + style_td( NULL ) + {} + + virtual ~SPITextDecoration() + {} - public: - SPITextDecoration() : SPIBase( "text-decoration" ), style_td( NULL ) {}; - virtual ~SPITextDecoration() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); style_td = NULL; - }; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -858,9 +1158,11 @@ class SPITextDecoration : public SPIBase { // Use CSS2 value virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - public: +public: SPStyle* style_td; // Style to be used for drawing CSS2 text decorations }; diff --git a/src/style.cpp b/src/style.cpp index c17f06761..364dd2c21 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -121,10 +121,6 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : // Text related properties text_indent( "text-indent", 0.0 ), // SPILength text_align( "text-align", enum_text_align, SP_CSS_TEXT_ALIGN_START ), - text_decoration(), - text_decoration_line(), - text_decoration_style(), - text_decoration_color( "text-decoration-color" ), // SPIColor letter_spacing( "letter-spacing", 0.0 ), // SPILengthOrNormal word_spacing( "word-spacing", 0.0 ), // SPILengthOrNormal @@ -137,6 +133,11 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : text_anchor( "text-anchor", enum_text_anchor, SP_CSS_TEXT_ANCHOR_START ), white_space( "white-space", enum_white_space, SP_CSS_WHITE_SPACE_NORMAL ), + text_decoration(), + text_decoration_line(), + text_decoration_style(), + text_decoration_color( "text-decoration-color" ), // SPIColor + // General visual properties clip_rule( "clip-rule", enum_clip_rule, SP_WIND_RULE_NONZERO ), display( "display", enum_display, SP_CSS_DISPLAY_INLINE, false ), -- cgit v1.2.3 From 79bf9b103a578281c06a0ea1dd706f11783bf18e Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Wed, 15 Oct 2014 11:54:49 -0400 Subject: Fix build (bzr r13616) --- src/sp-paint-server.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index 3211b75d7..af510cd11 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -78,6 +78,7 @@ void SPPaintServer::setBBox(unsigned int /*key*/, Geom::OptRect const &/*bbox*/) cairo_pattern_t* SPPaintServer::pattern_new(cairo_t * /*ct*/, Geom::OptRect const &/*bbox*/, double /*opacity*/) { + return NULL; } -- cgit v1.2.3 From 83e50305d77a98329ea2085f90b6a9f154e1509f Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Wed, 15 Oct 2014 20:01:50 +0200 Subject: LiamW's initial font caching work. (bzr r13616.1.1) --- src/ink-comboboxentry-action.cpp | 2 + src/libnrtype/FontFactory.cpp | 76 ++ src/libnrtype/FontFactory.h | 10 +- src/libnrtype/font-lister.cpp | 1686 +++++++++++++++++++------------------- src/libnrtype/font-lister.h | 556 ++++++------- src/ui/dialog/text-edit.cpp | 6 +- src/widgets/font-selector.cpp | 4 +- src/widgets/text-toolbar.cpp | 10 +- 8 files changed, 1225 insertions(+), 1125 deletions(-) (limited to 'src') diff --git a/src/ink-comboboxentry-action.cpp b/src/ink-comboboxentry-action.cpp index 6579c8ff8..f7d1c8724 100644 --- a/src/ink-comboboxentry-action.cpp +++ b/src/ink-comboboxentry-action.cpp @@ -390,6 +390,8 @@ GtkWidget* create_tool_item( GtkAction* action ) NULL, NULL ); } + // FIXME: once gtk3 migration is done this can be removed + // https://bugzilla.gnome.org/show_bug.cgi?id=734915 gtk_widget_show_all (comboBoxEntry); // Optionally add formatting... diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index e11fed20c..cb9af5eb0 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -514,6 +514,82 @@ static bool StyleNameCompareInternal(const StyleNames &style1, const StyleNames return( StyleNameValue( style1.CssName ) < StyleNameValue( style2.CssName ) ); } +static bool ustringPairSort(std::pair const& first, std::pair const& second) +{ + // well, this looks weird. + return first.second < second.second; +} + +void font_factory::GetUIFamilies(std::vector& out) +{ + // Gather the family names as listed by Pango + PangoFontFamily** families = NULL; + int numFamilies = 0; + pango_font_map_list_families(fontServer, &families, &numFamilies); + + std::vector > sorted; + + // not size_t + for (int currentFamily = 0; currentFamily < numFamilies; ++currentFamily) { + const char* displayName = pango_font_family_get_name(families[currentFamily]); + + if (displayName == 0 || *displayName == '\0') { + continue; + } + sorted.push_back(std::make_pair(families[currentFamily], displayName)); + } + + std::sort(sorted.begin(), sorted.end(), ustringPairSort); + + for (size_t i = 0; i < sorted.size(); ++i) { + out.push_back(sorted[i].first); + } +} + +GList* font_factory::GetUIStyles(PangoFontFamily * in) +{ + GList* ret = NULL; + // Gather the styles for this family + PangoFontFace** faces = NULL; + int numFaces = 0; + pango_font_family_list_faces(in, &faces, &numFaces); + + for (int currentFace = 0; currentFace < numFaces; currentFace++) { + + // If the face has a name, describe it, and then use the + // description to get the UI family and face strings + const gchar* displayName = pango_font_face_get_face_name(faces[currentFace]); + if (displayName == NULL || *displayName == '\0') { + continue; + } + + PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]); + if (faceDescr) { + Glib::ustring familyUIName = GetUIFamilyString(faceDescr); + Glib::ustring styleUIName = GetUIStyleString(faceDescr); + + // Disable synthesized (faux) font faces except for CSS generic faces + if (pango_font_face_is_synthesized(faces[currentFace]) ) { + if (familyUIName.compare( "sans-serif" ) != 0 && + familyUIName.compare( "serif" ) != 0 && + familyUIName.compare( "monospace" ) != 0 && + familyUIName.compare( "fantasy" ) != 0 && + familyUIName.compare( "cursive" ) != 0 ) { + continue; + } + } + + if (!familyUIName.empty() && !styleUIName.empty()) { + // Add the style information + ret = g_list_append(ret, new StyleNames(styleUIName, displayName)); + } + } + pango_font_description_free(faceDescr); + } + g_free(faces); + return ret; +} + void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map) { g_assert(map); diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index bb3a8fa25..85576e08d 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -21,7 +21,7 @@ #include #include "nr-type-primitives.h" -#include "../style.h" +#include "style.h" /* Freetype */ #ifdef USE_PANGO_WIN32 @@ -121,7 +121,13 @@ public: // Gathers all strings needed for UI while storing pango information in // fontInstanceMap and fontStringMap + // don't use this function, it's too slow void GetUIFamiliesAndStyles(FamilyToStylesMap *map); + + // Helpfully inserts all font families into the provided vector + void GetUIFamilies(std::vector& out); + // Retrieves style information about a family in a newly allocated GList. + GList* GetUIStyles(PangoFontFamily * in); /// Retrieve a font_instance from a style object, first trying to use the font-specification, the CSS information font_instance* FaceFromStyle(SPStyle const *style); @@ -174,4 +180,4 @@ private: fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp index 6727cdda6..9a978b0fd 100644 --- a/src/libnrtype/font-lister.cpp +++ b/src/libnrtype/font-lister.cpp @@ -1,5 +1,5 @@ #ifdef HAVE_CONFIG_H -# include +#include #endif #include @@ -27,410 +27,432 @@ // CSS dictates that font family names are case insensitive. // This should really implement full Unicode case unfolding. -bool familyNamesAreEqual( const Glib::ustring &a, const Glib::ustring &b ) { +bool familyNamesAreEqual(const Glib::ustring &a, const Glib::ustring &b) +{ + return (a.casefold().compare(b.casefold()) == 0); +} - return( a.casefold().compare( b.casefold() ) == 0 ); +static const char* sp_font_family_get_name(PangoFontFamily* family) +{ + const char* name = pango_font_family_get_name(family); + if (strncmp(name, "Sans", 4) == 0 && strlen(name) == 4) + return "sans-serif"; + if (strncmp(name, "Serif", 5) == 0 && strlen(name) == 5) + return "serif"; + if (strncmp(name, "Monospace", 9) == 0 && strlen(name) == 9) + return "monospace"; + return name; } -namespace Inkscape +namespace Inkscape { + +FontLister::FontLister() { - FontLister::FontLister () - { - font_list_store = Gtk::ListStore::create (FontList); - font_list_store->freeze_notify(); - - FamilyToStylesMap familyStyleMap; - font_factory::Default()->GetUIFamiliesAndStyles(&familyStyleMap); - - // Grab the family names into a list and then sort them - std::list familyList; - for (FamilyToStylesMap::iterator iter = familyStyleMap.begin(); - iter != familyStyleMap.end(); - ++iter) { - familyList.push_back((*iter).first); + font_list_store = Gtk::ListStore::create(FontList); + font_list_store->freeze_notify(); + + /* Create default styles for use when font-family is unknown on system. */ + default_styles = g_list_append(NULL, new StyleNames("Normal")); + default_styles = g_list_append(default_styles, new StyleNames("Italic")); + default_styles = g_list_append(default_styles, new StyleNames("Bold")); + default_styles = g_list_append(default_styles, new StyleNames("Bold Italic")); + + // Get sorted font families from Pango + std::vector familyVector; + font_factory::Default()->GetUIFamilies(familyVector); + + // Traverse through the family names and set up the list store + for (size_t i = 0; i < familyVector.size(); ++i) { + const char* displayName = sp_font_family_get_name(familyVector[i]); + + if (displayName == 0 || *displayName == '\0') { + continue; } - familyList.sort(); - // Traverse through the family names and set up the list store (note that - // the styles list that are the map's values are already sorted) - while (!familyList.empty()) { - Glib::ustring familyName = familyList.front(); - familyList.pop_front(); - - if (!familyName.empty()) { - Gtk::TreeModel::iterator treeModelIter = font_list_store->append(); - //(*treeModelIter)[FontList.family] = reinterpret_cast(g_strdup(familyName.c_str())); - (*treeModelIter)[FontList.family] = familyName; - - // Now go through the styles - GList *styles = NULL; - std::list &styleStrings = familyStyleMap[familyName]; - for (std::list::iterator it=styleStrings.begin(); - it != styleStrings.end(); - ++it) { - // Our own copy - StyleNames *copy = new StyleNames( *it ); - styles = g_list_append(styles, copy); - } - - (*treeModelIter)[FontList.styles] = styles; - (*treeModelIter)[FontList.onSystem] = true; - } + Glib::ustring familyName = displayName; + if (!familyName.empty()) { + Gtk::TreeModel::iterator treeModelIter = font_list_store->append(); + (*treeModelIter)[FontList.family] = familyName; + + // we don't set this now (too slow) but the style will be cached if the user + // ever decides to use this font + (*treeModelIter)[FontList.styles] = NULL; + // store the pango representation for generating the style + (*treeModelIter)[FontList.pango_family] = familyVector[i]; + (*treeModelIter)[FontList.onSystem] = true; } - current_family_row = 0; - current_family = "sans-serif"; - current_style = "Normal"; - current_fontspec = "sans-serif"; // Empty style -> Normal - current_fontspec_system = "Sans"; - - /* Create default styles for use when font-family is unknown on system. */ - default_styles = g_list_append( NULL, new StyleNames( "Normal" ) ); - default_styles = g_list_append( default_styles, new StyleNames( "Italic" ) ); - default_styles = g_list_append( default_styles, new StyleNames( "Bold" ) ); - default_styles = g_list_append( default_styles, new StyleNames( "Bold Italic" ) ); - - font_list_store->thaw_notify(); - - style_list_store = Gtk::ListStore::create (FontStyleList); - - // Initialize style store with defaults - style_list_store->freeze_notify(); - style_list_store->clear(); - for (GList *l=default_styles; l; l = l->next) { - Gtk::TreeModel::iterator treeModelIter = style_list_store->append(); - (*treeModelIter)[FontStyleList.cssStyle] = ((StyleNames*)l->data)->CssName; - (*treeModelIter)[FontStyleList.displayStyle] = ((StyleNames*)l->data)->DisplayName; + } + + current_family_row = 0; + current_family = "sans-serif"; + current_style = "Normal"; + current_fontspec = "sans-serif"; // Empty style -> Normal + current_fontspec_system = "Sans"; + + font_list_store->thaw_notify(); + + style_list_store = Gtk::ListStore::create(FontStyleList); + + // Initialize style store with defaults + style_list_store->freeze_notify(); + style_list_store->clear(); + for (GList *l = default_styles; l; l = l->next) { + Gtk::TreeModel::iterator treeModelIter = style_list_store->append(); + (*treeModelIter)[FontStyleList.cssStyle] = ((StyleNames *)l->data)->CssName; + (*treeModelIter)[FontStyleList.displayStyle] = ((StyleNames *)l->data)->DisplayName; + } + style_list_store->thaw_notify(); +} + +FontLister::~FontLister() +{ + // Delete default_styles + for (GList *l = default_styles; l; l = l->next) { + delete ((StyleNames *)l->data); + } + + // Delete other styles + Gtk::TreeModel::iterator iter = font_list_store->get_iter("0"); + while (iter != font_list_store->children().end()) { + Gtk::TreeModel::Row row = *iter; + GList *styles = row[FontList.styles]; + for (GList *l = styles; l; l = l->next) { + delete ((StyleNames *)l->data); } - style_list_store->thaw_notify(); + ++iter; } +} - FontLister::~FontLister() { +FontLister *FontLister::get_instance() +{ + static Inkscape::FontLister *instance = new Inkscape::FontLister(); + return instance; +} - // Delete default_styles - for (GList *l=default_styles; l; l = l->next) { - delete ((StyleNames*)l->data); +void FontLister::ensureRowStyles(GtkTreeModel* model, GtkTreeIter const* iterator) +{ + Gtk::TreeIter iter(model, iterator); + Gtk::TreeModel::Row row = *iter; + if (!row[FontList.styles]) { + if (row[FontList.pango_family]) { + row[FontList.styles] = font_factory::Default()->GetUIStyles(row[FontList.pango_family]); } + } +} - // Delete other styles - Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" ); - while( iter != font_list_store->children().end() ) { - Gtk::TreeModel::Row row = *iter; - GList *styles = row[FontList.styles]; - for (GList *l=styles; l; l = l->next) { - delete ((StyleNames*)l->data); +// Example of how to use "foreach_iter" +// bool +// FontLister::print_document_font( const Gtk::TreeModel::iterator &iter ) { +// Gtk::TreeModel::Row row = *iter; +// if( !row[FontList.onSystem] ) { +// std::cout << " Not on system: " << row[FontList.family] << std::endl; +// return false; +// } +// return true; +// } +// font_list_store->foreach_iter( sigc::mem_fun(*this, &FontLister::print_document_font )); + +/* Used to insert a font that was not in the document and not on the system into the font list. */ +void FontLister::insert_font_family(Glib::ustring new_family) +{ + GList *styles = default_styles; + + /* In case this is a fallback list, check if first font-family on system. */ + std::vector tokens = Glib::Regex::split_simple(",", new_family); + if (!tokens.empty() && !tokens[0].empty()) { + Gtk::TreeModel::iterator iter2 = font_list_store->get_iter("0"); + while (iter2 != font_list_store->children().end()) { + Gtk::TreeModel::Row row = *iter2; + if (row[FontList.onSystem] && familyNamesAreEqual(tokens[0], row[FontList.family])) { + if (!row[FontList.styles]) { + row[FontList.styles] = font_factory::Default()->GetUIStyles(row[FontList.pango_family]); + } + styles = row[FontList.styles]; + break; } - ++iter; + ++iter2; } } - // Example of how to use "foreach_iter" - // bool - // FontLister::print_document_font( const Gtk::TreeModel::iterator &iter ) { - // Gtk::TreeModel::Row row = *iter; - // if( !row[FontList.onSystem] ) { - // std::cout << " Not on system: " << row[FontList.family] << std::endl; - // return false; - // } - // return true; - // } - // font_list_store->foreach_iter( sigc::mem_fun(*this, &FontLister::print_document_font )); - - /* Used to insert a font that was not in the document and not on the system into the font list. */ - void - FontLister::insert_font_family( Glib::ustring new_family ) { - - GList *styles = default_styles; - - /* In case this is a fallback list, check if first font-family on system. */ - std::vector tokens = Glib::Regex::split_simple(",", new_family ); - if( !tokens.empty() && !tokens[0].empty() ) { - - Gtk::TreeModel::iterator iter2 = font_list_store->get_iter( "0" ); - while( iter2 != font_list_store->children().end() ) { - Gtk::TreeModel::Row row = *iter2; - if( row[FontList.onSystem] && familyNamesAreEqual( tokens[0], row[FontList.family] ) ) { - styles = row[FontList.styles]; + Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); + (*treeModelIter)[FontList.family] = new_family; + (*treeModelIter)[FontList.styles] = styles; + (*treeModelIter)[FontList.onSystem] = false; +} + +void FontLister::update_font_list(SPDocument *document) +{ + SPObject *r = document->getRoot(); + if (!r) { + return; + } + + font_list_store->freeze_notify(); + + /* Find if current row is in document or system part of list */ + gboolean row_is_system = false; + if (current_family_row > -1) { + Gtk::TreePath path; + path.push_back(current_family_row); + Gtk::TreeModel::iterator iter = font_list_store->get_iter(path); + if (iter) { + row_is_system = (*iter)[FontList.onSystem]; + // std::cout << " In: row: " << current_family_row << " " << (*iter)[FontList.family] << std::endl; + } + } + + /* Clear all old document font-family entries */ + Gtk::TreeModel::iterator iter = font_list_store->get_iter("0"); + while (iter != font_list_store->children().end()) { + Gtk::TreeModel::Row row = *iter; + if (!row[FontList.onSystem]) { + // std::cout << " Not on system: " << row[FontList.family] << std::endl; + iter = font_list_store->erase(iter); + } else { + // std::cout << " First on system: " << row[FontList.family] << std::endl; break; - } - ++iter2; } - } + } + + /* Get "font-family"s used in document. */ + std::list fontfamilies; + update_font_list_recursive(r, &fontfamilies); + + fontfamilies.sort(); + fontfamilies.unique(); + fontfamilies.reverse(); + - Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); - (*treeModelIter)[FontList.family] = reinterpret_cast(g_strdup(new_family.c_str())); - (*treeModelIter)[FontList.styles] = styles; - (*treeModelIter)[FontList.onSystem] = false; + /* Insert separator */ + if (!fontfamilies.empty()) { + Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); + (*treeModelIter)[FontList.family] = "#"; + (*treeModelIter)[FontList.onSystem] = false; } - void - FontLister::update_font_list( SPDocument* document ) { - - SPObject *r = document->getRoot(); - if( !r ) { - return; - } - - font_list_store->freeze_notify(); - - /* Find if current row is in document or system part of list */ - gboolean row_is_system = false; - if( current_family_row > -1 ) { - Gtk::TreePath path; - path.push_back( current_family_row ); - Gtk::TreeModel::iterator iter = font_list_store->get_iter( path ); - if( iter ) { - row_is_system = (*iter)[FontList.onSystem]; - // std::cout << " In: row: " << current_family_row << " " << (*iter)[FontList.family] << std::endl; - } - } - - /* Clear all old document font-family entries */ - Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" ); - while( iter != font_list_store->children().end() ) { - Gtk::TreeModel::Row row = *iter; - if( !row[FontList.onSystem] ) { - // std::cout << " Not on system: " << row[FontList.family] << std::endl; - iter = font_list_store->erase( iter ); - } else { - // std::cout << " First on system: " << row[FontList.family] << std::endl; - break; - } - } - - /* Get "font-family"s used in document. */ - std::list fontfamilies; - update_font_list_recursive( r, &fontfamilies ); - - fontfamilies.sort(); - fontfamilies.unique(); - fontfamilies.reverse(); - - /* Insert separator */ - if( !fontfamilies.empty() ) { - Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); - (*treeModelIter)[FontList.family] = "#"; - (*treeModelIter)[FontList.onSystem] = false; - } - - /* Insert font-family's in document. */ - std::list::iterator i; - for( i = fontfamilies.begin(); i != fontfamilies.end(); ++i) { - - GList *styles = default_styles; + /* Insert font-family's in document. */ + std::list::iterator i; + for (i = fontfamilies.begin(); i != fontfamilies.end(); ++i) { + + GList *styles = default_styles; /* See if font-family (or first in fallback list) is on system. If so, get styles. */ - std::vector tokens = Glib::Regex::split_simple(",", *i ); - if( !tokens.empty() && !tokens[0].empty() ) { - - Gtk::TreeModel::iterator iter2 = font_list_store->get_iter( "0" ); - while( iter2 != font_list_store->children().end() ) { - Gtk::TreeModel::Row row = *iter2; - if( row[FontList.onSystem] && familyNamesAreEqual( tokens[0], row[FontList.family] ) ) { - styles = row[FontList.styles]; - break; - } - ++iter2; - } - } - - Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); - (*treeModelIter)[FontList.family] = reinterpret_cast(g_strdup((*i).c_str())); - (*treeModelIter)[FontList.styles] = styles; - (*treeModelIter)[FontList.onSystem] = false; - } - - /* Now we do a song and dance to find the correct row as the row corresponding - * to the current_family may have changed. We can't simply search for the - * family name in the list since it can occur twice, once in the document - * font family part and once in the system font family part. Above we determined - * which part it is in. - */ - if( current_family_row > -1 ) { - int start = 0; - if( row_is_system ) start = fontfamilies.size(); - int length = font_list_store->children().size(); - for( int i = 0; i < length; ++i ) { - int row = i + start; - if( row >= length ) row -= length; - Gtk::TreePath path; - path.push_back( row ); - Gtk::TreeModel::iterator iter = font_list_store->get_iter( path ); - if( iter ) { - if( familyNamesAreEqual( current_family, (*iter)[FontList.family] ) ) { - current_family_row = row; - break; - } - } - } - } - // std::cout << " Out: row: " << current_family_row << " " << current_family << std::endl; - - font_list_store->thaw_notify(); + std::vector tokens = Glib::Regex::split_simple(",", *i); + if (!tokens.empty() && !tokens[0].empty()) { + + Gtk::TreeModel::iterator iter2 = font_list_store->get_iter("0"); + while (iter2 != font_list_store->children().end()) { + Gtk::TreeModel::Row row = *iter2; + if (row[FontList.onSystem] && familyNamesAreEqual(tokens[0], row[FontList.family])) { + if (!row[FontList.styles]) { + row[FontList.styles] = font_factory::Default()->GetUIStyles(row[FontList.pango_family]); + } + styles = row[FontList.styles]; + break; + } + ++iter2; + } + } + + Gtk::TreeModel::iterator treeModelIter = font_list_store->prepend(); + (*treeModelIter)[FontList.family] = reinterpret_cast(g_strdup((*i).c_str())); + (*treeModelIter)[FontList.styles] = styles; + (*treeModelIter)[FontList.onSystem] = false; + } - void - FontLister::update_font_list_recursive( SPObject *r, std::list *l ) { + /* Now we do a song and dance to find the correct row as the row corresponding + * to the current_family may have changed. We can't simply search for the + * family name in the list since it can occur twice, once in the document + * font family part and once in the system font family part. Above we determined + * which part it is in. + */ + if (current_family_row > -1) { + int start = 0; + if (row_is_system) + start = fontfamilies.size(); + int length = font_list_store->children().size(); + for (int i = 0; i < length; ++i) { + int row = i + start; + if (row >= length) + row -= length; + Gtk::TreePath path; + path.push_back(row); + Gtk::TreeModel::iterator iter = font_list_store->get_iter(path); + if (iter) { + if (familyNamesAreEqual(current_family, (*iter)[FontList.family])) { + current_family_row = row; + break; + } + } + } + } + // std::cout << " Out: row: " << current_family_row << " " << current_family << std::endl; - const gchar *font_family = r->style->font_family.value; - if( font_family ) { - l->push_back( Glib::ustring( font_family ) ); - } + font_list_store->thaw_notify(); +} - for (SPObject *child = r->firstChild(); child; child = child->getNext()) { - update_font_list_recursive( child, l ); - } +void FontLister::update_font_list_recursive(SPObject *r, std::list *l) +{ + const gchar *font_family = r->style->font_family.value; + if (font_family) { + l->push_back(Glib::ustring(font_family)); } - Glib::ustring - FontLister::canonize_fontspec( Glib::ustring fontspec ) { - - // Pass fontspec to and back from Pango to get a the fontspec in - // canonical form. -inkscape-font-specification relies on the - // Pango constructed fontspec not changing form. If it does, - // this is the place to fix it. - PangoFontDescription *descr = pango_font_description_from_string( fontspec.c_str() ); - gchar* canonized = pango_font_description_to_string ( descr ); - Glib::ustring Canonized = canonized; - g_free( canonized ); - pango_font_description_free( descr ); - - // Pango canonized strings remove space after comma between family names. Put it back. - size_t i = 0; - while( (i = Canonized.find(",", i)) != std::string::npos) { - Canonized.replace(i, 1, ", "); - i += 2; - } - - return Canonized; + for (SPObject *child = r->firstChild(); child; child = child->getNext()) { + update_font_list_recursive(child, l); } +} - Glib::ustring - FontLister::system_fontspec( Glib::ustring fontspec ) { +Glib::ustring FontLister::canonize_fontspec(Glib::ustring fontspec) +{ - // Find what Pango thinks is the closest match. - Glib::ustring out = fontspec; + // Pass fontspec to and back from Pango to get a the fontspec in + // canonical form. -inkscape-font-specification relies on the + // Pango constructed fontspec not changing form. If it does, + // this is the place to fix it. + PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str()); + gchar *canonized = pango_font_description_to_string(descr); + Glib::ustring Canonized = canonized; + g_free(canonized); + pango_font_description_free(descr); + + // Pango canonized strings remove space after comma between family names. Put it back. + size_t i = 0; + while ((i = Canonized.find(",", i)) != std::string::npos) { + Canonized.replace(i, 1, ", "); + i += 2; + } + + return Canonized; +} - PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str()); - font_instance *res = (font_factory::Default())->Face(descr); - if (res->pFont) { +Glib::ustring FontLister::system_fontspec(Glib::ustring fontspec) +{ + + // Find what Pango thinks is the closest match. + Glib::ustring out = fontspec; + + PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str()); + font_instance *res = (font_factory::Default())->Face(descr); + if (res->pFont) { PangoFontDescription *nFaceDesc = pango_font_describe(res->pFont); out = sp_font_description_get_family(nFaceDesc); - } - pango_font_description_free(descr); - - return out; } + pango_font_description_free(descr); - std::pair - FontLister::ui_from_fontspec( Glib::ustring fontspec ) { - - PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str()); - const gchar* family = pango_font_description_get_family(descr); - if(!family) - family = "sans-serif"; - Glib::ustring Family = family; - - // PANGO BUG... - // A font spec of Delicious, 500 Italic should result in a family of 'Delicious' - // and a style of 'Medium Italic'. It results instead with: a family of - // 'Delicious, 500' with a style of 'Medium Italic'. We chop of any weight numbers - // at the end of the family: match ",[1-9]00^". - Glib::RefPtr weight = Glib::Regex::create(",[1-9]00$"); - Family = weight->replace( Family, 0, "", Glib::REGEX_MATCH_PARTIAL ); - - // Pango canonized strings remove space after comma between family names. Put it back. - size_t i = 0; - while( (i = Family.find(",", i)) != std::string::npos) { - Family.replace(i, 1, ", "); - i += 2; - } - - pango_font_description_unset_fields(descr, PANGO_FONT_MASK_FAMILY); - gchar* style = pango_font_description_to_string( descr ); - Glib::ustring Style = style; - pango_font_description_free(descr); - g_free( style ); - - return std::make_pair( Family, Style ); + return out; +} + +std::pair FontLister::ui_from_fontspec(Glib::ustring fontspec) +{ + PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str()); + const gchar *family = pango_font_description_get_family(descr); + if (!family) + family = "sans-serif"; + Glib::ustring Family = family; + + // PANGO BUG... + // A font spec of Delicious, 500 Italic should result in a family of 'Delicious' + // and a style of 'Medium Italic'. It results instead with: a family of + // 'Delicious, 500' with a style of 'Medium Italic'. We chop of any weight numbers + // at the end of the family: match ",[1-9]00^". + Glib::RefPtr weight = Glib::Regex::create(",[1-9]00$"); + Family = weight->replace(Family, 0, "", Glib::REGEX_MATCH_PARTIAL); + + // Pango canonized strings remove space after comma between family names. Put it back. + size_t i = 0; + while ((i = Family.find(",", i)) != std::string::npos) { + Family.replace(i, 1, ", "); + i += 2; } - std::pair - FontLister::selection_update () { + pango_font_description_unset_fields(descr, PANGO_FONT_MASK_FAMILY); + gchar *style = pango_font_description_to_string(descr); + Glib::ustring Style = style; + pango_font_description_free(descr); + g_free(style); + + return std::make_pair(Family, Style); +} + +std::pair FontLister::selection_update() +{ #ifdef DEBUG_FONT - std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; - std::cout << "FontLister::selection_update: entrance" << std::endl; + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::selection_update: entrance" << std::endl; #endif - // Get fontspec from a selection, preferences, or thin air. - Glib::ustring fontspec; - SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT); - - // Directly from stored font specification. - int result = - sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION); - - //std::cout << " Attempting selected style" << std::endl; - if( result != QUERY_STYLE_NOTHING && query->font_specification.set ) { - fontspec = query->font_specification.value; - //std::cout << " fontspec from query :" << fontspec << ":" << std::endl; - } - - // From style - if( fontspec.empty() ) { + // Get fontspec from a selection, preferences, or thin air. + Glib::ustring fontspec; + SPStyle *query = sp_style_new(SP_ACTIVE_DOCUMENT); + + // Directly from stored font specification. + int result = + sp_desktop_query_style(SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION); + + //std::cout << " Attempting selected style" << std::endl; + if (result != QUERY_STYLE_NOTHING && query->font_specification.set) { + fontspec = query->font_specification.value; + //std::cout << " fontspec from query :" << fontspec << ":" << std::endl; + } + + // From style + if (fontspec.empty()) { //std::cout << " Attempting desktop style" << std::endl; - int rfamily = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); - int rstyle = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); - - // Must have text in selection - if( rfamily != QUERY_STYLE_NOTHING && rstyle != QUERY_STYLE_NOTHING ) { - fontspec = fontspec_from_style( query ); - } - //std::cout << " fontspec from style :" << fontspec << ":" << std::endl; - } - - // From preferences - if( fontspec.empty() ) { + int rfamily = sp_desktop_query_style(SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); + int rstyle = sp_desktop_query_style(SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); + + // Must have text in selection + if (rfamily != QUERY_STYLE_NOTHING && rstyle != QUERY_STYLE_NOTHING) { + fontspec = fontspec_from_style(query); + } + //std::cout << " fontspec from style :" << fontspec << ":" << std::endl; + } + + // From preferences + if (fontspec.empty()) { //std::cout << " Attempting preferences" << std::endl; sp_style_read_from_prefs(query, "/tools/text"); - fontspec = fontspec_from_style( query ); - //std::cout << " fontspec from prefs :" << fontspec << ":" << std::endl; - } - sp_style_unref(query); + fontspec = fontspec_from_style(query); + //std::cout << " fontspec from prefs :" << fontspec << ":" << std::endl; + } + sp_style_unref(query); - // From thin air - if( fontspec.empty() ) { + // From thin air + if (fontspec.empty()) { //std::cout << " Attempting thin air" << std::endl; - fontspec = current_family + ", " + current_style; - //std::cout << " fontspec from thin air :" << fontspec << ":" << std::endl; - } - - // Do we really need? Removes spaces between font-families. - //current_fontspec = canonize_fontspec( fontspec ); - current_fontspec = fontspec; // Ignore for now + fontspec = current_family + ", " + current_style; + //std::cout << " fontspec from thin air :" << fontspec << ":" << std::endl; + } - current_fontspec_system = system_fontspec( current_fontspec ); + // Do we really need? Removes spaces between font-families. + //current_fontspec = canonize_fontspec( fontspec ); + current_fontspec = fontspec; // Ignore for now - std::pair ui = ui_from_fontspec( current_fontspec ); - set_font_family( ui.first ); - set_font_style( ui.second ); + current_fontspec_system = system_fontspec(current_fontspec); + + std::pair ui = ui_from_fontspec(current_fontspec); + set_font_family(ui.first); + set_font_style(ui.second); #ifdef DEBUG_FONT - std::cout << " family_row: :" << current_family_row << ":" << std::endl; - std::cout << " canonized: :" << current_fontspec << ":" << std::endl; - std::cout << " system: :" << current_fontspec_system << ":" << std::endl; - std::cout << " family: :" << current_family << ":" << std::endl; - std::cout << " style: :" << current_style << ":" << std::endl; - std::cout << "FontLister::selection_update: exit" << std::endl; - std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; + std::cout << " family_row: :" << current_family_row << ":" << std::endl; + std::cout << " canonized: :" << current_fontspec << ":" << std::endl; + std::cout << " system: :" << current_fontspec_system << ":" << std::endl; + std::cout << " family: :" << current_family << ":" << std::endl; + std::cout << " style: :" << current_style << ":" << std::endl; + std::cout << "FontLister::selection_update: exit" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; #endif - return std::make_pair( current_family, current_style ); - } - + return std::make_pair(current_family, current_style); +} + // Set fontspec. If check is false, best style match will not be done. -void FontLister::set_fontspec(Glib::ustring new_fontspec, gboolean /*check*/) +void FontLister::set_fontspec(Glib::ustring new_fontspec, bool /*check*/) { - std::pair ui = ui_from_fontspec( new_fontspec ); + std::pair ui = ui_from_fontspec(new_fontspec); Glib::ustring new_family = ui.first; Glib::ustring new_style = ui.second; @@ -439,13 +461,13 @@ void FontLister::set_fontspec(Glib::ustring new_fontspec, gboolean /*check*/) << " style:" << new_style << std::endl; #endif - set_font_family( new_family, false ); - set_font_style( new_style ); + set_font_family(new_family, false); + set_font_style(new_style); } // TODO: use to determine font-selector best style -std::pair FontLister::new_font_family (Glib::ustring new_family, gboolean /*check_style*/ ) +std::pair FontLister::new_font_family(Glib::ustring new_family, bool /*check_style*/) { #ifdef DEBUG_FONT std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; @@ -453,12 +475,12 @@ std::pair FontLister::new_font_family (Glib::ustri #endif // No need to do anything if new family is same as old family. - if ( familyNamesAreEqual( new_family, current_family ) ) { + if (familyNamesAreEqual(new_family, current_family)) { #ifdef DEBUG_FONT - std::cout << "FontLister::new_font_family: exit: no change in family." << std::endl; - std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; + std::cout << "FontLister::new_font_family: exit: no change in family." << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; #endif - return std::make_pair( current_family, current_style ); + return std::make_pair(current_family, current_style); } // We need to do two things: @@ -466,601 +488,602 @@ std::pair FontLister::new_font_family (Glib::ustri // 2. Select best valid style match to old style. // For finding style list, use list of first family in font-family list. - GList* styles = NULL; - Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" ); - while( iter != font_list_store->children().end() ) { + GList *styles = NULL; + Gtk::TreeModel::iterator iter = font_list_store->get_iter("0"); + while (iter != font_list_store->children().end()) { - Gtk::TreeModel::Row row = *iter; + Gtk::TreeModel::Row row = *iter; - if( familyNamesAreEqual( new_family, row[FontList.family] ) ) { + if (familyNamesAreEqual(new_family, row[FontList.family])) { + if (!row[FontList.styles]) { + row[FontList.styles] = font_factory::Default()->GetUIStyles(row[FontList.pango_family]); + } styles = row[FontList.styles]; break; - } - ++iter; + } + ++iter; } // Newly typed in font-family may not yet be in list... use default list. // TODO: if font-family is list, check if first family in list is on system // and set style accordingly. - if( styles == NULL ) { - styles = default_styles; + if (styles == NULL) { + styles = default_styles; } - + // Update style list. style_list_store->freeze_notify(); style_list_store->clear(); - for (GList *l=styles; l; l = l->next) { - Gtk::TreeModel::iterator treeModelIter = style_list_store->append(); - (*treeModelIter)[FontStyleList.cssStyle] = ((StyleNames*)l->data)->CssName; - (*treeModelIter)[FontStyleList.displayStyle] = ((StyleNames*)l->data)->DisplayName; + for (GList *l = styles; l; l = l->next) { + Gtk::TreeModel::iterator treeModelIter = style_list_store->append(); + (*treeModelIter)[FontStyleList.cssStyle] = ((StyleNames *)l->data)->CssName; + (*treeModelIter)[FontStyleList.displayStyle] = ((StyleNames *)l->data)->DisplayName; } style_list_store->thaw_notify(); - + // Find best match to the style from the old font-family to the // styles available with the new font. // TODO: Maybe check if an exact match exists before using Pango. - Glib::ustring best_style = get_best_style_match( new_family, current_style ); + Glib::ustring best_style = get_best_style_match(new_family, current_style); #ifdef DEBUG_FONT std::cout << "FontLister::new_font_family: exit: " << new_family << " " << best_style << std::endl; std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; #endif - return std::make_pair( new_family, best_style ); + return std::make_pair(new_family, best_style); } - - std::pair - FontLister::set_font_family (Glib::ustring new_family, gboolean check_style) { + +std::pair FontLister::set_font_family(Glib::ustring new_family, bool check_style) +{ #ifdef DEBUG_FONT - std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; - std::cout << "FontLister::set_font_family: " << new_family << std::endl; + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::set_font_family: " << new_family << std::endl; #endif - std::pair ui = new_font_family( new_family, check_style ); - current_family = ui.first; - current_style = ui.second; - current_fontspec = canonize_fontspec( current_family + ", " + current_style ); - current_fontspec_system = system_fontspec( current_fontspec ); + std::pair ui = new_font_family(new_family, check_style); + current_family = ui.first; + current_style = ui.second; + current_fontspec = canonize_fontspec(current_family + ", " + current_style); + current_fontspec_system = system_fontspec(current_fontspec); #ifdef DEBUG_FONT - std::cout << " family_row: :" << current_family_row << ":" << std::endl; - std::cout << " canonized: :" << current_fontspec << ":" << std::endl; - std::cout << " system: :" << current_fontspec_system << ":" << std::endl; - std::cout << " family: :" << current_family << ":" << std::endl; - std::cout << " style: :" << current_style << ":" << std::endl; - std::cout << "FontLister::set_font_family: end" << std::endl; - std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; + std::cout << " family_row: :" << current_family_row << ":" << std::endl; + std::cout << " canonized: :" << current_fontspec << ":" << std::endl; + std::cout << " system: :" << current_fontspec_system << ":" << std::endl; + std::cout << " family: :" << current_family << ":" << std::endl; + std::cout << " style: :" << current_style << ":" << std::endl; + std::cout << "FontLister::set_font_family: end" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; #endif - return ui; - } - + return ui; +} - std::pair - FontLister::set_font_family (int row, gboolean check_style) { + +std::pair FontLister::set_font_family(int row, bool check_style) +{ #ifdef DEBUG_FONT - std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; - std::cout << "FontLister::set_font_family( row ): " << row << std::endl; + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::set_font_family( row ): " << row << std::endl; #endif - current_family_row = row; - Gtk::TreePath path; - path.push_back( row ); - Glib::ustring new_family = current_family; - Gtk::TreeModel::iterator iter = font_list_store->get_iter( path ); - if( iter ) { - new_family = (*iter)[FontList.family]; - } + current_family_row = row; + Gtk::TreePath path; + path.push_back(row); + Glib::ustring new_family = current_family; + Gtk::TreeModel::iterator iter = font_list_store->get_iter(path); + if (iter) { + new_family = (*iter)[FontList.family]; + } - std::pair ui = set_font_family( new_family, check_style ); + std::pair ui = set_font_family(new_family, check_style); #ifdef DEBUG_FONT - std::cout << "FontLister::set_font_family( row ): end" << std::endl; - std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; + std::cout << "FontLister::set_font_family( row ): end" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; #endif - return ui; - } + return ui; +} - void - FontLister::set_font_style (Glib::ustring new_style) { +void FontLister::set_font_style(Glib::ustring new_style) +{ - // TODO: Validate input using Pango. If Pango doesn't recognize a style it will - // attach the "invalid" style to the font-family. +// TODO: Validate input using Pango. If Pango doesn't recognize a style it will +// attach the "invalid" style to the font-family. #ifdef DEBUG_FONT - std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; - std::cout << "FontLister:set_font_style: " << new_style << std::endl; + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister:set_font_style: " << new_style << std::endl; #endif - current_style = new_style; - current_fontspec = canonize_fontspec( current_family + ", " + current_style ); - current_fontspec_system = system_fontspec( current_fontspec ); + current_style = new_style; + current_fontspec = canonize_fontspec(current_family + ", " + current_style); + current_fontspec_system = system_fontspec(current_fontspec); #ifdef DEBUG_FONT - std::cout << " canonized: :" << current_fontspec << ":" << std::endl; - std::cout << " system: :" << current_fontspec_system << ":" << std::endl; - std::cout << " family: " << current_family << std::endl; - std::cout << " style: " << current_style << std::endl; - std::cout << "FontLister::set_font_style: end" << std::endl; - std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; + std::cout << " canonized: :" << current_fontspec << ":" << std::endl; + std::cout << " system: :" << current_fontspec_system << ":" << std::endl; + std::cout << " family: " << current_family << std::endl; + std::cout << " style: " << current_style << std::endl; + std::cout << "FontLister::set_font_style: end" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; #endif - } +} - // We do this ourselves as we can't rely on FontFactory. - void - FontLister::fill_css( SPCSSAttr *css, Glib::ustring fontspec ) { +// We do this ourselves as we can't rely on FontFactory. +void FontLister::fill_css(SPCSSAttr *css, Glib::ustring fontspec) +{ - if( fontspec.empty() ) { - fontspec = current_fontspec; - } - std::pair ui = ui_from_fontspec( fontspec ); + if (fontspec.empty()) { + fontspec = current_fontspec; + } + std::pair ui = ui_from_fontspec(fontspec); - Glib::ustring family = ui.first; + Glib::ustring family = ui.first; - // Font spec is single quoted... for the moment - Glib::ustring fontspec_quoted( fontspec ); - css_quote( fontspec_quoted ); - sp_repr_css_set_property (css, "-inkscape-font-specification", fontspec_quoted.c_str() ); + // Font spec is single quoted... for the moment + Glib::ustring fontspec_quoted(fontspec); + css_quote(fontspec_quoted); + sp_repr_css_set_property(css, "-inkscape-font-specification", fontspec_quoted.c_str()); - // Font families needs to be properly quoted in CSS (used unquoted in font-lister) - css_font_family_quote( family ); - sp_repr_css_set_property (css, "font-family", family.c_str() ); + // Font families needs to be properly quoted in CSS (used unquoted in font-lister) + css_font_family_quote(family); + sp_repr_css_set_property(css, "font-family", family.c_str()); - PangoFontDescription *desc = pango_font_description_from_string( fontspec.c_str() ); - PangoWeight weight = pango_font_description_get_weight( desc ); - switch ( weight ) { - case PANGO_WEIGHT_THIN: - sp_repr_css_set_property (css, "font-weight", "100" ); - break; - case PANGO_WEIGHT_ULTRALIGHT: - sp_repr_css_set_property (css, "font-weight", "200" ); - break; - case PANGO_WEIGHT_LIGHT: - sp_repr_css_set_property (css, "font-weight", "300" ); - break; + PangoFontDescription *desc = pango_font_description_from_string(fontspec.c_str()); + PangoWeight weight = pango_font_description_get_weight(desc); + switch (weight) { + case PANGO_WEIGHT_THIN: + sp_repr_css_set_property(css, "font-weight", "100"); + break; + case PANGO_WEIGHT_ULTRALIGHT: + sp_repr_css_set_property(css, "font-weight", "200"); + break; + case PANGO_WEIGHT_LIGHT: + sp_repr_css_set_property(css, "font-weight", "300"); + break; #if PANGO_VERSION_CHECK(1,36,6) - case PANGO_WEIGHT_SEMILIGHT: - sp_repr_css_set_property (css, "font-weight", "350" ); - break; + case PANGO_WEIGHT_SEMILIGHT: + sp_repr_css_set_property (css, "font-weight", "350"); + break; #endif - case PANGO_WEIGHT_BOOK: - sp_repr_css_set_property (css, "font-weight", "380" ); - break; - case PANGO_WEIGHT_NORMAL: - sp_repr_css_set_property (css, "font-weight", "normal" ); - break; - case PANGO_WEIGHT_MEDIUM: - sp_repr_css_set_property (css, "font-weight", "500" ); - break; - case PANGO_WEIGHT_SEMIBOLD: - sp_repr_css_set_property (css, "font-weight", "600" ); - break; - case PANGO_WEIGHT_BOLD: - sp_repr_css_set_property (css, "font-weight", "bold" ); - break; - case PANGO_WEIGHT_ULTRABOLD: - sp_repr_css_set_property (css, "font-weight", "800" ); - break; - case PANGO_WEIGHT_HEAVY: - sp_repr_css_set_property (css, "font-weight", "900" ); - break; - case PANGO_WEIGHT_ULTRAHEAVY: - sp_repr_css_set_property (css, "font-weight", "1000" ); - break; - } - - PangoStyle style = pango_font_description_get_style( desc ); - switch ( style ) { - case PANGO_STYLE_NORMAL: - sp_repr_css_set_property (css, "font-style", "normal" ); - break; - case PANGO_STYLE_OBLIQUE: - sp_repr_css_set_property (css, "font-style", "oblique" ); - break; - case PANGO_STYLE_ITALIC: - sp_repr_css_set_property (css, "font-style", "italic" ); - break; - } - - PangoStretch stretch = pango_font_description_get_stretch( desc ); - switch ( stretch ) { - case PANGO_STRETCH_ULTRA_CONDENSED: - sp_repr_css_set_property (css, "font-stretch", "ultra-condensed" ); - break; - case PANGO_STRETCH_EXTRA_CONDENSED: - sp_repr_css_set_property (css, "font-stretch", "extra-condensed" ); - break; - case PANGO_STRETCH_CONDENSED: - sp_repr_css_set_property (css, "font-stretch", "condensed" ); - break; - case PANGO_STRETCH_SEMI_CONDENSED: - sp_repr_css_set_property (css, "font-stretch", "semi-condensed" ); - break; - case PANGO_STRETCH_NORMAL: - sp_repr_css_set_property (css, "font-stretch", "normal" ); - break; - case PANGO_STRETCH_SEMI_EXPANDED: - sp_repr_css_set_property (css, "font-stretch", "semi-expanded" ); - break; - case PANGO_STRETCH_EXPANDED: - sp_repr_css_set_property (css, "font-stretch", "expanded" ); - break; - case PANGO_STRETCH_EXTRA_EXPANDED: - sp_repr_css_set_property (css, "font-stretch", "extra-expanded" ); - break; - case PANGO_STRETCH_ULTRA_EXPANDED: - sp_repr_css_set_property (css, "font-stretch", "ultra-expanded" ); - break; - } - - PangoVariant variant = pango_font_description_get_variant( desc ); - switch ( variant ) { - case PANGO_VARIANT_NORMAL: - sp_repr_css_set_property (css, "font-variant", "normal" ); - break; - case PANGO_VARIANT_SMALL_CAPS: - sp_repr_css_set_property (css, "font-variant", "small-caps" ); - break; - } + case PANGO_WEIGHT_BOOK: + sp_repr_css_set_property(css, "font-weight", "380"); + break; + case PANGO_WEIGHT_NORMAL: + sp_repr_css_set_property(css, "font-weight", "normal"); + break; + case PANGO_WEIGHT_MEDIUM: + sp_repr_css_set_property(css, "font-weight", "500"); + break; + case PANGO_WEIGHT_SEMIBOLD: + sp_repr_css_set_property(css, "font-weight", "600"); + break; + case PANGO_WEIGHT_BOLD: + sp_repr_css_set_property(css, "font-weight", "bold"); + break; + case PANGO_WEIGHT_ULTRABOLD: + sp_repr_css_set_property(css, "font-weight", "800"); + break; + case PANGO_WEIGHT_HEAVY: + sp_repr_css_set_property(css, "font-weight", "900"); + break; + case PANGO_WEIGHT_ULTRAHEAVY: + sp_repr_css_set_property(css, "font-weight", "1000"); + break; } - // We do this ourselves as we can't rely on FontFactory. - Glib::ustring - FontLister::fontspec_from_style (SPStyle* style) { + PangoStyle style = pango_font_description_get_style(desc); + switch (style) { + case PANGO_STYLE_NORMAL: + sp_repr_css_set_property(css, "font-style", "normal"); + break; + case PANGO_STYLE_OBLIQUE: + sp_repr_css_set_property(css, "font-style", "oblique"); + break; + case PANGO_STYLE_ITALIC: + sp_repr_css_set_property(css, "font-style", "italic"); + break; + } - //std::cout << "FontLister:fontspec_from_style: " << std::endl; + PangoStretch stretch = pango_font_description_get_stretch(desc); + switch (stretch) { + case PANGO_STRETCH_ULTRA_CONDENSED: + sp_repr_css_set_property(css, "font-stretch", "ultra-condensed"); + break; + case PANGO_STRETCH_EXTRA_CONDENSED: + sp_repr_css_set_property(css, "font-stretch", "extra-condensed"); + break; + case PANGO_STRETCH_CONDENSED: + sp_repr_css_set_property(css, "font-stretch", "condensed"); + break; + case PANGO_STRETCH_SEMI_CONDENSED: + sp_repr_css_set_property(css, "font-stretch", "semi-condensed"); + break; + case PANGO_STRETCH_NORMAL: + sp_repr_css_set_property(css, "font-stretch", "normal"); + break; + case PANGO_STRETCH_SEMI_EXPANDED: + sp_repr_css_set_property(css, "font-stretch", "semi-expanded"); + break; + case PANGO_STRETCH_EXPANDED: + sp_repr_css_set_property(css, "font-stretch", "expanded"); + break; + case PANGO_STRETCH_EXTRA_EXPANDED: + sp_repr_css_set_property(css, "font-stretch", "extra-expanded"); + break; + case PANGO_STRETCH_ULTRA_EXPANDED: + sp_repr_css_set_property(css, "font-stretch", "ultra-expanded"); + break; + } - Glib::ustring fontspec; - if (style) { + PangoVariant variant = pango_font_description_get_variant(desc); + switch (variant) { + case PANGO_VARIANT_NORMAL: + sp_repr_css_set_property(css, "font-variant", "normal"); + break; + case PANGO_VARIANT_SMALL_CAPS: + sp_repr_css_set_property(css, "font-variant", "small-caps"); + break; + } +} + +// We do this ourselves as we can't rely on FontFactory. +Glib::ustring FontLister::fontspec_from_style(SPStyle *style) +{ + + //std::cout << "FontLister:fontspec_from_style: " << std::endl; - // First try to use the font specification if it is set - if (style->font_specification.set - && style->font_specification.value - && *style->font_specification.value) { + Glib::ustring fontspec; + if (style) { - fontspec = style->font_specification.value; + // First try to use the font specification if it is set + if (style->font_specification.set && style->font_specification.value && *style->font_specification.value) { + + fontspec = style->font_specification.value; } else { - fontspec = style->font_family.value; - fontspec += ","; - - // Use weight names as defined by Pango - switch (style->font_weight.computed) { + fontspec = style->font_family.value; + fontspec += ","; - case SP_CSS_FONT_WEIGHT_100: - fontspec += " Thin"; - break; - - case SP_CSS_FONT_WEIGHT_200: - fontspec += " Ultra-Light"; - break; + // Use weight names as defined by Pango + switch (style->font_weight.computed) { - case SP_CSS_FONT_WEIGHT_300: - fontspec += " Light"; - break; + case SP_CSS_FONT_WEIGHT_100: + fontspec += " Thin"; + break; - case SP_CSS_FONT_WEIGHT_400: - case SP_CSS_FONT_WEIGHT_NORMAL: - //fontspec += " normal"; - break; - - case SP_CSS_FONT_WEIGHT_500: - fontspec += " Medium"; - break; - - case SP_CSS_FONT_WEIGHT_600: - fontspec += " Semi-Bold"; - break; - - case SP_CSS_FONT_WEIGHT_700: - case SP_CSS_FONT_WEIGHT_BOLD: - fontspec += " Bold"; - break; - - case SP_CSS_FONT_WEIGHT_800: - fontspec += " Ultra-Bold"; - break; - - case SP_CSS_FONT_WEIGHT_900: - fontspec += " Heavy"; - break; - - case SP_CSS_FONT_WEIGHT_LIGHTER: - case SP_CSS_FONT_WEIGHT_BOLDER: - default: - g_warning("Unrecognized font_weight.computed value"); - break; - } - - switch (style->font_style.computed) { - case SP_CSS_FONT_STYLE_ITALIC: - fontspec += " italic"; - break; - - case SP_CSS_FONT_STYLE_OBLIQUE: - fontspec += " oblique"; - break; - - case SP_CSS_FONT_STYLE_NORMAL: - default: - //fontspec += " normal"; - break; - } - - switch (style->font_stretch.computed) { - - case SP_CSS_FONT_STRETCH_ULTRA_CONDENSED: - fontspec += " ultra-condensed"; - break; - - case SP_CSS_FONT_STRETCH_EXTRA_CONDENSED: - fontspec += " extra-condensed"; - break; - - case SP_CSS_FONT_STRETCH_CONDENSED: - case SP_CSS_FONT_STRETCH_NARROWER: - fontspec += " condensed"; - break; - - case SP_CSS_FONT_STRETCH_SEMI_CONDENSED: - fontspec += " semi-condensed"; - break; - - case SP_CSS_FONT_STRETCH_NORMAL: - //fontspec += " normal"; - break; - - case SP_CSS_FONT_STRETCH_SEMI_EXPANDED: - fontspec += " semi-expanded"; - break; - - case SP_CSS_FONT_STRETCH_EXPANDED: - case SP_CSS_FONT_STRETCH_WIDER: - fontspec += " expanded"; - break; - - case SP_CSS_FONT_STRETCH_EXTRA_EXPANDED: - fontspec += " extra-expanded"; - break; - - case SP_CSS_FONT_STRETCH_ULTRA_EXPANDED: - fontspec += " ultra-expanded"; - break; - - default: - //fontspec += " normal"; - break; - } - - switch (style->font_variant.computed) { - - case SP_CSS_FONT_VARIANT_SMALL_CAPS: - fontspec += "small-caps"; - break; - - default: - //fontspec += "normal"; - break; - } - } - } - return canonize_fontspec( fontspec ); - } + case SP_CSS_FONT_WEIGHT_200: + fontspec += " Ultra-Light"; + break; + case SP_CSS_FONT_WEIGHT_300: + fontspec += " Light"; + break; - Gtk::TreeModel::Row - FontLister::get_row_for_font (Glib::ustring family) - { - Gtk::TreePath path; + case SP_CSS_FONT_WEIGHT_400: + case SP_CSS_FONT_WEIGHT_NORMAL: + //fontspec += " normal"; + break; + + case SP_CSS_FONT_WEIGHT_500: + fontspec += " Medium"; + break; + + case SP_CSS_FONT_WEIGHT_600: + fontspec += " Semi-Bold"; + break; - Gtk::TreeModel::iterator iter = font_list_store->get_iter( "0" ); - while( iter != font_list_store->children().end() ) { + case SP_CSS_FONT_WEIGHT_700: + case SP_CSS_FONT_WEIGHT_BOLD: + fontspec += " Bold"; + break; - Gtk::TreeModel::Row row = *iter; + case SP_CSS_FONT_WEIGHT_800: + fontspec += " Ultra-Bold"; + break; - if( familyNamesAreEqual( family, row[FontList.family] ) ) { - return row; - } + case SP_CSS_FONT_WEIGHT_900: + fontspec += " Heavy"; + break; - ++iter; - } + case SP_CSS_FONT_WEIGHT_LIGHTER: + case SP_CSS_FONT_WEIGHT_BOLDER: + default: + g_warning("Unrecognized font_weight.computed value"); + break; + } - throw FAMILY_NOT_FOUND; - } + switch (style->font_style.computed) { + case SP_CSS_FONT_STYLE_ITALIC: + fontspec += " italic"; + break; - Gtk::TreePath - FontLister::get_path_for_font (Glib::ustring family) - { - return font_list_store->get_path( get_row_for_font ( family ) ); + case SP_CSS_FONT_STYLE_OBLIQUE: + fontspec += " oblique"; + break; + + case SP_CSS_FONT_STYLE_NORMAL: + default: + //fontspec += " normal"; + break; + } + + switch (style->font_stretch.computed) { + + case SP_CSS_FONT_STRETCH_ULTRA_CONDENSED: + fontspec += " ultra-condensed"; + break; + + case SP_CSS_FONT_STRETCH_EXTRA_CONDENSED: + fontspec += " extra-condensed"; + break; + + case SP_CSS_FONT_STRETCH_CONDENSED: + case SP_CSS_FONT_STRETCH_NARROWER: + fontspec += " condensed"; + break; + + case SP_CSS_FONT_STRETCH_SEMI_CONDENSED: + fontspec += " semi-condensed"; + break; + + case SP_CSS_FONT_STRETCH_NORMAL: + //fontspec += " normal"; + break; + + case SP_CSS_FONT_STRETCH_SEMI_EXPANDED: + fontspec += " semi-expanded"; + break; + + case SP_CSS_FONT_STRETCH_EXPANDED: + case SP_CSS_FONT_STRETCH_WIDER: + fontspec += " expanded"; + break; + + case SP_CSS_FONT_STRETCH_EXTRA_EXPANDED: + fontspec += " extra-expanded"; + break; + + case SP_CSS_FONT_STRETCH_ULTRA_EXPANDED: + fontspec += " ultra-expanded"; + break; + + default: + //fontspec += " normal"; + break; + } + + switch (style->font_variant.computed) { + + case SP_CSS_FONT_VARIANT_SMALL_CAPS: + fontspec += "small-caps"; + break; + + default: + //fontspec += "normal"; + break; + } + } } + return canonize_fontspec(fontspec); +} - Gtk::TreeModel::Row - FontLister::get_row_for_style (Glib::ustring style) - { - Gtk::TreePath path; - Gtk::TreeModel::iterator iter = style_list_store->get_iter( "0" ); - while( iter != style_list_store->children().end() ) { +Gtk::TreeModel::Row FontLister::get_row_for_font(Glib::ustring family) +{ + Gtk::TreePath path; - Gtk::TreeModel::Row row = *iter; + Gtk::TreeModel::iterator iter = font_list_store->get_iter("0"); + while (iter != font_list_store->children().end()) { - if( familyNamesAreEqual( style, row[FontStyleList.cssStyle] ) ) { - return row; - } + Gtk::TreeModel::Row row = *iter; - ++iter; - } + if (familyNamesAreEqual(family, row[FontList.family])) { + return row; + } - throw STYLE_NOT_FOUND; + ++iter; } - static gint - compute_distance (const PangoFontDescription *a, - const PangoFontDescription *b ) { - - // Weight: multiples of 100 - gint distance = abs( pango_font_description_get_weight( a ) - - pango_font_description_get_weight( b ) ); - - distance += 10000 * abs( pango_font_description_get_stretch( a ) - - pango_font_description_get_stretch( b ) ); - - PangoStyle style_a = pango_font_description_get_style( a ); - PangoStyle style_b = pango_font_description_get_style( b ); - if( style_a != style_b ) { - if( (style_a == PANGO_STYLE_OBLIQUE && style_b == PANGO_STYLE_ITALIC) || - (style_b == PANGO_STYLE_OBLIQUE && style_a == PANGO_STYLE_ITALIC) ) { - distance += 1000; // Oblique and italic are almost the same - } else { - distance += 100000; // Normal vs oblique/italic, not so similar - } - } - - // Normal vs small-caps - distance += 1000000 * abs( pango_font_description_get_variant( a ) - - pango_font_description_get_variant( b ) ); - return distance; + throw FAMILY_NOT_FOUND; +} + +Gtk::TreePath FontLister::get_path_for_font(Glib::ustring family) +{ + return font_list_store->get_path(get_row_for_font(family)); +} + +Gtk::TreeModel::Row FontLister::get_row_for_style(Glib::ustring style) +{ + Gtk::TreePath path; + + Gtk::TreeModel::iterator iter = style_list_store->get_iter("0"); + while (iter != style_list_store->children().end()) { + + Gtk::TreeModel::Row row = *iter; + + if (familyNamesAreEqual(style, row[FontStyleList.cssStyle])) { + return row; + } + + ++iter; } - // This is inspired by pango_font_description_better_match, but that routine - // always returns false if variant or stretch are different. This means, for - // example, that PT Sans Narrow with style Bold Condensed is never matched - // to another font-family with Bold style. - gboolean - font_description_better_match( PangoFontDescription* target, - PangoFontDescription* old_desc, - PangoFontDescription* new_desc ) { + throw STYLE_NOT_FOUND; +} + +static gint compute_distance(const PangoFontDescription *a, const PangoFontDescription *b) +{ - if( old_desc == NULL ) return true; - if( new_desc == NULL ) return false; + // Weight: multiples of 100 + gint distance = abs(pango_font_description_get_weight(a) - + pango_font_description_get_weight(b)); - int old_distance = compute_distance( target, old_desc ); - int new_distance = compute_distance( target, new_desc ); - //std::cout << "font_description_better_match: old: " << old_distance << std::endl; - //std::cout << " new: " << new_distance << std::endl; + distance += 10000 * abs(pango_font_description_get_stretch(a) - + pango_font_description_get_stretch(b)); - return (new_distance < old_distance ); + PangoStyle style_a = pango_font_description_get_style(a); + PangoStyle style_b = pango_font_description_get_style(b); + if (style_a != style_b) { + if ((style_a == PANGO_STYLE_OBLIQUE && style_b == PANGO_STYLE_ITALIC) || + (style_b == PANGO_STYLE_OBLIQUE && style_a == PANGO_STYLE_ITALIC)) { + distance += 1000; // Oblique and italic are almost the same + } else { + distance += 100000; // Normal vs oblique/italic, not so similar + } } - // void - // font_description_dump( PangoFontDescription* target ) { - // std::cout << " Font: " << pango_font_description_to_string( target ) << std::endl; - // std::cout << " style: " << pango_font_description_get_style( target ) << std::endl; - // std::cout << " weight: " << pango_font_description_get_weight( target ) << std::endl; - // std::cout << " variant: " << pango_font_description_get_variant( target ) << std::endl; - // std::cout << " stretch: " << pango_font_description_get_stretch( target ) << std::endl; - // std::cout << " gravity: " << pango_font_description_get_gravity( target ) << std::endl; - // } - - /* Returns style string */ - // TODO: Remove or turn into function to be used by new_font_family. - Glib::ustring - FontLister::get_best_style_match (Glib::ustring family, Glib::ustring target_style) { + // Normal vs small-caps + distance += 1000000 * abs(pango_font_description_get_variant(a) - + pango_font_description_get_variant(b)); + return distance; +} + +// This is inspired by pango_font_description_better_match, but that routine +// always returns false if variant or stretch are different. This means, for +// example, that PT Sans Narrow with style Bold Condensed is never matched +// to another font-family with Bold style. +gboolean font_description_better_match(PangoFontDescription *target, PangoFontDescription *old_desc, PangoFontDescription *new_desc) +{ + if (old_desc == NULL) + return true; + if (new_desc == NULL) + return false; + + int old_distance = compute_distance(target, old_desc); + int new_distance = compute_distance(target, new_desc); + //std::cout << "font_description_better_match: old: " << old_distance << std::endl; + //std::cout << " new: " << new_distance << std::endl; + + return (new_distance < old_distance); +} + +// void +// font_description_dump( PangoFontDescription* target ) { +// std::cout << " Font: " << pango_font_description_to_string( target ) << std::endl; +// std::cout << " style: " << pango_font_description_get_style( target ) << std::endl; +// std::cout << " weight: " << pango_font_description_get_weight( target ) << std::endl; +// std::cout << " variant: " << pango_font_description_get_variant( target ) << std::endl; +// std::cout << " stretch: " << pango_font_description_get_stretch( target ) << std::endl; +// std::cout << " gravity: " << pango_font_description_get_gravity( target ) << std::endl; +// } + +/* Returns style string */ +// TODO: Remove or turn into function to be used by new_font_family. +Glib::ustring FontLister::get_best_style_match(Glib::ustring family, Glib::ustring target_style) +{ #ifdef DEBUG_FONT - std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; - std::cout << "FontLister::get_best_style_match: " << family << " : " << target_style << std::endl; + std::cout << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + std::cout << "FontLister::get_best_style_match: " << family << " : " << target_style << std::endl; #endif - Glib::ustring fontspec = family + ", " + target_style; + Glib::ustring fontspec = family + ", " + target_style; - Gtk::TreeModel::Row row; - try { - row = get_row_for_font( family ); - } catch (...) { - //std::cout << " ERROR: can't find family: " << family << std::endl; - return (target_style); - } + Gtk::TreeModel::Row row; + try + { + row = get_row_for_font(family); + } + catch (...) + { + //std::cout << " ERROR: can't find family: " << family << std::endl; + return (target_style); + } - PangoFontDescription* target = pango_font_description_from_string( fontspec.c_str() ); - PangoFontDescription* best = NULL; + PangoFontDescription *target = pango_font_description_from_string(fontspec.c_str()); + PangoFontDescription *best = NULL; - //font_description_dump( target ); + //font_description_dump( target ); - GList* styles = row[FontList.styles]; - for (GList *l=styles; l; l = l->next) { + if (!row[FontList.styles]) { + row[FontList.styles] = font_factory::Default()->GetUIStyles(row[FontList.pango_family]); + } + GList *styles = row[FontList.styles]; + for (GList *l = styles; l; l = l->next) { Glib::ustring fontspec = family + ", " + ((StyleNames *)l->data)->CssName; - PangoFontDescription* candidate = pango_font_description_from_string( fontspec.c_str() ); - //font_description_dump( candidate ); - //std::cout << " " << font_description_better_match( target, best, candidate ) << std::endl; - if( font_description_better_match( target, best, candidate ) ) { - pango_font_description_free( best ); - best = candidate; - //std::cout << " ... better: " << std::endl; - } else { - pango_font_description_free( candidate ); - //std::cout << " ... not better: " << std::endl; - } - } - - Glib::ustring best_style = target_style; - if( best ) { - pango_font_description_unset_fields( best, PANGO_FONT_MASK_FAMILY ); - best_style = pango_font_description_to_string( best ); - } - - if( target ) pango_font_description_free( target ); - if( best ) pango_font_description_free( best ); + PangoFontDescription *candidate = pango_font_description_from_string(fontspec.c_str()); + //font_description_dump( candidate ); + //std::cout << " " << font_description_better_match( target, best, candidate ) << std::endl; + if (font_description_better_match(target, best, candidate)) { + pango_font_description_free(best); + best = candidate; + //std::cout << " ... better: " << std::endl; + } else { + pango_font_description_free(candidate); + //std::cout << " ... not better: " << std::endl; + } + } + + Glib::ustring best_style = target_style; + if (best) { + pango_font_description_unset_fields(best, PANGO_FONT_MASK_FAMILY); + best_style = pango_font_description_to_string(best); + } + + if (target) + pango_font_description_free(target); + if (best) + pango_font_description_free(best); #ifdef DEBUG_FONT - std::cout << " Returning: " << best_style << std::endl; - std::cout << "FontLister::get_best_style_match: exit" << std::endl; - std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; + std::cout << " Returning: " << best_style << std::endl; + std::cout << "FontLister::get_best_style_match: exit" << std::endl; + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl; #endif - return best_style; - } + return best_style; +} - const Glib::RefPtr - FontLister::get_font_list () const - { - return font_list_store; - } +const Glib::RefPtr FontLister::get_font_list() const +{ + return font_list_store; +} - const Glib::RefPtr - FontLister::get_style_list () const - { - return style_list_store; - } +const Glib::RefPtr FontLister::get_style_list() const +{ + return style_list_store; } +} // namespace Inkscape + // Helper functions // Separator function (if true, a separator will be drawn) -gboolean font_lister_separator_func(GtkTreeModel *model, - GtkTreeIter *iter, - gpointer /*data*/) +gboolean font_lister_separator_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer /*data*/) { - gchar* text = 0; - gtk_tree_model_get(model, iter, 0, &text, -1 ); // Column 0: FontList.family - return (text && strcmp(text,"#") == 0); + gchar *text = 0; + gtk_tree_model_get(model, iter, 0, &text, -1); // Column 0: FontList.family + return (text && strcmp(text, "#") == 0); } -void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer /*data*/) +void font_lister_cell_data_func(GtkCellLayout * /*cell_layout*/, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer /*data*/) { gchar *family; gboolean onSystem = false; gtk_tree_model_get(model, iter, 0, &family, 2, &onSystem, -1); - Glib::ustring family_escaped = g_markup_escape_text(family, -1); + gchar* family_escaped = g_markup_escape_text(family, -1); //g_free(family); Glib::ustring markup; - if( !onSystem ) { + if (!onSystem) { markup = ""; /* See if font-family on system */ - std::vector tokens = Glib::Regex::split_simple("\\s*,\\s*", family_escaped ); - for( size_t i=0; i < tokens.size(); ++i ) { + std::vector tokens = Glib::Regex::split_simple("\\s*,\\s*", family_escaped); + for (size_t i = 0; i < tokens.size(); ++i) { Glib::ustring token = tokens[i]; @@ -1069,17 +1092,17 @@ void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, gchar *family = 0; gboolean onSystem = true; gboolean found = false; - for( valid = gtk_tree_model_get_iter_first( GTK_TREE_MODEL(model), &iter ); + for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter); valid; - valid = gtk_tree_model_iter_next( GTK_TREE_MODEL(model), &iter ) ) { + valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter)) { gtk_tree_model_get(model, &iter, 0, &family, 2, &onSystem, -1); - if( onSystem && familyNamesAreEqual( token, family ) ) { + if (onSystem && familyNamesAreEqual(token, family)) { found = true; break; } } - if( found ) { + if (found) { markup += g_markup_escape_text(token.c_str(), -1); markup += ", "; } else { @@ -1090,13 +1113,13 @@ void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, } } // Remove extra comma and space from end. - if( markup.size() >= 2 ) { - markup.resize( markup.size()-2 ); + if (markup.size() >= 2) { + markup.resize(markup.size() - 2); } markup += ""; // std::cout << markup << std::endl; } else { - markup = family_escaped; + markup = family_escaped; } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -1104,17 +1127,20 @@ void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, if (show_sample) { Glib::ustring sample = prefs->getString("/tools/text/font_sample"); - Glib::ustring sample_escaped = g_markup_escape_text(sample.data(), -1); + gchar* sample_escaped = g_markup_escape_text(sample.data(), -1); markup += " "; markup += sample_escaped; markup += ""; + g_free(sample_escaped); } - g_object_set (G_OBJECT (cell), "markup", markup.c_str(), NULL); + g_object_set(G_OBJECT(cell), "markup", markup.c_str(), NULL); + g_free(family_escaped); } + /* Local Variables: mode:c++ @@ -1124,4 +1150,4 @@ void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : diff --git a/src/libnrtype/font-lister.h b/src/libnrtype/font-lister.h index c89dab550..18e0d4b6e 100644 --- a/src/libnrtype/font-lister.h +++ b/src/libnrtype/font-lister.h @@ -28,294 +28,280 @@ class SPDocument; class SPCSSAttr; class SPStyle; -namespace Inkscape -{ - /** - * This class enumerates fonts using libnrtype into reusable data stores and - * allows for random access to the font-family list and the font-style list. - * Setting the font-family updates the font-style list. "Style" in this case - * refers to everything but family and size (e.g. italic/oblique, weight). - * - * This class handles font-family lists and fonts that are not on the system, - * where there is not an entry in the fontInstanceMap. - * - * This class uses the idea of "font_spec". This is a plain text string as used by - * Pango. It is similar to the CSS font shorthand except that font-family comes - * first and in this class the font-size is not used. - * - * This class uses the FontFactory class to get a list of system fonts - * and to find best matches via Pango. The Pango interface is only setup - * to deal with fonts that are on the system so care must be taken. For - * example, best matches should only be done with the first font-family - * in a font-family list. If the first font-family is not on the system - * then a generic font-family should be used (sans-serif -> Sans). - * - * This class is used by the UI interface (text-toolbar, font-select, etc.). - * - * "Font" includes family and style. It should not be used when one - * means font-family. - */ - class FontLister - { - public: - - enum Exceptions - { - FAMILY_NOT_FOUND, - STYLE_NOT_FOUND - }; - - - virtual ~FontLister (); - - /** GtkTreeModelColumnRecord for the font-family list Gtk::ListStore - */ - class FontListClass - : public Gtk::TreeModelColumnRecord - { - public: - /** Column containing the family name - */ - Gtk::TreeModelColumn family; - - /** Column containing the styles for each family name. - */ - Gtk::TreeModelColumn styles; - - /** Column containing flag if font is on system - */ - Gtk::TreeModelColumn onSystem; - - FontListClass () - { - add (family); - add (styles); - add (onSystem); - } - }; - - FontListClass FontList; - - class FontStyleListClass - : public Gtk::TreeModelColumnRecord - { - public: - /** Column containing the styles as Font designer used. - */ - Gtk::TreeModelColumn displayStyle; - - /** Column containing the styles in CSS/Pango format. - */ - Gtk::TreeModelColumn cssStyle; - - FontStyleListClass () - { - add (cssStyle); - add (displayStyle); - } - }; - - FontStyleListClass FontStyleList; - - /** Returns the ListStore with the family names - * - * The return is const and the function is declared as const. - * The ListStore is ready to be used after class instantiation - * and should not (cannot) be modified. - */ - const Glib::RefPtr - get_font_list () const; - - /** Returns the ListStore with the styles - * - */ - const Glib::RefPtr - get_style_list () const; - - /** Inserts a font family or font-fallback list (for use when not - * already in document or on system). - */ - void - insert_font_family ( Glib::ustring new_family ); - - /** Updates font list to include fonts in document - * - */ - void - update_font_list ( SPDocument* document); - - private: - void - update_font_list_recursive( SPObject *r, std::list *l ); - - public: - static Inkscape::FontLister* - get_instance () - { - static Inkscape::FontLister* instance = new Inkscape::FontLister(); - return instance; - } - - /** Takes a hand written font spec and returns a Pango generated one in - * standard form. - */ - Glib::ustring canonize_fontspec( Glib::ustring fontspec ); - - /** Find closest system font to given font. - */ - Glib::ustring system_fontspec( Glib::ustring fontspec ); - - /** Gets font-family and style from fontspec. - * font-family and style returned. - */ - std::pair - ui_from_fontspec (Glib::ustring fontspec); - - /** Sets font-family and style after a selection change. - * New font-family and style returned. - */ - std::pair - selection_update (); - - /** Sets current_fontspec, etc. If check is false, won't - * try to find best style match (assumes style in fontspec - * valid for given font-family). - */ - void - set_fontspec (Glib::ustring fontspec, gboolean check=true); - - Glib::ustring - get_fontspec () - { - return current_fontspec; - } - - /** Changes font-family, updating style list and attempting to find - * closest style to current_style style (if check_style is true). - * New font-family and style returned. - * Does NOT update current_family and current_style. - * (For potential use in font-selector which doesn't update until - * "Apply" button clicked.) - */ - std::pair - new_font_family (Glib::ustring family, gboolean check_style = true); - - /** Sets font-family, updating style list and attempting - * to find closest style to old current_style. - * New font-family and style returned. - * Updates current_family and current_style. - * Calls new_font_family(). - * (For use in text-toolbar where update is immediate.) - */ - std::pair - set_font_family (Glib::ustring family, gboolean check_style = true); - - /** Sets font-family from row in list store. - * The row can be used to determine if we are in the - * document or system part of the font-family list. - * This is needed to handle scrolling through the - * font-family list correctly. - * Calls set_font_family(). - */ - std::pair - set_font_family (int row, gboolean check_style = true); - - Glib::ustring - get_font_family () - { - return current_family; - } - - int - get_font_family_row () - { - return current_family_row; - } - - /** Sets style. Does not validate style for family. - */ - void - set_font_style (Glib::ustring style); - - Glib::ustring - get_font_style () - { - return current_style; - } - - Glib::ustring - fontspec_from_style (SPStyle* style); - - /** Fill css using current_fontspec. - */ - void - fill_css( SPCSSAttr *css, Glib::ustring fontspec = "" ); - - Gtk::TreeModel::Row - get_row_for_font (Glib::ustring family); - - Gtk::TreePath - get_path_for_font (Glib::ustring family); - - Gtk::TreeModel::Row - get_row_for_style (Glib::ustring style); - - Gtk::TreePath - get_path_for_style (Glib::ustring style); - - std::pair - get_paths (Glib::ustring family, Glib::ustring style); - - /** Return best style match for new font given style for old font. - */ - Glib::ustring - get_best_style_match (Glib::ustring family, Glib::ustring style); - - /* Not Used */ - const NRNameList - get_name_list () const - { - return families; - } - - private: - - FontLister (); - - NRNameList families; - - Glib::RefPtr font_list_store; - Glib::RefPtr style_list_store; - - /** Info for currently selected font (what is shown in the UI). - * May include font-family lists and fonts not on system. - */ - int current_family_row; - Glib::ustring current_family; - Glib::ustring current_style; - Glib::ustring current_fontspec; - - /** fontspec of system font closest to current_fontspec. - * (What the system will use to display current_fontspec.) - */ - Glib::ustring current_fontspec_system; - - /** If a font-family is not on system, this list of styles is used. - */ - GList *default_styles; - }; -} +namespace Inkscape { + +/** + * This class enumerates fonts using libnrtype into reusable data stores and + * allows for random access to the font-family list and the font-style list. + * Setting the font-family updates the font-style list. "Style" in this case + * refers to everything but family and size (e.g. italic/oblique, weight). + * + * This class handles font-family lists and fonts that are not on the system, + * where there is not an entry in the fontInstanceMap. + * + * This class uses the idea of "font_spec". This is a plain text string as used by + * Pango. It is similar to the CSS font shorthand except that font-family comes + * first and in this class the font-size is not used. + * + * This class uses the FontFactory class to get a list of system fonts + * and to find best matches via Pango. The Pango interface is only setup + * to deal with fonts that are on the system so care must be taken. For + * example, best matches should only be done with the first font-family + * in a font-family list. If the first font-family is not on the system + * then a generic font-family should be used (sans-serif -> Sans). + * + * This class is used by the UI interface (text-toolbar, font-select, etc.). + * + * "Font" includes family and style. It should not be used when one + * means font-family. + */ + +class FontLister { +public: + enum Exceptions { + FAMILY_NOT_FOUND, + STYLE_NOT_FOUND + }; + + virtual ~FontLister(); + + /** + * GtkTreeModelColumnRecord for the font-family list Gtk::ListStore + */ + struct FontListClass : public Gtk::TreeModelColumnRecord { + /** + * Column containing the family name + */ + Gtk::TreeModelColumn family; + + /** + * Column containing the styles for each family name. + */ + Gtk::TreeModelColumn styles; + + /** + * Column containing flag if font is on system + */ + Gtk::TreeModelColumn onSystem; + + /** + * Not actually a column. + * Necessary for quick initialization of FontLister, + * we initially store the pango family and if the + * font style is actually used we'll cache it in + * %styles. + */ + Gtk::TreeModelColumn pango_family; + + FontListClass() + { + add(family); + add(styles); + add(onSystem); + add(pango_family); + } + }; + + FontListClass FontList; + + struct FontStyleListClass : public Gtk::TreeModelColumnRecord { + /** + * Column containing the styles as Font designer used. + */ + Gtk::TreeModelColumn displayStyle; + + /** + * Column containing the styles in CSS/Pango format. + */ + Gtk::TreeModelColumn cssStyle; + + FontStyleListClass() + { + add(cssStyle); + add(displayStyle); + } + }; + + FontStyleListClass FontStyleList; + + /** + * @return the ListStore with the family names + * + * The return is const and the function is declared as const. + * The ListStore is ready to be used after class instantiation + * and should not be modified. + */ + const Glib::RefPtr get_font_list() const; + + /** + * @return the ListStore with the styles + */ + const Glib::RefPtr get_style_list() const; + + /** + * Inserts a font family or font-fallback list (for use when not + * already in document or on system). + */ + void insert_font_family(Glib::ustring new_family); + + /** + * Updates font list to include fonts in document. + */ + void update_font_list(SPDocument *document); + +public: + static Inkscape::FontLister *get_instance(); + + /** + * Takes a hand written font spec and returns a Pango generated one in + * standard form. + */ + Glib::ustring canonize_fontspec(Glib::ustring fontspec); + + /** + * Find closest system font to given font. + */ + Glib::ustring system_fontspec(Glib::ustring fontspec); + + /** + * Gets font-family and style from fontspec. + * font-family and style returned. + */ + std::pair ui_from_fontspec(Glib::ustring fontspec); + + /** + * Sets font-family and style after a selection change. + * New font-family and style returned. + */ + std::pair selection_update(); + + /** + * Sets current_fontspec, etc. If check is false, won't + * try to find best style match (assumes style in fontspec + * valid for given font-family). + */ + void set_fontspec(Glib::ustring fontspec, bool check = true); + + Glib::ustring get_fontspec() + { + return current_fontspec; + } + + /** + * Changes font-family, updating style list and attempting to find + * closest style to current_style style (if check_style is true). + * New font-family and style returned. + * Does NOT update current_family and current_style. + * (For potential use in font-selector which doesn't update until + * "Apply" button clicked.) + */ + std::pair new_font_family(Glib::ustring family, bool check_style = true); + + /** + * Sets font-family, updating style list and attempting + * to find closest style to old current_style. + * New font-family and style returned. + * Updates current_family and current_style. + * Calls new_font_family(). + * (For use in text-toolbar where update is immediate.) + */ + std::pair set_font_family(Glib::ustring family, bool check_style = true); + + /** + * Sets font-family from row in list store. + * The row can be used to determine if we are in the + * document or system part of the font-family list. + * This is needed to handle scrolling through the + * font-family list correctly. + * Calls set_font_family(). + */ + std::pair set_font_family(int row, bool check_style = true); + + Glib::ustring get_font_family() + { + return current_family; + } + + int get_font_family_row() + { + return current_family_row; + } + + /** + * Sets style. Does not validate style for family. + */ + void set_font_style(Glib::ustring style); + + Glib::ustring get_font_style() + { + return current_style; + } + + Glib::ustring fontspec_from_style(SPStyle *style); + + /** + * Fill css using current_fontspec. + */ + void fill_css(SPCSSAttr *css, Glib::ustring fontspec = ""); + + Gtk::TreeModel::Row get_row_for_font(Glib::ustring family); + + Gtk::TreePath get_path_for_font(Glib::ustring family); + + Gtk::TreeModel::Row get_row_for_style(Glib::ustring style); + + Gtk::TreePath get_path_for_style(Glib::ustring style); + + std::pair get_paths(Glib::ustring family, Glib::ustring style); + + /** + * Return best style match for new font given style for old font. + */ + Glib::ustring get_best_style_match(Glib::ustring family, Glib::ustring style); + + void ensureRowStyles(GtkTreeModel* model, GtkTreeIter const* iter); + +private: + FontLister(); + + void update_font_list_recursive(SPObject *r, std::list *l); + + Glib::RefPtr font_list_store; + Glib::RefPtr style_list_store; + + /** + * Info for currently selected font (what is shown in the UI). + * May include font-family lists and fonts not on system. + */ + int current_family_row; + Glib::ustring current_family; + Glib::ustring current_style; + Glib::ustring current_fontspec; + + /** + * fontspec of system font closest to current_fontspec. + * (What the system will use to display current_fontspec.) + */ + Glib::ustring current_fontspec_system; + + /** + * If a font-family is not on system, this list of styles is used. + */ + GList *default_styles; +}; + +} // namespace Inkscape // Helper functions gboolean font_lister_separator_func(GtkTreeModel *model, - GtkTreeIter *iter, - gpointer /*data*/); + GtkTreeIter *iter, + gpointer /*data*/); -void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer /*data*/); +void font_lister_cell_data_func(GtkCellLayout * /*cell_layout*/, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer /*data*/); #endif @@ -328,4 +314,4 @@ void font_lister_cell_data_func(GtkCellLayout */*cell_layout*/, fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : diff --git a/src/ui/dialog/text-edit.cpp b/src/ui/dialog/text-edit.cpp index a00db1715..9996fc0f9 100644 --- a/src/ui/dialog/text-edit.cpp +++ b/src/ui/dialog/text-edit.cpp @@ -343,9 +343,9 @@ void TextEdit::onReadSelection ( gboolean dostyle, gboolean /*docontent*/ ) Inkscape::FontLister* fontlister = Inkscape::FontLister::get_instance(); - // This is done for us by text-toolbar. No need to do it twice. - // fontlister->update_font_list( sp_desktop_document( SP_ACTIVE_DESKTOP )); - // fontlister->selection_update(); + // This is normally done for us by text-toolbar but only when we are in text editing context + fontlister->update_font_list(sp_desktop_document(this->desktop)); + fontlister->selection_update(); Glib::ustring fontspec = fontlister->get_fontspec(); diff --git a/src/widgets/font-selector.cpp b/src/widgets/font-selector.cpp index f00f05768..327349844 100644 --- a/src/widgets/font-selector.cpp +++ b/src/widgets/font-selector.cpp @@ -304,6 +304,9 @@ static void sp_font_selector_family_select_row(GtkTreeSelection *selection, GtkTreeModel *model; GtkTreeIter iter; if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; + + Inkscape::FontLister *fontlister = Inkscape::FontLister::get_instance(); + fontlister->ensureRowStyles(model, &iter); // Next get family name with its style list gchar *family; @@ -311,7 +314,6 @@ static void sp_font_selector_family_select_row(GtkTreeSelection *selection, gtk_tree_model_get (model, &iter, 0, &family, 1, &list, -1); // Find best style match for selected family with current style (e.g. of selected text). - Inkscape::FontLister *fontlister = Inkscape::FontLister::get_instance(); Glib::ustring style = fontlister->get_font_style(); Glib::ustring best = fontlister->get_best_style_match (family, style); diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index 36a151c52..2697beb8a 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -819,7 +819,7 @@ static void sp_text_set_sizes(GtkListStore* model_size, int unit) * It is called whenever a text selection is changed, including stepping cursor * through text, or setting focus to text. */ -static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/, GObject *tbl) +static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/, GObject *tbl, bool subselection = false) // don't bother to update font list if subsel changed { #ifdef DEBUG_TEXT static int count = 0; @@ -859,7 +859,9 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontStyleAction" ) ); Inkscape::FontLister* fontlister = Inkscape::FontLister::get_instance(); - fontlister->update_font_list( sp_desktop_document( SP_ACTIVE_DESKTOP )); + if (!subselection) { + fontlister->update_font_list( sp_desktop_document( SP_ACTIVE_DESKTOP )); + } fontlister->selection_update(); // Update font list, but only if widget already created. @@ -1154,7 +1156,7 @@ static void sp_text_toolbox_selection_modified(Inkscape::Selection *selection, g static void sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl) { - sp_text_toolbox_selection_changed (NULL, tbl); + sp_text_toolbox_selection_changed (NULL, tbl, true); } // TODO: possibly share with font-selector by moving most code to font-lister (passing family name) @@ -1640,7 +1642,7 @@ static void text_toolbox_watch_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolB if (SP_IS_TEXT_CONTEXT(ec)) { // Watch selection - c_selection_changed = sp_desktop_selection(desktop)->connectChanged(bind(ptr_fun(sp_text_toolbox_selection_changed), holder)); + c_selection_changed = sp_desktop_selection(desktop)->connectChanged(bind(ptr_fun(sp_text_toolbox_selection_changed), holder, false)); c_selection_modified = sp_desktop_selection (desktop)->connectModified(bind(ptr_fun(sp_text_toolbox_selection_modified), holder)); c_subselection_changed = desktop->connectToolSubselectionChanged(bind(ptr_fun(sp_text_toolbox_subselection_changed), holder)); } else { -- cgit v1.2.3 From a7384aa7c43a5fe4b56e5d5d610ece0cafcaa748 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Wed, 15 Oct 2014 20:03:43 +0200 Subject: Fix strict build with Gtk+ 2 (experimental commit r13513) (bzr r13616.1.2) --- src/libnrtype/font-lister.cpp | 2 +- src/ui/tools/tool-base.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp index 9a978b0fd..39c557cc6 100644 --- a/src/libnrtype/font-lister.cpp +++ b/src/libnrtype/font-lister.cpp @@ -2,8 +2,8 @@ #include #endif -#include #include +#include #include #include diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index f1d90f6c6..1c1e8a17a 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -18,6 +18,8 @@ # include "config.h" #endif +#include "widgets/desktop-widget.h" + #if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H #include #endif @@ -40,7 +42,6 @@ #include "desktop-handles.h" #include "desktop-events.h" #include "desktop-style.h" -#include "widgets/desktop-widget.h" #include "sp-namedview.h" #include "selection.h" #include "interface.h" -- cgit v1.2.3 From 89c93099750a1123ea99bc98feb00ff51ee64373 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Wed, 15 Oct 2014 20:17:43 +0200 Subject: More direct way of finding font-family. One entry per unique style. (experimental r13580 and r12581) (bzr r13616.1.3) --- src/extension/internal/pdfinput/svg-builder.cpp | 11 +- src/libnrtype/FontFactory.cpp | 483 ++---------------------- src/libnrtype/FontFactory.h | 38 +- 3 files changed, 50 insertions(+), 482 deletions(-) (limited to 'src') diff --git a/src/extension/internal/pdfinput/svg-builder.cpp b/src/extension/internal/pdfinput/svg-builder.cpp index 71e6dc6ae..7a504add0 100644 --- a/src/extension/internal/pdfinput/svg-builder.cpp +++ b/src/extension/internal/pdfinput/svg-builder.cpp @@ -123,12 +123,11 @@ void SvgBuilder::_init() { _height = 0; // Fill _availableFontNames (Bug LP #179589) (code cfr. FontLister) - FamilyToStylesMap familyStyleMap; - font_factory::Default()->GetUIFamiliesAndStyles(&familyStyleMap); - for (FamilyToStylesMap::iterator iter = familyStyleMap.begin(); - iter != familyStyleMap.end(); - ++iter) { - _availableFontNames.push_back(iter->first.c_str()); + std::vector families; + font_factory::Default()->GetUIFamilies(families); + for ( std::vector::iterator iter = families.begin(); + iter != families.end(); ++iter ) { + _availableFontNames.push_back(pango_font_family_get_name(*iter)); } _transp_group_stack = NULL; diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index cb9af5eb0..cb9602394 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -130,13 +130,6 @@ font_factory::~font_factory(void) delete tmp; loadedPtr = 0; } - - // Delete the pango font pointers in the string to instance map - PangoStringToDescrMap::iterator it = fontInstanceMap.begin(); - while (it != fontInstanceMap.end()) { - pango_font_description_free((*it).second); - ++it; - } } @@ -249,248 +242,6 @@ Glib::ustring font_factory::GetUIStyleString(PangoFontDescription const *fontDes return style; } -/** - Replace font family leaving style alone (if possible). - @param fontSpec the given font - @param newFamily - @return the changed fontspec, if the property can not be set return an empty string - The routine first searches for an exact match. - If no exact match found, calls FontSpecificationBestMatch(). -*/ -Glib::ustring font_factory::ReplaceFontSpecificationFamily(const Glib::ustring & fontSpec, const Glib::ustring & newFamily) -{ - Glib::ustring newFontSpec; - - // Although we are using the string from pango_font_description_to_string for the - // font specification, we definitely cannot just set the new family in the - // PangoFontDescription structure and ask for a new string. This is because - // what constitutes a "family" in our own UI may be different from how Pango - // sees it. - - // Find the PangoFontDescription associated with the old font specification string. - PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec); - - - if (it != fontInstanceMap.end()) { - // Description found! - - // Make copy - PangoFontDescription *descr = pango_font_description_copy((*it).second); - - // Grab the old UI Family string from the descr - Glib::ustring uiFamily = GetUIFamilyString(descr); - - // Replace the UI Family name with the new family name - std::size_t found = fontSpec.find(uiFamily); - if (found != Glib::ustring::npos) { - - // Add comma to end of newFamily... commas at end don't hurt but are - // required if the last part of a family name is a valid font style - // (e.g. "Arial Black"). - Glib::ustring newFamilyComma = newFamily; - if( *newFamilyComma.rbegin() != ',' ) { - newFamilyComma += ","; - } - newFontSpec = fontSpec; - newFontSpec.erase(found, uiFamily.size()); - newFontSpec.insert(found, newFamilyComma); - - // If the new font specification does not exist in the reference maps, - // search for the next best match for the faces in that style - it = fontInstanceMap.find(newFontSpec); - if (it == fontInstanceMap.end()) { - - // Search for best match, empty string returned if not found. - newFontSpec = FontSpecificationBestMatch( newFontSpec ); - - } - } - - pango_font_description_free(descr); - } - - return newFontSpec; -} - -/** - Apply style property to the given font - @param fontSpec the given font - @param turnOn true to set italic style - @return the changed fontspec, if the property can not be set return an empty string - The routine first searches for an exact match to "FontFamily Italic" or - "Font Family Oblique" (turnOn is true) or "FontFamily" (turnOn is false). - If no exact match found, calls FontSpecificationBestMatch(). -*/ -Glib::ustring font_factory::FontSpecificationSetItalic(const Glib::ustring & fontSpec, bool turnOn) -{ - Glib::ustring newFontSpec; - - // Find the PangoFontDescription associated with the font specification string. - PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec); - - if (it != fontInstanceMap.end()) { - // Description found! - - // Make copy. - PangoFontDescription *descr = pango_font_description_copy((*it).second); - - PangoStyle style; - if (turnOn) { - // First try Oblique, we'll try Italic later - style = PANGO_STYLE_OBLIQUE; - } else { - style = PANGO_STYLE_NORMAL; - } - - pango_font_description_set_style(descr, style); - - newFontSpec = ConstructFontSpecification(descr); - - bool exactMatchFound = true; - if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) { - - exactMatchFound = false; - if (turnOn) { - // Next try Italic - style = PANGO_STYLE_ITALIC; - pango_font_description_set_style(descr, style); - - exactMatchFound = true; - if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) { - exactMatchFound = false; - } - } - } - - // Search for best match, empty string returned if not found. - if( !exactMatchFound ) { - newFontSpec = FontSpecificationBestMatch( newFontSpec ); - } - - pango_font_description_free(descr); - } - - return newFontSpec; // Empty if not found. -} - -/** - Apply weight property to the given font - @param fontSpec the given font - @param turnOn true to set bold - @return the changed fontspec, if the property can not be set return an empty string - This routine first searches for an exact match, if none found - it calls FontSpecificationBestMatch(). -*/ -Glib::ustring font_factory::FontSpecificationSetBold(const Glib::ustring & fontSpec, bool turnOn) -{ - Glib::ustring newFontSpec; - - // Find the PangoFontDescription associated with the font specification string. - PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec); - - if (it != fontInstanceMap.end()) { - // Description found! - - // Make copy. - PangoFontDescription *descr = pango_font_description_copy((*it).second); - - - PangoWeight weight; - if (turnOn) { - weight = PANGO_WEIGHT_BOLD; - } else { - weight = PANGO_WEIGHT_NORMAL; - } - - pango_font_description_set_weight(descr, weight); - - newFontSpec = ConstructFontSpecification(descr); - - if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) { - // Search for best match, empty string returned if not found. - newFontSpec = FontSpecificationBestMatch( newFontSpec ); - } - - pango_font_description_free(descr); - } - - return newFontSpec; // Empty if not found. -} - -/** - Use pango_font_description_better_match() to find best font match. - This handles cases like Century Schoolbook L where the "normal" - font is Century Schoolbook L Medium so just removing Italic - from the font name doesn't yield the correct name. - @param fontSpec the given font - @return the changed fontspec, if the property can not be set return an empty string -*/ -// http://library.gnome.org/devel/pango/1.28/pango-Fonts.html#pango-font-description-better-match -Glib::ustring font_factory::FontSpecificationBestMatch(const Glib::ustring & fontSpec ) -{ - - Glib::ustring newFontSpec; - - // Look for exact match - PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec); - - // If there is no exact match, look for the best match. - if (it != fontInstanceMap.end()) { - - newFontSpec = fontSpec; - - } else { - - PangoFontDescription *fontDescr = pango_font_description_from_string(fontSpec.c_str()); - PangoFontDescription *bestMatchDescr = NULL; - - // Grab the UI Family string from the descr - Glib::ustring family = GetUIFamilyString(fontDescr); - Glib::ustring bestMatchDescription; - - bool setFirstFamilyMatch = false; - for (it = fontInstanceMap.begin(); it != fontInstanceMap.end(); ++it) { - - Glib::ustring currentFontSpec = (*it).first; - Glib::ustring currentFamily = GetUIFamilyString((*it).second); - - // Save some time by only looking at the right family. - // Must use family name rather than fontSpec - // (otherwise DejaVu Sans matches DejaVu Sans Mono). - if (currentFamily == family) { - if (!setFirstFamilyMatch) { - // This ensures that the closest match is at least within the correct - // family rather than the first font in the list - bestMatchDescr = pango_font_description_copy((*it).second); - bestMatchDescription = currentFontSpec; - setFirstFamilyMatch = true; - } else { - // Get the font description that corresponds, and - // then see if we've found a better match - PangoFontDescription *possibleMatch = pango_font_description_copy((*it).second); - - if (pango_font_description_better_match( - fontDescr, bestMatchDescr, possibleMatch)) { - - pango_font_description_free(bestMatchDescr); - bestMatchDescr = possibleMatch; - bestMatchDescription = currentFontSpec; - } else { - pango_font_description_free(possibleMatch); - } - } - } - } // for - - newFontSpec = bestMatchDescription; // If NULL, then no match found - - pango_font_description_free(fontDescr); - pango_font_description_free(bestMatchDescr); - - } - - return newFontSpec; -} ///// @@ -509,10 +260,10 @@ static int StyleNameValue( const Glib::ustring &style ) } // Determines order in which styles are presented (sorted by CSS style values) -static bool StyleNameCompareInternal(const StyleNames &style1, const StyleNames &style2) -{ - return( StyleNameValue( style1.CssName ) < StyleNameValue( style2.CssName ) ); -} +//static bool StyleNameCompareInternal(const StyleNames &style1, const StyleNames &style2) +//{ +// return( StyleNameValue( style1.CssName ) < StyleNameValue( style2.CssName ) ); +//} static bool ustringPairSort(std::pair const& first, std::pair const& second) { @@ -559,6 +310,7 @@ GList* font_factory::GetUIStyles(PangoFontFamily * in) // If the face has a name, describe it, and then use the // description to get the UI family and face strings const gchar* displayName = pango_font_face_get_face_name(faces[currentFace]); + // std::cout << "Display Name: " << displayName << std::endl; if (displayName == NULL || *displayName == '\0') { continue; } @@ -579,7 +331,19 @@ GList* font_factory::GetUIStyles(PangoFontFamily * in) } } - if (!familyUIName.empty() && !styleUIName.empty()) { + bool exists = false; + for(GList *temp = ret; temp; temp = temp->next) { + if( ((StyleNames*)temp->data)->CssName.compare( styleUIName ) == 0 ) { + exists = true; + std::cerr << "Warning: Font face with same CSS values already added: " + << familyUIName << " " << styleUIName + << " (" << ((StyleNames*)temp->data)->DisplayName + << ", " << displayName << ")" << std::endl; + break; + } + } + + if (!exists && !familyUIName.empty() && !styleUIName.empty()) { // Add the style information ret = g_list_append(ret, new StyleNames(styleUIName, displayName)); } @@ -590,140 +354,6 @@ GList* font_factory::GetUIStyles(PangoFontFamily * in) return ret; } -void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map) -{ - g_assert(map); - - if (map) { - - // Gather the family names as listed by Pango - PangoFontFamily** families = NULL; - int numFamilies = 0; - pango_font_map_list_families(fontServer, &families, &numFamilies); - - for (int currentFamily=0; currentFamily < numFamilies; currentFamily++) { - - // Gather the styles for this family - PangoFontFace** faces = NULL; - int numFaces = 0; - pango_font_family_list_faces(families[currentFamily], &faces, &numFaces); - - for (int currentFace=0; currentFace < numFaces; currentFace++) { - - // If the face has a name, describe it, and then use the - // description to get the UI family and face strings - - const gchar* displayName = pango_font_face_get_face_name(faces[currentFace]); - if (displayName == NULL) { - continue; - } - - PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]); - if (faceDescr) { - Glib::ustring familyUIName = GetUIFamilyString(faceDescr); - Glib::ustring styleUIName = GetUIStyleString(faceDescr); - // std::cout << familyUIName << " " << styleUIName << " (" << displayName << ")" << std::endl; - - // Disable synthesized (faux) font faces except for CSS generic faces - if (pango_font_face_is_synthesized(faces[currentFace]) ) { - if( familyUIName.compare( "sans-serif" ) != 0 && - familyUIName.compare( "serif" ) != 0 && - familyUIName.compare( "monospace" ) != 0 && - familyUIName.compare( "fantasy" ) != 0 && - familyUIName.compare( "cursive" ) != 0 ) { - //std::cout << "faux: " << familyUIName << " | " << styleUIName << std::endl; - continue; - } - } - - // Pango breaks the 1 to 1 mapping between Pango weights and CSS weights by - // adding Semi-Light (as of 1.36.7), Book (as of 1.24), and Ultra-Heavy (as of - // 1.24). We need to map these weights to CSS weights. Book and Ultra-Heavy - // are rarely used. Semi-Light (350) is problematic as it is halfway between - // Light (300) and Normal (400) and if care is not taken it is converted to - // Normal, rather than Light. - // - // Note: The ultimate solution to handling various weight in the same - // font family is to support the @font rules from CSS. - // - // Additional notes, helpful for debugging: - // Pango's FC backend: - // Weights defined in fontconfig/fontconfig.h - // String equivalents in src/fcfreetype.c - // Weight set from os2->usWeightClass - // Use Fontforge: Element->Font Info...->OS/2->Misc->Weight Class to check font weight - size_t f = styleUIName.find( "Book" ); - if( f != Glib::ustring::npos ) { - styleUIName.replace( f, 4, "Normal" ); - } - f = styleUIName.find( "Semi-Light" ); - if( f != Glib::ustring::npos ) { - styleUIName.replace( f, 10, "Light" ); - } - f = styleUIName.find( "Ultra-Heavy" ); - if( f != Glib::ustring::npos ) { - styleUIName.replace( f, 11, "Heavy" ); - } - - if (!familyUIName.empty() && !styleUIName.empty()) { - - // Find the right place to put the style information, adding - // a map entry for the family name if it doesn't yet exist - - FamilyToStylesMap::iterator iter = map->find(familyUIName); - - // Insert new family - if (iter == map->end()) { - map->insert(std::make_pair(familyUIName, std::list())); - } - - // Insert into the style list and save the info in the reference maps - // only if the style does not yet exist - - bool exists = false; - std::list &styleList = (*map)[familyUIName]; - - for (std::list::iterator it=styleList.begin(); - it != styleList.end(); - ++it) { - if ( (*it).CssName == styleUIName) { - exists = true; - std::cerr << "Warning: Font face with same CSS values already added: " << familyUIName << " " << styleUIName << " (" << (*it).DisplayName << ", " << displayName << ")" << std::endl; - break; - } - } - - if (!exists) { - styleList.push_back( StyleNames(styleUIName,displayName) ); - - // Add the string info needed in the reference maps - fontStringMap.insert( - std::make_pair( - Glib::ustring(familyUIName) + Glib::ustring(styleUIName), - ConstructFontSpecification(faceDescr))); - fontInstanceMap.insert( - std::make_pair(ConstructFontSpecification(faceDescr), faceDescr)); - - } else { - pango_font_description_free(faceDescr); - } - } else { - pango_font_description_free(faceDescr); - } - } - } - g_free(faces); - faces = 0; - } - g_free(families); - families = 0; - - // Sort the style lists - for (FamilyToStylesMap::iterator iter = map->begin() ; iter != map->end(); ++iter) { - (*iter).second.sort(StyleNameCompareInternal); - } - } -} font_instance* font_factory::FaceFromStyle(SPStyle const *style) { @@ -869,14 +499,6 @@ font_instance* font_factory::FaceFromStyle(SPStyle const *style) font = Face(temp_descr); pango_font_description_free(temp_descr); - - // We now find closest match to this font: - Glib::ustring fontSpec = font_factory::Default()->ConstructFontSpecification(font); - Glib::ustring newFontSpec = FontSpecificationBestMatch( fontSpec ); - if( fontSpec != newFontSpec ) { - font->Unref(); - font = FaceFromFontSpecification( newFontSpec.c_str() ); - } } } @@ -892,37 +514,6 @@ font_instance *font_factory::FaceFromDescr(char const *family, char const *style return res; } -font_instance* font_factory::FaceFromUIStrings(char const *uiFamily, char const *uiStyle) -{ - font_instance *fontInstance = NULL; - - g_assert(uiFamily && uiStyle); - if (uiFamily && uiStyle) { - - // If font list, take only first font in list - gchar** tokens = g_strsplit( uiFamily, ",", 0 ); - g_strstrip( tokens[0] ); - - Glib::ustring uiString = Glib::ustring(tokens[0]) + Glib::ustring(uiStyle); - - g_strfreev( tokens ); - - UIStringToPangoStringMap::iterator uiToPangoIter = fontStringMap.find(uiString); - - if (uiToPangoIter != fontStringMap.end ()) { - PangoStringToDescrMap::iterator pangoToDescrIter = fontInstanceMap.find((*uiToPangoIter).second); - if (pangoToDescrIter != fontInstanceMap.end()) { - // We found the pango description - now we can make a font_instance - PangoFontDescription *tempDescr = pango_font_description_copy((*pangoToDescrIter).second); - fontInstance = Face(tempDescr); - pango_font_description_free(tempDescr); - } - } - } - - return fontInstance; -} - font_instance* font_factory::FaceFromPangoString(char const *pangoString) { font_instance *fontInstance = NULL; @@ -930,25 +521,15 @@ font_instance* font_factory::FaceFromPangoString(char const *pangoString) g_assert(pangoString); if (pangoString) { - PangoFontDescription *descr = NULL; - - // First attempt to find the font specification in the reference map - PangoStringToDescrMap::iterator it = fontInstanceMap.find(Glib::ustring(pangoString)); - if (it != fontInstanceMap.end()) { - descr = pango_font_description_copy((*it).second); - } - // Or create a font description from the string - this may fail or + // Create a font description from the string - this may fail or // produce unexpected results if the string does not have a good format - if (!descr) { - descr = pango_font_description_from_string(pangoString); - } - - if (descr && (sp_font_description_get_family(descr) != NULL)) { - fontInstance = Face(descr); - } + PangoFontDescription *descr = pango_font_description_from_string(pangoString); if (descr) { + if (sp_font_description_get_family(descr) != NULL) { + fontInstance = Face(descr); + } pango_font_description_free(descr); } } @@ -1113,24 +694,6 @@ void font_factory::AddInCache(font_instance *who) nbEnt++; } -/* - { - std::cout << " Printing out fontInstanceMap: " << std::endl; - PangoStringToDescrMap::iterator it = fontInstanceMap.begin(); - while (it != fontInstanceMap.end()) { - - PangoFontDescription *descr = pango_font_description_copy((*it).second); - - // Grab the UI Family string from the descr - Glib::ustring uiFamily = GetUIFamilyString(descr); - Glib::ustring uiStyle = GetUIStyleString(descr); - std::cout << " " << uiFamily << " " << uiStyle << std::endl; - - it++; - } - } -*/ - /* Local Variables: mode:c++ diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index 85576e08d..bd5a4460c 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -112,18 +112,6 @@ public: Glib::ustring GetUIFamilyString(PangoFontDescription const *fontDescr); Glib::ustring GetUIStyleString(PangoFontDescription const *fontDescr); - /// Modifiers for the font specification (returns new font specification) - Glib::ustring ReplaceFontSpecificationFamily(const Glib::ustring & fontSpec, const Glib::ustring & newFamily); - Glib::ustring FontSpecificationSetItalic(const Glib::ustring & fontSpec, bool turnOn); - Glib::ustring FontSpecificationSetBold(const Glib::ustring & fontSpec, bool turnOn); - - Glib::ustring FontSpecificationBestMatch(const Glib::ustring& fontSpec ); - - // Gathers all strings needed for UI while storing pango information in - // fontInstanceMap and fontStringMap - // don't use this function, it's too slow - void GetUIFamiliesAndStyles(FamilyToStylesMap *map); - // Helpfully inserts all font families into the provided vector void GetUIFamilies(std::vector& out); // Retrieves style information about a family in a newly allocated GList. @@ -152,19 +140,37 @@ public: private: void* loadedPtr; + + // The following two commented out maps were an attempt to allow Inkscape to use font faces + // that could not be distinguished by CSS values alone. In practice, they never were that + // useful as PangoFontDescription, which is used throughout our code, cannot distinguish + // between faces anymore than raw CSS values (with the exception of two additional weight + // values). + // + // During various works, for example to handle font-family lists and fonts that are not + // installed on the system, the code has become less reliant on these maps. And in the work to + // catch style information to speed up start up times, the maps were not being filled. + // I've removed all code that used these maps as of Oct 2014 in the experimental branch. + // The commented out maps are left here as a reminder of the path that was attempted. + // + // One possible method to keep track of font faces would be to use the 'display name', keeping + // pointers to the appropriate PangoFontFace. The font_factory loadedFaces map indexing would + // have to be changed to incorporate 'display name' (InkscapeFontDescription?). + + // These two maps are used for translating between what's in the UI and a pango // font description. This is necessary because Pango cannot always // reproduce these structures from the names it gave us in the first place. // Key: A string produced by font_factory::ConstructFontSpecification // Value: The associated PangoFontDescription - typedef std::map PangoStringToDescrMap; - PangoStringToDescrMap fontInstanceMap; + // typedef std::map PangoStringToDescrMap; + // PangoStringToDescrMap fontInstanceMap; // Key: Family name in UI + Style name in UI // Value: The associated string that should be produced with font_factory::ConstructFontSpecification - typedef std::map UIStringToPangoStringMap; - UIStringToPangoStringMap fontStringMap; + // typedef std::map UIStringToPangoStringMap; + // UIStringToPangoStringMap fontStringMap; }; -- cgit v1.2.3 From 91741bf6f23e79c0c709e08b0ec0c0f8aad8139f Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Wed, 15 Oct 2014 20:38:31 +0200 Subject: Various small font things. (experimental r13577-13579) (bzr r13616.1.4) --- src/libnrtype/FontFactory.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index cb9602394..d052eeb42 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -265,6 +265,12 @@ static int StyleNameValue( const Glib::ustring &style ) // return( StyleNameValue( style1.CssName ) < StyleNameValue( style2.CssName ) ); //} +static gint StyleNameCompareInternalGlib(gconstpointer a, gconstpointer b) +{ + return( StyleNameValue( ((StyleNames *)a)->CssName ) < + StyleNameValue( ((StyleNames *)b)->CssName ) ? -1 : 1 ); +} + static bool ustringPairSort(std::pair const& first, std::pair const& second) { // well, this looks weird. @@ -319,7 +325,7 @@ GList* font_factory::GetUIStyles(PangoFontFamily * in) if (faceDescr) { Glib::ustring familyUIName = GetUIFamilyString(faceDescr); Glib::ustring styleUIName = GetUIStyleString(faceDescr); - + // std::cout << familyUIName << " " << styleUIName << " " << displayName << std::endl; // Disable synthesized (faux) font faces except for CSS generic faces if (pango_font_face_is_synthesized(faces[currentFace]) ) { if (familyUIName.compare( "sans-serif" ) != 0 && @@ -331,6 +337,35 @@ GList* font_factory::GetUIStyles(PangoFontFamily * in) } } + // Pango breaks the 1 to 1 mapping between Pango weights and CSS weights by + // adding Semi-Light (as of 1.36.7), Book (as of 1.24), and Ultra-Heavy (as of + // 1.24). We need to map these weights to CSS weights. Book and Ultra-Heavy + // are rarely used. Semi-Light (350) is problematic as it is halfway between + // Light (300) and Normal (400) and if care is not taken it is converted to + // Normal, rather than Light. + // + // Note: The ultimate solution to handling various weight in the same + // font family is to support the @font rules from CSS. + // + // Additional notes, helpful for debugging: + // Pango's FC backend: + // Weights defined in fontconfig/fontconfig.h + // String equivalents in src/fcfreetype.c + // Weight set from os2->usWeightClass + // Use Fontforge: Element->Font Info...->OS/2->Misc->Weight Class to check font weight + size_t f = styleUIName.find( "Book" ); + if( f != Glib::ustring::npos ) { + styleUIName.replace( f, 4, "Normal" ); + } + f = styleUIName.find( "Semi-Light" ); + if( f != Glib::ustring::npos ) { + styleUIName.replace( f, 10, "Light" ); + } + f = styleUIName.find( "Ultra-Heavy" ); + if( f != Glib::ustring::npos ) { + styleUIName.replace( f, 11, "Heavy" ); + } + bool exists = false; for(GList *temp = ret; temp; temp = temp->next) { if( ((StyleNames*)temp->data)->CssName.compare( styleUIName ) == 0 ) { @@ -351,10 +386,12 @@ GList* font_factory::GetUIStyles(PangoFontFamily * in) pango_font_description_free(faceDescr); } g_free(faces); + + // Sort the style lists + ret = g_list_sort( ret, StyleNameCompareInternalGlib ); return ret; } - font_instance* font_factory::FaceFromStyle(SPStyle const *style) { font_instance *font = NULL; -- cgit v1.2.3 From ee307b787adb19aadadbe8f3cac785e2317eceaf Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Wed, 15 Oct 2014 23:31:29 -0700 Subject: Cleanup of unintialized and leaked values. (bzr r13618) --- src/extension/internal/pdfinput/pdf-parser.cpp | 182 +++++++++++++++++-------- src/extension/internal/pdfinput/pdf-parser.h | 30 +--- 2 files changed, 124 insertions(+), 88 deletions(-) (limited to 'src') diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp index c5f03e5aa..0b7dc7905 100644 --- a/src/extension/internal/pdfinput/pdf-parser.cpp +++ b/src/extension/internal/pdfinput/pdf-parser.cpp @@ -247,30 +247,74 @@ PdfOperator PdfParser::opTab[] = { #define numOps (sizeof(opTab) / sizeof(PdfOperator)) +namespace { + +GfxPatch blankPatch() +{ + GfxPatch patch; + memset(&patch, 0, sizeof(patch)); // quick-n-dirty + return patch; +} + +} // namespace + //------------------------------------------------------------------------ -// PdfParser +// ClipHistoryEntry //------------------------------------------------------------------------ -PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA, - int /*pageNum*/, int rotate, Dict *resDict, - PDFRectangle *box, PDFRectangle *cropBox) -{ - xref = xrefA; - subPage = gFalse; - printCommands = false; +class ClipHistoryEntry { +public: + + ClipHistoryEntry(GfxPath *clipPath = NULL, GfxClipType clipType = clipNormal); + virtual ~ClipHistoryEntry(); + + // Manipulate clip path stack + ClipHistoryEntry *save(); + ClipHistoryEntry *restore(); + GBool hasSaves() { return saved != NULL; } + void setClip(GfxPath *newClipPath, GfxClipType newClipType = clipNormal); + GfxPath *getClipPath() { return clipPath; } + GfxClipType getClipType() { return clipType; } - // start the resource stack - res = new GfxResources(xref, resDict, NULL); +private: - // initialize - state = new GfxState(72.0, 72.0, box, rotate, gTrue); - clipHistory = new ClipHistoryEntry(); + ClipHistoryEntry *saved; // next clip path on stack + + GfxPath *clipPath; // used as the path to be filled for an 'sh' operator + GfxClipType clipType; + + ClipHistoryEntry(ClipHistoryEntry *other); +}; + +//------------------------------------------------------------------------ +// PdfParser +//------------------------------------------------------------------------ + +PdfParser::PdfParser(XRef *xrefA, + Inkscape::Extension::Internal::SvgBuilder *builderA, + int /*pageNum*/, + int rotate, + Dict *resDict, + PDFRectangle *box, + PDFRectangle *cropBox) : + xref(xrefA), + builder(builderA), + subPage(gFalse), + printCommands(false), + res(new GfxResources(xref, resDict, NULL)), // start the resource stack + state(new GfxState(72.0, 72.0, box, rotate, gTrue)), + fontChanged(gFalse), + clip(clipNone), + ignoreUndef(0), + baseMatrix(), + formDepth(0), + parser(NULL), + colorDeltas(), + maxDepths(), + clipHistory(new ClipHistoryEntry()), + operatorHistory(NULL) +{ setDefaultApproximationPrecision(); - fontChanged = gFalse; - clip = clipNone; - ignoreUndef = 0; - operatorHistory = NULL; - builder = builderA; builder->setDocumentSize(Inkscape::Util::Quantity::convert(state->getPageWidth(), "pt", "px"), Inkscape::Util::Quantity::convert(state->getPageHeight(), "pt", "px")); @@ -306,50 +350,62 @@ PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *bui pushOperator("startPage"); } -PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA, - Dict *resDict, PDFRectangle *box) { - - int i; - parser = NULL; - - xref = xrefA; - subPage = gTrue; - printCommands = false; - - // start the resource stack - res = new GfxResources(xref, resDict, NULL); - - // initialize - operatorHistory = NULL; - builder = builderA; - state = new GfxState(72, 72, box, 0, gFalse); - clipHistory = new ClipHistoryEntry(); +PdfParser::PdfParser(XRef *xrefA, + Inkscape::Extension::Internal::SvgBuilder *builderA, + Dict *resDict, + PDFRectangle *box) : + xref(xrefA), + builder(builderA), + subPage(gTrue), + printCommands(false), + res(new GfxResources(xref, resDict, NULL)), // start the resource stack + state(new GfxState(72, 72, box, 0, gFalse)), + fontChanged(gFalse), + clip(clipNone), + ignoreUndef(0), + baseMatrix(), + formDepth(0), + parser(NULL), + colorDeltas(), + maxDepths(), + clipHistory(new ClipHistoryEntry()), + operatorHistory(NULL) +{ setDefaultApproximationPrecision(); - fontChanged = gFalse; - clip = clipNone; - ignoreUndef = 0; - for (i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { baseMatrix[i] = state->getCTM()[i]; } formDepth = 0; } PdfParser::~PdfParser() { - while (state->hasSaves()) { + while(operatorHistory) { + OpHistoryEntry *tmp = operatorHistory->next; + delete operatorHistory; + operatorHistory = tmp; + } + + while (state && state->hasSaves()) { restoreState(); } + if (!subPage) { //out->endPage(); } + while (res) { popResources(); } + if (state) { delete state; + state = NULL; } + if (clipHistory) { delete clipHistory; + clipHistory = NULL; } } @@ -460,7 +516,8 @@ void PdfParser::go(GBool /*topLevel*/) } } -void PdfParser::pushOperator(const char *name) { +void PdfParser::pushOperator(const char *name) +{ OpHistoryEntry *newEntry = new OpHistoryEntry; newEntry->name = name; newEntry->state = NULL; @@ -2086,13 +2143,19 @@ void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) { } void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) { - GfxPatch patch00, patch01, patch10, patch11; + GfxPatch patch00 = blankPatch(); + GfxPatch patch01 = blankPatch(); + GfxPatch patch10 = blankPatch(); + GfxPatch patch11 = blankPatch(); #ifdef POPPLER_NEW_GFXPATCH - GfxColor color; + GfxColor color = {{0}}; #endif - double xx[4][8], yy[4][8]; - double xxm, yym; - double patchColorDelta = colorDeltas[pdfPatchMeshShading-1]; + double xx[4][8]; + double yy[4][8]; + double xxm; + double yym; + double patchColorDelta = colorDeltas[pdfPatchMeshShading - 1]; + int i; for (i = 0; i < nComps; ++i) { @@ -3463,9 +3526,7 @@ void PdfParser::popResources() { } void PdfParser::setDefaultApproximationPrecision() { - int i; - - for (i = 1; i <= pdfNumShadingTypes; ++i) { + for (int i = 1; i <= pdfNumShadingTypes; ++i) { setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth); } } @@ -3484,19 +3545,18 @@ void PdfParser::setApproximationPrecision(int shadingType, double colorDelta, // ClipHistoryEntry //------------------------------------------------------------------------ -ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) { - if (clipPathA) { - clipPath = clipPathA->copy(); - } else { - clipPath = NULL; - } - clipType = clipTypeA; - saved = NULL; +ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) : + saved(NULL), + clipPath((clipPathA) ? clipPathA->copy() : NULL), + clipType(clipTypeA) +{ } -ClipHistoryEntry::~ClipHistoryEntry() { +ClipHistoryEntry::~ClipHistoryEntry() +{ if (clipPath) { delete clipPath; + clipPath = NULL; } } @@ -3510,6 +3570,7 @@ void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) { clipType = clipTypeA; } else { clipPath = NULL; + clipType = clipNormal; } } @@ -3526,7 +3587,7 @@ ClipHistoryEntry *ClipHistoryEntry::restore() { if (saved) { oldEntry = saved; saved = NULL; - delete this; + delete this; // TODO really should avoid deleting from inside. } else { oldEntry = this; } @@ -3540,6 +3601,7 @@ ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) { this->clipType = other->clipType; } else { this->clipPath = NULL; + this->clipType = clipNormal; } saved = NULL; } diff --git a/src/extension/internal/pdfinput/pdf-parser.h b/src/extension/internal/pdfinput/pdf-parser.h index a63d669c7..e28fecc2e 100644 --- a/src/extension/internal/pdfinput/pdf-parser.h +++ b/src/extension/internal/pdfinput/pdf-parser.h @@ -58,6 +58,8 @@ class AnnotBorderStyle; class PdfParser; +class ClipHistoryEntry; + //------------------------------------------------------------------------ #ifndef GFX_H @@ -100,34 +102,6 @@ struct OpHistoryEntry { unsigned depth; // total number of entries descending from this }; -//------------------------------------------------------------------------ -// ClipHistoryEntry -//------------------------------------------------------------------------ - -class ClipHistoryEntry { -public: - - ClipHistoryEntry(GfxPath *clipPath=NULL, GfxClipType clipType=clipNormal); - virtual ~ClipHistoryEntry(); - - // Manipulate clip path stack - ClipHistoryEntry *save(); - ClipHistoryEntry *restore(); - GBool hasSaves() { return saved != NULL; } - void setClip(GfxPath *newClipPath, GfxClipType newClipType=clipNormal); - GfxPath *getClipPath() { return clipPath; } - GfxClipType getClipType() { return clipType; } - -private: - - ClipHistoryEntry *saved; // next clip path on stack - - GfxPath *clipPath; // used as the path to be filled for an 'sh' operator - GfxClipType clipType; - - ClipHistoryEntry(ClipHistoryEntry *other); -}; - //------------------------------------------------------------------------ // PdfParser //------------------------------------------------------------------------ -- cgit v1.2.3 From 14a9c655de4723dda0c280f4717f2b29d46287a1 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 16 Oct 2014 15:27:50 +0200 Subject: Update to bspline branch, now helper path fixed and optional (bzr r13341.1.275) --- src/live_effects/lpe-bspline.cpp | 126 ++++++++++++++++++++++++++------------- src/live_effects/lpe-bspline.h | 18 +++--- 2 files changed, 94 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp index e8df6e464..b68799d08 100644 --- a/src/live_effects/lpe-bspline.cpp +++ b/src/live_effects/lpe-bspline.cpp @@ -59,17 +59,22 @@ const double noPower = 0.0; const double defaultStartPower = 0.3334; const double defaultEndPower = 0.6667; -LPEBSpline::LPEBSpline(LivePathEffectObject *lpeobject) : - Effect(lpeobject), - steps(_("Steps whith CTRL:"), _("Change number of steps whith CTRL pressed"), "steps", &wr, this, 2), - ignoreCusp(_("Ignore cusp nodes"), _("Change ignoring cusp nodes"), "ignoreCusp", &wr, this, true), - onlySelected(_("Change only selected nodes"), _("Change only selected nodes"), "onlySelected", &wr, this, false), - weight(_("Change weight:"), _("Change weight of the effect"), "weight", &wr, this, defaultStartPower) +LPEBSpline::LPEBSpline(LivePathEffectObject *lpeobject) + : Effect(lpeobject), + // initialise your parameters here: + //testpointA(_("Test Point A"), _("Test A"), "ptA", &wr, this, + //Geom::Point(100,100)), + steps(_("Steps whith CTRL:"), _("Change number of steps whith CTRL pressed"), "steps", &wr, this, 2), + ignoreCusp(_("Ignore cusp nodes"), _("Change ignoring cusp nodes"), "ignoreCusp", &wr, this, true), + onlySelected(_("Change only selected nodes"), _("Change only selected nodes"), "onlySelected", &wr, this, false), + showHelper(_("Show helper paths"), _("Show helper paths"), "showHelper", &wr, this, false), + weight(_("Change weight:"), _("Change weight of the effect"), "weight", &wr, this, defaultStartPower) { - registerParameter(&weight); - registerParameter(&steps); - registerParameter(&ignoreCusp); - registerParameter(&onlySelected); + registerParameter(dynamic_cast(&weight)); + registerParameter(dynamic_cast(&steps)); + registerParameter(dynamic_cast(&ignoreCusp)); + registerParameter(dynamic_cast(&onlySelected)); + registerParameter(dynamic_cast(&showHelper)); weight.param_set_range(noPower, 1); weight.param_set_increments(0.1, 0.1); @@ -89,7 +94,9 @@ void LPEBSpline::doBeforeEffect (SPLPEItem const* lpeitem) } } -void LPEBSpline::createAndApply(const char *name, SPDocument *doc, SPItem *item) + +void LPEBSpline::createAndApply(const char *name, SPDocument *doc, + SPItem *item) { if (!SP_IS_SHAPE(item)) { g_warning("LPE BSpline can only be applied to shapes (not groups)."); @@ -99,7 +106,8 @@ void LPEBSpline::createAndApply(const char *name, SPDocument *doc, SPItem *item) Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect"); repr->setAttribute("effect", name); - doc->getDefs()->getRepr()->addChild(repr, NULL); // adds to and assigns the 'id' attribute + doc->getDefs()->getRepr() + ->addChild(repr, NULL); // adds to and assigns the 'id' attribute const gchar *repr_id = repr->attribute("id"); Inkscape::GC::release(repr); @@ -111,13 +119,13 @@ void LPEBSpline::createAndApply(const char *name, SPDocument *doc, SPItem *item) void LPEBSpline::doEffect(SPCurve *curve) { + if (curve->get_segment_count() < 1){ return; } - + // Make copy of old path as it is changed during processing Geom::PathVector const original_pathv = curve->get_pathvector(); curve->reset(); - double radiusHelperNodes = 6.0; SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop){ @@ -125,7 +133,6 @@ void LPEBSpline::doEffect(SPCurve *curve) SPNamedView *nv = sp_desktop_namedview(desktop); radiusHelperNodes = Inkscape::Util::Quantity::convert(radiusHelperNodes, "px", nv->doc_units->abbr); } - for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) @@ -133,7 +140,7 @@ void LPEBSpline::doEffect(SPCurve *curve) Geom::Path::const_iterator curve_it1 = path_it->begin(); Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); - Geom::Path::const_iterator curve_endit = path_it->end_default(); + Geom::Path::const_iterator curve_endit = path_it->end_default(); SPCurve *nCurve = new SPCurve(); Geom::Point previousNode(0, 0); Geom::Point node(0, 0); @@ -145,13 +152,29 @@ void LPEBSpline::doEffect(SPCurve *curve) Geom::D2 SBasisHelper; Geom::CubicBezier const *cubic = NULL; if (path_it->closed()) { - const Geom::Curve &closingline = path_it->back_closed(); + // if the path is closed, maybe we have to stop a bit earlier because the + // closing line segment has zerolength. + const Geom::Curve &closingline = + path_it->back_closed(); // the closing line segment is always of type + // Geom::LineSegment. if (are_near(closingline.initialPoint(), closingline.finalPoint())) { + // closingline.isDegenerate() did not work, because it only checks for + // *exact* zero length, which goes wrong for relative coordinates and + // rounding errors... + // the closing line segment has zero-length. So stop before that one! curve_endit = path_it->end_open(); } } + //Si la curva está cerrada calculamos el punto donde + //deveria estar el nodo BSpline de cierre/inicio de la curva + //en posible caso de que se cierre con una linea recta creando un nodo + //BSPline nCurve->moveto(curve_it1->initialPoint()); + //Recorremos todos los segmentos menos el último while (curve_it1 != curve_endit) { + //previousPointAt3 = pointAt3; + //Calculamos los puntos que dividirían en tres segmentos iguales el path + //recto de entrada y de salida SPCurve *in = new SPCurve(); in->moveto(curve_it1->initialPoint()); in->lineto(curve_it1->finalPoint()); @@ -192,7 +215,6 @@ void LPEBSpline::doEffect(SPCurve *curve) out->reset(); delete out; } - Geom::Point startNode = path_it->begin()->initialPoint(); if (path_it->closed() && curve_it2 == curve_endit) { SPCurve *start = new SPCurve(); start->moveto(path_it->begin()->initialPoint()); @@ -225,9 +247,9 @@ void LPEBSpline::doEffect(SPCurve *curve) SBasisHelper = lineHelper->first_segment()->toSBasis(); lineHelper->reset(); delete lineHelper; - startNode = SBasisHelper.valueAt(0.5); - nCurve->curveto(pointAt1, pointAt2, startNode); - nCurve->move_endpoints(startNode, startNode); + node = SBasisHelper.valueAt(0.5); + nCurve->curveto(pointAt1, pointAt2, node); + nCurve->move_endpoints(node, node); } else if ( curve_it2 == curve_endit) { nCurve->curveto(pointAt1, pointAt2, curve_it1->finalPoint()); nCurve->move_endpoints(path_it->begin()->initialPoint(), curve_it1->finalPoint()); @@ -238,17 +260,26 @@ void LPEBSpline::doEffect(SPCurve *curve) SBasisHelper = lineHelper->first_segment()->toSBasis(); lineHelper->reset(); delete lineHelper; + //almacenamos el punto del anterior bucle -o el de cierre- que nos hara de + //principio de curva previousNode = node; + //Y este hará de final de curva node = SBasisHelper.valueAt(0.5); Geom::CubicBezier const *cubic2 = dynamic_cast(&*curve_it1); if((cubic && are_near((*cubic)[0],(*cubic)[1])) || (cubic2 && are_near((*cubic2)[2],(*cubic2)[3]))) { node = curve_it1->finalPoint(); } nCurve->curveto(pointAt1, pointAt2, node); - if(!are_near(node,curve_it1->finalPoint())){ - drawHandle(node, radiusHelperNodes); - } } + if(!are_near(node,curve_it1->finalPoint()) && showHelper){ + drawHandle(node, radiusHelperNodes); + } + //La curva BSpline se forma calculando el centro del segmanto de unión + //de el punto situado en las 2/3 partes de el segmento de entrada + //con el punto situado en la posición 1/3 del segmento de salida + //Estos dos puntos ademas estan posicionados en el lugas correspondiente + //de los manejadores de la curva + //aumentamos los valores para el siguiente paso en el bucle ++curve_it1; ++curve_it2; } @@ -259,6 +290,8 @@ void LPEBSpline::doEffect(SPCurve *curve) curve->append(nCurve, false); nCurve->reset(); delete nCurve; + } + if(showHelper){ Geom::PathVector const pathv = curve->get_pathvector(); hp.push_back(pathv[0]); } @@ -293,32 +326,44 @@ Gtk::Widget *LPEBSpline::newWidget() while (it != param_vector.end()) { if ((*it)->widget_is_visible) { Parameter *param = *it; - Gtk::Widget *widg = Gtk::manage(param->param_newWidget()); + Gtk::Widget *widg = dynamic_cast(param->param_newWidget()); if (param->param_key == "weight") { - Gtk::HBox * buttons = Gtk::manage(new Gtk::HBox(true, 0)); - - Gtk::Button *defaultWeight = Gtk::manage(new Gtk::Button(Glib::ustring(_("Default weight")))); - defaultWeight->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &LPEBSpline::toDefaultWeight), widg)); + Gtk::HBox * buttons = Gtk::manage(new Gtk::HBox(true,0)); + Gtk::Button *defaultWeight = + Gtk::manage(new Gtk::Button(Glib::ustring(_("Default weight")))); + defaultWeight->signal_clicked() + .connect(sigc::bind(sigc::mem_fun(*this, &LPEBSpline::toDefaultWeight), widg)); buttons->pack_start(*defaultWeight, true, true, 2); - - Gtk::Button *makeCusp = Gtk::manage(new Gtk::Button(Glib::ustring(_("Make cusp")))); - makeCusp->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &LPEBSpline::toMakeCusp), widg)); + Gtk::Button *makeCusp = + Gtk::manage(new Gtk::Button(Glib::ustring(_("Make cusp")))); + makeCusp->signal_clicked() + .connect(sigc::bind(sigc::mem_fun(*this, &LPEBSpline::toMakeCusp), widg)); buttons->pack_start(*makeCusp, true, true, 2); - vbox->pack_start(*buttons, true, true, 2); } if (param->param_key == "weight" || param->param_key == "steps") { - Inkscape::UI::Widget::Scalar *widgRegistered = Gtk::manage(dynamic_cast(widg)); - widgRegistered->signal_value_changed().connect(sigc::mem_fun(*this, &LPEBSpline::toWeight)); - + Inkscape::UI::Widget::Scalar *widgRegistered = + Gtk::manage(dynamic_cast(widg)); + widgRegistered->signal_value_changed() + .connect(sigc::mem_fun(*this, &LPEBSpline::toWeight)); + widg = dynamic_cast(widgRegistered); if (widg) { Gtk::HBox * scalarParameter = dynamic_cast(widg); - std::vector childList = scalarParameter->get_children(); + std::vector< Gtk::Widget* > childList = scalarParameter->get_children(); Gtk::Entry* entryWidg = dynamic_cast(childList[1]); entryWidg->set_width_chars(6); } } - + if (param->param_key == "onlySelected") { + Gtk::CheckButton *widgRegistered = + Gtk::manage(dynamic_cast(widg)); + widg = dynamic_cast(widgRegistered); + } + if (param->param_key == "ignoreCusp") { + Gtk::CheckButton *widgRegistered = + Gtk::manage(dynamic_cast(widg)); + widg = dynamic_cast(widgRegistered); + } Glib::ustring *tip = param->param_getTooltip(); if (widg) { vbox->pack_start(*widg, true, true, 2); @@ -333,7 +378,7 @@ Gtk::Widget *LPEBSpline::newWidget() ++it; } - return vbox; + return dynamic_cast(vbox); } void LPEBSpline::toDefaultWeight(Gtk::Widget *widgWeight) @@ -379,7 +424,8 @@ void LPEBSpline::changeWeight(double weightValue) g_free(str); curve->unref(); desktop->clearWaitingCursor(); - DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_LPE, _("Modified the weight of a BSpline")); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_LPE, + _("Modified the weight of the BSpline")); } bool LPEBSpline::nodeIsSelected(Geom::Point nodePoint) diff --git a/src/live_effects/lpe-bspline.h b/src/live_effects/lpe-bspline.h index e066015ad..169658b94 100644 --- a/src/live_effects/lpe-bspline.h +++ b/src/live_effects/lpe-bspline.h @@ -24,19 +24,16 @@ public: return SUPPRESS_FLASH; } virtual void doEffect(SPCurve *curve); - virtual void doBeforeEffect(SPLPEItem const* lpeitem); + virtual void doBeforeEffect (SPLPEItem const* lpeitem); void drawHandle(Geom::Point p, double radiusHelperNodes); void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector &hp_vec); - - void doBSplineFromWidget(SPCurve *curve, double value); - bool nodeIsSelected(Geom::Point nodePoint); - + virtual void doBSplineFromWidget(SPCurve *curve, double value); + virtual bool nodeIsSelected(Geom::Point nodePoint); virtual Gtk::Widget *newWidget(); - - void changeWeight(double weightValue); - void toDefaultWeight(Gtk::Widget *widgWeight); - void toMakeCusp(Gtk::Widget *widgWeight); - void toWeight(); + virtual void changeWeight(double weightValue); + virtual void toDefaultWeight(Gtk::Widget *widgWeight); + virtual void toMakeCusp(Gtk::Widget *widgWeight); + virtual void toWeight(); // TODO make this private ScalarParam steps; @@ -45,6 +42,7 @@ private: std::vector points; BoolParam ignoreCusp; BoolParam onlySelected; + BoolParam showHelper; ScalarParam weight; Geom::PathVector hp; -- cgit v1.2.3 From c006641818185435dcb6b98c49fdae2fa561d762 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Thu, 16 Oct 2014 18:05:08 -0400 Subject: Fix previous revision. revid:jtx@jtx.marker.es-20141016153516-eng0y49ppsk2vt5b 13617 Jabiertxof2014-10-16 Change LPE from envelope-perspective to perspective-envelope For me is easy to use/find with this new name. Also fix a rounding error on envelope at small sizes (bzr r13341.1.276) --- src/live_effects/CMakeLists.txt | 5 +- src/live_effects/Makefile_insert | 6 +- src/live_effects/effect-enum.h | 2 +- src/live_effects/effect.cpp | 8 +- src/live_effects/lpe-envelope-perspective.cpp | 440 -------------------------- src/live_effects/lpe-envelope-perspective.h | 76 ----- src/live_effects/lpe-perspective-envelope.cpp | 384 ++++++++++++++++++++++ src/live_effects/lpe-perspective-envelope.h | 70 ++++ 8 files changed, 464 insertions(+), 527 deletions(-) delete mode 100644 src/live_effects/lpe-envelope-perspective.cpp delete mode 100644 src/live_effects/lpe-envelope-perspective.h create mode 100644 src/live_effects/lpe-perspective-envelope.cpp create mode 100644 src/live_effects/lpe-perspective-envelope.h (limited to 'src') diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 30c2b2f41..c8a02c810 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -15,7 +15,6 @@ set(live_effects_SRC lpe-dynastroke.cpp lpe-ellipse-5pts.cpp lpe-envelope.cpp - lpe-envelope-perspective.cpp lpe-extrude.cpp lpe-fill-between-many.cpp lpe-fill-between-strokes.cpp @@ -32,6 +31,7 @@ set(live_effects_SRC lpe-patternalongpath.cpp lpe-perp_bisector.cpp lpe-perspective_path.cpp + lpe-perspective-envelope.cpp lpe-powerstroke.cpp lpe-recursiveskeleton.cpp lpe-rough-hatches.cpp @@ -47,7 +47,6 @@ set(live_effects_SRC lpe-bspline.cpp lpe-text_label.cpp lpe-vonkoch.cpp - lpe-envelope-perspective.cpp lpegroupbbox.cpp lpeobject-reference.cpp lpeobject.cpp @@ -106,6 +105,7 @@ set(live_effects_SRC lpe-patternalongpath.h lpe-perp_bisector.h lpe-perspective_path.h + lpe-perspective-envelope.h lpe-powerstroke.h lpe-powerstroke-interpolators.h lpe-recursiveskeleton.h @@ -122,7 +122,6 @@ set(live_effects_SRC lpe-bspline.h lpe-text_label.h lpe-vonkoch.h - lpe-envelope-perspective.h lpegroupbbox.h lpeobject-reference.h lpeobject.h diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index f18dcdef0..8f0a3ac57 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -70,6 +70,8 @@ ink_common_sources += \ live_effects/lpe-circle_with_radius.h \ live_effects/lpe-perspective_path.cpp \ live_effects/lpe-perspective_path.h \ + live_effects/lpe-perspective-envelope.cpp \ + live_effects/lpe-perspective-envelope.h \ live_effects/lpe-mirror_symmetry.cpp \ live_effects/lpe-mirror_symmetry.h \ live_effects/lpe-circle_3pts.cpp \ @@ -112,6 +114,4 @@ ink_common_sources += \ live_effects/lpe-jointype.cpp \ live_effects/lpe-jointype.h \ live_effects/lpe-taperstroke.cpp \ - live_effects/lpe-taperstroke.h \ - live_effects/lpe-envelope-perspective.cpp \ - live_effects/lpe-envelope-perspective.h + live_effects/lpe-taperstroke.h diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h index c53f1a5b9..383eec19e 100644 --- a/src/live_effects/effect-enum.h +++ b/src/live_effects/effect-enum.h @@ -63,7 +63,7 @@ enum EffectType { BOUNDING_BOX, JOIN_TYPE, TAPER_STROKE, - ENVELOPE_PERSPECTIVE, + PERSPECTIVE_ENVELOPE, FILLET_CHAMFER, INVALID_LPE // This must be last (I made it such that it is not needed anymore I think..., Don't trust on it being last. - johan) }; diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 0885ad6f0..1a64defd9 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -25,6 +25,7 @@ #include "live_effects/lpe-curvestitch.h" #include "live_effects/lpe-circle_with_radius.h" #include "live_effects/lpe-perspective_path.h" +#include "live_effects/lpe-perspective-envelope.h" #include "live_effects/lpe-spiro.h" #include "live_effects/lpe-lattice.h" #include "live_effects/lpe-lattice2.h" @@ -60,7 +61,6 @@ #include "live_effects/lpe-bounding-box.h" #include "live_effects/lpe-jointype.h" #include "live_effects/lpe-taperstroke.h" -#include "live_effects/lpe-envelope-perspective.h" #include "live_effects/lpe-fillet-chamfer.h" #include "xml/node-event-vector.h" @@ -152,7 +152,7 @@ const Util::EnumData LPETypeData[] = { /* 0.91 */ {SIMPLIFY, N_("Simplify"), "simplify"}, {LATTICE2, N_("Lattice Deformation 2"), "lattice2"}, - {ENVELOPE_PERSPECTIVE, N_("Envelope-Perspective"), "envelope-perspective"}, + {PERSPECTIVE_ENVELOPE, N_("Perspective/Envelope"), "perspective-envelope"}, {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"}, {INTERPOLATE_POINTS, N_("Interpolate points"), "interpolate_points"}, }; @@ -311,8 +311,8 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case LATTICE2: neweffect = static_cast ( new LPELattice2(lpeobj) ); break; - case ENVELOPE_PERSPECTIVE: - neweffect = static_cast ( new LPEEnvelopePerspective(lpeobj) ); + case PERSPECTIVE_ENVELOPE: + neweffect = static_cast ( new LPEPerspectiveEnvelope(lpeobj) ); break; case FILLET_CHAMFER: neweffect = static_cast ( new LPEFilletChamfer(lpeobj) ); diff --git a/src/live_effects/lpe-envelope-perspective.cpp b/src/live_effects/lpe-envelope-perspective.cpp deleted file mode 100644 index c62dbbf6a..000000000 --- a/src/live_effects/lpe-envelope-perspective.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/** \file - * LPE implementation - - */ -/* - * Authors: - * Jabiertxof Code migration from python extensions envelope and perspective - * Aaron Spike, aaron@ekips.org from envelope and perspective phyton code - * Dmitry Platonov, shadowjack@mail.ru, 2006 perspective approach & math - * Jose Hevia (freon) Transform algorithm from envelope - * - * Copyright (C) 2007-2014 Authors - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include -#include "live_effects/lpe-envelope-perspective.h" -#include "helper/geom.h" -#include "display/curve.h" -#include "svg/svg.h" -#include "ui/tools-switch.h" -#include -#include "desktop.h" - -using namespace Geom; - -namespace Inkscape { -namespace LivePathEffect { - -enum DeformationType { - DEFORMATION_ENVELOPE, - DEFORMATION_PERSPECTIVE -}; - -static const Util::EnumData DeformationTypeData[] = { - {DEFORMATION_ENVELOPE , N_("Envelope deformation"), "Envelope deformation"}, - {DEFORMATION_PERSPECTIVE , N_("Perspective"), "Perspective"} -}; - -static const Util::EnumDataConverter DeformationTypeConverter(DeformationTypeData, sizeof(DeformationTypeData)/sizeof(*DeformationTypeData)); - -LPEEnvelopePerspective::LPEEnvelopePerspective(LivePathEffectObject *lpeobject) : - Effect(lpeobject), - // initialise your parameters here: - deform_type(_("Type"), _("Select the type of deformation"), "deform_type", DeformationTypeConverter, &wr, this, DEFORMATION_ENVELOPE), - Up_Left_Point(_("Top Left"), _("Top Left - Ctrl+Alt+Click to reset"), "Up_Left_Point", &wr, this), - Up_Right_Point(_("Top Right"), _("Top Right - Ctrl+Alt+Click to reset"), "Up_Right_Point", &wr, this), - Down_Left_Point(_("Down Left"), _("Down Left - Ctrl+Alt+Click to reset"), "Down_Left_Point", &wr, this), - Down_Right_Point(_("Down Right"), _("Down Right - Ctrl+Alt+Click to reset"), "Down_Right_Point", &wr, this) -{ - // register all your parameters here, so Inkscape knows which parameters this effect has: - registerParameter( dynamic_cast(&deform_type)); - registerParameter( dynamic_cast(&Up_Left_Point) ); - registerParameter( dynamic_cast(&Up_Right_Point) ); - registerParameter( dynamic_cast(&Down_Left_Point) ); - registerParameter( dynamic_cast(&Down_Right_Point) ); -} - -LPEEnvelopePerspective::~LPEEnvelopePerspective() -{ -} - -void LPEEnvelopePerspective::doEffect(SPCurve *curve) { - using Geom::X; - using Geom::Y; - double projmatrix[3][3]; - if(deform_type == DEFORMATION_PERSPECTIVE){ - std::vector handles(4); - handles[0] = Down_Left_Point; - handles[1] = Up_Left_Point; - handles[2] = Up_Right_Point; - handles[3] = Down_Right_Point; - std::vector sourceHandles(4); - sourceHandles[0] = Geom::Point(boundingbox_X.min(), boundingbox_Y.max()); - sourceHandles[1] = Geom::Point(boundingbox_X.min(), boundingbox_Y.min()); - sourceHandles[2] = Geom::Point(boundingbox_X.max(), boundingbox_Y.min()); - sourceHandles[3] = Geom::Point(boundingbox_X.max(), boundingbox_Y.max()); - double solmatrix[8][8] = {{0}}; - double free_term[8] = {0}; - double gslSolmatrix[64]; - for(unsigned int i = 0; i < 4; ++i){ - solmatrix[i][0] = sourceHandles[i][X]; - solmatrix[i][1] = sourceHandles[i][Y]; - solmatrix[i][2] = 1; - solmatrix[i][6] = -handles[i][X] * sourceHandles[i][X]; - solmatrix[i][7] = -handles[i][X] * sourceHandles[i][Y]; - solmatrix[i+4][3] = sourceHandles[i][X]; - solmatrix[i+4][4] = sourceHandles[i][Y]; - solmatrix[i+4][5] = 1; - solmatrix[i+4][6] = -handles[i][Y] * sourceHandles[i][X]; - solmatrix[i+4][7] = -handles[i][Y] * sourceHandles[i][Y]; - free_term[i] = handles[i][X]; - free_term[i+4] = handles[i][Y]; - } - int h = 0; - for( int i = 0; i < 8; i++ ) { - for( int j = 0; j < 8; j++ ) { - gslSolmatrix[h] = solmatrix[i][j]; - h++; - } - } - //this is get by this page: - //http://www.gnu.org/software/gsl/manual/html_node/Linear-Algebra-Examples.html#Linear-Algebra-Examples - gsl_matrix_view m = gsl_matrix_view_array (gslSolmatrix, 8, 8); - gsl_vector_view b = gsl_vector_view_array (free_term, 8); - gsl_vector *x = gsl_vector_alloc (8); - int s; - gsl_permutation * p = gsl_permutation_alloc (8); - gsl_linalg_LU_decomp (&m.matrix, p, &s); - gsl_linalg_LU_solve (&m.matrix, p, &b.vector, x); - h = 0; - for( int i = 0; i < 3; i++ ) { - for( int j = 0; j < 3; j++ ) { - if(h==8){ - projmatrix[2][2] = 1.0; - continue; - } - projmatrix[i][j] = gsl_vector_get(x, h); - h++; - } - } - gsl_permutation_free (p); - gsl_vector_free (x); - } - Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(curve->get_pathvector()); - curve->reset(); - Geom::CubicBezier const *cubic = NULL; - Geom::Point pointAt1(0, 0); - Geom::Point pointAt2(0, 0); - Geom::Point pointAt3(0, 0); - for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { - //Si está vacío... - if (path_it->empty()) - continue; - //Itreadores - SPCurve *nCurve = new SPCurve(); - Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve - Geom::Path::const_iterator curve_it2 = - ++(path_it->begin()); // outgoing curve - Geom::Path::const_iterator curve_endit = - path_it->end_default(); // this determines when the loop has to stop - - if (path_it->closed()) { - // if the path is closed, maybe we have to stop a bit earlier because the - // closing line segment has zerolength. - const Geom::Curve &closingline = - path_it->back_closed(); // the closing line segment is always of type - // Geom::LineSegment. - if (are_near(closingline.initialPoint(), closingline.finalPoint())) { - // closingline.isDegenerate() did not work, because it only checks for - // *exact* zero length, which goes wrong for relative coordinates and - // rounding errors... - // the closing line segment has zero-length. So stop before that one! - curve_endit = path_it->end_open(); - } - } - if(deform_type == DEFORMATION_PERSPECTIVE){ - nCurve->moveto(project_point(curve_it1->initialPoint(),projmatrix)); - }else{ - nCurve->moveto(project_point(curve_it1->initialPoint())); - } - while (curve_it2 != curve_endit) { - cubic = dynamic_cast(&*curve_it1); - if (cubic) { - pointAt1 = (*cubic)[1]; - pointAt2 = (*cubic)[2]; - } else { - pointAt1 = curve_it1->initialPoint(); - pointAt2 = curve_it1->finalPoint(); - } - pointAt3 = curve_it1->finalPoint(); - if(deform_type == DEFORMATION_PERSPECTIVE){ - pointAt1 = project_point(pointAt1,projmatrix); - pointAt2 = project_point(pointAt2,projmatrix); - pointAt3 = project_point(pointAt3,projmatrix); - }else{ - pointAt1 = project_point(pointAt1); - pointAt2 = project_point(pointAt2); - pointAt3 = project_point(pointAt3); - } - nCurve->curveto(pointAt1, pointAt2, pointAt3); - ++curve_it1; - ++curve_it2; - } - cubic = dynamic_cast(&*curve_it1); - if (cubic) { - pointAt1 = (*cubic)[1]; - pointAt2 = (*cubic)[2]; - } else { - pointAt1 = curve_it1->initialPoint(); - pointAt2 = curve_it1->finalPoint(); - } - pointAt3 = curve_it1->finalPoint(); - if(deform_type == DEFORMATION_PERSPECTIVE){ - pointAt1 = project_point(pointAt1,projmatrix); - pointAt2 = project_point(pointAt2,projmatrix); - pointAt3 = project_point(pointAt3,projmatrix); - }else{ - pointAt1 = project_point(pointAt1); - pointAt2 = project_point(pointAt2); - pointAt3 = project_point(pointAt3); - } - nCurve->curveto(pointAt1, pointAt2, pointAt3); - if(deform_type == DEFORMATION_PERSPECTIVE){ - nCurve->move_endpoints(project_point(path_it->begin()->initialPoint(),projmatrix), pointAt3); - }else{ - nCurve->move_endpoints(project_point(path_it->begin()->initialPoint()), pointAt3); - } - //y cerramos la curva - if (path_it->closed()) { - nCurve->closepath_current(); - } - curve->append(nCurve, false); - nCurve->reset(); - delete nCurve; - } -} - -Geom::Point -LPEEnvelopePerspective::project_point(Geom::Point p){ - double width = boundingbox_X.extent(); - double height = boundingbox_Y.extent(); - Geom::Coord xratio = abs(Geom::Point(boundingbox_X.min(), boundingbox_Y.max())[X]-p[X])/width; - Geom::Coord yratio = abs(Geom::Point(boundingbox_X.min(), boundingbox_Y.max())[Y]-p[Y])/height; - Geom::Line* horiz = new Geom::Line(); - Geom::Line* vert = new Geom::Line(); - vert->setPoints (pointAtRatio(yratio,Down_Left_Point,Up_Left_Point),pointAtRatio(yratio,Down_Right_Point,Up_Right_Point)); - horiz->setPoints (pointAtRatio(xratio,Down_Left_Point,Down_Right_Point),pointAtRatio(xratio,Up_Left_Point,Up_Right_Point)); - - OptCrossing crossPoint = intersection(*horiz,*vert); - if(crossPoint){ - return horiz->pointAt(Geom::Coord(crossPoint->ta)); - }else{ - return p; - } -} - -Geom::Point -LPEEnvelopePerspective::project_point(Geom::Point p, double m[][3]){ - Geom::Coord x = p[0]; - Geom::Coord y = p[1]; - return Geom::Point( - Geom::Coord((x*m[0][0] + y*m[0][1] + m[0][2])/(x*m[2][0]+y*m[2][1]+m[2][2])), - Geom::Coord((x*m[1][0] + y*m[1][1] + m[1][2])/(x*m[2][0]+y*m[2][1]+m[2][2]))); -} - -Geom::Point -LPEEnvelopePerspective::pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B){ - Geom::Coord x = A[X] + (ratio * (B[X]-A[X])); - Geom::Coord y = A[Y]+ (ratio * (B[Y]-A[Y])); - return Point(x, y); -} - - -Gtk::Widget * -LPEEnvelopePerspective::newWidget() -{ - // use manage here, because after deletion of Effect object, others might still be pointing to this widget. - Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); - - vbox->set_border_width(5); - vbox->set_homogeneous(false); - vbox->set_spacing(6); - std::vector::iterator it = param_vector.begin(); - Gtk::HBox * hboxUpHandles = Gtk::manage(new Gtk::HBox(false,0)); - Gtk::HBox * hboxDownHandles = Gtk::manage(new Gtk::HBox(false,0)); - while (it != param_vector.end()) { - if ((*it)->widget_is_visible) { - Parameter * param = *it; - Gtk::Widget * widg = dynamic_cast(param->param_newWidget()); - if (param->param_key == "Up_Left_Point" || - param->param_key == "Up_Right_Point" || - param->param_key == "Down_Left_Point" || - param->param_key == "Down_Right_Point") - { - Gtk::HBox * pointParameter = dynamic_cast(widg); - std::vector< Gtk::Widget* > childList = pointParameter->get_children(); - Gtk::HBox * pointParameterHBox = dynamic_cast(childList[0]); - std::vector< Gtk::Widget* > childList2 = pointParameterHBox->get_children(); - pointParameterHBox->remove(childList2[0][0]); - Glib::ustring * tip = param->param_getTooltip(); - if (widg) { - if(param->param_key == "Up_Left_Point"){ - Gtk::Label* handles = Gtk::manage(new Gtk::Label(Glib::ustring(_("Handles:")),Gtk::ALIGN_START)); - vbox->pack_start(*handles, false, false, 2); - hboxUpHandles->pack_start(*widg, true, true, 2); -#if WITH_GTKMM_3_0 - hboxUpHandles->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), Gtk::PACK_EXPAND_WIDGET); -#else - hboxUpHandles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET); -#endif - }else if(param->param_key == "Up_Right_Point"){ - hboxUpHandles->pack_start(*widg, true, true, 2); - }else if(param->param_key == "Down_Left_Point"){ - hboxDownHandles->pack_start(*widg, true, true, 2); -#if WITH_GTKMM_3_0 - hboxDownHandles->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), Gtk::PACK_EXPAND_WIDGET); -#else - hboxDownHandles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET); -#endif - }else{ - hboxDownHandles->pack_start(*widg, true, true, 2); - } - if (tip) { - widg->set_tooltip_text(*tip); - } else { - widg->set_tooltip_text(""); - widg->set_has_tooltip(false); - } - } - }else{ - Glib::ustring * tip = param->param_getTooltip(); - if (widg) { - vbox->pack_start(*widg, true, true, 2); - if (tip) { - widg->set_tooltip_text(*tip); - } else { - widg->set_tooltip_text(""); - widg->set_has_tooltip(false); - } - } - } - } - - ++it; - } - vbox->pack_start(*hboxUpHandles,true, true, 2); - Gtk::HBox * hboxMiddle = Gtk::manage(new Gtk::HBox(true,2)); -#if WITH_GTKMM_3_0 - hboxMiddle->pack_start(*Gtk::manage(new Gtk::Separator()), Gtk::PACK_EXPAND_WIDGET); - hboxMiddle->pack_start(*Gtk::manage(new Gtk::Separator()), Gtk::PACK_EXPAND_WIDGET); -#else - hboxMiddle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); - hboxMiddle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); -#endif - vbox->pack_start(*hboxMiddle, false, true, 2); - vbox->pack_start(*hboxDownHandles, true, true, 2); - Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); - Gtk::Button* resetButton = Gtk::manage(new Gtk::Button(Gtk::Stock::CLEAR)); - resetButton->signal_clicked().connect(sigc::mem_fun (*this,&LPEEnvelopePerspective::resetGrid)); - resetButton->set_size_request(140,45); - vbox->pack_start(*hbox, true,true,2); - hbox->pack_start(*resetButton, false, false,2); - return dynamic_cast(vbox); -} - -void -LPEEnvelopePerspective::doBeforeEffect (SPLPEItem const* lpeitem) -{ - original_bbox(lpeitem); - setDefaults(); - SPLPEItem * item = const_cast(lpeitem); - item->apply_to_clippath(item); - item->apply_to_mask(item); -} - -void -LPEEnvelopePerspective::setDefaults() -{ - Geom::Point Up_Left(boundingbox_X.min(), boundingbox_Y.min()); - Geom::Point Up_Right(boundingbox_X.max(), boundingbox_Y.min()); - Geom::Point Down_Left(boundingbox_X.min(), boundingbox_Y.max()); - Geom::Point Down_Right(boundingbox_X.max(), boundingbox_Y.max()); - - Up_Left_Point.param_update_default(Up_Left); - Up_Right_Point.param_update_default(Up_Right); - Down_Right_Point.param_update_default(Down_Right); - Down_Left_Point.param_update_default(Down_Left); -} - -void -LPEEnvelopePerspective::resetGrid() -{ - Up_Left_Point.param_set_and_write_default(); - Up_Right_Point.param_set_and_write_default(); - Down_Right_Point.param_set_and_write_default(); - Down_Left_Point.param_set_and_write_default(); - //todo:this hack is only to reposition the knots on reser grid button - //Better update path effect in LPEITEM - SPDesktop * desktop = inkscape_active_desktop(); - tools_switch(desktop, TOOLS_SELECT); - tools_switch(desktop, TOOLS_NODES); -} - -void -LPEEnvelopePerspective::resetDefaults(SPItem const* item) -{ - Effect::resetDefaults(item); - original_bbox(SP_LPE_ITEM(item)); - setDefaults(); - resetGrid(); -} - -void -LPEEnvelopePerspective::calculateCurve(Geom::Point a,Geom::Point b, SPCurve* c, bool horizontal, bool move) -{ - using Geom::X; - using Geom::Y; - if(move) c->moveto(a); - Geom::Point cubic1 = a + (1./3)* (b - a); - Geom::Point cubic2 = b + (1./3)* (a - b); - if(horizontal) c->curveto(Geom::Point(cubic1[X],a[Y]),Geom::Point(cubic2[X],b[Y]),b); - else c->curveto(Geom::Point(a[X],cubic1[Y]),Geom::Point(b[X],cubic2[Y]),b); -} - -void -LPEEnvelopePerspective::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector &hp_vec) -{ - hp_vec.clear(); - - SPCurve *c = new SPCurve(); - c->reset(); - c->moveto(Up_Left_Point); - c->lineto(Up_Right_Point); - c->lineto(Down_Right_Point); - c->lineto(Down_Left_Point); - c->lineto(Up_Left_Point); - hp_vec.push_back(c->get_pathvector()); -} - - -/* ######################## */ - -} //namespace LivePathEffect -} /* 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 : diff --git a/src/live_effects/lpe-envelope-perspective.h b/src/live_effects/lpe-envelope-perspective.h deleted file mode 100644 index 0de9a0e35..000000000 --- a/src/live_effects/lpe-envelope-perspective.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef INKSCAPE_LPE_ENVELOPE_PERSPECTIVE_H -#define INKSCAPE_LPE_ENVELOPE_PERSPECTIVE_H - -/** \file - * LPE implementation , see lpe-envelope-perspective.cpp. - - */ -/* - * Authors: - * Jabiertxof Code migration from python extensions envelope and perspective - * Aaron Spike, aaron@ekips.org from envelope and perspective phyton code - * Dmitry Platonov, shadowjack@mail.ru, 2006 perspective approach & math - * Jose Hevia (freon) Transform algorithm from envelope - * - * Copyright (C) 2007-2014 Authors - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "live_effects/parameter/enum.h" -#include "live_effects/effect.h" -#include "live_effects/parameter/pointreseteable.h" -#include "live_effects/lpegroupbbox.h" - -namespace Inkscape { -namespace LivePathEffect { - -class LPEEnvelopePerspective : public Effect, GroupBBoxEffect { -public: - - LPEEnvelopePerspective(LivePathEffectObject *lpeobject); - virtual ~LPEEnvelopePerspective(); - - virtual void doEffect(SPCurve *curve); - - virtual Geom::Point project_point(Geom::Point p); - - virtual Geom::Point project_point(Geom::Point p, double m[][3]); - - Geom::Point pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B); - - virtual void resetDefaults(SPItem const* item); - - virtual void doBeforeEffect(SPLPEItem const* lpeitem); - - virtual Gtk::Widget * newWidget(); - - virtual void calculateCurve(Geom::Point a,Geom::Point b, SPCurve *c, bool horizontal, bool move); - - virtual void setDefaults(); - - virtual void resetGrid(); - - //virtual void original_bbox(SPLPEItem const* lpeitem, bool absolute = false); - - //virtual void addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector &/*hp_vec*/); - - //virtual std::vector getHelperPaths(SPLPEItem const* lpeitem); -protected: - void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector &hp_vec); -private: - - EnumParam deform_type; - PointReseteableParam Up_Left_Point; - PointReseteableParam Up_Right_Point; - PointReseteableParam Down_Left_Point; - PointReseteableParam Down_Right_Point; - - LPEEnvelopePerspective(const LPEEnvelopePerspective&); - LPEEnvelopePerspective& operator=(const LPEEnvelopePerspective&); -}; - -} //namespace LivePathEffect -} //namespace Inkscape - -#endif diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp new file mode 100644 index 000000000..249269b6f --- /dev/null +++ b/src/live_effects/lpe-perspective-envelope.cpp @@ -0,0 +1,384 @@ +/** \file + * LPE implementation + + */ +/* + * Authors: + * Jabiertxof Code migration from python extensions envelope and perspective + * Aaron Spike, aaron@ekips.org from envelope and perspective phyton code + * Dmitry Platonov, shadowjack@mail.ru, 2006 perspective approach & math + * Jose Hevia (freon) Transform algorithm from envelope + * + * Copyright (C) 2007-2014 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include "live_effects/lpe-perspective-envelope.h" +#include "helper/geom.h" +#include "display/curve.h" +#include "svg/svg.h" +#include "ui/tools-switch.h" +#include +#include "desktop.h" + +using namespace Geom; + +namespace Inkscape { +namespace LivePathEffect { + +enum DeformationType { + DEFORMATION_PERSPECTIVE, + DEFORMATION_ENVELOPE +}; + +static const Util::EnumData DeformationTypeData[] = { + {DEFORMATION_PERSPECTIVE , N_("Perspective"), "Perspective"}, + {DEFORMATION_ENVELOPE , N_("Envelope deformation"), "Envelope deformation"} +}; + +static const Util::EnumDataConverter DeformationTypeConverter(DeformationTypeData, sizeof(DeformationTypeData)/sizeof(*DeformationTypeData)); + +LPEPerspectiveEnvelope::LPEPerspectiveEnvelope(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + // initialise your parameters here: + deform_type(_("Type"), _("Select the type of deformation"), "deform_type", DeformationTypeConverter, &wr, this, DEFORMATION_PERSPECTIVE), + Up_Left_Point(_("Top Left"), _("Top Left - Ctrl+Alt+Click to reset"), "Up_Left_Point", &wr, this), + Up_Right_Point(_("Top Right"), _("Top Right - Ctrl+Alt+Click to reset"), "Up_Right_Point", &wr, this), + Down_Left_Point(_("Down Left"), _("Down Left - Ctrl+Alt+Click to reset"), "Down_Left_Point", &wr, this), + Down_Right_Point(_("Down Right"), _("Down Right - Ctrl+Alt+Click to reset"), "Down_Right_Point", &wr, this) +{ + // register all your parameters here, so Inkscape knows which parameters this effect has: + registerParameter( dynamic_cast(&deform_type)); + registerParameter( dynamic_cast(&Up_Left_Point) ); + registerParameter( dynamic_cast(&Up_Right_Point) ); + registerParameter( dynamic_cast(&Down_Left_Point) ); + registerParameter( dynamic_cast(&Down_Right_Point) ); +} + +LPEPerspectiveEnvelope::~LPEPerspectiveEnvelope() +{ +} + +void LPEPerspectiveEnvelope::doEffect(SPCurve *curve) { + using Geom::X; + using Geom::Y; + double projmatrix[3][3]; + if(deform_type == DEFORMATION_PERSPECTIVE){ + std::vector handles(4); + handles[0] = Down_Left_Point; + handles[1] = Up_Left_Point; + handles[2] = Up_Right_Point; + handles[3] = Down_Right_Point; + std::vector sourceHandles(4); + sourceHandles[0] = Geom::Point(boundingbox_X.min(), boundingbox_Y.max()); + sourceHandles[1] = Geom::Point(boundingbox_X.min(), boundingbox_Y.min()); + sourceHandles[2] = Geom::Point(boundingbox_X.max(), boundingbox_Y.min()); + sourceHandles[3] = Geom::Point(boundingbox_X.max(), boundingbox_Y.max()); + double solmatrix[8][8] = {{0}}; + double free_term[8] = {0}; + double gslSolmatrix[64]; + for(unsigned int i = 0; i < 4; ++i){ + solmatrix[i][0] = sourceHandles[i][X]; + solmatrix[i][1] = sourceHandles[i][Y]; + solmatrix[i][2] = 1; + solmatrix[i][6] = -handles[i][X] * sourceHandles[i][X]; + solmatrix[i][7] = -handles[i][X] * sourceHandles[i][Y]; + solmatrix[i+4][3] = sourceHandles[i][X]; + solmatrix[i+4][4] = sourceHandles[i][Y]; + solmatrix[i+4][5] = 1; + solmatrix[i+4][6] = -handles[i][Y] * sourceHandles[i][X]; + solmatrix[i+4][7] = -handles[i][Y] * sourceHandles[i][Y]; + free_term[i] = handles[i][X]; + free_term[i+4] = handles[i][Y]; + } + int h = 0; + for( int i = 0; i < 8; i++ ) { + for( int j = 0; j < 8; j++ ) { + gslSolmatrix[h] = solmatrix[i][j]; + h++; + } + } + //this is get by this page: + //http://www.gnu.org/software/gsl/manual/html_node/Linear-Algebra-Examples.html#Linear-Algebra-Examples + gsl_matrix_view m = gsl_matrix_view_array (gslSolmatrix, 8, 8); + gsl_vector_view b = gsl_vector_view_array (free_term, 8); + gsl_vector *x = gsl_vector_alloc (8); + int s; + gsl_permutation * p = gsl_permutation_alloc (8); + gsl_linalg_LU_decomp (&m.matrix, p, &s); + gsl_linalg_LU_solve (&m.matrix, p, &b.vector, x); + h = 0; + for( int i = 0; i < 3; i++ ) { + for( int j = 0; j < 3; j++ ) { + if(h==8){ + projmatrix[2][2] = 1.0; + continue; + } + projmatrix[i][j] = gsl_vector_get(x, h); + h++; + } + } + gsl_permutation_free (p); + gsl_vector_free (x); + } + Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(curve->get_pathvector()); + curve->reset(); + Geom::CubicBezier const *cubic = NULL; + Geom::Point pointAt1(0, 0); + Geom::Point pointAt2(0, 0); + Geom::Point pointAt3(0, 0); + for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { + //Si está vacío... + if (path_it->empty()) + continue; + //Itreadores + SPCurve *nCurve = new SPCurve(); + Geom::Path::const_iterator curve_it1 = path_it->begin(); + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); + Geom::Path::const_iterator curve_endit = path_it->end_default(); + + if (path_it->closed()) { + const Geom::Curve &closingline = + path_it->back_closed(); + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { + curve_endit = path_it->end_open(); + } + } + if(deform_type == DEFORMATION_PERSPECTIVE){ + nCurve->moveto(project_point(curve_it1->initialPoint(),projmatrix)); + }else{ + nCurve->moveto(project_point(curve_it1->initialPoint())); + } + while (curve_it1 != curve_endit) { + cubic = dynamic_cast(&*curve_it1); + if (cubic) { + pointAt1 = (*cubic)[1]; + pointAt2 = (*cubic)[2]; + } else { + pointAt1 = curve_it1->initialPoint(); + pointAt2 = curve_it1->finalPoint(); + } + pointAt3 = curve_it1->finalPoint(); + if(deform_type == DEFORMATION_PERSPECTIVE){ + pointAt1 = project_point(pointAt1,projmatrix); + pointAt2 = project_point(pointAt2,projmatrix); + pointAt3 = project_point(pointAt3,projmatrix); + }else{ + pointAt1 = project_point(pointAt1); + pointAt2 = project_point(pointAt2); + pointAt3 = project_point(pointAt3); + } + nCurve->curveto(pointAt1, pointAt2, pointAt3); + ++curve_it1; + if(curve_it2 != curve_endit) { + ++curve_it2; + } + } + //y cerramos la curva + if (path_it->closed()) { + nCurve->move_endpoints(pointAt3, pointAt3); + nCurve->closepath_current(); + } + curve->append(nCurve, false); + nCurve->reset(); + delete nCurve; + } +} + +Geom::Point +LPEPerspectiveEnvelope::project_point(Geom::Point p){ + double width = boundingbox_X.extent(); + double height = boundingbox_Y.extent(); + double delta_x = boundingbox_X.min() - p[X]; + double delta_y = boundingbox_Y.max() - p[Y]; + Geom::Coord xratio = (delta_x * sgn(delta_x)) / width; + Geom::Coord yratio = (delta_y * sgn(delta_y)) / height; + Geom::Line* horiz = new Geom::Line(); + Geom::Line* vert = new Geom::Line(); + vert->setPoints (pointAtRatio(yratio,Down_Left_Point,Up_Left_Point),pointAtRatio(yratio,Down_Right_Point,Up_Right_Point)); + horiz->setPoints (pointAtRatio(xratio,Down_Left_Point,Down_Right_Point),pointAtRatio(xratio,Up_Left_Point,Up_Right_Point)); + + OptCrossing crossPoint = intersection(*horiz,*vert); + if(crossPoint){ + return horiz->pointAt(Geom::Coord(crossPoint->ta)); + }else{ + return p; + } +} + +Geom::Point +LPEPerspectiveEnvelope::project_point(Geom::Point p, double m[][3]){ + Geom::Coord x = p[0]; + Geom::Coord y = p[1]; + return Geom::Point( + Geom::Coord((x*m[0][0] + y*m[0][1] + m[0][2])/(x*m[2][0]+y*m[2][1]+m[2][2])), + Geom::Coord((x*m[1][0] + y*m[1][1] + m[1][2])/(x*m[2][0]+y*m[2][1]+m[2][2]))); +} + +Geom::Point +LPEPerspectiveEnvelope::pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B){ + Geom::Coord x = A[X] + (ratio * (B[X]-A[X])); + Geom::Coord y = A[Y]+ (ratio * (B[Y]-A[Y])); + return Point(x, y); +} + + +Gtk::Widget * +LPEPerspectiveEnvelope::newWidget() +{ + // use manage here, because after deletion of Effect object, others might still be pointing to this widget. + Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); + + vbox->set_border_width(5); + vbox->set_homogeneous(false); + vbox->set_spacing(6); + std::vector::iterator it = param_vector.begin(); + Gtk::HBox * hboxUpHandles = Gtk::manage(new Gtk::HBox(false,0)); + Gtk::HBox * hboxDownHandles = Gtk::manage(new Gtk::HBox(false,0)); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter * param = *it; + Gtk::Widget * widg = dynamic_cast(param->param_newWidget()); + if (param->param_key == "Up_Left_Point" || + param->param_key == "Up_Right_Point" || + param->param_key == "Down_Left_Point" || + param->param_key == "Down_Right_Point") + { + Gtk::HBox * pointParameter = dynamic_cast(widg); + std::vector< Gtk::Widget* > childList = pointParameter->get_children(); + Gtk::HBox * pointParameterHBox = dynamic_cast(childList[0]); + std::vector< Gtk::Widget* > childList2 = pointParameterHBox->get_children(); + pointParameterHBox->remove(childList2[0][0]); + Glib::ustring * tip = param->param_getTooltip(); + if (widg) { + if(param->param_key == "Up_Left_Point"){ + Gtk::Label* handles = Gtk::manage(new Gtk::Label(Glib::ustring(_("Handles:")),Gtk::ALIGN_START)); + vbox->pack_start(*handles, false, false, 2); + hboxUpHandles->pack_start(*widg, true, true, 2); + hboxUpHandles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET); + }else if(param->param_key == "Up_Right_Point"){ + hboxUpHandles->pack_start(*widg, true, true, 2); + }else if(param->param_key == "Down_Left_Point"){ + hboxDownHandles->pack_start(*widg, true, true, 2); + hboxDownHandles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET); + }else{ + hboxDownHandles->pack_start(*widg, true, true, 2); + } + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + }else{ + Glib::ustring * tip = param->param_getTooltip(); + if (widg) { + vbox->pack_start(*widg, true, true, 2); + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + } + + ++it; + } + vbox->pack_start(*hboxUpHandles,true, true, 2); + Gtk::HBox * hboxMiddle = Gtk::manage(new Gtk::HBox(true,2)); + hboxMiddle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); + hboxMiddle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); + vbox->pack_start(*hboxMiddle, false, true, 2); + vbox->pack_start(*hboxDownHandles, true, true, 2); + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); + Gtk::Button* resetButton = Gtk::manage(new Gtk::Button(Gtk::Stock::CLEAR)); + resetButton->signal_clicked().connect(sigc::mem_fun (*this,&LPEPerspectiveEnvelope::resetGrid)); + resetButton->set_size_request(140,45); + vbox->pack_start(*hbox, true,true,2); + hbox->pack_start(*resetButton, false, false,2); + return dynamic_cast(vbox); +} + +void +LPEPerspectiveEnvelope::doBeforeEffect (SPLPEItem const* lpeitem) +{ + original_bbox(lpeitem); + setDefaults(); +} + +void +LPEPerspectiveEnvelope::setDefaults() +{ + Geom::Point Up_Left(boundingbox_X.min(), boundingbox_Y.min()); + Geom::Point Up_Right(boundingbox_X.max(), boundingbox_Y.min()); + Geom::Point Down_Left(boundingbox_X.min(), boundingbox_Y.max()); + Geom::Point Down_Right(boundingbox_X.max(), boundingbox_Y.max()); + + Up_Left_Point.param_update_default(Up_Left); + Up_Right_Point.param_update_default(Up_Right); + Down_Right_Point.param_update_default(Down_Right); + Down_Left_Point.param_update_default(Down_Left); +} + +void +LPEPerspectiveEnvelope::resetGrid() +{ + Up_Left_Point.param_set_and_write_default(); + Up_Right_Point.param_set_and_write_default(); + Down_Right_Point.param_set_and_write_default(); + Down_Left_Point.param_set_and_write_default(); + //todo:this hack is only to reposition the knots on reser grid button + //Better update path effect in LPEITEM + SPDesktop * desktop = inkscape_active_desktop(); + tools_switch(desktop, TOOLS_SELECT); + tools_switch(desktop, TOOLS_NODES); +} + +void +LPEPerspectiveEnvelope::resetDefaults(SPItem const* item) +{ + Effect::resetDefaults(item); + original_bbox(SP_LPE_ITEM(item)); + setDefaults(); + resetGrid(); +} + +void +LPEPerspectiveEnvelope::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector &hp_vec) +{ + hp_vec.clear(); + + SPCurve *c = new SPCurve(); + c->reset(); + c->moveto(Up_Left_Point); + c->lineto(Up_Right_Point); + c->lineto(Down_Right_Point); + c->lineto(Down_Left_Point); + c->lineto(Up_Left_Point); + hp_vec.push_back(c->get_pathvector()); +} + + +/* ######################## */ + +} //namespace LivePathEffect +} /* 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: file_type=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/lpe-perspective-envelope.h b/src/live_effects/lpe-perspective-envelope.h new file mode 100644 index 000000000..2f253882e --- /dev/null +++ b/src/live_effects/lpe-perspective-envelope.h @@ -0,0 +1,70 @@ +#ifndef INKSCAPE_LPE_PERSPECTIVE_ENVELOPE_H +#define INKSCAPE_LPE_PERSPECTIVE_ENVELOPE_H + +/** \file + * LPE implementation , see lpe-perspective-envelope.cpp. + + */ +/* + * Authors: + * Jabiertxof Code migration from python extensions envelope and perspective + * Aaron Spike, aaron@ekips.org from envelope and perspective phyton code + * Dmitry Platonov, shadowjack@mail.ru, 2006 perspective approach & math + * Jose Hevia (freon) Transform algorithm from envelope + * + * Copyright (C) 2007-2014 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/parameter/enum.h" +#include "live_effects/effect.h" +#include "live_effects/parameter/pointreseteable.h" +#include "live_effects/lpegroupbbox.h" + +namespace Inkscape { +namespace LivePathEffect { + +class LPEPerspectiveEnvelope : public Effect, GroupBBoxEffect { +public: + + LPEPerspectiveEnvelope(LivePathEffectObject *lpeobject); + + virtual ~LPEPerspectiveEnvelope(); + + virtual void doEffect(SPCurve *curve); + + virtual Geom::Point project_point(Geom::Point p); + + virtual Geom::Point project_point(Geom::Point p, double m[][3]); + + virtual Geom::Point pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B); + + virtual void resetDefaults(SPItem const* item); + + virtual void doBeforeEffect(SPLPEItem const* lpeitem); + + virtual Gtk::Widget * newWidget(); + + virtual void setDefaults(); + + virtual void resetGrid(); + +protected: + void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector &hp_vec); +private: + + EnumParam deform_type; + PointReseteableParam Up_Left_Point; + PointReseteableParam Up_Right_Point; + PointReseteableParam Down_Left_Point; + PointReseteableParam Down_Right_Point; + + LPEPerspectiveEnvelope(const LPEPerspectiveEnvelope&); + LPEPerspectiveEnvelope& operator=(const LPEPerspectiveEnvelope&); +}; + +} //namespace LivePathEffect +} //namespace Inkscape + +#endif -- cgit v1.2.3 From 197f66062c6d6cc77539117526a9ecc30449bd61 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Thu, 16 Oct 2014 15:42:34 -0700 Subject: Removed dangerous GTK-mimicking macros. (bzr r13619) --- src/box3d-side.h | 4 +--- src/box3d.cpp | 23 +++++++++++++---------- src/desktop-style.cpp | 6 ++++-- 3 files changed, 18 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/box3d-side.h b/src/box3d-side.h index 04bd196c2..519ed495e 100644 --- a/src/box3d-side.h +++ b/src/box3d-side.h @@ -7,6 +7,7 @@ * Authors: * Maximilian Albert * Abhishek Sharma + * Jon A. Cruz * * Copyright (C) 2007 Authors * @@ -17,9 +18,6 @@ #include "axis-manip.h" -#define SP_BOX3D_SIDE(obj) (dynamic_cast((SPObject*)obj)) -#define SP_IS_BOX3D_SIDE(obj) (dynamic_cast((SPObject*)obj) != NULL) - class SPBox3D; class Persp3D; diff --git a/src/box3d.cpp b/src/box3d.cpp index eb82524dd..5f60766f4 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -6,6 +6,7 @@ * Lauris Kaplinski * bulia byak * Abhishek Sharma + * Jon A. Cruz * * Copyright (C) 2007 Authors * Copyright (C) 1999-2002 Lauris Kaplinski @@ -263,9 +264,10 @@ void box3d_position_set(SPBox3D *box) { /* This draws the curve and calls requestDisplayUpdate() for each side (the latter is done in box3d_side_position_set() to avoid update conflicts with the parent box) */ - for ( SPObject *child = box->firstChild(); child; child = child->getNext() ) { - if (SP_IS_BOX3D_SIDE(child)) { - box3d_side_position_set(SP_BOX3D_SIDE(child)); + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast(obj); + if (side) { + box3d_side_position_set(side); } } } @@ -1079,10 +1081,10 @@ box3d_recompute_z_orders (SPBox3D *box) { static std::map box3d_get_sides(SPBox3D *box) { std::map sides; - for ( SPObject *side = box->firstChild(); side; side = side->getNext() ) { - if (SP_IS_BOX3D_SIDE(side)){ - Box3DSide *bside = SP_BOX3D_SIDE(side); - sides[Box3D::face_to_int(bside->getFaceId())] = bside; + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast(obj); + if (side) { + sides[Box3D::face_to_int(side->getFaceId())] = side; } } sides.erase(-1); @@ -1280,9 +1282,10 @@ SPGroup *box3d_convert_to_group(SPBox3D *box) // create a new group and add the sides (converted to ordinary paths) as its children Inkscape::XML::Node *grepr = xml_doc->createElement("svg:g"); - for ( SPObject *child = box->firstChild(); child; child = child->getNext() ) { - if (SP_IS_BOX3D_SIDE(child)) { - Inkscape::XML::Node *repr = box3d_side_convert_to_path(SP_BOX3D_SIDE(child)); + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast(obj); + if (side) { + Inkscape::XML::Node *repr = box3d_side_convert_to_path(side); grepr->appendChild(repr); } else { g_warning("Non-side item encountered as child of a 3D box."); diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index 0b2e15d34..c2aa769f6 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -173,8 +173,10 @@ sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write for (const GSList *i = desktop->selection->itemList(); i != NULL; i = i->next) { /* last used styles for 3D box faces are stored separately */ - if (SP_IS_BOX3D_SIDE (i->data)) { - const char * descr = box3d_side_axes_string(SP_BOX3D_SIDE(i->data)); + SPObject *obj = reinterpret_cast(i->data); // TODO unsafe until Selection is refactored. + Box3DSide *side = dynamic_cast(obj); + if (side) { + const char * descr = box3d_side_axes_string(side); if (descr != NULL) { prefs->mergeStyle(Glib::ustring("/desktop/") + descr + "/style", css_write); } -- cgit v1.2.3 From 4f98972d426507aaceb1c55d5fdee5b258999445 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Fri, 17 Oct 2014 15:40:53 -0400 Subject: Small warning cleanup (bzr r13341.1.277) --- src/2geom/numeric/symmetric-matrix-fs-operation.h | 2 +- src/2geom/numeric/symmetric-matrix-fs-trace.h | 2 +- src/style-internal.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/2geom/numeric/symmetric-matrix-fs-operation.h b/src/2geom/numeric/symmetric-matrix-fs-operation.h index 5222d2734..37ece56ae 100644 --- a/src/2geom/numeric/symmetric-matrix-fs-operation.h +++ b/src/2geom/numeric/symmetric-matrix-fs-operation.h @@ -44,7 +44,7 @@ namespace Geom { namespace NL { template inline -SymmetricMatrix adj(const ConstBaseSymmetricMatrix & S) +SymmetricMatrix adj(const ConstBaseSymmetricMatrix & /*S*/) { THROW_NOTIMPLEMENTED(); return SymmetricMatrix(); diff --git a/src/2geom/numeric/symmetric-matrix-fs-trace.h b/src/2geom/numeric/symmetric-matrix-fs-trace.h index 099c834a8..dbabecf6e 100644 --- a/src/2geom/numeric/symmetric-matrix-fs-trace.h +++ b/src/2geom/numeric/symmetric-matrix-fs-trace.h @@ -74,7 +74,7 @@ template struct trace { static - double evaluate (const ConstBaseSymmetricMatrix & S) + double evaluate (const ConstBaseSymmetricMatrix & /*S*/) { THROW_NOTIMPLEMENTED(); return K; diff --git a/src/style-internal.h b/src/style-internal.h index 9158ed60e..5a853fcef 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -387,7 +387,7 @@ class SPIString : public SPIBase { public: SPIString() : SPIBase( "anonymous_string" ), value(NULL) {}; - SPIString( Glib::ustring name, gchar* value_default_in = NULL ) : + SPIString( Glib::ustring name, gchar const* value_default_in = NULL ) : SPIBase( name ) , value(NULL) , value_default(NULL) { value_default = value_default_in?g_strdup(value_default_in):NULL; }; -- cgit v1.2.3